TA的每日心情 | 开心 2021-12-13 21:45 |
---|
签到天数: 15 天 [LV.4]偶尔看看III
|
标 题:
汇编实验10—— 编写子程序
作 者: XHS_12302
时 间: 2016_7_28 16:56
实验10编写子程序
在这次实验中,我们将要编写3个子程序,通过它们来认识几个常见的问题和掌握解决这些问题的方法。同前面的所有实验一样,这个实验是必须要独立完成的,在后面的课程中,将要用到这个实验中编写的3个子程序。
1. 显示字符串
问题
显示字符串是现实工作中经常要用到的功能,应该编写一个通用的子程序来实现这个功能。我们应该提供灵活的调用接口,使调用者可以决定显示的位置(行、列)、内容和颜色。
提示
(1) 子程序的入口参数是屏幕上的行号和列号,注意在子程序内部要将它们转化为显存中的地址,首先要分析一下屏幕上的行列位置和显存地址的对应关系:
(2) 注意保存子程序中用到的相关寄存器:
(3) 这个子程序的内部处理和显存的结构密切相关,但是向外提供了与显存结构无关的接口。通过调用这个子程序,进行字符串的显示时可以不必了解显存的结构,为编程提供了方便。在实验中,注意体会这种设计思想。
子程序描述
名称: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: push dx
- push cx
- push ds
- push si
- mov ax,0b800h
- mov es,ax
-
- mov al,160
- mul dh
- mov bx,ax
- mov al,2
- mul dl
- add bx,ax
- mov al,cl
-
- s: mov cl,[si]
- jcxz ok
- mov dx,[si]
- mov es:[bx],dx
- mov es:[bx+1],al
- inc si
- add bx,2
- loop s
- ok:
- pop si
- pop ds
- pop cx
- pop dx
- ret
- code ends
- end start
复制代码
实验截图

2. 解决除法溢出的问题
问题
前面讲过,div指令可以做除法。当进行8位除法的时候,用al存储结果的商,ah存储结果的余数:进行16位除法的时候,用ax存储结果的商,dx存储结果的余数。可是,现在有一个问题,如果结果的商大于ah或ax所能存储的最大值,那么将如何?
比如,下面的程序段:
mov bh,1
mov ax,1000
div bh
进行的是8位除法,结果的商为1000,而1000在ah中放不下,
又比如,下面的程序段:
mov ax,1000h
mov dx,1
mov bx,1
div bx
进行的是16位除法,结果的商为11000H,而11000H在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 v dx,000fh
mov cx,0ah
call divdw
提示
给出一个公式:
X:被除数,范围:[0,FFFF FFFF]
N:除数,范围:[0,FFFF]
H:X高16位,范围:[0,FFFF]
L:X低16位,范围:[0,FFFF]
int():描述性运算符,取商,比如:rem(38/10)=8
rem():描述性运算符,取答数,比如:rem(38/10)=8
公式:X/N=int(H/N)*65536+[rem(H/N)*65536+L]/N
这个公式将可能产生溢出的除法运算:X/N,转变为多个不会产生溢出的除法运算。
公式中,等号右边的所有除法运算都可以用div指令来做,肯定不会导致除法溢出。
代码:
- assume cs:code
- code segment
- start:mov ax,4240h
- mov dx,000fh
- mov cx,0Ah
- call divdw
- mov ax,4c00H
- int 21h
- divdw:
- push ax
- mov ax,dx
- mov dx,0
- div cx
- mov bx,ax
- pop ax
- div cx
- mov cx,dx
- mov dx,bx
- ret
- code ends
- end start
复制代码
程序截图:




3.数值显示
问题
编程,将data段中的数据以十进制的形式显示出来。
data segment
dw 123,12666,1,8,3,38
data ends
这些数据在内存中都是二进制信息,标记了数值的大小。要把它们显示到屏幕上,成为我们能够读懂的信息,需要进行信息的转化。比如,数值12666,在机器中存储为二进制信息:0011000101111010B(317AH),计算机可以理解它。而我们要在显示器上读到可以理解的数值12666,我们看到的应该是一串字符:“12666”。由于 显卡遵循的是ASCII编码,为了让我们能在显示器上看到这串字符,它在机器中应以ASCII码的形式存储为:31H、32H、36H、36H、36H(字符“0”~“9”对应的ASCII码为30H~39H)。
通过上面的分析可以看到,在概念世界中,有一个抽象的数据12666,它表示了一个数值的大小。在现实世界中它可以有多种表示形式,可以在电子机器中以高低电平(二进制)的形式存储,也可以在纸上、黑板上、屏幕上以人类的语言“12666”来书写。现在,我们面临的问题就是,要将同一抽象的数据,从一种表示形式转化为另一种表示形式。
可见,要将数据用十进制形式显示到屏幕上,要进行两步工作:
(1) 将用二进制信息存储的数据转变为十进制形式的字符串:
(2) 显示十进制形式的字符串。
第二步我们在本次实验的第一个子程序中已经实现,在这里只要调用一下show_str即可。我们来讨论第一步,因为将二进制信息转变为十进制形式的字符串也是经常要用到的功能,我们应该为它编写一个通用的子程序。
子程序描述
名称:dtoc
功能:将word型数据转变为表示十进制数的字符串,字符串以0为结尾符。
参数:(ax)=word型数据
ds:si指向字符串的首地址
返回:无
应用举例:编程,将数据12666以十进制的形式在屏幕的8行3列,用绿色显示出来。
在显示时我们调用本次实验中的第一个子程序show-str。
提示
下面我们对这个问题进行一下简单地分析。
(1) 要得到字符串“12666”,就是要得到一列表示该字符的ASCII码:31H、32H、36H、36H、36H。
十进制数码字符对应的ASCII码=十进制数码值+30H
要得到表示十进制数的字符串,先求十进制数每位的值。
例:对于12666,先求得每位的值:1、2、6、6、6。再将这些数分别加上30H,便得到了表示12666的ASCII码串:31H、32H、36H、36H、36H。
(2) 那么,怎样得到每位的值呢?采用如图10.2所示的方法。

图10.2除10取余法示意
可见,用10除12666,共除5次,记下每次的余数,就得到了每位的值。
(3) 综合以上分析,可得出处理过程如下:
用12666除以10,循环5次,记下每次的余数;将每次的余数分别加30H,便得到了表示十进制数的ASCII码串,如图10.3所示。

图10.3得到十进制每位数字字符的方法示意
(4) 对(3)的质疑:
在已知数据是12666的情况下,知道进行5次循环。可在实际问题中,数据的值是多少
程序员并不知道,也就是说,程序员不能事先确定循环次数。
那么,如何确定数据各位的值已经全部求出了呢?我们可以看出,只要是除到商为0,各位的值就已经全部求出。可以使用jcxz指令来实现相关的功能。
代码:
- assume cs:code
- data segment
- db 16 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:
- mov cx,ax ;17
- jcxz bk
- push ax
- mov al,ah
- mov ah,0
- mov bl,10
- div bl
- mov cl,al
- mov ch,ah
- pop ax
- mov ah,ch
- div bl
- mov dl,ah
- mov dh,0
- push dx
- mov ah,cl
- jmp short dtoc ;29
- bk:pop ax
- add ax,30h
- mov [si],al
-
- pop ax
- add ax,30h
- mov [si+1],al
-
- pop ax
- add ax,30h
- mov [si+2],al
-
- pop ax
- add ax,30h
- mov [si+3],al ;44
-
- pop ax
- add ax,30h
- mov [si+4],al
- mov byte ptr [si+5],0
- ret
-
-
- show_str:
- mov si,0
- mov ax,0b800h
- mov es,ax
-
- mov al,160
- mul dh
- mov bx,ax
- mov al,2
- mul dl
- add bx,ax
- mov al,cl
-
- s: mov cl,[si]
- jcxz ok
- mov dx,[si]
- mov es:[bx],dx
- mov es:[bx+1],al
- inc si
- add bx,2
- loop s
- ok: ret
- code ends
- end start
复制代码
程序运行截图

至此 实验完成
题中有不足之处 希望不吝赐教
文中有的 文字是借鉴别人的 除了代码和截图
完成实验参考过别的资料,在第二个实验中受过小甲鱼视频(328/2)的启发
视频资料上云盘找:链接: http://pan.baidu.com/s/1nvpQiLz 密码: et5r
日期:2016_7_28 |
|