实验10

1 显示字符串

问题

显示字符串是现实工作中经常要用到的功能,应该编写一个通用的子程序来实现这个功能。我们应该提供灵活的调用接口,使调用者可以界定现实的位置(行、列)、内容和颜色。

子程序描述

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

提示

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

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:
        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
    loop_str:    
        ; 取得显示字符
        mov cl,ds:[si]
        ; 设置显示颜色
        mov ch,dl
        ; 将字符写入显存
        mov es:[bx],cx
        mov ch,0
        jcxz ok
        inc si
        add bx,2
        loop loop_str
    ok:        
        ret
    code ends
    end start

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,04240H     ;设置被除数
    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)=余数
;如果除数是16位的,被除数是DX:AX,商在AX中,余数在DX中
divdw: 

    ;保存低位
    mov bx,ax
    ;计算高位的商
    mov ax,dx
    mov dx,0
    div cx
    ;记录 高位的商
    mov si,ax

    mov ax,bx
    ;请注意 这时 商在ax 中 余数在dx中
    ;余数dx 和 新赋值进来的 低位 ax 正好符合 [rem(H/N)*65536 + L]
    div cx
    mov cx,dx
    mov dx,si
    ret
code ends
end start

3 数值显示

子程序描述

名称:dtoc
功能:将word型数据转变为表示十进制数的字符串,字符串以10为结尾符。
参数:(ax)=word型数据,ds:si指向字符串首地址
返回:无
应用举例:编程,将数据12666以十进制的形式在屏幕的8行3列,用绿色显示出来。在显示时我们调用本次实验中的第一个子程序show_str.
提示
09的ASCII码为30H39H,则先由十六进制转化为十进制,再转ACSII码,转换示意图如下:
图片3.6
同时要注意,依次得到的是个位,十位,百位……万位这与我们习惯的从左到右的万位……个位顺序颠倒

assume cs:code
    data segment
        db 10 dup (0)
    data ends

    code segment
    start: 
        mov ax,data
        mov ds,ax

        call dtoc

        mov dh,8          ; 行数
        mov dl,3          ; 列数
        mov cl,2          ; 绿色
        mov si,1          ; 数据
        call show_str
        mov ax,4c00h
        int 21h

    dtoc:
        mov ax,12666
        mov cx,5
    c:     mov bx,10
        mov dx,0
        div bx
        mov bx,cx
        add dl,30h
        mov ds:[bx],dl
        loop c
        ret
    show_str:
        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
    loop_str:    
        ; 取得显示字符
        mov cl,ds:[si]
        ; 设置显示颜色
        mov ch,dl
        ; 将字符写入显存
        mov es:[bx],cx
        mov ch,0
        jcxz ok
        inc si
        add bx,2
        loop loop_str
    ok:        
        ret
    code ends
    end start