- [kanwangxue] 感觉楼主很乖的。有点孩子气。呵呵。 01/27 08:58
- [kanwangxue] it界,进去干嘛啊,到时候自己牛比了,首先写一个自己的语言,自己用。进入IT界不就是随波做流吗/?他 01/27 08:57
- [kanwangxue] 哥们,不要为了赚钱而学计算机。 计算机是年轻时候干的, 30岁后可以开始想赚钱,年轻嘛,不要这么悲 01/27 08:50
- [游客] 怎么说? 01/26 16:22
- [游客] 你这不行啊,差远了! 01/10 22:29
- [chinatree] 谢谢你。相信你也会成功的。 01/09 14:22
- [landyking] 哥们儿,好久不见。相信你会成功的。 12/02 23:34
- [chinatree] 哇哇,被挖坟了。莫生气,只是感觉谦虚些好,就算你的程序真的是史上最强的,也别说出来嘛,自己偷着乐就行 11/20 00:41
- [xiaoyao2012] 你的程序和我的程序比较也不见得你的程序又节约空间,在某种意义上说你的程序用了没学的指令,所以不见得你 11/19 15:39
- [heliang6291] 一起加油啊 07/02 02:45
- [游客] 急需一个汇编程序大作业。。。。。。。。。。。。。。。。。。。??? 12/28 16:50
- [游客] 这么多人喜欢你,我嫉妒了。但你不可以喜欢那么多,允许你在我不需要你的时候帮别人。如果我需要立叫立到。 11/13 13:11
- [tily] 兄弟 我来看你啦 呵呵 最近可好呀 09/26 22:17
- [stqcw] 能加我qq吗?1270911891 我在做课程设计2的时候 遇到点问题 09/24 09:16
- [stqcw] 能加我qq吗?1270911891 我的环境还是有点问题。按下A的时候不能显示满屏的A 09/20 09:36
- [stqcw] 牛人 我在xp里面运行王爽 汇编第二版 p280的例子 按下esc键没反应 呀 怎样在虚拟机里配置一 09/17 15:18
- [landyking] 呵呵,交个朋友,共同学习. 09/08 00:03
- [luyingdzx] 能知道你的QQ吗?或者请加一下我的QQ271190440,可以吗 虽然在论坛能获得解答,但是有的问 08/07 21:32
[2011-08-31 23:53] 第10章检测点及实验10
补全程序,实现从内存1000:0000处开始执行指令。
assume cs:code
stack segment
db 16 dup(0)
stack ends
code segment
start:mov ax,stack
mov ss,ax
mov sp,16
mov ax,1000h
push ax
mov ax,0h
push ax
retf
code ends
end start
检测点10·2
下面的程序执行后,ax中的数值为多少?
内存地址 机器码 汇编指令
1000:0 b8 00 00 mov ax,0
1000:3 e8 01 00 call s
1000:6 40 inc ax
1000:7 58 s:pop ax
ax=6
检测点10·3
下面的程序执行后,ax中的数值为多少?
内存地址 机器码 汇编指令
1000:0 b8 00 00 mov ax,0
1000:3 9a 09 00 00 10 call far ptr s
1000:8 40 inc ax
1000:9 58 s:pop ax
pop ax
add ax,ax
pop bx
add ax,bx
ax=1010h
检测点 10·4
下面的程序执行后,ax中的数值为多少?
内存地址 机器码 汇编指令
1000:0 b8 06 00 mov ax,6
1000:3 ff d0 call ax
1000:5 40 inc ax
1000:6 mov bp,sp
add ax,[bp]
ax=0bh
检测点10·5
(1)下面的程序执行后,ax中的数值为多少?(注意:用call指令的原理来分析,不
要在Debug中单步跟踪来验证你的结论。对于此程序,在Debug中单步跟踪的结果,不
能代表CPU的实际执行结果。)
assume cs:code
stack segment
dw 8 dup(0)
stack ends
code segment
start:mov ax,stack
mov ss,ax
mov sp,16
mov ds,ax
mov ax,0
call word ptr ds:[0eh]
inc ax
inc ax
inc ax
code ends
end start
ax=3
(2)下面的程序执行后,ax和bx中的数值为多少?
assume cs:code
data segment
dw 8 dup(0)
data ends
code segment
start:mov ax,data
mov ss,ax
mov sp,16
mov word ptr ss:[0],offset s
mov ss:[2],cs
call dword ptr ss:[0]
nop
s:mov ax,Offset s
sub ax,ss:[0cH]
mov bx,cs
sub bx,ss:[0eH]
mov ax,4c00h
int 21h
code ends
end start
ax=1,bx=0
实验10 编写子程序
在这次实验中,我们将要编写3个子程序,通过它们来认识几个常见的问题和掌握解
决这些问题的方法。同前面的所有实验一样,这个实验是必须独立完成的,在后面的课程中,将要用到这个实验中编写的3个子程序。
1.显示字符串
问题
显示字符串是现实工作中经常要用到的功能,应该编写一个通用的子程序来实现这个功
能。我们应该提供灵活的调用接口,使调用者可以决定显示的位置(行、列)、内容和颜色。
子程序描述
名称:show_str
功能:在指定的位置,用指定的颜色,显示一个用O结束的字符串。
参数:(dh):行号(取值范围0~24),(dl)=列号(取值范围0~79),
(cl)=颜色,ds:si指向字符串的首地址
返回:无
应用举例:在屏幕的8行3列,用绿色显示data段中的字符串。
assume cs:code
data segment
db 'Welcome to masm!',O
data ends
code segment
start:mov dh。8
mov dl,3
mov cl,2
mov ax,data
mov ds,ax
mov si,O
call show_str
mov ax,4c00h
int 21h
show_str: :
:
:
code ends
end start
提示
(1)子程序的入口参数是屏幕上的行号和列号,注意在子程序内部要将它们转化为显
存中的地址,首先要分析一下屏幕上的行列位置和显存地址的对应关系;
(2)注意保存予程序中用到的相关寄存器;
(3)这个子程序的内部处理和显存的结构密切相关,但是向外提供了与显存结构无关
的接口。通过调用这个子程序,进行字符串的显示时可以不必了解显存的结构,为编程提供了方便。在实验中,注意体会这种设计思想。
答:
show_str:push ax
push bx
push cx
push dx
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
mov ch,0
s:mov cl,[si]
jcxz q
mov es:[bx],cl
mov es:[bx+1],dl
add bx,2
inc si
jmp s
q:pop dx
pop cx
pop bx
pop ax
ret
2.解决除法溢出的问题
问题
前面讲过,div指令可以做除法。当进行8位除法的时候,用al存储结果的商,ah存
储结果的余数;进行16位除法的时候,用ax存储结果的商,dx存储结果的余数。可
是,现在有一个问题,如果结果的商大于al或ax所能存储的最大值,那么将如何?
比如,下面的程序段:
mov bh.1
mov ax,1000
div bh
进行的是8位除法,结果的商为1000,而1000在al中放不下。
又比如,下面的程序段:
进行的是16位除法,结果的商为11000h,而1 1000I-{在ax中存放不下。
我们在用div指令做除法的时候,很可能发生上而的情况:结果的商过大,超出了寄
存器所能存储的范围。当CPU执行div等除法指令的时候,如果发生这样的情况,将引
发CPU的一个内部错误,这个错误被称为:除法溢出。我们可以通过特殊的程序来处理
这个错误,但在这里我们不讨论这个错误的处理,这是后面的课程中要涉及的内容。下面我们仅仅来看一下除法溢出发生时的一些现象,如图10.1所示。
图10.1 除法溢出时发生的现象
图中展示了在Windows 2000中使用Debug执行相关程序段的结果,div指令引发了
CPU的除法溢出,系统对其进行了相关的处理。
好了,我们已经清楚了问题的所在:用div指令做除法的时候可能产生除法溢出。由
于有这样的问题,在进行除法运算的时候要注意除数和被除数的值,比如1000000/10就
不能用div指令来计算。那么怎么办呢?我们用下面的子程序divdw解决。
子程序描述
名称:divdw
功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为
dword型。
参数:(ax)=dword型数据的低16位
(dx)=dword型数据的高1 6位
(cx)=除数
返回:(dx)=结果的高16位,(ax)=结果的低16位
(cx)=余数
应用举例:计算1000000/10(F4240H/0AH)
mov ax,4240H
mov dx,000FH
mov CX,0AH
call divdw
结果:(dx)=0001H,(ax)=86AOH,(cx)=0
提示
给出一个公式:
X:被除数,范围:【0,FFFFFFFF]
N:除数,范围:【0,FFFF]
H:X高16位,范围:【0,FFFF]
L:X低16位,范围:【0,FFFF】
intO:描述性运算符,取商,比如,int(38/10)=3 ·
rem0:描述性运算符,取余数,比如,rem(38/10)=8
公式:X/N=int(H/N)*65536+[rem(H/N)*65536+L]/N
这个公式将可能产生溢出的除法运算:X/N,转变为多个不会产生溢出的除法运算。
公式中,等号右边的所有除法运算都可以用div指令来做,肯定不会导致除法溢出。
(关于这个公式的推导,有兴趣的读者请参看附注5)
答:
divdw:push ax
mov ax,dx
xor dx,dx
div cx
push ax
mov bp,sp
mov ax,[bp+2]
div cx
mov cx,dx
pop dx
add sp,2
ret
3.数值显示
问题
编程,将data段中的数据以十进制的形式显示出来。
data segment
dw 123,12666,1,8,3,38
data ends
这些数据在内存中都是二进制信息,标记了数值的大小。要把它们显示到屏幕上,成
为我们能够读懂的信息,需要进行信息的转化。比如,数值12666,在机器中存储为二进
制信息:001100010111lOl0B(317AH),计算机可以理解它。而要在显示器上读到可以理
解的数值12666,我们看到的应该是一串字符: “12666”。由于显卡遵循的是ASCII编码,为了让我们能在显示器上看到这串字符,它在机器中应以ASCII码的形式存储为:
3lH、32H、36H、36H、36H(字符“0”~“9”对应的ASCII码为30H~391扪。
通过上面的分析可以看到,在概念世界中,有一个抽象的数据12666,它表示了一个
数值的大小。在现实世界中它可以有多种表示形式,可以在电子机器中以高低电平(二进
制)的形式存储,也可以在纸上、黑板上、屏幕上以人类的语言“12666”来书写。现在,我们面临的问题就是,要将同一抽象的数据,从一种表示形式转化为另一种表示形式。
可见,要将数据用十进制形式显示到屏幕上,要进行两步工作:
(1)将用二进制信息存储的数据转变为十进制形式的字符串;
(2)显示十进制形式的字符串。
第二步我们在本次实验的第一个子程序中已经实现,在这里只要调用一下show str即
可。我们来讨论第一步,因为将二进制信息转变为十进制形式的字符串也是经常要用到的
功能,我们应该为它编写一个通用的子程序。
子程序描述
名称:dtoc
功能:将word型数据转变为表示十进制数的字符串,字符串以0为结尾符。
参数:(ax)=word型数据
ds:si指向字符串的首地址
返回:无
应用举例:编程,将数据12666以十进制的形式在屏幕的8行3列,用绿色显示出
来。在显示时我们调用本次实验中的第一个子程序show str。
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
call show_str
code ends
end start
提示
下面我们对这个问题进行一下简单地分析。
(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除12666,共除5次,记下每次的余数,就得到了每位的值。
(3)综合以上分析,可得出处理过程如下。
用12666除以10,循环5次,记下每次的余数;将每次的余数分别加30H,便得到
了表示十进制数的ASCII码串。如下:
(4)对(3)的质疑。
在已知数据是12666的情况下,知道进行5次循环。可在实际问题中,数据的值是多
少程序员并不知道,也就是说,程序员不能事先确定循环次数。
那么,如何确定数据各位的值已经全部求出了呢?我们可以看出,只要是除到商为
0,各位的值就已经全部求出。可以使用jcxz指令来实现相关的功能。
答:
dtoc:push cx
push dx
push bx
xor bx,bx
mov cx,10
s:xor,dx,dx
div cx
add dx,30h
push dx
inc bl
cmp ax,0
jnz s
mov cx,bx
s1:pop [si]
add si,2
loop s1
pop bx
pop dx
pop cx
ret
[ tomato 发表于 2011-09-01 09:05 ]
dtoc子程序可以再优化一下。
[ rotapple 发表于 2011-09-01 11:50 ]
add sp,2
ret
这个不是可以直接ret 2吗。
不过貌似书里面没讲用ss建临时变量。
[ chinatree 发表于 2011-09-01 12:09 ]
那是在<加密与解密>中学到的,属于堆栈平衡。
dtoc:push cx
push bx
xor bx,bx
mov cx,10
s:xor,dx,dx
div cx
add dx,30h
push dx
inc bl
cmp ax,0
jnz s
mov cx,bx
s1:pop [si]
add si,2
loop s1
Mov [si],cl
pop bx
pop cx
ret
[ xiaoyao2012 发表于 2012-11-19 15:39 ]
你的程序和我的程序比较也不见得你的程序又节约空间,在某种意义上说你的程序用了没学的指令,所以不见得你的程序有多高效
[ chinatree 发表于 2012-11-20 00:41 ]
哇哇,被挖坟了。莫生气,只是感觉谦虚些好,就算你的程序真的是史上最强的,也别说出来嘛,自己偷着乐就行。