1. 程式人生 > >組合語言實驗十

組合語言實驗十

一、編寫一個通用的子程式來實現顯示字串的功能

名稱:show_str

功能:在指定的位置,用指定的顏色,顯示一個用 0 結束的字串。

引數:(dh)= 行號;(dl)= 列號;(cl)= 顏色,ds:si 指向字串的首地址

返回:無

應用舉例:在螢幕的 3 行 8 列,用綠色顯示 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: ............ code ends end start

 

本題由於引數的個數不是很多,所以不用將引數放進棧中儲存。

在編寫子程式之前還是先回答兩個問題:

  • 要處理的資料在什麼地方?
  • 要處理的資料有多長?

首先本題要處理的資料就是 data 段中的內容,在進行子程式呼叫之前就已經將 data 段的首地址儲存進了暫存器中以備用。

然後就是 data 段的資料有多長的問題,很顯然 data 段中的資料是以 0 字元作為結尾,可以使用 jcxz 指令來檢測 0 而知道資料是否處理完,所以子程式不需要字串的長度引數。

下面是子程式的程式碼:

assume cs:code,ds:data

data segment
        db 'welcome to masm!',0
data ends

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

    mov dh,8
    mov dl,3
    mov cl,2
    call show_str
    
mov ax,4c00h int 21h show_str: ;這裡是顯示字串子程式的入口 push ax ;因為子程式會用到相關的暫存器 push dx ;與 pop 指令結合進行相關暫存器的保護工作 push cx push es push di push si mov ax,0b800h ;B800H 是顯示緩衝區的首地址的段地址, mov es,ax sub ax,ax ;將 ax 暫存器中的值置零 mov al,160 ;顯示器上的一行總共有 80 個字元, 在顯示緩衝區中佔有 160 個位元組 mul dh ;上面一行字元所佔的位元組數存放在 al 暫存器中, 作為一個乘數, dh 暫存器中的資料作為另外一個乘數, 結果存放在 ax 中 sub dh,dh add dl,dl ;由於一個字元佔 2 個位元組, 所以需要將 dl 中的資料乘以 2 add ax,dx mov di,ax ;最後將字串在顯示緩衝區中首字元的地址存放在 di 暫存器中 mov al,cl sub cx,cx ;將 cx 暫存器置零, 以備下面 jcxz 使用 next: mov cl,[si] ;在呼叫子程式之前就將要操作字串的首偏移地址存放在了 si 中, jcxz sret ;判斷 cx 中即 ds:[si] 所指的記憶體單元是否為 0 , 如果為 0, 則跳轉到 sret 標號的位置 mov es:[di],cl mov es:[di+1],al ;在目的地址分別存放字元本身和字元的顏色屬性 inc si add di,2 jmp short next sret: pop si ;將暫存器中的值還原, pop 指令的順序與 push 指令相反 pop di pop es pop cx pop dx pop ax ret ;子程式返回 , 繼續執行 mov ax,4c00h code ends end start

 

二、解決除法溢位的問題

名稱:divdw

功能:進行不會產生溢位的除法運算,被除數為 dword 型,除數為 word 型,結果為 dword 型

引數:(ax)= dword 型資料的低 16 位;(dx)= dword 型資料的低 16 位;(cx)= 除數

返回:(dx)= 結果的高 16 位;(ax)= 結果的低 16 位;(cx)= 餘數

應用舉例:計算 1000000 / 10

在解決除法溢位的問題上,有這樣一個公式能夠完美的規避掉溢位現象:被除數 / 除數 = (被除數的高 16 位 / 除數)的商 * 65535 + [ (被除數的高 16 位 / 除數)的餘數 * 65535 + 被除數的低 16 位 ] / 除數

這個公式將可能會產生溢位的除法運算,轉變成了多個不會產生溢位的除法運算。在公式中,等號右邊的所有除法運算都可以用 div 指令來完成,並且不會產生溢位現象。

assume cs:code

code segment
start:
    mov dx,128          ;被除數的高 16 位
    mov ax,0            ;被除數的低 16 位
    mov cx,128            ;16 位除數
    call divdw

    mov ah,4ch
    int 21h

;返回引數:商得高16位dx;低16位ax;餘數cx
;32 位除法
divdw:
    jmp short divstart
    datareg dw  4 dup (0)
divstart:
    push bx                    ;照常進行暫存器的保護工作
    push ds
    push si

    cmp dx,cx                ;通過這裡實現相容沒有溢位的除法運算
    jb divnoflo

    mov bx,cs
    mov ds,bx                ;ds中存放程式碼段的段地址
    mov si,offset datareg    ;取得自定義資料 datareg 的偏移地址

    mov [si],ax              ;將被除數的低 16 位儲存進 datareg 處的第一個字裡
    mov ax,dx                ;
    sub dx,dx                ;對 dx 置零, 避免溢位
    div cx                   ;求被除數的高 16 位/除數, 得到商和餘數,分別儲存在ax和dx當中
    mov [si+2],dx            ;將餘數儲存進第 datareg 處的第二個字
    mov bx,512                   
   mul bx mov bx,128 mul bx ;將商*65536 , 其中512 * 128 = 65535 mov [si+4],ax ;儲存int(H/N)*65536 mov [si+6],dx mov ax,[si+2] ;求得rem(H/N)*65536 mov bx,512 mul bx mov bx,128 mul bx add ax,[si] ;求得rem(H/N)*65536+L div cx ;求得[rem(H/N)*65536+L]/N ***注意這裡進行的除法不能清除dx,這裡不可能會溢位 mov cx,dx ;求得結果的餘數 add ax,[si+4]     ;求得結果的低 16 位 mov dx,[si+6] ;求得結果的低高 16 位 jmp short dsret divnoflo: div cx mov cx,dx sub dx,dx dsret: pop si pop ds pop bx ret code ends end start

三、實現一個子程式,該子程式能將 word 型資料轉變為表示十進位制數的字串

子程式程式碼如下:

dtoc:   push ax
        push si
        push di
        push dx
        push bx
        push cx
        mov di, 0
        mov dx, 0
        mov bx, 10

devide: mov cx, ax         ;將 12666 這個存進暫存器 cx, 在暫存器中它的表現形式是 0011 0001 0111 1010
        jcxz stop
        div bx             ;利用除法來求得 12666 十進位制數每一位的值
        inc di
        push dx            ;將這個值存放進棧中
        mov dx, 0
        jmp devide
stop:   mov cx, di      
string: pop bx
        add bx, 30h        ;將每一位的值轉換為 ASCII 碼的表現形式
        mov [si], bl
        inc si
        loop string

        pop cx
        pop bx
        pop dx
        pop di
        pop si
        pop ax
ret