新人自己分析的,希望大神给予意见~!
任务:
将实验7中的Power idea公司的数据按照下图显示出来
需要重新编写一个子程序
编写一个新的数据到字符串转化的子程序,完成dword型数据到字符串的转化,说明如下:
名称:dtoc_dword(这里我自己取的名称)
功能:将dword型数转变为表示十进制的字符串,字符串以0为结尾符。
参数:(ax)=dword型数据的低16位,(dx)=dword型数据的高16位,ds:si指向字符串的首地址
返回:无
注:在这个子程序中要注意除法溢出的问题,可以用我们在试验10中设计的子程序divdw来解决。
分析
做一个简单的比喻:
我们是搬家公司的,要为4个人群搬家,从原来的数据段,搬到新家——显存段,而我们的工具是一辆搬家车——一个8各字节的空的字符串,这四类人群是:
1、年份:这类人群特点是,打包好了自己的行李,按时在家等我们去接他们去新的住所,而且上车就走。即,无需进行计算,也无需进行数值和ascii码之间的转换,从数据段复制到字符串然后写入显存相应位置就行。
2、年收入:这类人群的特点是,都是美女,虽然按时在家等我们去接,但是很懒,不打包行李而且行李又多,所以我们必须先打包行李再上车送她们去新的住所。即,需要进行数值和ascii码的转化,并且数值较大,必须写一个新的子程序,题干中有设计,不赘述。然后才可以从数据段复制到字符串后写入显存相应位置。
3、人数:这类人群和第二类人群很像,不过他们行李比较少,可以看做是比较懒的男孩子,行李不多。即,虽然需要转化ascii码,但是由于数值都比较小,所以子程序3完全可以胜任。
4、人均年收入:这类人群属于比较坑的,不仅行李不打包,而且人还不按时出现,需要我们去找到以后再帮他们打包行李,然后才能上车。即:需要计算后,然后进行数值和ascii码之间的转换,再存入字符串中后写入显存。
基本思路就是如此,现成的子程序有三个,由于刚学汇编,子程序写的不是很好,能不入栈的尽量不入,导致后寄存器冲突严重,调试了n次才可以正常运行,友情提示,尽量保存好主程序中的寄存器。
我是一列一列入显存的,还有我个格式是,列与列之间空出10个字符。
虽然我这样分析感觉不专业,但是没办法,刚学,希望用一个比较情景式的感觉来分析问题,可能比较好理解。
下面是我的代码,请大神指点:
assume cs:codesg
datasg segment
db '1975', '1976', '1977', '1978', '1979', '1980'
db '1981', '1982', '1983', '1984', '1985', '1986'
db '1987', '1988', '1989', '1990', '1991', '1992'
db '1993', '1994', '1995'
;上述是表示21年的21个字符串
dd 16, 22, 382, 1356, 2390, 8000, 16000, 24486, 50065
dd 97479, 140417, 197514, 345980, 590827, 803530
dd 1183000, 1843000, 2759000, 3753000, 4649000
dd 5937000
;以上表示21年公司总收入的21个dword型数据
dw 3, 7, 9, 13, 28, 38, 130, 220, 476, 778, 1001, 1442
dw 2258, 2793, 4037, 5635, 8226, 11542, 14430, 15257
dw 17800
;以上是表示21年公司雇员人数的21个word型数据
datasg ends
temp segment
db 8 dup(0)
;临时存储数值每一位的ascii码
temp ends
stacksg segment
db 256 dup(0)
stacksg ends
codesg segment
start:
mov ax, datasg
mov ds, ax
mov si, 0
mov ax, stacksg
mov ss, ax
mov sp, 100h
;第一列复制到显存中
mov ax, temp
mov es, ax ;es永远指向临时字符串
mov si, 0
mov bh, 3
mov bl, 13 ;行、列初始化
mov cx, 21
print_year:
mov ax, ds:[si]
mov es:[0], ax
mov ax, ds:[si+2]
mov es:[2], ax ;相当于复制一个dword型数据
mov dh, bh
mov dl, bl
call show_str
add si, 4
add bh, 1
loop print_year
;第二列复制到显存中
mov si, 0 ;数组偏移地址清0
mov bh, 3
mov bl, 27 ;行、列初始化
mov cx, 21
print_income:
mov ax, ds:84[si]
mov dx, ds:84[si+2] ;收入数组中的元素
call dtoc_dword ;dword型数据转换,结果在目标字符串
mov dh, bh
mov dl, bl ;传参——行列
call show_str ;输出在屏幕
inc bh ;每写一行换一行
add si, 4
loop print_income
;第三列复制到显存中
mov si, 0 ;数组偏移地址清0
mov bh, 3
mov bl, 44 ;行、列初始化
mov cx, 21
print_people:
mov ax, ds:168[si]
call dtoc_word ;数值转化字符
mov dh, bh
mov dl, bl
call show_str
add si, 2
inc bh
loop print_people ;复制人数进显存
;第4列复制到显存中,需要进行除法计算
mov si, 0 ;数组偏移地址清0
mov di, 0
mov bp, 0
mov bh, 3
mov bl, 59 ;行、列初始化
mov cx, 21
print_ave:
mov ax, ds:84[si]
mov dx, ds:84[si+2] ;被除数
mov bp, ds:168[di] ;除数
div bp
call dtoc_word ;ax保存商
mov dh, bh
mov dl, bl
call show_str
inc bh
add si, 4
add di, 2
loop print_ave
mov ax, 4c00h
int 21h
;以下为子程序
-------------双字数据转换ASCII码---------------------------
dtoc_dword:
push ax
push cx ;用来判断是否除完
push dx
push di ;计算几位数
push si ;目标字符串偏移地址
mov di, 0
mov si, 0
push_drem: ;取余数入栈
mov cx, 0ah
call divdw ;调用防止除法溢出子程序,返回值余数在cx中,商在dx和ax中
push cx ;第余数入栈
inc di ;位数+1
mov cx, dx
jcxz second ;第一次判断商高位是否为0
second:
mov cx, ax
jcxz ok_drem ;第二次判断商低位是否为0,若高位低位都为0,说明商为0
jmp short push_drem
ok_drem:
mov cx, di ;一共几位数,要出栈几次
pop_drem:
pop ax
add ax, 30H ;将数值转换成ASCII码
mov es:[si], al ;出栈到目标字符串中
inc si
loop pop_drem
mov al, 0
mov es:[si], al ;字符串末尾加0
pop si
pop di
pop dx
pop cx
pop ax
ret
--------------字型数据转换ASCII码---------------------
dtoc_word:
push ax
push bx ;用来存放除数0AH
push cx ;用来判断是否除完
push dx ;用32位除法,用来保存余数
push si ;数据段偏移地址
push di ;计算数的位数
mov si, 0
mov cx, 0
mov dx, 0
mov di, 0
mov bx, 0ah ;寄存器的初始化
push_wrem: ;开始短除法取余数并入栈
div bx
mov cx, ax
push dx ;余数入栈
mov dx, 0 ;重置被除数高位
inc di
jcxz ok_wrem ;若商为0,则取余数已经完成
jmp short push_wrem
ok_wrem: ;完成取余后,需要入数据段
mov cx, di ;出栈次数
pop_wrem:
pop ax
add ax, 30h ;对应ascii字符码
mov es:[si], al ;逆序入数据段
inc si
loop pop_wrem
mov al, 0
mov es:[si], al ;字符串末尾加0
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
-------------解决除法溢出-----------------------
divdw:
push bx ;临时保存中间运算结果
push ax ;首先计算数据的高位,低位入栈
mov ax, dx
mov dx, 0
div cx ;ax保存商的高位,dx保存数据高位运算后产生的余数
mov bx, ax ;商的高位临时在bx中保存
pop ax ;此时,dx和ax保存了余*10000H+L
div cx
mov cx, dx ;cx中保存最后的余数
mov dx, bx ;dx中保存商的高位
pop bx
ret
------------------屏幕输出-------------------------------
show_str:
push ds
push ax
push bx
push cx ;保存字符属性
push dx
push di
push si
mov ax, 0b800h
mov ds, ax ;初始化显示缓冲区
mov al, 0a0h ;进行行的计算,来确定行的偏移地址
sub dh, 1
mul dh
mov bx, ax ;行计算完毕
mov al, 2 ;进行列的计算,来确定列的偏移地址
sub dl, 1
mul dl
mov di, ax ;列计算完毕
mov al, 7
mov si, 0
mov cx, 0 ;重置一下寄存器,以后需要用它判断是否结束
print:
mov cl, es:[si]
jcxz print_ok ;判断如果字符读取完了就可以跳出循环了
mov ds:[bx][di], cl ;字符送入完毕
mov ds:[bx][di+1], al ;字符属性送入
inc si
add di, 2
jmp short print ;字符的循环复制
print_ok:
pop si
pop di
pop dx
pop cx
pop bx
pop ax
pop ds
ret
codesg ends
end start
其中还有很多不足之处,由于本身文字白色,也就不传递字符颜色属性等等,以后有时间再把这个做到最好。