课程设计
在整个课程中,我们一共有两个课程设计,编写两个比较综合的程序,这是第一个。任务:将实验7中的power idea公司的数据按照图10.2所示的格式在屏幕上显示出来。
效果
难点
- 从数字转换成字符串–通过除以10来不断获取余数,在加上30,最后再逆向输出
- 因为程序要显示的数据有些已经大于65535,应该编写一个新的数据到字符串转换的子程序,完成dword型到字符串的转换
- 在循环中如何选择寄存器(组合)来实现对有规律数据的复制
- 对数据存储结构和控制移位的寄存器值的变化的控制
总结
虽然实现了效果,但是在思路上还是比较凌乱,尤其是在整体的思路上面.还是改不了边写边改的毛病.
- 后面还是要不断的把代码再优化优化
整理
寄存器寻址组合
已知的是 di+bp / bx+si / bx+di 这几种可以用来在内存中遍历数据,在用之前,需要搞清楚,数据元素的确定最少需要几个变化的值来定位。
条件指令的转移范围
条件指令的转移范围只能是从(-128-127),如果想要跳转的位置超过了这个范围,可以先跳到附近的位置,再在附近的位置利用无条件指令跳到想跳到的位置。
汇编语言清除屏幕输出
mov ah,15
int 10h
mov ah,0
int 10h
编码
- 哪些寄存器在循环的过程中需要变化,哪些寄存器需要在下一次循环或者调用子程序之前初始化,这些都要搞清楚
- 再利用div等有范围和大小限制的指令时,考虑一下是否会有溢出的情况
- jmp和je等条件跳转指令如何实现循环的过程
代码
;TODO
;1.现在 char段前面四个字节的年份复制完成了 √
;2.着手将收入转换位为字符串并存在char段中 √
;3.下一步是 将雇员数转换为字符串存在char段中,这个应该不用divdw了 √
;4.最后一步就是显示了,不过应该也是比较头疼的把 √
;整理TODO
;1.整理一下到底哪几个寄存器可以组合起来用来寻址,我快疯了~~~
;已知的是di+bp/bx+si/bx+di
assume cs:codesg,ds:data
data segment
;0
db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
db '1993','1994','1995'
;54H
dd 16,22,382,1356,2390,8000,160000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;A8H
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
table segment
db 21 dup ('year summ ne ?? ')
table ends
char segment
db 672 dup(0) ;672 = 32*21 每一年的数据占23个字节(取个整数刚好是32个字节-16的倍数), 一共有21年
;00 XXXX
;03 XXXX
;06 XXXX
;09 XXXX
;年份 1997 4字节
;空格 1字节-5
;收入 max = 5937000 7个字节
;空格 1字节-13
;雇员数 max = 17800 5个字节
;空格 1字节-19
;人均收入 max = 333 3个字节
;空格 1字节
char ends
testsg segment
db 1680 dup(0)
testsg segment
codesg segment
start:
mov ah,15
int 10h
mov ah,0
int 10h
mov ax,data
mov ds,ax
mov ax,table
mov es,ax
call calculate_income
mov ax,table
mov ds,ax
mov ax,char
mov es,ax
call duplicate_year
call num_char
call employees_char
call average_salary_char
call show_str
mov ax,4c00h
int 21h
;将数据存入显存当中
show_str:
mov ax,char
mov ds,ax
mov ax,0B800H
mov es,ax
mov dh,4;行号
mov dl,0;列号
mov cl,2;颜色
mov bx,0
show_str_year:
push dx
;找行号对应的内存地址
mov ah,00
mov al,160
mul dh
mov bp,ax
;找列对应的内存地址
mov ah,00
mov al,2
mul dl
mov di,ax
; ;找行号对应的内存地址--数据段
; mov ah,00
; mov al,10H
; mov dl,dh
; mov dh,0
; mul dx
; mov bx,ax
;把颜色转移一下
mov al,cl
push cx
mov si,0
show_str_year_loop:
mov cl,ds:[bx+si]
mov ch,0
jcxz show_str_year_loop_ok
mov es:[bp+di],cl
mov es:[bp+di+1],al
add di,2
add si,1
jmp short show_str_year_loop
show_str_year_loop_ok:
pop cx
mov dl,10;列号
;更新下一个列地址
mov ah,00
mov al,2
mul dl
mov di,ax
;把颜色再设置回来
mov al,cl
push cx
mov si,0
show_str_income_loop:
mov cl,ds:[bx+si+5]
mov ch,0
jcxz show_str_income_loop_ok
mov es:[bp+di],cl
mov es:[bp+di+1],al
add di,2
add si,1
jmp short show_str_income_loop
show_str_income_loop_ok:
pop cx
mov dl,20;列号
;更新下一个列地址
mov ah,00
mov al,2
mul dl
mov di,ax
;把颜色再设置回来
mov al,cl
push cx
mov si,0
show_str_employees_loop:
mov cl,ds:[bx+si+13]
mov ch,0
jcxz show_str_employees_loop_ok
mov es:[bp+di],cl
mov es:[bp+di+1],al
add di,2
add si,1
jmp short show_str_employees_loop
show_str_employees_loop_ok:
pop cx
mov dl,30;列号
;更新下一个列地址
mov ah,00
mov al,2
mul dl
mov di,ax
;把颜色再设置回来
mov al,cl
push cx
mov si,0
show_str_average_loop:
mov cl,ds:[bx+si+19]
mov ch,0
jcxz show_str_average_loop_ok
mov es:[bp+di],cl
mov es:[bp+di+1],al
add di,2
add si,1
jmp short show_str_average_loop
show_str_average_loop_ok:
pop cx
mov dl,40;列号
;更新下一个列地址
mov ah,00
mov al,2
mul dl
mov di,ax
;把颜色再设置回来
mov al,cl
pop dx
add bx,20H
add dh,1;行号加一
mov dl,0;列号清零
mov ax,21
sub al,dh
cmp byte ptr al,0
; cmp byte ptr dh,21
;je show_str_ok;这很明显不相等啊,为什么还跳转了?
;你应该是跳转到循环里才对,如果不是跳到循环里,它其实没有发生跳转,只是按顺序往下执行刚好到下面了
je show_str_ok;这样写也跳转了。。??
show_str_year_relay:
jmp show_str_year
show_str_ok:
ret
;把人均工资部分转换成字符串
average_salary_char:
mov bx,0
mov di,0
mov cx,21
average_salary_char_all:
push cx
mov ax,ds:[bx+0DH]
mov dx,0
mov si,0
mov bp,10
average_salary_char_one:
div bp
cmp ax,0
je average_salary_char_one_ok
push dx
mov dx,0
inc si
jmp average_salary_char_one
average_salary_char_one_ok:
push dx
inc si
mov cx,si
mov si,0
mov bp,0
average_salary_char_one_ok_loop:
pop ax
add al,30H
mov es:[di+bp+19],al
inc bp
loop average_salary_char_one_ok_loop
pop cx
add bx,10H
add di,20H
loop average_salary_char_all
ret
;把雇员数部分转换成字符串
employees_char:
mov bx,0
mov di,0
mov cx,21
employees_char_all:
push cx
mov ax,ds:[bx+0AH]
mov dx,0
mov si,0
mov bp,10
employees_char_one:
div bp
cmp ax,0
je employees_char_one_ok
push dx
mov dx,0
inc si
jmp employees_char_one
employees_char_one_ok:
push dx
inc si
mov cx,si
mov si,0
mov bp,0
employees_char_one_ok_loop:
pop ax
add al,30H
mov es:[di+bp+13],al
inc bp
loop employees_char_one_ok_loop
pop cx
add bx,10H
add di,20H
loop employees_char_all
ret
;把收入部分的数字转换为字符串存储到内存当中
num_char:
mov bx,0
mov cx,21
num_char_loop:
push cx
mov si,0
mov di,0
mov ax,ds:[bx+5H]
mov dx,ds:[bx+7H]
mov cx,0AH
;具体的一个数转换为字符
num_char_loop_div:
call divdw
mov bp,0
;如果dx和ax都为零说明该数转换完成
mov bp,dx
or bp,ax
cmp bp,0
je num_char_loop_div_ok
push cx;余数入栈
inc si
mov cx,0AH;每次跳回去之前要把cx再设置为零
jmp num_char_loop_div
num_char_loop_div_ok:
;最后一个商小于10,上面没办法入栈了
push cx;在这里把这个入栈
inc si
mov cx,si;si中保留着这个具体数的所有余数
mov bp,0
mov ax,2H
mul bx
mov di,ax
num_char_loop_div_ok_loop:
pop ax
add ax,30H
mov es:[di+5H+bp],al
add bp,1
loop num_char_loop_div_ok_loop
; ;填了个空格
; mov al,' '
; mov es:[di+5H+bp],al
pop cx
add bx,10H
loop num_char_loop
;把data段中的年份复制到char段里面去
duplicate_year:
mov bx,0
mov si,0
mov di,0
mov cx,21
duplicate_year_all:
push cx
mov cx,2
mov bx,0
duplicate_year_year:
mov ax,ds:[di+bx]
mov es:[si+bx],ax
add bx,2
loop duplicate_year_year
; ;添个空格
; mov al,' '
; mov es:[si+bx],al
add si,20H
add di,10H
pop cx
loop duplicate_year_all
ret
;刚好是之前计算人均收入程序
calculate_income:
mov bx,0;bx定位data段,bx year和收入都是四个字节可以公用,但是雇员数是两个字节的没办法公用
mov bp,0;bp定位es段
mov di,0;di用来在循环中服务于雇员数,因为它是两个字节,没办法跟year和outcome公用一个递增寄存器
mov cx,21
overall:
push cx
mov cx,2
mov si,0
year:
;年份的4字节---两个字
mov ax,ds:[bx+si]
mov es:[bp+si],ax
add si,2
loop year
mov cx,2
mov si,0
outcome:
;收入的4字节---两个字
mov ax,ds:[bx+54H+si]
mov es:[bp+5H+si],ax
add si,2
loop outcome
;雇员数---一个字
mov ax,ds:[di+0A8H]
mov es:[bp+0AH],ax
;计算人均收入
mov ax,es:[bp+5H];计算人均收入的,要用es的定位,那偏移量就得按照es的来。最开始写成ds的定位了[bx+54H]了
mov dx,es:[bp+5H+2]
div word ptr es:[bp+0AH]
mov es:[bp+0DH],ax
;所有的空格
;第一次没有考虑到空格是一个字节,这里不应该用16位的,应该用8位的
mov al,' '
mov es:[bp+4H],al
mov es:[bp+9H],al
mov es:[bp+0FH],al
pop cx
add di,2
add bx,4
add bp,16
loop overall
ret
;名称:divdw
;功能:进行不会产生溢出的除法运算,被除数为dword,除数为word.结果为dword
;参数: ax dword的低16位 | dx dword高16位 | cx除数16位
;返回: dx 结果的高16位, ax 结果的低16位, cx 余数
;应用举例:计算 1000000/10(F4240H/0AH)
;结果: dx = 0001H ax = 86A0H cx = 0
divdw:
push si
push bx
push ax
mov ax,dx
div cl
mov bl,al
mov bh,00H
mov al,ah
mov ah,00H;bx保留商,ax保留余数
pop si
mov dx,si
mov dx,ax
mov ax,si
div cx;32/16 ax余数,dx商
mov si,bx
mov bx,dx
mov dx,si
mov cx,bx
pop bx
pop si
ret
codesg ends
end start