1 显示字符串
问题
显示字符串是现实工作中经常要用到的功能,应该编写一个通用的子程序来实现这个功能。我们应该提供灵活的调用接口,使调用者可以界定现实的位置(行、列)、内容和颜色。
子程序描述
名称:show_str
功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
参数:(dh)=行号(取值范围0~24),(dl)=列号(取值范围0~79),
(cl)=颜色,ds:si指向字符串的首地址
返回:无
应用举例:在屏幕的8行3列,用绿色显示data段中的字符串。
提示
(1)子程序的入口参数是屏幕上的行号和列号,注意在子程序内部要将它们转化为显存中的地址,首先在分析一下屏幕上的行列位置和显存地址的对应关系;(参见:王爽《汇编语言》实验9)
(2)注意保存子程序中用到的相关寄存器
(3)这个子程序的内部处理和显存的结构密切相关,但是向外提供了显存结构无关的接口。通过调用这个子程序,进行字符串的显示时可以不必了解显存的结构,为编程提供了方便。在实验中,注意体会这种设计思想。
assume cs:code
data segment
db 'Welcome to masm!',0
data ends
code segment
start:
mov dh,8 ; 行数
mov dl,3 ; 列数
mov cl,2 ; 绿色
mov ax,data
mov ds,ax
mov si,0 ; 数据
call show_str
mov ax,4c00h
int 21h
show_str:
mov ax,0b800h
mov es,ax
; 计算行的开始位置
mov al,160
mul dh
mov bx,ax
; 计算列的开始位置
mov al,2
mul dl
add bx,ax
; 保留颜色值
mov dl,cl
loop_str:
; 取得显示字符
mov cl,ds:[si]
; 设置显示颜色
mov ch,dl
; 将字符写入显存
mov es:[bx],cx
mov ch,0
jcxz ok
inc si
add bx,2
loop loop_str
ok:
ret
code ends
end start
2 解决除法溢出问题
子程序描述
名称:divdw
功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型。
参数:(ax)=dword型数据的低16位
(dx)=dword型数据的高16位
(cx)=除数
返回:(dx)=结果的高16位,(ax)=结果的低16位
(cx)=余数
应用举例:计算1000000/10(FA240H/0AH)
提示
结出一个公式:
X: 被除数,范围:[0,FFFFFFFF]
N: 除数,范围:[0,FFFF]
H: X高16位,范围:[0,FFFF]
L: X低16位,范围:[0,FFFF]
int():描述性运算符,取商,比如:int(38/10)=3
rem():描述性运算符,取余数,比如:rem(38/10)=8
公式:X/N=int(H/N)65536+[rem(H/N)65536+L]/N
这里的65536为10000H,也就是说,int(H/N)10000H为高16位的商,而[rem(H/N)65536+L]/N得到的是低16位的商和余数;
这个公式将可能产生溢出的除法运算:X/N,转变为多个不会产生溢出的除法运算公式中,等号右边的所有除法运算都可以用div指令来做,肯定不会导致溢出。
assume cs:code
code segment
start:
mov ax,04240H ;设置被除数
mov dx,000FH
mov cx,0AH ;设置除数
call divdw
mov ax,4c00h
int 21h
; 功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型
; 参数:(ax)=dword型数据的低16位
; (dx)=dword型数据的高16位
; (cx)=除数
; 返回:(dx)=结果的高16位,(ax)=结果的低16位
; (cx)=余数
;如果除数是16位的,被除数是DX:AX,商在AX中,余数在DX中
divdw:
;保存低位
mov bx,ax
;计算高位的商
mov ax,dx
mov dx,0
div cx
;记录 高位的商
mov si,ax
mov ax,bx
;请注意 这时 商在ax 中 余数在dx中
;余数dx 和 新赋值进来的 低位 ax 正好符合 [rem(H/N)*65536 + L]
div cx
mov cx,dx
mov dx,si
ret
code ends
end start
3 数值显示
子程序描述
名称:dtoc
功能:将word型数据转变为表示十进制数的字符串,字符串以10为结尾符。
参数:(ax)=word型数据,ds:si指向字符串首地址
返回:无
应用举例:编程,将数据12666以十进制的形式在屏幕的8行3列,用绿色显示出来。在显示时我们调用本次实验中的第一个子程序show_str.
提示
09的ASCII码为30H39H,则先由十六进制转化为十进制,再转ACSII码,转换示意图如下:
同时要注意,依次得到的是个位,十位,百位……万位这与我们习惯的从左到右的万位……个位顺序颠倒
assume cs:code
data segment
db 10 dup (0)
data ends
code segment
start:
mov ax,data
mov ds,ax
call dtoc
mov dh,8 ; 行数
mov dl,3 ; 列数
mov cl,2 ; 绿色
mov si,1 ; 数据
call show_str
mov ax,4c00h
int 21h
dtoc:
mov ax,12666
mov cx,5
c: mov bx,10
mov dx,0
div bx
mov bx,cx
add dl,30h
mov ds:[bx],dl
loop c
ret
show_str:
mov ax,0b800h
mov es,ax
; 计算行的开始位置
mov al,160
mul dh
mov bx,ax
; 计算列的开始位置
mov al,2
mul dl
add bx,ax
; 保留颜色值
mov dl,cl
loop_str:
; 取得显示字符
mov cl,ds:[si]
; 设置显示颜色
mov ch,dl
; 将字符写入显存
mov es:[bx],cx
mov ch,0
jcxz ok
inc si
add bx,2
loop loop_str
ok:
ret
code ends
end start