课程设计一
思路
1 (子程序一) 首先调整要显示的区域的背景色
2 (子程序二) 将年份按照指定格式显示再屏幕上(这个子程序比较简单)
3 (子程序三) 将总收入dd(double word)和员工总数dw(define word)转化位字符串 并存入指定的代码段
每个字符串以数值0结尾,注意不是带引号的0 及’0’
以0为结尾来判断一个字符串是否结束 来决定是否需要换行
同时需要记录转化后所有的字符的总个数
4 (子程序四) 将指定段内的字符串按指定格式显示再屏幕
5 (子程序五) 计算每年员工平局收入
不足之处
1 开辟的三个数据段newdata1 newdata2 newdata3 大小是自己随意设定的值 浪费了很多空间
(MD 烧脑两周,总算搞完了,开始新的征程了 第十一章 走起…)
assume cs:codesg
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,97497,140417,197514 ;注意这些数据在内存中就还是二进制的数值 需要转化为字符串 dd double word 占4个字节
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
newdata1 segment
db 4000 dup ('0') ;总收入字符数据域
newdata1 ends
newdata2 segment
db 4000 dup ('0') ;员工总数字符域
newdata2 ends
newdata3 segment
db 4000 dup ('0') ;员工总数字符域
newdata3 ends
codesg segment
start: ;整体思路为1 移动字符串同时转化为要显示的字符串 2 指定格式展示
mov ax,data
mov ds,ax
call makeblack ;将显示区域置为黑色背景
call showyear ;将年份按指定格式显示
call movsumnandne ;将总收入转化dd类型数据为字符并存入newdata段 这里是每转化一个就显示一个 如果连续存放到newdata段 无法判断字符的结束
call movdiv ;计算每年的平均值 并转化为字符串从到指定的newdata段
mov ax,4c00h
int 21h
;-------------------------movdiv start-------------------------------------------
;功能 计算每年的员工平均收入 并将平均收入转化为字符串存到newdata3段 同时将newdata3段数据按指定格式显示到屏幕
movdiv:
push cx
push bx
push ax
push dx
push es ;tdoc_dd会用到新的栈
push ds
push di
mov bx,data
mov ds,bx
mov bx,newdata3 ;将转换的字符串存到newdata3段 newdata3-096c
mov es,bx ;es:[0]要存放的转化后字符串地址
mov cx,21 ;21年的总收入 循环21次
mov bx,0 ;ds:[84]指向数据的第二部分 总收入
mov si,0 ;使用si来 记录当前newdata1段的存放的数的起始地址,不能每次都从起始地址为0的地方存放 会覆盖原来数据
mov di,0 ;除数的索引
s9: mov ax,ds:[bx+84] ;ax存低16位
mov dx,ds:[bx+84+2] ;dx存高16位 注意这里是+2 因为dd占4个字节
call divav ;调用子程序 计算平局值
call tdoc_dd ;将dd类型的平均收入转化为字符串 并存放到newdata段 参数 1、ax第16为 2、dx高16位 3、si newdata段的存放位置标 4、 di data段取数的位置 5、bx 将转化的字符串存放到新的数据段
add bx,4 ;一次循环占4个字节
add di,2
loop s9
mov di,74 ;屏幕显示的起始地址
call showdoc ;注意si已经指向到了 newdata1段的末尾 无需再减2操作 将newdata1段转化的dd字符串 按指定的格式显示到屏幕
pop di
pop ds
pop es
pop dx
pop ax
pop bx
pop cx
ret
;-------------------------movdiv end-------------------------------------------------
;-------------------------divav start-------------------------------------------------
divav: ;计算平均值 不能除法溢出 结果高16位放到dx中 低16位放到ax中
;push ax ;ax和dx的值需要作为tdoc_dd子程序的参数传入 所以不能还原位原来的值
;push dx
push bx
push cx
push di
push ds
push ax
push dx
mov dx,0
pop ax ;将dx值赋值给ax同时将dx置为0 防止除法溢出
div word ptr ds:[di+168] ;ax存商 dx存放余数
mov bx,ax ;暂存第一次的商 第一次的商就是最终结果的高十六位
pop ax
div word ptr ds:[di+168] ;这时dx中存放了第一的余数最为第二次除法的高十六位 ax存商 dx存放余数
mov dx,bx ;第一的结果存入到dx中 最终结果 dx存放高16为 ax存放低16为
pop ds
pop di
pop cx
pop bx
;pop dx
;pop ax
ret
;-------------------------divav end-------------------------------------------------
;-------------------------movsumnandne start-------------------------------------------
;功能 将总收入及员工总数转化为字符串存到newdata1和newdata2段 同时按指定格式显示到屏幕
movsumnandne:
push cx
push bx
push ax
push dx
push es ;tdoc_dd会用到新的栈
push ds
push di
mov bx,data
mov ds,bx
mov bx,newdata1 ;将转换的字符串存到newdata1段 newdata1-0778
mov es,bx ;es:[0]要存放的转化后字符串地址
mov cx,21 ;21年的总收入 循环21次
mov bx,84 ;ds:[84]指向数据的第二部分 总收入
mov si,0 ;使用si来 记录当前newdata1段的存放的数的起始地址,不能每次都从起始地址为0的地方存放 会覆盖原来数据
s4: mov ax,ds:[bx] ;ax存低16位
mov dx,ds:[bx+2] ;dx存高16位 注意这里是+2 因为dd占4个字节
call tdoc_dd ;调用子程序 将dd转化为字符串 并存放到newdata段 参数 1、ax第16为 2、dx高16位 3、si newdata段的存放位置标 4、 di data段取数的位置 5、bx 将转化的字符串存放到新的数据段
add bx,4 ;一次循环占4个字节
loop s4
mov di,24 ;屏幕显示的起始地址
call showdoc ;注意si已经指向到了 newdata1段的末尾 无需再减2操作 将newdata1段转化的dd字符串 按指定的格式显示到屏幕
mov bx,newdata2 ;将转换的字符串存到newdata2段 newdata2-0872
mov es,bx
mov cx,21 ;21年的员工总数 循环21次
mov bx,168 ;ds:[168]指向数据的第三部分 员工总数
mov si,0 ;使用si来 记录当前newdata2段的存放的数的起始地址,不能每次都从起始地址为0的地方存放 会覆盖原来数据
s5: mov ax,ds:[bx] ;ax存低16位
mov dx,0 ;dx存高16位 注意这里是+2 因为dw占4个字节
call tdoc_dd ;调用子程序 将dd转化为字符串 并存放到newdata段
add bx,2 ;一次循环占2个字节
loop s5
mov di,50 ;屏幕显示的起始地址
call showdoc ;注意si已经指向到了 newdata2段的末尾 无需再减2操作 将newdata2段转化的dd字符串 按指定的格式显示到屏幕
pop di
pop ds
pop es
pop dx
pop ax
pop bx
pop cx
ret ;子程序总收入显示完成
;-------------------------movsumnandne start-------------------------------------------
;-------------------------tdoc_dd start-------------------------------------------
;参数 1、ax低16为 2、dx高16位 3、si es:[si] newdata段的存放位置标 4、 di data段取数的位置 5、es将转化的字符串存放到新的数据段
;返回值 si要作为返回值使用 不能存放到栈中 因为再newdata1和newdata2段 时连续存放数据的
tdoc_dd: ;将ax和dx中数据 用取10的余数的方法转化位字符,注意除法的溢出问题
push cx
push bx
push dx
push ax
push di ;存放之要循环的总收入 员工总数 的位置 子程序中用来统计字符串的长度
push es ;es:[si] 用来指向存放 newdata1 newdata2段数据
mov di,0
s6: push ax
push dx
pop ax ;dx中值放到ax中,做第一次的除法
mov dx,0 ;高16未置0防止除法溢出
mov cx,10
div cx ;dx存余数,ax存商
mov bx,ax ;第一次的结果也就是商的高16位暂时保存到bx中
pop ax ;从栈中去除低16位
div cx ;dx存余数,ax存商
mov cx,dx ;保存余数
mov dx,bx ;将第一的结果从bx中去除放到dx中
add cx,30h ;将余数转位数字,规律加30H
push cx ;字符倒叙存放,出栈时正好正序为数字,注意这里实际上只有cl中有值 ch中值位0
mov cx,ax ;判断商是否为0
inc di ;长度统计
jcxz ok1 ;商为0时跳出循环 而不是余数为0,结束子程序
jmp short s6 ;正好dx存高16位,ax存低16位 形成一个循环
ok1: mov cx,di ;将得到的字符串正序存放,以0结尾 注意这里是以0结尾 不是'0' di字符串的长度
s7: pop es:[si] ;字符串正序存放到newdata段
add si,2 ;一次占两个字节 这里的si需要覆盖主程序的si 因为下次再执行子程序时 newdata段的数据是连续存放的
loop s7
mov word ptr es:[si],0 ;在newdata段的末尾加上0 表示结束 注意这里不是'0'
add si,2 ;必须要加2操作 让si指向末尾 且取数操作时 不需要减2来判断长度 本身就已经时 总长度了 需要加2防止 下一次存放时覆盖了末尾的0
pop es
pop di
pop ax
pop dx ;这里的出栈会和字符串数据有冲突,不能直接结束子方法
pop bx
pop cx
ret ;子程序tdoc_dd结束 总收入显示完成
;-------------------------tdoc_dd end----------------------------------------------
;-------------------------showdoc start-------------------------------------------
;功能 显示newdata段的数据到屏幕上
showdoc: ;需要两个判断 判断是否换行 判断是否到达末尾
push es ;es:[0] 指向newdata1 或 newdata2的起始地址
push ds
push bx
push di ;di 屏幕显示的起始地址
push si ;si这时指向newdata段的末尾,程序结束时必须要还原si 这个很重要
push cx
push ax
mov ax,di ;将屏幕显示的起始地址放到ax中 换行的时候需要重置di起始值
push si ;用来判断是否到达newdata段的末尾
mov bx,0b800h
mov ds,bx
mov bx,0 ;行数
mov cx,si ;cx先存newdata段字符串的总长度
mov si,0 ;newdata段的起始地址
s8: pop cx ;这里取得值时刚才push si的值 优先判断是否到达newdata段的末尾,在判断是否需要换行 如果先判断换行 最后一次取数会越界
jcxz ok3 ;如果到达了newdata存放字符串数据末端 结束字符串,如果没有 取出当前位置的字符
push cx
mov cx,es:[si] ;取出一个字符,注意换行操作过程中di的值不能动
jcxz ok4 ;判断是否需要换行,到达一个字符串的结尾 需要取下一个字符串
mov byte ptr ds:[bx+di],cl ;指定区域显示字符
mov byte ptr ds:[bx+di+1],00001011b ;设置字体颜色为绿色高亮
add di,2 ;di的数值需要每次加2
add si,2
pop cx
sub cx,2 ;已经取出一个数了 需要减少2个字节
push cx
jmp short s8
ok4: add bx,160
mov di,ax ;换行需要重置di为起始地址 注意不是以0起始的
add si,2
pop cx
sub cx,2 ;换行表示一个字符串结束 newdata的指向也许要改变
push cx
jmp short s8
ok3: pop ax
pop cx
pop si
pop di
pop bx
pop ds
pop es
ret ;子程序showsumn结束
;-------------------------showdoc end-------------------------------------------
;-------------------------showyear start-------------------------------------------
showyear:
push ax
push si
push bx
push es
push cx
mov si,0
mov cx,21
mov ax,0b800h
mov es,ax
mov bx,0
mov ax,data
mov ds,ax
s3: mov al,[si] ;取除第一个字
mov byte ptr es:[bx],al
mov byte ptr es:[bx+1],00001011b ;设置字体颜色为绿色高亮
mov al,[si+1] ;取除第二个字
mov byte ptr es:[bx+2],al ;取除第二个字
mov byte ptr es:[bx+3],00001011b
mov al,[si+2]
mov byte ptr es:[bx+4],al ;取除第三个字
mov byte ptr es:[bx+5],00001011b
mov al,[si+3]
mov byte ptr es:[bx+6],al ;取除第四个字
mov byte ptr es:[bx+7],00001011b
add si,4
add bx,160 ;第二行的起始地址160 每行80个字 160个字节
loop s3
pop cx
pop es
pop bx
pop si
pop ax
ret ;子程序showyear结束 年份显示完成
;-------------------------makeblack end-------------------------------------------
;-------------------------makeblack start-------------------------------------------
makeblack: ;背景色黑白相间
push cx
push si
push ax
push ds
push bx
mov ax,0b800h
mov ds,ax
mov cx,10 ;外层循环 循环10次 置为黑白相间
mov bx,0
s1: push cx ;放入栈中暂存这个数据
mov cx,80 ;25*160 共4000个字节
mov si,0
s2: mov byte ptr [bx+si],20h ;字符设置为空格
mov byte ptr [bx+si+1],0000000b ;背景色设置为黑色
mov byte ptr [bx+si+160],20h ;字符设置为空格
mov byte ptr [bx+si+161],0000000b ;背景色设置为白色 01110000b
add si,2
loop s2
add bx,320
pop cx
loop s1
pop bx
pop ds
pop ax
pop si
pop cx
ret ;子程序makeblack 背景色显示调整完毕
;-------------------------makeblack end-------------------------------------------
codesg ends
end start