汇编网首页登录博客注册
chinatree的学习博客
博客首页博客互动【做检测题】论坛求助

我的博客

个人首页 |  我的文章 |  我的相册 |  我的好友 |  最新访客 |  文章收藏 |  论坛提问 |  友情链接 |  给我留言  
图片载入中
学习动态
友情链接

[2011-08-31 23:53] 第10章检测点及实验10

检测点10·1
补全程序,实现从内存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
评论次数(5)  |  浏览次数(882)  |  类型(汇编作业) |  收藏此文  | 

[  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  ]

哇哇,被挖坟了。莫生气,只是感觉谦虚些好,就算你的程序真的是史上最强的,也别说出来嘛,自己偷着乐就行。

 
 请输入验证码  (提示:点击验证码输入框,以获取验证码