实验10
- 显示字符串
-
问题
显示字符串是现实工作中经常要用到的功能,应该编写一个通用的子程序来实现这个功能、我们应该提供最灵活的调用接口,使调用者可以决定显示的位置(行和列)、内容和颜色 -
子程序描述
名称:show_str
功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
参数:(dh)= 行号(取值范围0~24),(dl)= 列号(取值范围0-79),
(cl)= 颜色, ds : si 指向字符串的首地址
返回:无
应用举例:在屏幕的8行3列,用绿色显示data段中的字符串
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; 一行160个字节
dec dh
mul dh; 计算所在行数
mov dh,0; 因为dl存着列数,另一个加数一个字大小,故将dl变为一个字
push ax
mov ax,0
mov al,2
dec dl
mul dl; 计算所在列的下标
mov dx,ax
pop ax
add ax,dx; 相加得到开始所在下标
mov bx,ax; 将偏移地址传给bx
mov ah,cl; 将颜色传给ah。因为cx要存储循环次数
mov cx,16; 共16个字符
s:
push cx; 因为cx要用作判断,故先要保存cx的值
mov ch,0
mov cl,[si]
jcxz ok; 判断循环结束条件,当前字符是否为0
mov al,[si]
mov es:[bx],ax; 只需要将ax低位不断改变即可,因为颜色不变
add bx,2
inc si
pop cx
loop s
ok:
ret
code ends
end start
- 解决除法溢出的问题
前面讲过,div指令可以做除法。当进行8位除法的时候,用al存储结果的商,ah存储结果的余数;进行16位除法的时候,用ax才能出结果的商,dx存储结果的余数,可是,现在有一个问题,如果结果的商大于al或ax所能存储的最大值,那么将如何?
比如下面的程序段:
Mov bh,1
Mov ax,1000
Div bh
进行的是8位除法,结果的商为1000,而1000在al中放不下。
又比如,下面的程序段:
Mov ax,1000h
Mov dx,1
Mov bx,1
Div bx
进行的是16位除法,结果的商为11000h,而11000h在al中放不下。
我们在用div的指令做除法时,很可能会发生上面的情况:结果商过大,超出了寄存器所能存储的范围。当cpu执行div等除法指令的时候,如果发生这样的情况,将引发cpu内部的一个错误,这个错误被称为:除法溢出。我们可以通过特殊的程序来处理这个错误,但在这里我们不讨论这个错误的处理,这是后面的程序所涉及到的内容。
好了,我们已经清楚了问题的所在:用div指令做除法的时候可能会产生除法溢出。由于有这样的问题,在进行除法运算的时候要注意除数和被除数的值,比如1000000/10就不能用div指令来计算。那么怎么办呢?我们用下面的子程序divdw解决。
子程序描述
名称:divdw
功能:进行不会溢出的除法运算,被除数为dword型,除数为word型,结果为dword。
参数:(ax)=dword型数据的低16位
(dx)=dword型数据的高16位
(cx)=除数
返回:(dx)=结果的高16位,ax=结果的低16位
(cx)=余数
应用举例:计算1000000/10(F4240H/0AH)
Mov ax,4240h
Mov dx,000fh
Mov cx,0ah
Call divdw
结果:dx=0001h,ax=86a0h,cx=0
提示:
给出一个公式:
X:被除数,范围:[0,ffffffff]
N:除数,范围:[0,ffff]
H:X高16位,范围:[0,ffff]
L:X低16位,范围:[0,ffff]
Int():描述性运算符,取商,比如,int(38/10)=10
Rem():描述性运算符,取余数,比如,rem(38/10)=8
公式:X/N=int(H/N)*65536+[rem(H/N)*65536+L]/N
assume cs:code
code segment
start:
mov ax,4240h
mov dx,000fh
mov cx,0ah
call divdw
mov ax,4c00h
int 21h
divdw:
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位
ret; 返回
code ends
end start
- 问题:
编程,将data段中的数据以十进制的形式显示出来。
data segment
dw 123,12666,1,8,3,38
data ends
子程序描述:
名称:dtoc
功能:将word型数据转变为表示十进制数的字符串,字符串以0位结尾符。
参数:(ax)=word型数据 ds:si指向字符串的首地址
返回:无
应用举例:编程,将数据12666以十进制的形式在屏幕的8行3列,用绿色显示出来。在显示时我们调用本次实验中的第一个子程序show_str。
assume cs:code
data segment
db 10 dup(0)
data ends
code segment
start:
mov ax,12666
mov bx,data
mov ds,bx
mov si,0
mov bx,0
call dtoc
mov dh,8
mov dl,3
mov cl,2
call show_str
mov ax,4c00h
int 21h
dtoc:
mov dx,0; 除法dx保存高16位,必须清零,不然程序会出错,具体表现为不知道会跳转到哪里
mov cx,10; 除数
div cx
add dx,30h; 存该数字代表的ASCII码
mov [bx],dx; 存进data段中
mov cx,ax; 判断商是否为0,需要借助cx判断
add bx,2; 因为商是一个字大小,所以要加2个字节
inc si; 存一共多少位
jcxz ok
jmp dtoc
show_str:
mov cx,si; si存着数字一共多少位
mov di,cx; 因为要进行两次循环,si后面会用到,所以用di存
mov al,dh; 做乘法,获得该行所在的下标
dec al
mov ah,160
mul ah
mov bx,ax
mov al,dl; 做乘法,获得列所在的下标
mov ah,2
mul ah
add bx,ax; 行加列,获得下标
mov ax,0b800h; 显存段号
mov es,ax
mov si,0; 存储data段偏移量
s1:; 因为data段的结果与实际结果相反,故可以存入栈中再输出,得到正确的顺序
mov al,[si]
mov ah,2; 组合,低位ASCII码,高位颜色
push ax
add si,2
loop s1
mov cx,di; 将数字的位数传给cx
s2:
pop ax
mov es:[bx],ax; 赋值给显存,bx存着之前算得的下标
add bx,2
loop s2
ret
ok:
ret
code ends
end start
标签:10,16,mov,cx,实验,dx,ax,bx
From: https://www.cnblogs.com/alphadoiy/p/17564113.html