# 课设一

课设一

实验任务:将 实验7 寻址方式在结构化数据访问中的应用 中提供的公司数据,呈现在屏幕上,效果如下。

img

assume cs:codesg,ds:table,es:data

data segment
db '1975', '1976', '1977', '1978', '1979', '1980', '1981'
db '1982', '1983', '1984', '1985', '1986', '1987', '1988'
db '1989', '1990', '1991', '1992', '1993', '1994', '1995'

dd 16, 22, 382, 1356, 2390, 8000, 16000, 24486, 50065, 97479
dd 140417, 197514, 345980, 590827, 803530, 1183000, 1843000
dd 2759000, 3753000, 4649000, 5937000

dw 3, 7, 9, 13, 28, 38, 130, 220, 476, 778, 1001, 1442, 2258
dw 2793, 4037, 5635, 8226, 11542, 14430, 15257, 17800
data ends

table segment
db 21 dup ('year summ ne ?? ')
table ends

codesg segment
start: mov ax,data
mov es,ax

mov ax,table
mov ds,ax

mov bx,0
mov si,0
mov di,168

mov cx,21
s:mov ax,es:[si] ; 存年份
mov [bx].0h[0],ax ; 大小为两个字
mov ax,es:[si+2] ; 所以要存两次
mov [bx].0h[2],ax

mov ax,es:[si+84] ; 存收入
mov [bx].5[0],ax
mov ax,es:[si+86] ; 同理,存两次
mov [bx].5[2],ax

mov ax,es:[di] ; 存雇员人数
mov [bx].10[0],ax

mov ax,es:[si+84]
mov dx,es:[si+86]
div word ptr es:[di] ;因为除数被除数都在内存中,要指明除数的长度
mov [bx].13[0],ax

add bx,10h
add si,4
add di,2
loop s
; 以上是向table表传输数据
; 下面要将table表中的数据输出到显存中

 

mov ax,table
mov ds,ax
mov ax,0b81eh
mov es,ax; es存显存第三行开头
mov bx,0; bx是table块的数据下标
mov si,20; si存每行数据在第几列,是显存块数据下标
mov cx,21
; 下面分成四个循环,分别对年份,收入(千美元),雇员(人),人均收入(千美元)进行输出
s1:
; 年份已经是字符串格式,直接输出就好
mov al,[bx]
mov ah,2; 低位存ASCII,高位存颜色
mov es:[si],ax
mov al,[bx+1]
mov ah,2
mov es:[si+2],ax
mov al,[bx+2]
mov ah,2
mov es:[si+4],ax
mov al,[bx+3]
mov ah,2
mov es:[si+6],ax; 年份4位,存四次
add bx,16; table 16位换行
add si,160; 显存160位换行
loop s1


mov bx,0; 每次循环开始都要重新初始化
mov si,20
mov cx,21
; s2,s3,s4 都是类似的,以s2为例
s2:
mov ax,[bx+5]; 被除数低16位
mov dx,[bx+7]; 被除数高16位
push cx; 在dtoc中,cx会存余数,故压栈保存数据
push si; 在dtoc中,si存最终结果的位数,故压栈保存数据
mov si,0
push bx; 在dtoc中,bx被用来当作数据段下标,故压栈保存数据
mov bx,0
call dtoc
pop bx; 弹出bx,用来将table段数据传输给被除数
mov cx,si
mov dx,cx; 多少位循环多少次
pop si; si需恢复为显存段下标
mov di,-100; 可以自定义,我是将table段前缀-100当作dtoc保存数据的段前缀
push si; 再压栈,后面循环会改变si
s22:
; dtoc存数据是从低位存的,所以可以通过压栈恢复正常顺序
mov al,[di]
mov ah,2
push ax
add di,2
loop s22
mov cx,dx
s23:
; 出栈恢复正常顺序
pop ax
mov es:[si+40],ax
add si,2
loop s23
pop si; 注意各寄存器出栈顺序
pop cx
add bx,16; 不论是bx还是si,都是表示每行开头的下标,要记住这一点,不然循环之后,都会偏移开头
add si,160
loop s2

 

mov bx,0
mov si,20
mov cx,21
s3:
mov ax,[bx+10]
mov dx,0
push cx
push si
mov si,0
push bx
mov bx,0
call dtoc
pop bx
mov cx,si
pop si
mov di,-100
push si
s33:
mov al,[di]
mov ah,2
mov es:[si+80],ax
add di,2
add si,2
loop s33
pop si
pop cx
add bx,16
add si,160
loop s3


mov bx,0
mov si,20
mov cx,21
s4:
mov ax,[bx+13]
mov dx,0
push cx
push si
mov si,0
push bx
mov bx,0
call dtoc
pop bx
mov cx,si
pop si
mov di,-100
push si
s44:
mov al,[di]
mov ah,2
mov es:[si+120],ax
add di,2
add si,2
loop s44
pop si
pop cx
add bx,16
add si,160
loop s4

mov ax,4c00h
int 21h

 

dtoc:
mov cx,10; 除数
call divdw
add cx,30h; 存该数字代表的ASCII码
mov [bx-100],cx; 存进data段中
add bx,2; 因为商是一个字大小,所以要加2个字节
inc si; 存一共多少位
mov cx,ax; 判断商是否为0,需要借助cx判断
jcxz jud; 如何进行双重判断,可以按顺序进行判断,满足进入下一个判断,不满足循环
jmp dtoc
jud:
mov cx,dx
jcxz ok
jmp dtoc

ok:
ret


divdw:
push bx
mov bx,ax; bx寄存器空着,因为要做除法,会占用ax,故先将ax的值保存
mov ax,dx; 要做高位除法,将被除数先存进ax中
mov dx,0; dx清零,保存余数
div cx; 进行(H/N)
push ax; 将商的高16位保存,因为还有一次除法,dx正好可以直接用到,因为*65536相当于左移16位,低16位加上L正好组成被除数
mov ax,bx; bx就是低16位
div cx
mov cx,dx; dx存的余数,赋值给cx
pop dx; dx存的商高16位
; ax正好已经存了商的低16位
pop bx
ret; 返回
codesg ends
end start

心得体会:

写汇编真的感觉很杂,主要是寄存器有限,每次使用都要注意存的数据是否有用,不然就要想办法保存。存入栈后,当调用call时或将值存入栈后,又要注意栈的内容是什么,出栈的顺序是什么。有时候写了一大段,寄存器存的内容也忘了。

本次实验本来想用一个循环写的,写了之后编译器报错循环之间的代码太长,超出 jmp 距离了,所以才分成4个循环分开写。此外显存显示区域也很奇怪,我的dos里前三行显示不了,所以将显示区域段前缀加了三行,才能正常显示。

posted @ 2023-07-19 21:30  三年、  阅读(23)  评论(0)    收藏  举报