王爽 汇编语言第三版 第10章 call 和 ret 指令 以及 子程序设计
生活随笔
收集整理的這篇文章主要介紹了
王爽 汇编语言第三版 第10章 call 和 ret 指令 以及 子程序设计
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
?
?
第10章?call?和?ret?指令
?
10.1?ret?和?reft?指令
call?和?ret?指令都是轉移指令,他們都修改?IP,或同事修改?CS?和?IP 。他們經常被共同來實現子程序的設計。
?
?
10.2?call?指令?和?根據位移?轉移的call指令
?
?
?
段間轉移?的?call?指令
?
?
轉移地址 在 寄存器?中 的?call?指令
?
?
轉移地址在內存?中 的?call?指令
?
?
10.7?call?和?ret?的配合使用?來 實現子程序
?
子程序?框架
?
10.8?乘法指令
?
?
10.12?寄存器的沖突問題
?
?
?
實驗 10?解析
?
王爽《匯編語言》(第三版)實驗10解析:https://www.cnblogs.com/nojacky/p/9523904.html
?
1.?顯示字符串
示例代碼:
assume cs:codedatasg segmentdb 'welcome to masm!', 0 datasg endscode segmentstart: mov dh, 8mov dl, 3mov cl, 2mov ax, datasgmov ds, axmov si, 0call show_strmov ax, 4c00hint 21hshow_str: push dx push cx push si ; 保護子程序寄存器中用到的寄存器; 由于主程序的限定; 這里由CPU自動為我們分配棧空間mov di, 0 ; 顯示緩存區中的偏移量mov bl, dh dec bl ; bl-1才是真正的行,因為行號從0開始計數mov al, 160 mul bl ; 每行160字節 用 行數*每行偏移量 得到目標行的偏移量mov bx, ax ; mul bl之后,乘積存儲在ax中,這里要轉存入bx中mov al, 2 ; 列的偏移量為2,兩個字節代表一列!!!mul dl ; 與行偏移量同理add bl, al ; 將列偏移量與行偏移量相加,得到指定位置的偏移量。mov ax, 0b800hmov es, ax ; 指定顯示緩存區的內存位置mov al, cl ; 由于后面jcxz語句的判斷要用到cx,所以我們要將; cl(顏色)先存下來。s: mov ch, 0mov cl, ds:[si] ; 首先將當前指向字符串的某個字符存入cx中jcxz ok ; 如果cx為0,則轉移到ok標號執行相應代碼mov es:[bx+di],cl ; 將字符傳入低地址mov es:[bx+di+1],al ; 將顏色傳入高地址add di, 2 ; 列偏移量為2inc si ; 字符串的偏移量為1loop s ; 不為0,繼續復制ok: pop dx pop cxpop si ; 還原寄存器變量ret ; 結束子程序調用 code endsend start運行結果:
示例代碼 2:
data segmentdb 'Welcome to masm!',0 data endscode segmentassume cs:code,ds:data start:mov dh,1 ;dh裝行號(范圍:1--25)mov dl,1 ;dl裝列號(范圍:1--80)[注:每超過80等于行號自動加1]mov cl,0cah ;cl中存放顏色屬性(0cah為紅底高亮閃爍綠色屬性)mov ax,datamov ds,axmov si,0call show_strmov ax,4c00hint 21h show_str: ;顯示字符串的子程序[定義開始]push cxpush simov al,0A0hdec dh ;行號在顯存中下標從0開始,所以減1mul dhmov bx,axmov al,2mul dlsub ax,2 ;列號在顯存中下標從0開始,又因為偶字節存放字符,所以減2add bx,ax ;此時bx中存放的是行與列號的偏移地址mov ax,0B800hmov es,ax ;es中存放的是顯存的第0頁(共0--7頁)的起始的段地址mov di,0mov al,clmov ch,0 s: mov cl,ds:[si]jcxz okmov es:[bx+di],cl ;偶地址存放字符mov es:[bx+di+1],al ;奇地址存放字符的顏色屬性inc siadd di,2jmp short s ok: pop sipop cxret ;顯示字符串的子程序[定義結束] code endsend start?
?
2.?解決除法溢出的問題
示例代碼:
assume cs:code,ss:stackstack segmentdw 8 dup(0) stack endscode segment start:mov ax, stackmov ss, axmov sp, 10hmov ax, 4240hmov dx, 0fhmov cx, 0ah call divdwmov ax, 4c00hint 21hdivdw: ;子程序定義開始push axmov ax, dxmov dx, 0div cxmov bx, axpop axdiv cxmov cx, dxmov dx, bxret ;子程序定義結束 code endsend start測試1:?
計算:1000000/10(F4240H/0AH) 商:100000(186A0H) 余數:0?
調試結果:?
測試2:?
計算:1000020/11(F4254H/0BH) 商:90910(1631EH) 余數:10(0AH)?
調試結果:?
?
?
3.?數值顯示
示例代碼:
assume cs:code,ds:datadata segmentdb 10 dup (0) data endscode segment start:mov ax,12666mov bx,datamov ds,bxmov si,0call dtoc mov dh,8mov dl,3mov cl,0cahcall show_strmov ax,4c00hint 21h dtoc: ;數值顯示的子程序定義push dxpush cxpush axpush sipush bxmov bx,0 s1: mov cx,10dmov dx,0div cx mov cx,axjcxz s2add dx,30hpush dxinc bxjmp short s1 s2: add dx,30hpush dxinc bx ;再進行一次棧操作(補充當"商為零而余數不為零"時的情況)mov cx,bxmov si,0 s3: pop axmov [si],alinc siloop s3 okay: pop bxpop sipop axpop cxpop dxret ;數值顯示的子程序定義結束 show_str: ;顯示字符串的子程序已經在第一題中說明,在此不再贅述。push bxpush cxpush simov al,0A0hdec dhmul dhmov bx,axmov al,2mul dlsub ax,2add bx,axmov ax,0B800hmov es,axmov di,0mov al,clmov ch,0 s: mov cl,ds:[si]jcxz okmov es:[bx+di],clmov es:[bx+di+1],alinc siadd di,2jmp short s ok: pop sipop cxpop bxret code ends end start運行結果:
詳解版:
源地址:https://blog.csdn.net/include_heqile/article/details/80602772
assume cs:codedata segmentdw 123, 12666, 1, 8, 3, 38data endsascii segmentdb 100 dup(0);ascii碼值,一個字節即可存儲ascii endsdiv segmentdw 16 dup(0);除法溢出計算需要使用該數據段來臨時保存結果div endscode segmentstart: mov bx, datamov ds, bx;ds段寄存器用來存放待處理的數據mov si, 0call dtocmov cx, 6mov ax, 0mov dh, 8show: push cxmov dl, 3mov cl, 2call show_strpop cx;我們需要更改行號來避免覆蓋inc dhloop showmov ax, 4c00hint 21h dtoc: ;該子程序用于將數值型的數字轉換為字符串;十進制數值轉換為ASCII碼值,轉換關系為:ascii=10進制+30H;要想將一個十進制的整數拆分成一個一個的數值,那我們需要讓這個數;除以10,然后將得到的結果依次入棧,除完之后再依次出棧,即可得到由高位到低位;的所有數值,之后將這些值加上30H,即得到其對應的ASCII碼值,然后將這些;ASCII碼值存放到一個數據段中,調用show_str函數,來在屏幕上顯示這些數值;為了存儲轉換后的ASCII碼值,我們需要新開辟一個數據段push axpush bxpush cxpush dxpush dspush sipush esmov ax, asciimov es, ax;使用es段寄存器來存儲轉換后的ascii碼值mov di, 0;存儲ASCII數據時用來指向ascii段中的每個內存單元 mov cx, 6loop_zone: push cx;因為內層循環會更改cx的值,所以我們需要使用棧結構來保存cx的值mov dx, 0;記錄十進制數據的位數mov ax, ds:[si];ax存放被除數split: push dx;下面要用到dx寄存器,因此我們先保存dxmov cx, 0ah;cx存放除數 mov dx, 0;dx作為被除數高16位,置0div cx;32/16的除法運算,商存儲在ax中,余數存儲在dx中mov cx, dx;call divdw;其實用不著調用divdw,這個除法溢出問題不是真正的除法溢出問題;我們只需要將被除數湊成32位的,除數當做16位的即可;此程序返回運算后的商和余數,分別保存在ax和cx中;如果被除數大于2550,al是無法存放商的,會造成溢出,因此,我們需要調用本實驗中第二個函數;專門用于解決除法溢出問題的函數,雖然程序2解決的是32/16的除法運算的溢出問題,但是對于16/8位的;除法運算也是完全適用的;由于入棧時只能使用字型數據,所以我們壓入的是ax,此時需要將;無關數據,也就是al置0pop dx;取出dx更改前的值push cx;余數入棧inc dx;當循環終止的時候可以進行彈棧存儲操作了,但是我們需要一個標記,來標識我們需要;彈出多少次,我們使用dx來進行存儲mov cx, axadd cx, dx;ax中的值在下一次運算中一定會用到,dx中的值也有可能會用到(當被除數很大時);此時可以臨時保存數據的只有cx了,因此我們直接將運算結果放到cx中;一舉兩得jcxz ok1;處理過程是需要循環的,循環結束的條件是商==0 ;我們只需要將執行jcxz指令即可,當cx的值位0的時候,它會自動跳轉到ok1循環的jmp short split ok1: pop axadd al, 30hmov byte ptr es:[di], alinc didec dxmov cx, dxjcxz lastjmp short ok1last: ;最后一步,在數據的ASCII數據形式的最后加上一個0mov ah, 0mov byte ptr es:[di], ah inc di;從split到ok1到最后一步是對data段中第一個數據的處理;這個過程需要進行循環操作;在這個循環中,di,bx是放在循環外的pop cxadd si, 2loop loop_zonepop espop sipop dspop dxpop bxpop cxpop axretdivdw: push dspush dxpush cxpush ax mov ax, divmov ds, axmov dx, 0;由于本程序中被除數是16位,但是divdw是32/16,所以我們需要將被除數的高位補16個0,也就是將dx置0mov ax, dxmov dx, 0div cx;ax存放商,dx存放余數;根據公式,使用被除數高位除以除數得到的商×65536;*65536等價于在低位加16個0,因此操作就會變得非常簡單;使用被除數高位除以除數得到的余數×65536+被除數的低位,再將得到的結果除以除數;兩者的結果相加,即可得到32位/16位的無溢出結果push dx;使用棧臨時保存余數mov dx, axmov ax, 0mov ds:[0], axmov ds:[2], dxpop dx;彈出余數,作為右操作數中被除數的高16位pop bx;得到被除數的低16位push bx;恢復棧頂數據,避免對主程序造成干擾;add ax, bx;將右操作數[]中的左操作數的低16位和被除數的低16位相加;但是右操作數[]中的左操作數的低16位一定是全0的,因此我們可以省略這一步;直接執行mov ax, bxmov ax, bxdiv cx;ax存放商,dx存放余數;由于左操作數的低16位一定是全0,所以不必與其相加,直接將;右操作數的低16位存儲到ds:[0]內存單元即可mov ds:[0], ax;商的低16位放到ds:[0]單元中mov ds:[4], dx ;余數放到ds:[4]單元中;ds:[2]中一直保存的都是商的高16位,且沒有被更改過,因此無須任何操作pop axpop cxpop dxmov ax, ds:[0];ax保存商的低16位mov dx, ds:[2];dx保存商的高16位mov cx, ds:[4];cx保存余數 pop ds;之所以要在pop ds之前將數據轉移,是因為子程序divdw調用前,ds已經被使用;指向的是其他的段,如果不在pop之前轉移數據,那么div段的數據就無法獲取了retshow_str: push bxpush cxpush dxpush dspush espush dipush axpush si;根據上節中的框架,為了不讓子程序干擾主程序中寄存器的值,將所有子程序會用到的寄存器進行壓棧mov di, axmov ax, 0b800hmov es, ax;顏色區的段地址mov ax, asciimov ds, ax;待輸出的ASCII碼值數據段mov al, 160 mul dh;每行占160個字節,乘以行數push ax;將行計算的結果存儲到棧中mov al, 2mul dl;每列占2個字節,乘以列數pop bx;將上次運算的結果(160×行數)的值轉移到bx中add bx, ax ;此時的ax值為(2×列數);將兩者相加,最終結果保存到bx中mov dl, cl;因為下面的跳轉指令jcxz需要用到cx寄存器,故需要將cl的值先保存在dl中change: mov cl, ds:[di]mov ch, 0 inc di;我們需要記錄下di的值,下一輪循環還會用到它;這樣一來,我們就需要調整入棧和出棧寄存器的位置了;我們在pop di之前pop ax,然后使用ax來保存di的值jcxz ok2mov ch, dl mov es:[bx+si], cxadd si, 2jmp short changeok2: pop sipop axmov ax, dipop dipop espop dspop dxpop cxpop bx retcode endsend start示例代碼 2:
assume cs:code,ds:datadata segmentdb 10 dup(0) data endscode segment start:mov ax,12666mov bx,datamov ds,bxmov si,0call dtocmov dh,8mov dl,3mov cl,02hcall show_strmov ax,4c00Hint 21Hdtoc:push dxpush cxpush axpush sipush bxmov bx,0 ;bx在子程序中用來存放位數,用棧來臨時存放修改后的字符dtoc00:mov cx,10d ;d表示十進制mov dx,0div cx ;除以10mov cx,ax ;得到的商賦給cxjcxz dtoc01 ;當前商為0則調到s2add dx,30H ;將余數加上30H得到相應的ASCII碼push dxinc bxjmp short dtoc00dtoc01: ;當商為0時,余數為個位add dx,30Hpush dxinc bx ;再進行一次棧操作(補充當商為0而余數不為0時的情況)mov cx,bx mov si,0dtoc02: ;s3實現將棧中的數據依次出棧放到制定內存中pop axmov [si],alinc siloop dtoc02dtoc03:pop bxpop sipop axpop cxpop dxretshow_str: ;顯示字符串的子程序(定義開始)push cxpush simov al,0a0H ;每行為160字節 a0H=160dec dh ;行號在顯存中下標從0開始,所以減1mul dh ;相當于從(n-1)*0a0H個Byte單元開始mov bx,ax ;定位好的位置偏移地址存放在bx里面(行)mov al,2 ;每個字符占兩個字節mul dl ;定位列,結果ax存放的是定位好的列的位置sub ax,2 ;列號在顯存中下標從0開始,又因為偶字節存放字符,所以減2add bx,ax ;此時bx存放的是行與列的偏移地址mov ax,0b800Hmov es,axmov di,0 ;指向顯存的偏移地址mov al,cl ;cl是存放顏色的參數,這時候al存放顏色了mov ch,0 ;下邊cx存放的是每次準備處理的字符show_str00:mov cl,ds:[si] ;ds:[si]指向'Weclome to masm!'jcxz show_str01mov es:[bx+di],cl ;偶數地址存放字符mov es:[bx+di+1],al ;奇數地址存放顏色屬性inc siadd di,2jmp short show_str00 show_str01:pop sipop cxretcode ends end start?
?
?
總結
以上是生活随笔為你收集整理的王爽 汇编语言第三版 第10章 call 和 ret 指令 以及 子程序设计的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL 语句执行顺序
- 下一篇: Python 包管理工具 pip 安装