汇编语言(王爽)实验十 编写子程序

标 题: 汇编实验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

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页