第一步、调整 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
最终测试结果
相关文章
「汇编语言 第 3 版 王爽」- 参考答案:实验 7 寻址方式在结构化数据访问中的应用
「汇编语言 第 3 版 王爽」- 参考答案:实验 16 编写包含多个功能子程序的中断例程
「汇编语言 第 3 版 王爽」- 参考答案:实验 14 访问 CMOS RAM
「汇编语言 第 3 版 王爽」- 参考答案:实验 15 安装新的 int 9 中断例程
「汇编语言 第 3 版 王爽」- 参考答案:检测点 11.2
「汇编语言 第 3 版 王爽」- 参考答案:检测点 13.1
「汇编语言 第 3 版 王爽」- 参考答案:检测点 16.2
「汇编语言 第 3 版 王爽」- 参考答案:实验 13 编写、应用中断例程
「汇编语言 第 3 版 王爽」- 参考答案:检测点 11.3
「汇编语言 第 3 版 王爽」- 参考答案:实验 17 编写包含多个功能子程序的中断例程
「汇编语言 第 3 版 王爽」- 参考答案:实验 11 编写子程序
「汇编语言 第 3 版 王爽」- 参考答案:实验 10 编写子程序
「汇编语言 第 3 版 王爽」- 参考答案:检测点 14.1
「汇编语言 第 3 版 王爽」- 参考答案:实验 12 编写 0 号中断的处理程序