彙編語言實驗十

1、編寫一個通用的子程序來實現顯示字符串的功能ide

名稱:show_stroop

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

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

返回:無blog

應用舉例:在屏幕的 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

 

本題因爲參數的個數不是不少,因此不用將參數放進棧中存儲。rem

在編寫子程序以前仍是先回答兩個問題:字符串

  • 要處理的數據在什麼地方?
  • 要處理的數據有多長?

首先本題要處理的數據就是 data 段中的內容,在進行子程序調用以前就已經將 data 段的首地址存儲進了寄存器中以備用。string

而後就是 data 段的數據有多長的問題,很顯然 data 段中的數據是以 0 字符做爲結尾,可使用 jcxz 指令來檢測 0 而知道數據是否處理完,因此子程序不須要字符串的長度參數。asm

下面是子程序的代碼:

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

 

2、解決除法溢出的問題

名稱: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

3、實現一個子程序,該子程序能將 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
相關文章
相關標籤/搜索