课程设计1 :题目描述
任务:将实验7中的Power Idea公式的数据按照图10.2所示的格式在屏幕上显示出来。
实际运行
完整源码
assume cs:code,ss:stack
data segment
db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
db '1993','1994','1995'
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
data ends
cahe segment
db 20 dup (0)
cahe ends
stack segment
db 100 dup (0)
stack ends
code segment
start: mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,100
mov ax,0B800H
mov es,ax
mov si,01E0H
mov bx,0
mov di,0
mov cx,21
s: call year
call mm
call avg
call summ
add si,160
add bx,4
add di,2
loop s
mov ax,4c00H
int 21H
year: mov al,02H
mov ah,[bx+0]
mov es:[si+0],ah
mov es:[si+1],al
mov ah,[bx+1]
mov es:[si+2],ah
mov es:[si+3],al
mov ah,[bx+2]
mov es:[si+4],ah
mov es:[si+5],al
mov ah,[bx+3]
mov es:[si+6],ah
mov es:[si+7],al
ret
mm: push di
push bx
push ds
push cx
mov ax,[0A8H+di]
mov bx,cahe
mov ds,bx
mov di,0
call dtoc
mov al,02H
mov di,0
mov bx,0
mm_show: mov cl,ds:[di]
mov ch,0
jcxz mm_end
mov es:[si+2EH+bx],cl
mov es:[si+2FH+bx],al
add bx,2
add di,1
jmp short mm_show
mm_end: pop cx
pop ds
pop bx
pop di
ret
dtoc: push ax
push bx
push cx
push dx
push ds
push di
push ss
mov dx,0
mov bx,0AH
s0: mov cx,ax
jcxz ok
div bx
add dx,30H
mov ds:[di],dx
mov dx,0
inc di
jmp short s0
ok: mov bx,stack
mov ss,bx
mov bx,di
mov cx,di
mov di,0
s1: push ds:[di]
inc di
loop s1
mov cx,bx
mov di,0
s2: pop ds:[di]
inc di
loop s2
mov bx,0
mov ds:[di],bx
mov di,bx
pop ss
pop di
pop ds
pop dx
pop cx
pop bx
pop ax
ret
avg: push di
push bx
push ds
push cx
push ax
push dx
mov ax,[54H+bx]
mov dx,[56H+bx]
div word ptr ds:[0A8H+di]
mov bx,cahe
mov ds,bx
mov di,0
call dtoc
mov al,02H
mov di,0
mov bx,0
avg_show: mov cl,ds:[di]
mov ch,0
jcxz avg_end
mov es:[si+44H+bx],cl
mov es:[si+45H+bx],al
add bx,2
add di,1
jmp short avg_show
avg_end: pop dx
pop ax
pop cx
pop ds
pop bx
pop di
ret
summ: push di
push bx
push ds
push cx
push ax
push dx
mov ax,[54H+bx]
mov dx,[56H+bx]
mov bx,cahe
mov ds,bx
mov di,0
call dtoc2
mov al,02H
mov di,0
mov bx,0
summ_show: mov cl,ds:[di]
mov ch,0
jcxz summ_end
mov es:[si+14H+bx],cl
mov es:[si+15H+bx],al
add bx,2
add di,1
jmp short summ_show
summ_end: pop dx
pop ax
pop cx
pop ds
pop bx
pop di
ret
dtoc2: push ax
push bx
push cx
push dx
push ds
push di
push ss
ss2: mov cx,ax
jcxz ss3
mov cx,0AH
call divdw
add cx,30H
mov ds:[di],cx
inc di
jmp short ss2
ss3: mov cx,dx
jcxz sok2
mov cx,0AH
call divdw
add cx,30H
mov ds:[di],cx
inc di
jmp short ss2
sok2: mov bx,stack
mov ss,bx
mov bx,di
mov cx,di
mov di,0
ss12: push ds:[di]
inc di
loop ss12
mov cx,bx
mov di,0
ss22: pop ds:[di]
inc di
loop ss22
mov bx,0
mov ds:[di],bx
mov di,bx
pop ss
pop di
pop ds
pop dx
pop cx
pop bx
pop ax
ret
divdw: push bx
push ax
mov ax,dx
mov dx,0
div cx
mov bx,ax
pop ax
div cx
mov cx,dx
mov dx,bx
pop bx
ret
code ends
end start
代码结构
- data段 放全部的数据,之后是 cahe段缓冲区 放数值转换的ASCII码,最后是 stack段栈 空间,其中, cahe段缓冲区 不断被重复填写利用
DS寄存器
读取数据时指向 DATA段
用于子程序dtoc时,指向cahe缓冲区
ES寄存器
全局指向显存 始终为 ES=B800H
- 程序按照行输出数据,最外层的循环体现的就是21行(位于 start段)
mov cx,21
s: call year
call mm
call avg
call summ
add si,160 ;控制换行
add bx,4 ;年份、收入的遍历
add di,2 ;雇员人数的遍历
loop s
- 如何读取数据,以首行 1975 16 3 5举例
DS指向data段
db 型
‘1’ [bx+0]
‘9’ [bx+1]
‘7’ [bx+2]
‘5’ [bx+3]
结合 add bx,4 ;年份、收入的遍历
--------------------------------------------------------------
dd型 32位
16
mov ax,[54H+bx]
mov dx,[56H+bx]
结合 add bx,4 ;年份、收入的遍历
----------------------------------------------------------------
dw型 16位
3
[0A8H+di]
结合 add di,2 ;雇员人数的遍历
几段关键代码说明
- CX 的在会溢出的除法运算中的多重角色(位于dtoc2)
ss2: mov cx,ax
jcxz ss3
mov cx,0AH
call divdw
add cx,30H
mov ds:[di],cx
inc di
jmp short ss2
ss3: mov cx,dx
jcxz sok2
mov cx,0AH
call divdw
add cx,30H
mov ds:[di],cx
inc di
jmp short ss2
溢出的除法运算
商 存到 高16位(DX)、低16位(AX)
余数 存到 (CX)
因此,判断用商是否为零,就要判断两部分,高位以及低位
所以,才有这个嵌套的循环
ss2 看 AX是否为零,如果为零,则需要继续走到ss3 判断 DX是否零;
都为零才是真正的商为零,可以跳转到除法的结束;
AX和DX有任何一个不为0,就需要继续进行除法。
CX 在每次除法运算之后,会存着余数
这个余数正是我们需要求到的位数
在把这个位数保存到缓冲区 cahe之后,
需要重新赋予 CX 作为除数的使命 mov CX,0AH
这样除法才能整除继续下去。
子程序以及关键代码的参考
- show 显示字符串 【实验10 第1题】
https://www.jianshu.com/p/fcf799fb1bec- divdw 会溢出的除法运算【实验10 第2题】
https://www.jianshu.com/p/706c76363800- dtoc 数值显示【实验10 第3题】
https://www.jianshu.com/p/187d1fce3e45- es:[si+X+bx],cl 数据访问 【实验7】
https://www.jianshu.com/p/976b1ac15720
调试过程回忆
- 栈空间不足,导致G命令卡死 DOSBOX
- PUSH POP指令没有一 一对应,导致G命令卡死 DOSBOX
- 会溢出的除法计算,将记录高16位的DX寄存器写成了BX
- 标号命名混乱、重复
- 复制有风险,粘贴需谨慎:一段运行正确的代码,复制粘贴也不一定就对,因为那段看上去可以运行正确的代码可能没有正确地返回寄存器内容,比如,BX在代码段1使用了、但是代码段2不用,这样的话, 在测试只有代码段1、2时,都不会出BUG,一旦复制代码段1的代码到代码段3,BX再次被使用就会暴露出来这个隐藏很深的BUG
代码段1
push ax
push bx
....
pop dx 【这里把应该写的BX写错成了DX】
pop ax
--------------------------------------------------------------------------
代码段3 使用 BX 【就会出现BUG,因为之前BX没有正确返回】
同时如果代码段3 为了方便是复制的代码段1
代码段3 也有
push ax
push bx
....
pop dx 【这里把应该写的BX写错成了DX】
pop ax
如果代码段3,如果代码段3没有使用BX,
后面用到BX的就会把这个隐藏的BUG深深的地传递下去
非常恐怖!
心得
- 大力出奇迹
- 发现从外往里包着写,比从下往上一直抽象要好写很多
- 寄存器真的不够用,真的不够用,真的不够用!