組合語言實驗十
一、編寫一個通用的子程式來實現顯示字串的功能
名稱: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 ; 列數 movcl,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_strmov 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