王爽《汇编语言》实验10

1.显示字符串

子程序描述
名称:show_str
功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
参数:(dh)=行号(取值范围0~24),(dl)=列号(取值范围0~79),
          (cl)=颜色,ds:si指向字符串的首地址
返回:无
应用举例:在屏幕的8行3列,用绿色显示data段中的字符串。
提示
(1)子程序的入口参数是屏幕上的行号和列号,注意在子程序内部要将它们转化为显存中的地址,首先在分析一下屏幕上的行列位置和显存地址的对应关系;(参见:王爽《汇编语言》实验9)
(2)注意保存子程序中用到的相关寄存器
(3)这个子程序的内部处理和显存的结构密切相关,但是向外提供了显存结构无关的接口。通过调用这个子程序,进行字符串的显示时可以不必了解显存的结构,为编程提供了方便。在实验中,注意体会这种设计思想。

参考代码:

; 在屏幕的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

; 在指定的位置,用指定的颜色,显示一个用0结束的字符串
; 参数: (dh)=行号(取值范围0~24),(dl)=列号(取值范围0~79),
; (cl)=颜色,ds:si指向字符串的首地址
; 返回:无
show_str: mov ax,0B800H
          mov es,ax
          mov di,0      ; 显存0页的起始位置,0行,0列

          mov al,0A0H
          mul dh
          add di,ax     ; 计算行的开始位置

          mov al,2
          mul dl
          add di,ax     ; 计算列的开始位置

          mov bl,cl     ; 保留颜色值
       s: mov cl,[si]   ; 取得显示字符
          mov ch,0
          jcxz ok
          mov es:[di],cl   ; 将字符写入显存
          mov es:[di+1],bl
          add di,2
          inc si
          loop s
      ok: ret
code ends
end start

在MS-DOS 7.10下运行结果:

show_str

2.解决除法溢出问题

子程序描述
名称:divdw
功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型。
参数:(ax)=dword型数据的低16位 
          (dx)=dword型数据的高16位
          (cx)=除数
返回:(dx)=结果的高16位,(ax)=结果的低16位 
          (cx)=余数
应用举例:计算1000000/10(FA240H/0AH)
提示
结出一个公式:
X: 被除数,范围:[0,FFFFFFFF]
N: 除数,范围:[0,FFFF]
H: X高16位,范围:[0,FFFF]
L: X低16位,范围:[0,FFFF]
int():描述性运算符,取商,比如:int(38/10)=3
rem():描述性运算符,取余数,比如:rem(38/10)=8
公式:X/N=int(H/N)*65536+[rem(H/N)*65536+L]/N
这里的65536为10000H,也就是说,int(H/N)*10000H为高16位的商,而[rem(H/N)*65536+L]/N得到的是低16位的商和余数;
这个公式将可能产生溢出的除法运算: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

; 功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型
; 参数:(ax)=dword型数据的低16位
;       (dx)=dword型数据的高16位
;       (cx)=除数
; 返回:(dx)=结果的高16位,(ax)=结果的低16位
;       (cx)=余数
divdw: mov bx,ax      ; 保存低位

       mov ax,dx
       mov dx,0H      ; 进行H/N 的运算
       div cx         ; 因为除数为16位的,使用div,则设置的被除数为32位

       mov si,ax
	   ; 用于保存int(H/N),因为rem(H/N)*65536就相当于高16位,dx了,故得到的余数在dx直接做高位

       mov ax,bx      ; [rem(H/N)*65536+L]
       div cx         ; [rem(H/N)*65536+L]/N,结果是的ax保存为16位的低位

       mov cx,dx      ; 将余数写入cx
       mov dx,si      ; 将高位写入dx

       ret
code ends
end start

运行结果:

divdw

由AX和DX可知:这里得到的结果为186A0H=100000;余数CX为0,结果正确。

3.数值显示

子程序描述
名称:dtoc
功能:将word型数据转变为表示十进制数的字符串,字符串以10为结尾符。
参数:(ax)=word型数据,ds:si指向字符串首地址
返回:无
应用举例:编程,将数据12666以十进制的形式在屏幕的8行3列,用绿色显示出来。在显示时我们调用本次实验中的第一个子程序show_str.
提示
0~9的ASCII码为30H~39H,则先由十六进制转化为十进制,再转ACSII码,转换示意图如下:

dataToChar

同时要注意,依次得到的是个位,十位,百位……万位这与我们习惯的从左到右的万位……个位顺序颠倒。

参考代码:

assume cs:code
data segment
  db 10 dup (0)
  ; 用于存入转换为ASCII码的数据
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
 ; 功能:将word型数据转变为表示十进制的字符串,字符串以0为结尾符
 ; 参数: (ax)=word型数据,ds:si指向字符串首地址
 ; 返回: 无
 dtoc: mov si,8           ; 从存ASCII码数据段倒数第2个字节单元开始,相当于添加结尾符0
   s0: mov cx,10
       mov dx,00
       div cx
       add dx,30H         ; 余数+30H 得到ASCII码
       mov [si],dl

       mov cx,ax
       jcxz break         ; 判断商是否为0,如果为0则返回

       dec si
       inc cx             ; 注意在执行loop循环时,先执行的是CX=CX-1,然后判断CX是否为0
       loop s0
break: ret

show_str: mov ax,0B800H
          mov es,ax
          mov di,0

          mov al,0A0H
          mul dh
          add di,ax

          mov al,2
          mul dl
          add di,ax 

          mov bl,cl
       s: mov cl,[si]
          mov ch,0
          jcxz ok
          mov es:[di],cl
          mov es:[di+1],bl
          add di,2
          inc si
          loop s
      ok: ret
code ends
end start

MS-DOS 7.10下运行结果:

show_d

总结:

这里的子程序都只是以实现编程目地而编写的,不具有一定的通用性,因为在每一个子程序中都直接修改寄存器来做为临时变量,8086CPU中的寄存器是有限的,这样可以在使用ret指令退回到主程序时可以造成寄存器冲突,解决的办法是使用如下编程框架:

子程序开始:子程序中使用的寄存器入栈
            子程序内容
			子程序中使用的寄存器出栈
			返回(ret,retf)

转载于:https://my.oschina.net/websoft/blog/77361

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值