# 2020-09-30 #「汇编语言 第 3 版 王爽」- 参考答案:课程设计 1
第一步、调整 dtoc 程序
在实验 10 中编写 dtoc 程序能够显示数值,但是存在以下问题:
1)原有 dtoc 只能支持 16 位被除数,但是收入数据是 32 位,需要添加支持;
2)除法存在溢出问题,并且原有 dtoc 程序直接使用 DIV 指令,因此可能溢出;
首先调整 dtoc 程序,使其能处理除法溢出问题,这只需使用我们之前编写的 divdw 函数即可。为了清晰问题,我们去掉原有注释:
; 将数值转化为字符换(使用函数 divdw 处理溢出) ; @desc 参数:dx => 数据高位,ax => 数据低位,ds:si => 数据写入地址 ; @desc 结果:保存到 ds:si 中,并以 0 结尾 dtoc: push ax push bx push cx push dx ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 计算数值字符串(重点调整的部分 开始) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; mov bx, 0 push bx ; 无需理解此步骤,往下看就知道了 _do_some_stuff_start: mov dx, dx ; 调用 divdw 函数,高位 dx mov ax, ax ; 调用 divdw 函数,地位 ax mov cx, 10 ; 调用 divdw 函数,除数 cx call divdw ; 调用 divdw 函数 add cx, 30H ; 余数加 30H 得到对应的字符 push cx ; 计算结果与显示结果是相反的,所以先入栈,以后再弹出 mov cx, 0 ; 开始判断商是否为零,以决定是否进入下轮 or cx, ax or cx, dx jcxz _do_some_stuff_end ; 如果 cx 为零,则表示 ax dx 都为 零,即商为零 jmp short _do_some_stuff_start _do_some_stuff_end: ; 至此,数字对应的字符串都在栈中。“栈底”为零,作为 pop 边界 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 计算数值字符串(重点调整的部分 结束) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; mov bx, 0 save_to_dateseg: pop cx jcxz _append_zero_to_sting mov ds:[bx], cl inc bx jmp short save_to_dateseg _append_zero_to_sting: mov byte ptr ds:[bx], 0 dtoc_end: pop dx pop cx pop bx pop ax ret
第二步、某些注意事项
在这个课程设计之前,我们已经编写 show_str divdw dtoc 程序,我们会直接利用这些程序,而不会再编写新程序。
这就带来某些限制,比如说:
1)我们想将 单行数据写入内存 之后,然后再调用 show_str 函数,这样总共调用 21 次。但是 dtoc 程序生成的字符串是包含零字符的,因此没有办法生成单个字符串。因此我们只能写一个列之后,就立刻调用 show_str 函数。
第三步、编写 append_space 程序
根据题目要求,每个字段(列)的宽度是固定的。因此我们需要某种函数,该函数可以追加空格来调整字符串的宽度:
; 将内存中的字符串扩展到指定长度 ; @desc ds:si => 字符串地址,bx => 字符串 最终宽度 append_space: push cx push si push bx _find_zero_start: mov ch, 0 mov cl, ds:[si] jcxz _find_zero_start_end inc si sub bx, 1 jmp short _find_zero_start _find_zero_start_end: mov cx, bx mov bl, 20H append_space_start: mov ds:[si], bl inc si loop append_space_start ; 为字符串结尾 mov bl, 0 mov ds:[si], bl pop bx pop si pop cx ret
第四步、最终的程序
由于篇幅原因,我们去除 show_str divdw 等函数的注释,只保留主体的注释:
assume cs:codeseg
datasg 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,220H,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
datasg ends
table segment
db 160 dup(0)
table ends
codeseg segment
start:
mov dh, 1 ; 设置显示行号
; 21 条数据,21 次循环
mov cx, 21
; 用做下标,获取数据
mov bp, 0
mov di, 0
; 数据写入 table 段,ds 代表号
mov ax, table
mov ds, ax
mov si, 0
; 每次循环都要取年份、收入、人数数据
mov ax, datasg
mov es, ax ; 年份 => es:[0],收入 => es:[84],人数 => es:[168]
loop_01:
push cx
mov cx, 2 ; 设置显示颜色
; mov dh, dh ; 设置显示行号
; 写入年份 => 显存
mov ax, es:0[bp] ; 开始/初始化字符串
mov ds:[0], ax
mov ax, es:0[bp+2]
mov ds:[2], ax
mov al, 0
mov ds:[4], al
mov bx, 20 ; 开始/调整字符串宽度 => 20
call append_space
mov dl, 1 ; 开始/显示字符串
call show_str
; 写入收入
push dx ; 开始/初始化字符串
mov ax, es:84[bp]
mov dx, es:84[bp+2]
call dtoc
pop dx
mov bx, 20 ; 开始/调整字符串宽度 => 20
call append_space
mov dl, 21 ; 开始/显示字符串
call show_str
; 写入人数
push dx ; 开始/初始化字符串
mov dx, 0
mov ax, es:168[di]
call dtoc
pop dx
mov bx, 20 ; 开始/调整字符串宽度 => 20
call append_space
mov dl, 41 ; 开始/显示字符串
call show_str
; 写入平均收入
push dx ; 开始/初始化字符串
push cx
mov dx, es:84[bp+2]
mov ax, es:84[bp]
mov cx, es:168[di]
call divdw ; dx, ax...cx
call dtoc
pop cx
pop dx
mov bx, 20 ; 开始/调整字符串宽度 => 20
call append_space
mov dl, 61 ; 开始/显示字符串
call show_str
; 调整变量,进入下一轮循环
add bp, 4 ; 取值的索引需要调整
add di, 2
inc dh ; 行号需要调整
; 进入下一轮循环
pop cx
loop loop_01
mov ax, 4c00h
int 21h
; 将内存中的字符串扩展到指定长度
; @desc ds:si => 字符串地址,bx => 字符串 最终宽度
append_space:
push cx
push si
push bx
_find_zero_start:
mov ch, 0
mov cl, ds:[si]
jcxz _find_zero_start_end
inc si
sub bx, 1
jmp short _find_zero_start
_find_zero_start_end:
mov cx, bx
mov bl, 20H
append_space_start:
mov ds:[si], bl
inc si
loop append_space_start
; 为字符串结尾
mov bl, 0
mov ds:[si], bl
pop bx
pop si
pop cx
ret
; 将数值转化为字符换(使用函数 divdw 处理溢出)
; @desc 参数:dx => 数据高位,ax => 数据低位,ds:si => 数据写入地址
; @desc 结果:保存到 ds:si 中,并以 0 结尾
dtoc:
push ax
push bx
push cx
push dx
mov bx, 0
push bx
_do_some_stuff_start:
mov dx, dx
mov ax, ax
mov cx, 10
call divdw
add cx, 30H
push cx
mov cx, 0
or cx, ax
or cx, dx
jcxz _do_some_stuff_end
jmp short _do_some_stuff_start
_do_some_stuff_end:
mov bx, 0
save_to_dateseg:
pop cx
jcxz _append_zero_to_sting
mov ds:[bx], cl
inc bx
jmp short save_to_dateseg
_append_zero_to_sting:
mov byte ptr ds:[bx], 0
dtoc_end:
pop dx
pop cx
pop bx
pop ax
ret
; 显示字符串
; @desc 参数:dh => 行,dl => 列,cl => 颜色,ds:si => 字符串地址,以 0 结尾
show_str:
_show_str_start:
push ax
push bx
push dx
push cx
push es
push si
mov ax, 0B800H
mov es, ax
sub dh, 1
mov al, dh
mov bl, 0A0H
mul bl ; ax
sub dl, 1
add dl, dl
mov dh, 0 ; dx
add dx, ax
mov bx, dx
mov ah, cl ; 颜色
_copy_char:
mov cl, ds:[si]
mov ch, 0
jcxz _show_str_end
mov al, cl
mov es:[bx], ax
inc si
add bx, 2
jmp short _copy_char
_show_str_end:
pop si
pop es
pop cx
pop dx
pop bx
pop ax
ret
; 解决除法溢出问题
; @desc 参数:dx => 被除数高位,ax => 被除数低位,cx => 除数
; @desc 结果:dx => 结果高位,ax => 结果低位,cx => 余数
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
codeseg ends
end start
最终测试结果
参考文献
K4NZ / 参考答案:课程设计 1
CSDN/汇编语言王爽第三版答案
百度文库/汇编语言实验答案 (王爽)
浙公网安备 33010602011771号