|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 kingfon 于 2017-12-7 13:16 编辑
;思路:将原始数据通过字符转换程序写入table表中,再通过显示子程序将table表中的数据写入显存
;table表内存空间计算,每行字节数,年份5个字节(包含0),收入8个字节(包含0)
;员工数6个字节(包含0),平均收入6个字节(包含0),合计25字节,共21行
assume cs:code, ds:data
data segment
db '1975','1976','1977','1978','1979','1980','1981','1982','1983','1984'
db '1985','1986','1987','1988','1989','1990','1991','1992','1993','1994'
db '1995'
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514,345980
dd 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,11542,14430,15257,17800
data ends
table segment
db 525 dup (0) ;25*21=525字节
table ends
stack segment
dw 30 dup (0)
stack ends
code segment
start:
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,60
mov ax,table
mov es,ax
mov bx,0 ;原始数据年份起始偏移地址,每个数据相差4个字节,总收入也是相差4个字节
mov si,0 ;原始数据员工数起始偏移地址,每个数据相差2个字节
mov di,0 ;table表中起始位置偏移地址
mov cx,21
put: ;将数据处理放入table表
mov ax,[bx]
mov dx,[bx+2]
mov es:[di],ax
mov es:[di+2],dx ;将年份写入table表中对应位置
mov ax,[bx+84] ;将收入(4个字节)通过子程序处理成阿拉伯数字再存入目标位置,取低16位存入ax
mov dx,[bx+86] ;取高16位存入dx
push di ;di参与主程序的循环,接下来要做为table表的偏移地址传给子程序,要暂存起来
add di,5 ;将写入table表中的起始位置给子程序
call dtoc ;调用子程序
pop di ;调用结束还原di的值
mov ax,[si+168] ;将员工数(2个字节)通过子程序处理成阿拉伯数字再存入目标位置,同上
mov dx,0 ;原始数据为16位,dx置0
push di
add di,13
call dtoc
pop di
mov ax,[bx+84] ;计算平均收入(2个字节)并通过子程序处理成阿拉伯数字再存入目标位置
mov dx,[bx+86]
push cx ;将要调用的子程序需用cx作为除数做为参数,返回的cx值是余数,先将循环次数暂存
mov cx,[si+168]
call divdw ;调用除法子程序得出商低16放ax,高16位放dx
push di
add di,19 ;指定平均收入数据在table表中的偏移地址
call dtoc ;将平均收入数据写入table表中
pop di
pop cx ;从栈中取回循环次数
add di,25 ;定位table表中下一条记录起始位置,每条记录长度25字节
add bx,4 ;改变下次循环年份的起始位置,每次递增4个字节
add si,2 ;改变下次循环员工数的起始位置,每次递增2个字节
loop put
mov ax,table ;输出显示器的程序,table表作为原始数据
mov ds,ax
mov si,0 ;从table表中第0个字节开始读取
mov cx,21 ;输出21行
mov dh,3 ;从第3行开始
mov al,7 ;定义颜色,黑底白字
display: ;将数据输出到显示器
mov dl,15 ;每行从第15列开始
call show_str ;输出年份
add dl,13 ;列与列间距
push si ;si将作为参数传给子程序,暂存
add si,5 ;定位table表中总收入起始位置偏移地址
call show_str ;输出总收入
add dl,13
add si,8 ;定位table表中员工数起始位置偏移地址
call show_str ;输出员工数
add dl,13
add si,6 ;定位table表中平均收入起始位置偏移地址
call show_str ;输出平均收入
inc dh ;换行
pop si ;一行输出结束,还原si值
add si,25 ;取table表下一条记录起始偏移地址
loop display
mov ax, 4c00h
int 21h
show_str: ;dh存放行数1-25,dl存放列数1-80,al存放颜色值,si存放原始数据位置偏移地址
push cx ;子程序用到的寄存器值暂时入栈
push si
push dx
push ax
push bx
mov bh,al ;将颜色值存入bh
push ax ;接下来要更换显存的段地址,要用到ax,先暂存ax值
mov ax,0b800h ;将显存段地址给es
mov es,ax
pop ax ;之前存了颜色值,出栈
dec dh ;计算机用0做为起始地址,此处dh-1
dec dl ;同上
mov al,160 ;计算目标行数、列数的起始偏移地址,每行160个字节,颜色80个字节,要显示的字符80个字节
mul dh ;行数*160,得到行数
mov di,ax ;将行的位置存入di
mov al,2 ;计算列的起始位置,每个字符2个字节
mul dl ;列数*2
add di,ax ;把结果与di相加,最终定位目标偏移地址
write:
mov ch,0
mov cl,[si] ;将原始字符的ascii值给cx,遇0写入结束
jcxz ok
mov bl,[si] ;将字符放在bl中,bh此时存放颜色
mov es:[di],bx ;写入显存
inc si ;原始数据偏移地址+1
add di,2 ;显存偏移地址+2
jmp short write
ok:
pop bx ;子程序运行前的寄存器值依次出栈
pop ax
pop dx
pop si
pop cx
ret
dtoc: ;将给定的数字以阿拉伯数字的形式存放在内存中,参数ax存放原数据的低16位,dx存放原数据的高16位,di为目标位置偏移地址的开始位置
push cx
push bx
push di
mov bx,0 ;用于存放取余的次数
chu:
mov cx,10 ;除数10,取余
call divdw ;为避免运算过程中出现除法溢出,除法运算调用处理过除法溢出的子程序
push cx ;将余数进栈
push ax ;先将ax暂存
or ax,dx ;因子程序返回的商为32位,为证明商是否为0,不能单纯判断低16位的值是否为0,dx中高16位的数也要做为判断依剧
mov cx,ax ;上步运算结果若为0,表示得到的商为0,取余过程结束
pop ax ;若执行or ax,dx之后,ax中的值不为0就要继续取余,所以还原ax的值
inc bx ;取余次数加1
jcxz s2 ;取到商为0时,跳到s2
jmp short chu ;商不为0继续取余
s2:
mov cx,bx ;将取余的次数作为写入数字的次数,原数几位数就写几次
s3:
pop ax ;将前面取余的数出栈
add al,30h ;除数是10,余数为0-9
mov es:[di],al ;存入di位置,单个字节
inc di ;指向下一个地址
loop s3
pop di
pop bx
pop cx
ret
divdw: ;被除数ax存放低16位,dx存放高16位,cx存放除数,运算结束ax存放商,dx存放余数
push bx ;因这个子程序要用到bx,所以开始前先将其暂存,以免修改了调用者的数值
push ax ;低16位暂存
mov ax,dx ;高16位移到ax,即右移16位
mov dx,0 ;dx置0,确保以下的除法不会溢出
div cx ;16位除法,结果ax为商,dx为余数
mov bx,ax ;将商暂存在dx中
pop ax ;原低16位放回ax中,此时dx中的数是余数,小于除数,所以再进行1次16位除法也不会溢出
div cx
mov cx,dx ;第二次除法后就有结果了,将余数给cx返回给调用者
mov dx,bx ;第一次的除法实际是右位16位再除,得到的商最后还是放回高16位
pop bx
ret
code ends
end start |
|