一、寻址方式在结构化数据访问中的应用(P172)
题目要求:Power idea 公司从1975年成立一直到1995年的基本情况如下。
图1 原始数据示例
数据汇编定定义如下:
data segment
;以下是表示21年的21个字符串
db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
db '1993','1994','1995'
;以下表示21年公司总收入的21个dword型数据
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;以下表示21年公司雇员人数的21个word型数据
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
tabel segment
db 21 dup('year sumn ne ?? ')
tabel ends
编程,将data段中的数据按如下格式写入到table段中,并计算21年中的人均收入(取整),结果也按照下面的格式保存在table段中。
图2 题目具体要求
汇编代码实现:
assume cs:codesg
data segment
;以下是表示21年的21个字符串
db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
db '1993','1994','1995'
;以下表示21年公司总收入的21个dword型数据
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;以下表示21年公司雇员人数的21个word型数据
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
tabel segment
db 21 dup('year sumn ne ?? ')
tabel ends
codesg segment
start:
mov ax,data
mov ds,ax
mov ax,tabel
mov es,ax
mov cx,21
mov bx,0
mov si,0
mov di,0
s:
;存放公司年份
mov ax,[bx]
mov es:[di],ax
mov ax,[bx+2]
mov es:[di+2],ax
;存放公司总收入
mov ax,54h[bx]
mov dx,56h[bx]
mov es:5h[di],ax
mov es:7h[di],dx
;存放公司雇员人数
mov ax,0a8h[si]
mov es:0ah[di],ax
;存放平均收入
mov ax,54h[bx]
mov dx,56h[bx]
div word ptr ds:0a8h[si]
mov es:0ch[di],ax
;更新下次循环的bx,si,di值,其中di标识目标地址tabel的行,
;bx代表原始数据更新4个字节大小的数据(年份、总收入)
;si代表原始数据更新2个字节大小的数据(雇员人数)
add bx,4
add si,2
add di,16
loop s
mov ax,4c00H
int 21H
codesg ends
end start
运行截图:
初始寄存器信息:
图3 初始寄存器信息
初始数据信息:
图4 初始年份信息
图5 初始总收入信息
图6 雇员信息
图7 tabel表(目的存储地址)初始状态存储信息
运行结束后tabel表信息:(debug时使用-p 可以运行到此次loop结束的地方)
图8 运行结束后tabel表信息
二、在屏幕中间分显示绿色、绿底红色、白底蓝色的字符串'welcome to masm!'(P188)别
编程所需的知识通过阅读、分析下面的材料获得。(从下向上阅读)
图9 书中提供的材料信息
汇编代码实现:
assume cs:codesg
data segment
;保存要显示的字符串信息
db 'welcome to masm!'
;保存输出字符串的属性信息(背景色、闪烁、高亮等)
;分别为绿色、绿底红色、白底蓝色
db 2,36,113
data ends
codesg segment
start:
mov ax,data
mov ds,ax
;保存显示缓冲区的段地址
;从屏幕中间第十二行开始显示
mov ax,0B878h
mov es,ax
;dx用于表示字符串的属性信息的偏移下标
;dx每次更新时递增1
mov dx,0
mov cx,3
;第一层循环输出三次
s1:
;si用于表示要输出字符的原始偏移下标
;si每次递增1
;di用于表示输出字符到缓冲区目的偏移下标
;由于每个字符有一个字节的属性标识,所以每次递增2
;es用于表示缓冲区每行起始的段地址
;由于显示缓冲区每行可显示80个字符共160个字节
;所以es每次更新时加10
mov si,0
mov di,0
mov bx,cx
mov cx,16
;第二层循环输出整句话和属性标识到缓冲器
s2:
;将字符信息保存到al中
mov al,[si]
;将字符属性信息保存到ah中
mov bp,bx
mov bx,dx
mov ah,10h[bx]
mov bx,bp
;将ax的值保存到指定缓冲器内存中
mov es:40h[di],ax
;更新si,di的值
add si,1
add di,2
loop s2
;更新es的值
mov ax,es
add ax,10
mov es,ax
;更新属性下标dx的值
mov cx,bx
add dx,1
loop s1
mov ax,4c00H
int 21H
codesg ends
end start
运行结果:
图10 运行结果展示
三、编写子程序(P206)
(1)显示字符串
问题描述:显示字符串是现实工作中经常要用到的功能,应该编写一个通用的子程序来实现这个功能。我们应该提供灵活的调用接口,是调用者可以决定显示的位置(行、列)、内容和颜色。
图11 显示字符串子程序描述
代码实现
assume cs:codesg
data segment
;保存要显示的字符串信息
db 'welcome to masm!',0
data ends
codesg segment
start:
mov dh,8 ;dh装行号(范围:1-25)
mov dl,80 ;dl装列号(范围:1-80)[注:每超过80等于行号自动加1]
mov cl,2 ;cl中存放颜色属性
mov ax,data
mov ds,ax
mov si,0
call show_str
mov ax,4c00H
int 21H
show_str:
push cx
push si
s1:
;判断字符是否为0,如果为0则直接返回
mov bh,cl
mov cl,[si]
mov ch,0
jcxz ok
;es用于保存显示缓冲区的起始段地址
mov ax,0B800h
mov es,ax
;将字符和字符属性信息保存到bx中
mov cl,bh
mov bl,[si]
;如果dl大于80需更新行号
mov ah,0
mov al,dl
mov dl,81
div dl
add dh,al
mov dl,ah
;根据行号更新es
mov al,dh
sub al,1
mov ah,10
mul ah
mov di,es
add ax,di
mov es,ax
;根据列号计算缓冲区索引,并保存到di中
mov ah,0
mov al,dl
sub ax,1
add ax,ax
mov di,ax
;将bx中的数据保存到缓冲区
mov es:[di],bx
;更新列号
add dl,1
;更新si
add si,1
jmp short s1
ok:
pop si
pop cx
ret
codesg ends
end start
运行结果:
图12 显示字符串子程序运行结果展示
(2)解决除法溢出的问题
具体问题描述如下:
图13 问题描述
图14 问题要求
代码实现:
assume cs:codesg
codesg segment
start:
mov ax,4240h ;数据低16位记作L
mov dx,000Fh ;数据高16位记作H
mov cx,0ah ;除数记作N
call divdw
mov ax,4c00H
int 21H
divdw:
;计算int(H/N)保存到si中和rem(H/N)保存在dx中
mov bx,ax
mov ax,dx
mov dx,0
div cx
mov si,ax
mov ax,0
;计算[rem(H/N)*65536+L]/N
mov ax,bx
div cx
;将余数保存在cx中,将溢出(int(H/N)*65536)保存到dx中
mov cx,dx
mov dx,si
ret
codesg ends
end start
运行结果:
图15 除法溢出结果显示
(3)数值显示
问题描述:编程,将data段中的数据以十进制的形式显示出来。
图16 数值显示子程序描述
代码实现:
assume cs:codesg
data segment
db 10 dup (0)
data ends
codesg segment
start:
mov ax,0 ;数据低16位记作L
mov dx,1 ;数据高16位记作H
mov bx,data
mov ds,bx
mov si,0
call dtoc
mov dh,8
mov dl,3
mov cl,2
call show_str
mov ax,4c00H
int 21H
dtoc:
push cx
push si
mov bx,10
mov cx,ax
add cx,dx
s_div:
;如果商为0则直接返回
jcxz div_ok
call divdw
;将余数转化成对应字符的编码后保存
add cx,30H
mov [si],cl
add si,1
mov cx,ax
add cx,dx
jmp short s_div
div_ok:
mov cx,si
sub cx,1
jcxz revert_ok
;翻转保存的字符串
call revert
revert_ok:
pop si
pop cx
ret
divdw:
push si
push bx
mov cx,10
;计算int(H/N)保存到si中和rem(H/N)保存在dx中
mov bx,ax
mov ax,dx
mov dx,0
div cx
mov si,ax
mov ax,0
;计算[rem(H/N)*65536+L]/N
mov ax,bx
div cx
;将余数保存在cx中,将溢出(int(H/N)*65536)保存到dx中
mov cx,dx
mov dx,si
pop bx
pop si
ret
revert:
mov cx,0
mov ax,si
mov bl,2
div bl
mov ah,0
mov cx,ax
sub si,ax
revert_s:
mov di,cx
mov dl,[si]
mov bl,[di-1]
mov [si],bl
mov [di-1],dl
add si,1
loop revert_s
ret
show_str:
push cx
push si
s1:
;判断字符是否为0,如果为0则直接返回
mov bh,cl
mov cl,[si]
mov ch,0
jcxz ok
;es用于保存显示缓冲区的起始段地址
mov ax,0B800h
mov es,ax
;将字符和字符属性信息保存到bx中
mov cl,bh
mov bl,[si]
;如果dl大于80需更新行号
mov ah,0
mov al,dl
mov dl,81
div dl
add dh,al
mov dl,ah
;根据行号更新es
mov al,dh
sub al,1
mov ah,10
mul ah
mov di,es
add ax,di
mov es,ax
;根据列号计算缓冲区索引,并保存到di中
mov ah,0
mov al,dl
sub ax,1
add ax,ax
mov di,ax
;将bx中的数据保存到缓冲区
mov es:[di],bx
;更新列号
add dl,1
;更新si
add si,1
jmp short s1
ok:
pop si
pop cx
ret
codesg ends
end start
运行结果:
图18 数值显示的运行结果
四、课程设计
问题描述:将Power idea公司的数据按照下图所示格式在屏幕显示出来。
图19 问题描述
代码实现:
assume cs:codesg
data segment
;以下是表示21年的21个字符串
db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
db '1993','1994','1995'
;以下表示21年公司总收入的21个dword型数据
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;以下表示21年公司雇员人数的21个word型数据
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
tabel segment
db 21 dup('year sumn ne ?? ')
tabel ends
temp segment
db 10 dup (32)
temp ends
codesg segment
start:
mov ax,data
mov ds,ax
mov ax,tabel
mov es,ax
mov cx,21
mov bx,0
mov si,0
mov di,0
s:
;存放公司年份
mov ax,[bx]
mov es:[di],ax
mov ax,[bx+2]
mov es:[di+2],ax
;存放公司总收入
mov ax,54h[bx]
mov dx,56h[bx]
mov es:5h[di],ax
mov es:7h[di],dx
;存放公司雇员人数
mov ax,0a8h[si]
mov es:0ah[di],ax
;存放平均收入
mov ax,54h[bx]
mov dx,56h[bx]
div word ptr ds:0a8h[si]
mov es:0ch[di],ax
;更新下次循环的bx,si,di值,其中di标识目标地址tabel的行,
;bx代表原始数据更新4个字节大小的数据(年份、总收入)
;si代表原始数据更新2个字节大小的数据(雇员人数)
add bx,4
add si,2
add di,16
loop s
;循环将es数据放入到显示缓冲区
mov ax,es
mov ds,ax
mov dh,2 ;dh装行号(范围:1-25)
mov cx,21
mov ax,temp
mov es,ax ;临时存储数字转换成的字符串
s2:
push cx
mov cx,2 ;显示颜色配置
mov dl,20 ;dl装列号(范围:1-80)[注:每超过80等于行号自动加1]
;输出年份
call show_str
;输出公司总收入
call clear
add dl,10
mov bx,dx
mov dx,ds:[7h] ;要显示的数字
mov ax,ds:[5h] ;要显示的数字
mov di,0
call dtoc
call revert
mov dx,bx
mov ax,temp
mov bx,ds
mov ds,ax
call show_str
mov ds,bx
;输出公司总雇员数
call clear
add dl,10
mov bx,dx
mov dx,0
mov ax,ds:[0ah] ;要显示的数字
mov di,0
call dtoc
call revert
mov dx,bx
mov ax,temp
mov bx,ds
mov ds,ax
call show_str
mov ds,bx
;输出平均收入
call clear
add dl,10
mov bx,dx
mov dx,0
mov ax,ds:[0ch] ;要显示的数字
mov di,0
call dtoc
call revert
mov dx,bx
mov ax,temp
mov bx,ds
mov ds,ax
call show_str
mov ds,bx
add dh,1
pop cx
mov ax,ds
add ax,1
mov ds,ax
sub cx,1
jcxz s2_ok
jmp far ptr s2
s2_ok:
mov ax,4c00H
int 21H
show_str:
push cx
push di
push bx
push dx
push es
mov si,0
s1:
;判断字符是否为空格,如果为空格则直接返回
mov bh,cl
mov cl,[si]
mov ch,0
sub cl,32
jcxz ok
;es用于保存显示缓冲区的起始段地址
mov ax,0B800h
mov es,ax
;将字符和字符属性信息保存到bx中
mov cl,bh
mov bl,[si]
;如果dl大于80需更新行号
mov ah,0
mov al,dl
mov dl,81
div dl
add dh,al
mov dl,ah
;根据行号更新es
mov al,dh
sub al,1
mov ah,10
mul ah
mov di,es
add ax,di
mov es,ax
;根据列号计算缓冲区索引,并保存到di中
mov ah,0
mov al,dl
sub ax,1
add ax,ax
mov di,ax
;将bx中的数据保存到缓冲区
mov es:[di],bx
;更新列号
add dl,1
;更新si
add si,1
jmp short s1
ok:
pop es
pop dx
pop bx
pop di
pop cx
ret
dtoc:
push cx
push dx
push ds
push bx
mov bx,temp
mov ds,bx
mov cx,ax
add cx,dx
s_div:
;如果商为0则直接返回
jcxz div_ok
call divdw
;将余数转化成对应字符的编码后保存
add cx,30H
mov [di],cl
add di,1
mov cx,ax
add cx,dx
jmp short s_div
div_ok:
pop bx
pop ds
pop dx
pop cx
ret
divdw:
push si
push bx
mov cx,10
;计算int(H/N)保存到si中和rem(H/N)保存在dx中
mov bx,ax
mov ax,dx
mov dx,0
div cx
mov si,ax
mov ax,0
;计算[rem(H/N)*65536+L]/N
mov ax,bx
div cx
;将余数保存在cx中,将溢出(int(H/N)*65536)保存到dx中
mov cx,dx
mov dx,si
pop bx
pop si
ret
revert:
push cx
push dx
push ds
push bx
mov dx,0
mov cx,di
sub cx,1
jcxz revert_ok
mov cx,temp
mov ds,cx
mov cx,0
mov ax,di
mov bl,2
div bl
mov ah,0
mov cx,ax
sub di,ax
revert_s:
mov si,cx
mov dl,[si-1]
mov bl,[di]
mov [si-1],bl
mov [di],dl
add di,1
loop revert_s
revert_ok:
pop bx
pop ds
pop dx
pop cx
ret
clear:
push dx
push cx
push bx
mov cx,10
mov dx,temp
mov es,dx
mov dl,32
clear_s:
mov bx,cx
sub bx,1
mov es:[bx],dl
loop clear_s
mov di,0
pop bx
pop cx
pop dx
ret
codesg ends
end start
运行结果: