首页
论坛
课程
招聘
[成果2.1]汇编实验10—— 编写子程序
2008-1-7 09:39 17173

[成果2.1]汇编实验10—— 编写子程序

2008-1-7 09:39
17173
实验10编写子程序
        在这次实验中,我们将要编写3个子程序,通过它们来认识几个常见的问题和掌握解决这些问题的方法。同前面的所有实验一样,这个实验是必须要独立完成的,在后面的课程中,将要用到这个实验中编写的3个子程序。
1.        显示字符串
问题
显示字符串是现实工作中经常要用到的功能,应该编写一个通用的子程序来实现这个功能。我们应该提供灵活的调用接口,使调用者可以决定显示的位置(行、列)、内容和颜色。

提示
(1)        子程序的入口参数是屏幕上的行号和列号,注意在子程序内部要将它们转化为显存中的地址,首先要分析一下屏幕上的行列位置和显存地址的对应关系:
(2)        注意保存子程序中用到的相关寄存器:
(3)        这个子程序的内部处理和显存的结构密切相关,但是向外提供了与显存结构无关的接口。通过调用这个子程序,进行字符串的显示时可以不必了解显存的结构,为编程提供了方便。在实验中,注意体会这种设计思想。

子程序描述
名称:show_str
功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
参数:(dh)=行号(取值范围0~24),(dl)=列号(取值范围0~79),
          (cl)=颜色,ds:si指向字符串的首地址
返回:无
就用举例:在屏幕的8行3列,用绿色显示data段中的字符串。

;==========================================================================
;文件名:exp10a.asm
;目的:完成并测试在指定的位置,用指定的颜色,显示一个用0结束的字符串的子程序
;==========================================================================
assume cs:code,ds:data
data segment
 str db '^_^Welcome to masm! ^_^',0
data ends
code segment
start:
	mov ax,data
	mov ds,ax
	mov dh,12
	mov dl,30
	mov cl,10001010b
	mov si,0
	call show_str
	mov ax,4c00h
	int 21h
;==============================================================
;名称:show_str
;功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串
;参数:(dh)=行号(取值范围0~24),(dl)=列号(取值范围0~79),
;	(cl)=颜色,ds:si指向字符串的首地址
;返回:无
;==============================================================
show_str: 
	push dx
	push si
	push di
	push cx
	push ax
        mov ax,0b800h
        mov es,ax
	mov ax,160
	mul dh
        mov dh,0
        add ax,dx
        add ax,dx
	sub ax,2
	mov di,ax
        mov ah,cl
output:	
        mov ch,ds:[si]
        mov cl,0
	jcxz ok
        mov byte ptr es:[di],ch
        mov byte ptr es:[di+1],ah
	inc si
	inc di
	inc di
        jmp short output
ok:
	pop ax
	pop cx
	pop di
	pop si
	pop dx
	ret
code ends
end start

测试结果:

图10_1.1在指定位置以指定颜色显示字符串

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的一个内部错误。这个错误被称为:除法溢出。我们可以通过特殊的程序来处理这个错误, 这里我们不讨论这个错误的处理,这是后面的课程中要涉及的内容。下面我们仅仅来看一下除法溢出发生时的一些现象,参考图10.1

图10.1除法溢出

图10.1中展示了在windowsXP中使用DEBUG执行相关程序段的结果,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指令来做,肯定不会导致除法溢出。

程序如下:
;=================================================================
;文件名:exp10b.asm
;目的:8086下实现32位除法功能,解决16位除法溢出的问题
;=================================================================
assume cs:code,ss:stack
stack segment
dw 8 dup(0)
stack ends
code segment
start:
	mov ax,stack
	mov ss,ax
	mov sp,16
	mov ax,4c40h
	mov dx,0fh
	mov cx,0ah
	call divdw2
	mov ax,4c00h
	int 21h
;=================================================================================
;名称:divdw
;功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型
;参数:(ax)=dword型数据的低16位
;      (dx)=dword型数据的高16位
;      (cx)=除数
;返回:(dx)=结果的高16位,(ax)=结果的低16位
;      (cx)=余数
;==================================================================================
divdw:
	push bx
	push ax
	mov ax,dx
	mov dx,0
	div cx
	mov bx,ax
	pop ax
	div cx
	mov cx,dx
	mov dx,bx
	pop bx
 	ret
code ends
end start


分析:


图10_2.1加载程序到内存并查看相关寄存器的内容



图10_2.2运行程序并查看结果(DX)=0001H,(AX)=86A0H,(CX)=0,和题目所给结果一致

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所示的方法。



[LEFT]图10.2除10取余法示意
[/LEFT]

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



图10.3得到十进制每位数字字符的方法示意

(4)        对(3)的质疑:
在已知数据是12666的情况下,知道进行5次循环。可在实际问题中,数据的值是多少
程序员并不知道,也就是说,程序员不能事先确定循环次数。
        那么,如何确定数据各位的值已经全部求出了呢?我们可以看出,只要是除到商为0,各位的值就已经全部求出。可以使用jcxz指令来实现相关的功能。

;=====================================================
;文件名:exp10c.asm
;目的:实现一通用的将十进制数输出到屏幕的子程序
;=====================================================

assume cs:code,ds:data,ss:stack

data segment
 db 10 dup(0)
data ends

stack segment
 dw 128 dup(0)
stack ends

code segment
start:
	mov ax,stack
	mov ss,ax
	mov sp,128

	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
;功能:将word型数据转变为表示十进制数的字符串,字符串以0为结尾符
;参数:(ax)=word型数据
;	ds:si指向字符串的首地址
;返回:无
;=================================================================
dtoc:
	push ax
	push bx
	push cx
	push si	
	push di

	mov di,si
	mov bx,10	
	mov cx,0
s:
	push cx
	mov cx,ax
	jcxz enddtoc
	pop cx
	mov dx,0
	div bx
	mov ds:[di],dl
	add byte ptr ds:[di],30h 
	inc di
	inc cx
	jmp short s
enddtoc:
	pop cx
	mov ds:[di],0
	dec di
	mov ax,cx
	mov bl,2
	div bl
	mov cl,al
s1:
	mov al,ds:[di]
	mov bl,ds:[si]
	mov ds:[di],bl
	mov ds:[si],al
	inc si
	dec di
	loop s1
	
	pop di
	pop si
	pop cx
	pop bx
	pop ax
	ret	

;===========================================================
;名称:show_str
;功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串
;参数:(dh)=行号(取值范围0~24),(dl)=列号(取值范围0~79),
;	(cl)=颜色,ds:si指向字符串的首地址
;返回:无
;===========================================================

show_str: 
	push dx
	push si
	push di
	push cx
	push ax

        mov ax,0b800h
        mov es,ax
	mov ax,160
	mul dh
        mov dh,0
        add ax,dx
        add ax,dx
	sub ax,2
	mov di,ax
        mov ah,cl
output:	
        mov ch,ds:[si]
        mov cl,0
	jcxz ok

        mov byte ptr es:[di],ch
        mov byte ptr es:[di+1],ah
	inc si
	inc di
	inc di
        jmp short output
ok:
	pop ax
	pop cx
	pop di
	pop si
	pop dx
	ret
code ends
end start


运行结果:


图10_3.1结果为12666,处于8行3列,符合题目要求

总结:设计第一个子程序相当顺利,可能是前一个实验做得认真吧,所以没遇到什么大问题。
这次实验,感觉难度最大的就是做2个子程序的设计,在网上向好友请教了N回,可是最后也只是能把那个32位除法公式弄懂,而无法领悟使用div指令来实现那个32位除法公式。 最后不得不参考了一下正确的答案,才总算弄明白是怎么实现的.
        设计最后一个子程序dtoc的时候,没想过要写成现在这么长的,真正求余数的指令序列就几条,等我设计好之后,发现输出来的数字串和原来的数是倒过来的,晕啊!!我苦想了一下,想里面应该添加将转换后的数字串逆序排序的操作,这又涉及到了很多东西啊,最后我决定使用LOOP指令,在添加这个功能里去的时候,遇到了好多的问题啊,我还是犯了一些低级的错误,如将16位的寄存器的内容传送到字符型数字串里去,这很明显是不对的啊,在逆序时寄存器的高16位会直接把正常的数字字符冲掉,还有就是将当我直接将字型单元的内容传送字型单元时,编译器会报错,不过提示是它后面的指令错误,好郁闷啊!
好不容易才添加进这个功能,终于松一口气了。想到转换后的数字串的末尾如果不是0的话,在调用输出子程序时会出现BUG,于是又在转换子程序里加了条指令,在第一次转换出来后,在数字串的末尾添加一个终止标志0。

题外话:这次实验从开始到结束花了我两天的时间做完,继续加油!

[看雪官方培训] Unicorn Trace还原Ollvm算法!《安卓高级研修班》2021年6月班火热招生!!

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (16)
雪    币: 193
活跃值: 活跃值 (16)
能力值: ( LV12,RANK:370 )
在线值:
发帖
回帖
粉丝
CCDeath 活跃值 9 2008-1-7 09:59
2
0
学习 学习....
雪    币: 366
活跃值: 活跃值 (11)
能力值: ( LV9,RANK:430 )
在线值:
发帖
回帖
粉丝
没有风 活跃值 10 2008-1-7 11:03
3
0
占个位置留以后用。
雪    币: 207
活跃值: 活跃值 (29)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
sislcb 活跃值 7 2008-1-7 14:49
4
0
写的好详细啊。。不是成果吗?
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
girl 活跃值 1 2008-1-7 19:13
5
0
16位的,不会
雪    币: 247
活跃值: 活跃值 (10)
能力值: ( LV9,RANK:420 )
在线值:
发帖
回帖
粉丝
小子贼野 活跃值 10 2008-1-7 20:26
6
0
期待楼主所有贴的CHM,用来给我等菜鸟做汇编入门教程,真的忒好了,特别是那几个试验报告,忒爽了
雪    币: 201
活跃值: 活跃值 (22)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
szdbg 活跃值 5 2008-1-7 21:12
7
0
LZ长期对16位的汇编孜孜不倦,看来是胸怀大志啊,偑服
雪    币: 200
活跃值: 活跃值 (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
baicker 活跃值 2008-1-8 13:52
8
0
我要实验3
雪    币: 366
活跃值: 活跃值 (11)
能力值: ( LV9,RANK:430 )
在线值:
发帖
回帖
粉丝
没有风 活跃值 10 2008-1-8 22:30
9
0
这个是实验课题的补充,成果还没出来,不过就快了。


实验3有是有,很短的,主要是关于堆栈溢出导致的CPU执行无效指令的错误。

还有就是谢谢各位的关注和支持,同时我也欢迎对汇编有兴趣的朋友加入到这次专题运行中来,让我们一起努力吧,把我们的努力变成看得见摸得着的丰硕的成果。
雪    币: 1613
活跃值: 活跃值 (27)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
北极星2003 活跃值 25 2008-1-8 23:58
10
0
写的很好啊,顶!
雪    币: 478
活跃值: 活跃值 (574)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
sudami 活跃值 25 2008-1-9 08:35
11
0
学习

呵呵,
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
closeall 活跃值 2008-1-31 11:13
12
0
好文章, 学习
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zilaiye 活跃值 2008-1-31 13:16
13
0
楼主牛人啊!记得大学时有一个汇编课程设计是输入10个数并把它从大到小排序在屏幕上显示出来,哥们愣是们搞出来,不得已抄一个交了。惭愧啊!
雪    币: 173
活跃值: 活跃值 (10)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
Maggle 活跃值 1 2008-1-31 16:26
14
0
楼主这个专题是你的主意吗?这个是你自已想出来的吗?
我怎么看这个题目都是我老师王爽的书上原题,当时我在课堂上学的就是这段内容,是不是以后你的专题该有打印“DEC公司”数据的内容了、把“DEC”公司数据输出到显示器上,最后来个课程设计编写一个开机DOS下自启动的程序。

你现在拿出来随随便便就说是个专题这算什么,你起码也应该说明一下吧。
雪    币: 205
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
阳小子 活跃值 1 2008-4-15 15:37
15
0
确实这是学8088/8086书中的题目,不过这也应该是楼主的实验课题吧!
雪    币: 793
活跃值: 活跃值 (10)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
zyr零零发 活跃值 1 2008-6-16 12:24
16
0
的确是王爽老师的作业题。
王爽的书籍让我初步知道了什么是汇编原理,具体细节很详细。但是在WINDOWS下还是用32位的汇编吧
雪    币: 229
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
MDB 活跃值 2009-5-29 17:36
17
0
强人呀,向你学习,留下脚印
游客
登录 | 注册 方可回帖
返回