call和ret指令都是轉移指令,它們都修改IP,或同時修改CS和IP。它們經常被共同用來實作子程式的設計。
ret 和 retf
ret指令用棧中的資料,修改IP的内容,進而實作近轉移;
retf指令用棧中的資料,修改CS和IP的内容,進而實作遠轉移;
CPU執行ret指令時,進行下面兩步操作:
- (ip)=((ss)*16+(sp))
- (sp)=(sp)+2
CPU執行retf指令時,進行下面4步操作:
- (cs)=((ss)*16+(sp))
可以看出,如果我們用彙編文法來解釋ret和retf指令,則:
CPU執行ret指令時,相當于執行:
pop IP
CPU執行retf指令時,相當于執行:
pop IP
pop CS
依據位移進行轉移的call指令
call 标号(将目前的IP壓入棧後,轉到目标處執行指令)
CPU執行此種格式的call指令時,進行如下的操作:
- (sp)=(sp)-2
- ((ss)*16+(sp))=(ip)
- (ip)=(ip)+16位位移。
- 16位位移=标号處的位址 - call 指令後的第一個位元組的位址;
- near ptr 指明此處的位移為16位位移;
- 16位位移的範圍為 -32768~32767,用補碼表示;
- 16位位移由編譯程式在編譯時算出。
CPU執行 call 标号 時,相當于進行:
push IP
jmp near ptr 标号
轉移的目的位址在指令中的call指令
call far ptr 标号 實作的是段間轉移。
CPU執行此種格式的call指令時,進行如下的操作。
- ((ss)*16+(sp))=(cs)
- (CS)=标号所在段的段位址
- (IP)=标号在段中的偏移位址
CPU執行 call far ptr 标号 時,相當于進行:
push CS
push IP
jmp far ptr 标号
轉移位址在寄存器中的call指令
指令格式:call 16 位 reg
功能:
- (ip)=(16位reg)
push IP
jmp 16位 reg
轉移位址在記憶體中的call指令
轉移位址在記憶體中的call指令有兩種格式。
- call word ptr 記憶體單元位址
CPU執行 call word ptr 記憶體單元位址 時,相當于進行:
push IP
jmp word ptr 記憶體單元位址
- call dword ptr 記憶體單元位址
CPU執行 call dword ptr 記憶體單元位址 時,相當于進行:
push CS
push IP
jmp dword ptr 記憶體單元位址
mul 指令
mul 是乘法指令,使用mul 做乘法的時候注意以下兩點:
- 兩個相乘的數:兩個相乘的數,要麼都是8位,要麼都是16位。如果都是8位,一個預設放在AL中,另一個放在8位reg或記憶體字單元中;如果都是16位,一個預設在AX中,另一個放在16位reg或記憶體字單元中。
- 結構:如果是8位乘法,結構預設放在AX中;如果是16位乘法,結構高位預設在DX中存放,低位在AX中放。
寄存器沖突的問題
我們利用call和ret來實作子程式的機制。子程式的架構如下。
标号:
指令
ret
具有子程式的源程式的架構如下。
assume cs:code
code segment
main:
:
call sub1 ;調用子程式sub1
:
:
mov ax,4c00h
int 21h
sub1: ;子程式sub1開始
:
call sub2 ;調用子程式sub2
:
:
ret ;子程式傳回
sub2: ;子程式sub2開始
:
ret ;子程式傳回
code ends
end main
但可能引出一個一般化的問題:子程式中使用的寄存器,很有可能在主程式中也要使用,造成了寄存器使用上的沖突。
解決這個問題的簡捷方法是,在子程式的開始将子程式中所有用到的及寄存器中的内容都儲存起來,在子程式傳回前再恢複。 可以用棧來儲存寄存器中的内容。
是以,我們編寫子程式的标準架構:
子程式開始: 子程式中使用的寄存器入棧
子程式的内容
子程式中使用的寄存器出棧
傳回(ret,retf)