课程设计1 (材料详见书上211页)
题目描述:
以下是我解答的完整的代码:
:>----------------------------------------------------------------------------------------
assume cs:codesg, ss:stacksg
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,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
datasg ends
stacksg segment
dw 16 dup(0) ;栈-用来暂存数据
stacksg ends
mathsg segment
db 16 dup(0) ;用来保存十进制数对应的ASCII码值
mathsg ends
stringsg segment
db 80 dup(20h) ;定义字符串, 长度为一行, 用空格符初始化
stringsg ends
codesg segment
start: mov ax, datasg
mov ds, ax
mov ax, stacksg
mov ss, ax
mov sp, 20h ;初始化栈
mov ax, stringsg
mov es, ax
mov bx, 0 ;增量为4个字节
mov bp, 0 ;增量为2个字节
mov cx, 21 ;总共要处理的行数
mov dh, 0 ;指定显示的行
mov dl, 0 ;指定显示的列
p0: push cx ;外层循环
push dx
;----下面处理每行的日期数据
mov cx, 4
mov si, 0
mov di, 0
p1: mov al, ds:[bx][si]
mov es:[di].2, al
inc si
inc di
loop p1
;----下面处理每行的收入数据
mov si, 2
mov ax, ds:[bx].54h
mov dx, ds:[bx][si].54h
call dtoc
push ds
mov ds, di
mov ch, 0
mov di, 0
p2: mov cl, ds:[si]
jcxz p2end
mov es:[di].10h, cl
inc si
inc di
jmp p2
;----下面处理每行的雇员数据
p2end: pop ds
mov ax, ds:[bp].0A8h
mov dx, 0
call dtoc
push ds
mov ds, di
mov ch, 0
mov di, 0
p3: mov cl, ds:[si]
jcxz p3end
mov es:[di].20h, cl
inc si
inc di
jmp p3
;----下面处理每行的平均收入数据
p3end: pop ds
mov si, 2
mov ax, ds:[bx].54h
mov dx, ds:[bx][si].54h
mov cx, ds:[bp].0A8h
call divdw
call dtoc
push ds
mov ds, di
mov ch, 0
mov di, 0
p4: mov cl, ds:[si]
jcxz p4end
mov es:[di].30h, cl
inc si
inc di
jmp p4
;----下面加上结束符,并且显示处理好的行内容
p4end: pop ds
mov di, 4Fh
mov byte ptr es:[di], 0
;----运行到这里时已构造好一行的显示内容,存在stringsg段中
pop dx
mov cl, 07h
mov di, es
mov si, 0
call show_str ;显示一行内容
inc dh
add bx, 4
add bp, 2
pop cx
dec cx
cmp cx, 0
jne top0
je toend
top0: jmp far ptr p0
toend: mov ax, 4c00H
int 21H
;子程序(用来在屏幕指定位置显示内容的子程序, 内容默认放在ds段中)
; 参数:dh-显示的行数, dl-显示的列数, cl-属性字节, di-内容所在的段, si-移量地址
; 返回:无
show_str: push ds
push es
push ax
push bx
push dx ;以上保存子程序中要用到的寄存器
mov ds, di
mov ax, 0B800h ;显存首地址
mov es, ax
mov al, 160 ;显存中每行所占的字节数(每行能显示80个字符)
mul dh
mov bx, ax
mov al, 2 ;显存中每个显示字符所占的字节数(显示字符=属性字节+ASCII码)
mul dl
add bx, ax ;此时bx为由参数指定显示位在显存中的首地址
mov ch, 0
mov dl, cl ;因为要用cx来检测是否处理到字符串的结尾0, 先用dl保存参数cl
s0: mov cl, ds:[si] ;取出之前处理好的一个字符
jcxz sok
mov byte ptr es:[bx], cl ;往显存中存入ASCII码
mov byte ptr es:[bx].1h, dl ;往显存中存入属性字节
inc si ;移动到下一个字符
add bx, 2 ;移动到下一个显存字符空间的首地址
jmp s0
sok: pop dx
pop bx
pop ax
pop es
pop ds
ret
;子程序(用来转成十进制数对应的ASCII码的子程序)
; 参数:ax-保存数值的低16位, dx-保存数值的高16位
; 返回:si-保存十进制ASCII码的首地址, di-保存段地址
dtoc: push ds
push bx
push cx
mov cx, mathsg
mov ds, cx
mov di, cx
mov si, 0Eh ;初始化si指向mathsg段中倒数第二个字节空间
d0: mov cx, 0Ah ;cx保存除数
call divdw
add cl, 30h ;由于除数是10 所以结果中的余数一定小于10,取cl就行了
mov byte ptr [si], cl ;保存对应的十进制数的ASCII值
call zerodw
jcxz dok
dec si
jmp d0
dok: pop cx
pop bx
pop ds
ret
;子程序(进行不产生溢出的除法运算)
; 参数:dx为被除数的高位字,ax为被除数的低位字,cx为除数占一个字空间
; 返回:dx保存商的高位字,ax保存商的低位字,cx保存余数点一个字空间
divdw: push bx ;下面要用bx来保存第一次除法结果中的商,先入栈保存下之前的信息,以免冲突
push ax ; ax现在存有参数,但要用到ax参与除法运算,所以也先入栈保存份
mov ax, dx
mov dx, 0
div cx ; 执行分解后的第一次除法: H/n
mov bx, ax ; bx保存第一次除法结果中的商
pop ax ; 将入栈保存的ax参数值出栈,此时dx的值为第一次除法结果中的余数
div cx ; 执行第二次除法: [rem(H/n)*65536+L]/n
mov cx, dx ; 根据题目要求,将除法结果中的余数用cx保存
mov dx, bx ; 根据题目要求,将除法结果中的高位字用dx保存
pop bx ; 还原bx的值,避免子程序之外也用到了bx而产生寄存器冲突
ret
;子程序(判断32位的数是否为0)
; 参数:dx为被除数的高位字,ax为被除数的低位字
; 返回:cx 当dx、ax都为0时才为0
zerodw: cmp dx, 0
jne nz
cmp ax, 0
jne nz
mov cx, 0
jmp zok
nz: mov cx, 1
zok: ret
codesg ends
end start
:<----------------------------------------------------------------------------------------
运行结果:
总结:
调试了一个晚上,终于弄出来了,很激动,又进一小步。调试时又出现了死循环,由于对栈的操作不当引起的,下次一定要注意入栈与出栈要配对,并且出栈顺序与入栈顺序相反。
------------- 以上的习题部分及代码引用于王爽老师的<<汇编语言>>第二版 ----------------