王爽汇编第二版实验10(整理)

1.

;--------------------------------------------------------------------------------------
show_str:
;push指令保护子程序用到的寄存器
        push bx
        push cx
        push dx
        push si
        push di
;指定显存区域
        mov ax,0b800h
        mov es,ax
;初始化行
        mov ax,0a0h
 dec dh
        mul dh
        mov bx,ax
;初始化列
        mov ax,2
        mul dl
        mov di,ax
;将字符属性存入AH中
        mov ah,cl
        mov cx,16
;开始逐字符显示
s:
        mov al,[si]       ;源地址DS:SI指向数据段字符
        cmp al,0          ;如果遇到0,则表示循环结束跳至ok处退出子程序
        jz ok
        mov es:[bx+di],ax ;目标地址ES:[BX+DI],AH=属性,AL=字符
        inc si            ;指向数据段下一个字符
        add di,2          ;指向下一列
        loop s            ;如果CX≠0,继续循环

ok:
;恢复保护的寄存器
        pop di
        pop si
        pop dx
        pop cx
        pop bx
;返回调用
        ret

 

2.

 assume cs:code

stack segment
        dw 8 dup(0)
stack ends

code segment
start:  mov ax,stack
        mov ss,ax
        mov sp,16
        mov ax,4240h ;被除数为000f4240H
        mov dx,0fh 
        mov cx,0ah ;除数为0aH  
        call divdw ;int(H/N)
        
        mov ax,4c00h
        int 21h
        
divdw:  push ax
        mov ax,dx ;因为此时要做的是16位的除法,所以要设置AX,DX。根据公式这里做的是被除数的高位除以除数,被除数的高位为0FH,放到在16位除法中就应该是dx=0000h,ax=000fh

        mov dx,0
        div cx
        
        mov bx,ax ;16位除法完后商会保存在ax中,我们先把高位除以除数的商保存在bx中
        pop ax ;把被除数的低位从栈中取出
        div cx ;这一步是关键,很多人不明白公式的后半段[rem(H/N)*65536+L]/N,在这我跟大家解释一 下,65536是10进制数,转换为16进制也就是10000.
               ;这里很多人要问,我们并没有用高位的余数*10000H+低位,而是直接用低位当被除数直接开始除法了!类似与L/N了,我想说你错了!因为在高位的除法中它的余数直接保留在DX中,而在低位除法中,DX正是这次除法的高位!
               ;也就是说,rem(H/N)*10000H这一步我们已经做了,这段公式不就说让我们把高位的余数放到高位吗?然后再加上低位组成一个新的被除数再除以除数[rem(H/N)*65536+L]这段公式的目的并不是要我们把数值相加,而是让我们完成一个32位的被除数,也就是用原始被除数的余数与原始被除数低位凑成一个完整的数如:设rem(H/N)值为0006H,L=4240H,这个数应该是00064240H

        mov cx,dx
        mov dx,bx
        ret
code ends
end start

3.

第10章实验10.3  数值显示 (带详细注释);;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;名称: dtoc
;功能:将word型数据转变为表示十进制数的字符串,字符串以0为结尾符.
;参数:(ax)=word型数据
;        ds:si指向字符串的首地址
;返回: 无
;应用举例:编程将数据12666以十进制的形式在屏幕的8行3列,用绿色显示
;出来.在显示时我们调用本次实验中的第一个子程序show_str.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;author:chinaljg
;time:2011年1月22日

assume cs:code
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  ;调用完后,ds:si应指向data段中的以0结尾的字符串

        mov dh,8  ;提供入口参数8行
        mov dl,3  ;提供入口参数3列
        mov cl,2  ;提供入口参数绿色
        mov si,0  ;ds:si指向字符串首地址
        call show_str  ;调用子程序显示ds:si指向的以0结尾的字符串

        mov ax,4c00h ;程序正常返回
        int 21h
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;名称: dtoc
;功能:将word型数据转变为表示十进制数的字符串,字符串以0为结尾符.
;参数:(ax)=word型数据
;        ds:si指向字符串的首地址
;返回: 无
;应用举例:编程将数据12666以十进制的形式在屏幕的8行3列,用绿色显示
;出来.在显示时我们调用本次实验中的第一个子程序show_str.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
dtoc:
        push ax ;子程序调用的寄存器入栈
        push bx ;子程序调用的寄存器入栈
        push cx ;子程序调用的寄存器入栈
        push dx ;子程序调用的寄存器入栈
        push ds ;子程序调用的寄存器入栈
        
        mov dx,0
        mov bx,0ah
        div bx   ;用dxax/bx 16位除法,dx放余数,ax放商,ax是提供的入口参数
        add dx,30h ;把数据码转变为ASCII码
        push dx        ;入栈
        mov cx,ax
        jcxz savedata
        call dtoc   ;通过递归就不用考虑出栈的数量.考虑:在那种情况下用递归?(除法取余)
savedata:
        ;第一次:31 00
        ;第二次:31 32 00
        ;第三次:31 32 36 00
        ;......
        pop dx        
        mov ds:[si],dx
        inc si
        mov byte ptr ds:[si],0
        
        pop ds        ;子程序调用的寄存器出栈
        pop dx         ;子程序调用的寄存器出栈
        pop cx        ;子程序调用的寄存器出栈
        pop bx        ;子程序调用的寄存器出栈
        pop ax        ;子程序调用的寄存器出栈
        ret  ;递归后,ret返回到了savedata标号处,最后一层ret才返回到mov dh,8处.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



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

show_str:
        push ax        ;子程序用到的寄存器入栈
        push bx        ;子程序用到的寄存器入栈
        push cx        ;子程序用到的寄存器入栈
        push dx        ;子程序用到的寄存器入栈
        push ds        ;子程序用到的寄存器入栈
        push si        ;子程序用到的寄存器入栈
        push es        ;子程序用到的寄存器入栈
        push di        ;子程序用到的寄存器入栈

    mov ax,0b800h
        mov es,ax
        mov di,0   ;es:di指向显示缓存区,用来显示字符串

        ;根据入口参数行号和列号计算显存中的地址
                
        mov al,160  ;8位乘法,一个默认放在al中,另一个放在8位的reg或内存单元,结果默认放在ax中,思考:ah不用清0的原因。

         mul dh
        add ax,160  ;加上第0行,因为行号是从0开始的。行号是8指的其实是第9行
    add dl,dl ;一个字符占2列,一列放字符,另一列放这个字符的颜色属性,请思考:这儿为什么不加第0列?
    mov dh,0  ; ax与cl中的数相加就是显存地址,但是ax与cl长度不对等,不能直接相加
    add ax,dx   ;ax中存放的就是8行3列所对应的显存地址的开始位置
        mov di,ax   ;用di作为显存地址的偏移量

show_str_display:
        push cx   ;下面要用cx来做判断,为了不破坏原数据,入栈
        mov cl,ds:[si] ;将字符串中的字符传送到显示缓冲区
        mov es:[di],cl
        mov ch,0        ;构造cx用来判断是否到字符串的结束符‘0’
        jcxz  show_str_ok   ;字符已经显示完毕,返回   
        
        pop cx        ;没有到字符串结尾,出栈用来设置字符颜色。思考:若到字符串结尾了,你的出栈指令写在哪?
        mov es:[di].1,cl  ;给字符设置颜色属性
        inc si                 ;下一个字符
        add di,2             ;下一个用来显示字符的显存地址(一个字符要想显示要占2个显存地址,一个是字符,另一个是颜色)
        jmp short show_str_display

show_str_ok:
        pop cx  ;到字符串结尾,出栈。以免影响子程序返回的ip
        pop di  ;子程序用到的寄存器出栈,注意顺序
        pop es  ;子程序用到的寄存器出栈,注意顺序
        pop si  ;子程序用到的寄存器出栈,注意顺序
        pop ds  ;子程序用到的寄存器出栈,注意顺序
        pop dx ;子程序用到的寄存器出栈,注意顺序
        pop cx  ;子程序用到的寄存器出栈,注意顺序
        pop bx  ;子程序用到的寄存器出栈,注意顺序
        pop ax  ;子程序用到的寄存器出栈,注意顺序
        ret  ;返回

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
code ends
end start

你可能感兴趣的:(编程,c,汇编,360)