汇编网首页登录博客注册
unixandlinux的学习博客
博客首页博客互动【做检测题】论坛求助

我的博客

个人首页 |  我的文章 |  我的相册 |  我的好友 |  最新访客 |  文章收藏 |  论坛提问 |  友情链接 |  给我留言  
图片载入中
学习动态
最新评论
最新留言
好友圈
友情链接

[2023-07-21 09:38] 课程设计 1(方法 2)

方法 2

table 段中只设置一组字符串,全部用于存储一年的数据。下一年的数据覆盖前一年的数据字符串。
Ubuntu 18.04 系统安装的 DOSBox-X 无法运行,只能在其 DOSBox 中运行;而 Widows 中则两者都可运行

assume cs:code

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'
        ; 以上是表示 21 年的 21 个字符串,每个字符串包含 4 个字符,所以共计 84 个字符,占用 84 字节
        dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
        dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
        ; 以上是表示 21 年公司总收入的 21 个 dword 型数据,每个数据占用 4 字节,所以共计占用 84 字节
        dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
        dw 11542,14430,15257,17800
        ; 以上是表示 21 年公司雇员人数的 21 个 word 型数据,每个数据占用 2 字节,所以共计占用 42 字节
data ends

string segment
        db 10 dup (' ')
        db 0
string ends

code segment

start:        mov ax,data
                mov es,ax
                mov bx,0                ; BX 寄存器存储着 data 段中 db 部分数据的偏移地址起始值,标记年份数据
                mov di,168                ; DI 寄存器存储着 data 段中 dw 部分数据的偏移地址起始值,标记雇员人数数据

                mov dh,3                ; 设置需要在屏幕上显示的字符串的起始行
                mov dl,25                ; 设置需要在屏幕上显示的字符串的起始列

                mov ax,string
                mov ds,ax
                mov si,0                ; SI 寄存器存储着 string 段字符串起始偏移地址

                mov cx,21
; loop u 循环执行 21 次,每次对一年一组数据进行处理
u:                push cx                        ; 将 loop u 循环次数值压栈暂存
                push dx                        ; 将显示字符串的屏幕行、列信息压栈
                mov bp,sp                ; 需要将栈中元素复制使用(非出栈)。由于没有 [sp] 这样的用法,
                                                ; 而 BP 寄存器默认的段寄存器是 SS,因此可以利用 [bp+idata] 来调用栈元素

                ; 复制年份字符串到 string 段
                mov ax,es:[bx]
                mov [si],ax
                mov ax,es:[bx+2]
                mov [si+2],ax
                call show_str
                call cls

                ; 转换年收入数值为数字字符串,并复制到 string 段:先设置参数,再调用 dtoc 子程序
                mov ax,es:[bx+84]                        ; 读取 data 段中年收入值的低 16 位到 AX 寄存器中
                mov dx,es:[bx+84+2]                        ; 读取 data 段中年收入值的高 16 位到 DX 寄存器中
                mov cx,10                                        ; 设置将数值转换为字符串时要用到的除数数值 10,并存入 CX 寄存器
                call dtoc
                add byte ptr [bp],11                ; 更新字符串的屏幕列信息(每个字符串包含 11 个字符,即 11 列)
                mov dx,[bp]                                        ; 恢复 DX 寄存器中存储的显示字符串的屏幕行、列信息
                call show_str
                call cls

                ; 转换雇员人数数值为数字字符串,并复制到 string 段相应位置:先设置参数,再调用 dtoc 子程序
                mov ax,es:[di]                                ; 读取 data 段中雇员人数数值的(低)16 位到 AX 寄存器中
                mov dx,0                                        ; 读取(设置)data 段中雇员人数数值的高 16 位(0)到 DX 寄存器中
                mov cx,10                                        ; 设置将数值转换为字符串时要用到的除数数值 10,并存入 CX 寄存器
                call dtoc
                add byte ptr [bp],11                ; 更新字符串的屏幕列信息(每个字符串包含 11 个字符,即 11 列)
                mov dx,[bp]                                        ; 恢复 DX 寄存器中存储的屏幕显示字符串的行、列信息
                call show_str
                call cls

                ; 计算人均收入,将计算结果转换为字符串并存入 string 段相应位置
                mov ax,es:[bx+84]                        ; 读取 data 段中年收入值的低 16 位到 AX 寄存器中
                mov dx,es:[bx+84+2]                        ; 读取 data 段中年收入值的高 16 位到 DX 寄存器中
                mov cx,es:[di]                                ; 设置 data 段中雇员人数作为除数数值,并存入 CX 寄存器
                call divdw                                        ; 调用 divdw 子程序,计算人均收入
                ; 以上述 divdw 所得的人均收入作为被除数,以数值 10 作为除数,调用 dtoc 子程序,
                ; 将人均收入(被除数)由数值形式转换为字符串形式,并存入 string 段相应位置
                mov cx,10
                call dtoc
                add byte ptr [bp],11                ; 更新字符串的屏幕列信息(每个字符串包含 11 个字符,即 11 列)
                mov dx,[bp]                                        ; 恢复 DX 寄存器中存储的屏幕显示字符串的行、列信息
                call show_str
                call cls

                add bx,4                                        ; 令 es:[bx] 指向 data 段下一年年份字符串的首字符
                add di,2                                        ; 令 es:[di] 指向 data 段下一年的雇员人数值

                pop dx                                                ; 让行、列信息先出栈,是为了执行下一条 pop 指令
                pop cx                                                ; 恢复 loop u 循环次数值
                inc dh                                                ; 让行数自增 1,以便在屏幕上显示下一年的字符串
                mov dl,25                                        ; 重置需要在屏幕上显示的字符串的起始列
                loop u

                mov ax,4c00h
                int 21h

; begin dtoc
dtoc:        push si                                ; 将 ds:[si] 指向 string 段字符串首字符位置信息压栈
                push bx                                ; 将 es:[bx] 指向 data 段年份字符串首字符位置信息压栈
                mov bx,cx                        ; 将除数 10 暂存到 BX 寄存器
                ; 将数值 0 压栈,确定在 ok 标号处进行原序复制数值数字字符串时的最后一个结束符,
                ; 以作为数字字符串的长度界限(注意,数字字符串后面还有空格字符,但这不影响此处的设置)
                mov cx,0
                push cx                                ; 在本 dtoc 子程序的 over 标号处将会通过 pop 操作将该 0 值出栈

mca:        mov cx,ax                        ; 判断被除数的低 16 位值是否为 0
                jcxz mcd                        ; 如果为 0,则跳至标号 mcd 处,以对高 16 位值是否为 0 作进一步判断
                                                        ; 如果不为 0,则调用 divdw 子程序

cd:                mov cx,bx                        ; 恢复 CX 寄存器存储的除数数值
                call divdw                        ; 调用 divdw 子程序,执行能解决“除法溢出”问题的除法运算
                add cx,30h                        ; 将数值转换为字符
                push cx                                ; 将数值的数字字符压栈
                jmp mca                                ; 跳至 mca 标号处,对下一轮除法运算的被除数低 16 位进行是否为 0 的判断

mcd:        mov cx,dx                        ; 判断被除数的高 16 位值是否为 0
                jcxz over                        ; 如果为 0,则跳至 over 标号处恢复数字字符串正常顺序后,返回主程序;
                                                        ; 如果不为 0,则通过其后的 jmp 指令跳至 cd 标号处,调用 divdw 子程序
                jmp cd

over:        ; 将倒序显示的数字字符串恢复为原来数值的数字字符串正常序列
                pop cx                                ; 将数值的数字字符出栈存入 CX 寄存器
                jcxz ok                                ; 如果出栈字符的 ASCII 码值为 0,则说明数值的数字字符串已全部出栈完毕,
                                                        ; 应该跳至 ok 标号处返回主调程序
                mov [si],cl                        ; 将 CL 寄存器中存储的字符转存入 string 段相应位置;注意,不是 CX 寄存器
                inc si                                ; si 后移 1 位,指向 string 段下一个字符的存储位置
                jmp over                        ; 跳至 over 标号处继续执行出栈操作,再次判断出栈字符 ASCII 码值是否为 0

ok:                pop bx
                pop si
                ret
; end dtoc

; begin divdw
; 执行 divdw 子程序前已预先设置参数:
; (ax) = 被除数的低 16 位                (dx) = 被除数的高 16 位                (cx) = 除数
; 执行 divdw 子程序后返回的情况(指执行返回前的系列 pop 指令和 ret 指令之前):
; (ax) = 商数的低 16 位                        (dx) = 商数的高 16 位                (cx) = 余数
divdw:        push bx
                push bp
                ; 执行高 16 位被除数与原 16 位除数之间的除法运算
                mov bx,ax                        ; 将被除数的低 16 位暂存到 BX 寄存器
                mov ax,dx                        ; 将被除数的高 16 位转存到 AX 寄存器
                mov dx,0                        ; 设置原被除数高 16 位进行的除法运算所需要使用的高 16 位被除数
                div cx                                ; 执行高 16 位除法运算,计算结果的余数默认存储于 DX 寄存器
                mov bp,ax                        ; 将高 16 位除法运算所得的商数转存到 BP 寄存器
                mov ax,bx                        ; 将被除数的低 16 位传送回 AX 寄存器
                div cx                                ; 执行低 16 位除法运算,计算结果的商数默认存储于 AX 寄存器
                mov cx,dx                        ; 将低 16 位除法运算所得的余数(也是最终除法运算的余数)转存到 CX 寄存器
                mov dx,bp                        ; 将高 16 位除法运算所得的商数转存到 DX 寄存器
                ; 经 divdw 子程序上述操作后,AX、DX 和 CX 寄存器分别存储着商数的低 16 位、高 16 位和余数

                pop bp
                pop bx
                ret
; end divdw

; begin show_str
show_str:        push es                                ; 因为本子程序需要将显示缓冲区段地址传送到 ES 寄存器,
                                                                ; 因此需要先将原 ES 段寄存器中存储的 data 段地址压栈,
                        push si
                        push di
                        mov ax,0B800h                ; 再将显示缓冲区第 0 页的段地址存入 ES 段寄存器
                        mov es,ax
                        ; 计算列偏移量,结果存储在 DI 寄存器中
                        dec dl                                ; dec 指令与 inc 指令功能相反,执行自减操作,参检测点 9.3 所述
                        mov ax,2
                        mul dl                                ; 执行 8 位(8-bits)乘法运算,计算结果默认存放在 AX 寄存器中
                        mov di,ax
                        ; 计算行偏移量,将计算结果加到 DI 寄存器中
                        dec dh
                        mov ax,160                        ; 在显示缓冲区需要用一个字内存单元来存储一个字符的信息,
                                                                ; 其中低 8 位存储字符的 ASCII 码,高 8 位存储属性信息,
                                                                ; 因此在屏幕显示一行信息(80 个字符)就需要 160 个字节(80 个字)
                        mul dh                                ; 执行 8 位(8-bits)乘法运算,计算结果默认存放在 AX 寄存器中
                        add di,ax                        ; DI 寄存器中存储着显示缓冲区目标偏移地址

dis:                mov cl,[si]                        ; ds:[si] 存储着需要显示在屏幕的字符串当前的字符 ASCII 码
                        mov ch,0
                        jcxz ok0                        ; 判断当前需要显示的字符的 ASCII 码值是否为 0
                        mov ch,2                        ; 设置需要在屏幕上显示的字符串的属性信息 —— 字体颜色:绿色
                        mov es:[di],cx                ; 将存储着字符信息(ASCII 码)和属性信息的 (cx) 传送到显示缓冲区
                        inc si                                ; 自增 (si),使其指向 string 段中下一个需要显示的字符
                                                                ; 最后 ds:[si] 将指向字符串结束符 '\0'
                        add di,2                        ; 自增 (di),使其指向下一个显示缓冲区中所显示字符的位置
                        jmp short dis

ok0:                pop di
                        pop si
                        pop es
                        ret
; end show_str

; 还原 string 段存储的字符串原始状态,以便在后面的复制数字字符串后,不会保留前面字符串的任何字符
; begin cls
cls:                push si
                        push cx
                        mov cx,0
docls:                mov cl,[si]
                        jcxz ok1
                sub cl,' '
                        jcxz ok1
                        mov byte ptr [si],' '
                        inc si
                        jmp docls
ok1:                pop cx
                        pop si
                        ret
; end cls

code ends

end start
评论次数(0)  |  浏览次数(110)  |  类型(课程设计) |  收藏此文  | 
 
 请输入验证码  (提示:点击验证码输入框,以获取验证码