汇编语言(王爽第三版)实验十

实验十

题目预览

  1. 显示字符串
  2. 解决除法溢出的问题
  3. 数值显示

1

问题:
显示字符串是现实工作中经常要用到的功能,应该编写一个通用的子程序来实现这个功能。我们应该提供灵活的调用接口,使调用者可以决定显示的位置(行、列)、内容和颜色。

子程序描述:
名称: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:		··
			··
			··
code ends
end start			

有了实验九的基础我们知道,显示器是25*80。屏幕一行有80个字符串,占160个字节,即一个字符串占两个字节。有25行。要注意的本题的行取值范围是0~24,列取值范围是0~79。

程序如下:

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:
	push si				;有冲突的寄存器放入栈中
	push cx

	mov bl,cl  			;后面要用到cl因此把数据放入bx寄存器的低八位
	mov ax,0B800H		;显存的起始地址
	mov es,ax
	sub di,di

	mov ax,160			;计算显示位置
	mul dh
	add di,ax

	mov ax,2
	mul dl
	add di,ax			;计算显示位置

green:
	mov cl,ds:[si]
	mov ch,0
	jcxz ok				;判断是否为0

	mov es:[di],cx		
	inc di
	mov es:[di],bl
	inc di

	inc si

	jmp short green
ok:
	pop cx
	pop si
	ret
	
code ends
end start			

效果如下:
在这里插入图片描述
改为0行0列
在这里插入图片描述
在这里插入图片描述

2

问题:
前面讲过,div指令可以做除法。当进行8位除法的时候,用al存储结果的商,ah存储结果的余数;进行16位除法的时候,用ax存储结果的商,dx存储结果的余数。可是,现在有一个问题,如果结果的商大于al或ax所能存储的最大值,那么将如何?
我们在用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
moc 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)=3
rem():描述性运算符,取余数,比如,rem(38/10)=8
公式:X/N=int(H/N)*65536+[rem(H/N)*65536+L]/N

分析:
看看这个公式的应用,如计算1000000/10(F4240H/0AH)
X=F4240H,N=0AH,H=000FH,L=4240H
int(H/N)=int(0FH/0AH)=int(15/10)=1
rem(H/N)=rem(0FH/0AH)=int(15/10)=5

X/N=1*65536+[5*65536+4240H]/0AH=10000H+[50000H+4240H]/0AH
=10000H+86A0H(换成十进制计算在换成十六进制)=186A0H,结果就是(dx)=0001H(高位),(ax)=86A0H(低位),(cx)=0(余数)。

从材料中总结出
结果高位:Int(H/N)。
结果低位:[rem(H/N)*65536+L]/N的商。
结果余数:[rem(H/N)*65536+L]/N的余数。
[rem(H/N)*65536+L]中rem(H/N)*65536是高位,L是低位。如上面的0001H*65536变成10000H

从上面的公式推出主要过程

mov ax,dx		;H
div cx			;得到ax=int(H/N) bx=rem(H/N)

也不用在去乘65536,放在dx中即高位0001H就是10000H。

完整程序如下:

assume cs:code

code segment

start:
	mov ax,4240H		;低16位
	mov dx,000FH		;高16位
	mov cx,0AH
	call divdw

	mov ax,4c00h
	int 21h

divdw:
	mov bx,ax
	mov ax,dx
	sub dx,dx
	div cx		;得ax=int(H/H)	dx=rem(H/N)
	push ax		;结果高位入栈

	mov ax,bx
	div cx		;得ax=(结果低位)和dx=(结果余数)
	push ax;
	push dx;

	pop cx
	pop ax
	pop dx

	ret
	
code ends
end start						

第一个div指令
在这里插入图片描述
第二个div指令,上一个div指令把余数5放到了dx寄存器中,然后在这个div指令中dx是高位,L是ax,也就相当于了上面公式的[rem(H/N)*65536+L]/N,[5H*65536+4240H]/0AH=[50000H+4240H]/0AH=54240H/0AH=344640/10=34464=86A0H
在这里插入图片描述
在这里插入图片描述

3

问题:
编程,将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
	call dtoc

	mov dh,8
	mov dl,3
	mov cl,2
	call show_str
	··
	··
	··
	
code ends
end start			

思路,div 10得到余数+30H放入栈中,div 10得到的商循环。
在这里插入图片描述
关键代码如下:

	mov ax,12666
pushAscii:

	mov cx,10
	sub dx,dx
	div cx				;ax=商,dx=余数

	mov cx,ax
	jcxz lone			;商为0

	add dx,30H
	push dx
	inc	di				;记录有多少条数据

	jmp short pushAscii
lone:					;转换最后一个余数为ACSII
	add dx,30H
	push dx
	inc di

	mov cx,di			;将有多少条数据传入cx	

此时栈中数据如下:
在这里插入图片描述
出栈放入data段中,就变成和本实验中的(1)一样了
关键代码如下:

popAscii:
	pop ax
	mov ds:[si],al		;ACSII占用低8位,配合show_str下的green里的mov cl,ds:[si]
	inc si
	loop popAscii

data段的内容如下:
在这里插入图片描述
下面就和本实验中的(1)一样了

完整代码如下:

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
	call dtoc

	mov dh,8
	mov dl,3
	mov cl,2
	call show_str

	mov ax,4c00h
	int 21h

dtoc:
	push ax
	push si

	sub di,di

pushAscii:

	mov cx,10
	sub dx,dx
	div cx				;ax=商,dx=余数

	mov cx,ax
	jcxz lone			;商为0

	add dx,30H
	push dx
	inc	di				;记录有多少条数据

	jmp short pushAscii

lone:					;转换最后一个余数为ACSII
	add dx,30H
	push dx
	inc di

	mov cx,di			;将有多少条数据传入cx

popAscii:
	pop ax
	mov ds:[si],al		;ACSII占用低8位,配合show_str下的green里的mov cl,ds:[si]
	inc si
	loop popAscii

	pop si
	pop ax

	ret

show_str:
	push si				;有冲突的寄存器放入栈中
	push cx

	mov bl,cl  			;后面要用到cl因此把数据放入bx寄存器的低八位
	mov ax,0B800H		;显存的起始地址
	mov es,ax
	sub di,di

	mov ax,160			;计算显示位置
	mul dh
	add di,ax

	mov ax,2
	mul dl
	add di,ax			;计算显示位置

green:
	mov cl,ds:[si]
	mov ch,0
	jcxz greenOk		;判断是否为0

	mov es:[di],cx		
	inc di
	mov es:[di],bl
	inc di

	inc si

	jmp short green
greenOk:
	pop cx
	pop si
	ret

code ends
end start			

运行结果:
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值