十八、使用call和ret指令實現子程式的呼叫和返回
1. 子程式實現的基礎——跳轉:
1) 彙編中的子程式即等價於C語言的函式,即實現程式的模組化;
2) 在組合語言中,子程式其實就是以一個標號起始,最後有類似C函式的返回指令的一段程式碼塊,主程式可以在中途呼叫該程式碼塊(其實就是跳轉到子程式執行),呼叫結束後再從子程式返回到呼叫處(其實就是從子程式處跳轉回呼叫它的地方);
3) 也就是說子程式實現的基礎就是跳轉,即需要轉移指令的支援;
2. 利用call和ret指令來呼叫子程式和從子程式返回:
1) call的使用方法是:call 子程式入口處地址,該地址可以是標號,也可以存放在暫存器和記憶體中;
2) ret的使用方法就是直接在子程式中使用ret指令即可,可以沒有引數,執行該指令會直接返回至呼叫子程式的位置處;
3) 這兩條指令是如何實現的?
i. 由於呼叫和返回都是通過跳轉實現的,因此兩條指令的背後肯定都修改了cs:ip;
ii. 考慮到最後要返回至呼叫處,則肯定需要在調轉至子程式處之前先儲存好返回時的位置(即call後面一條指令的地址),待子程式執行完後再恢復該位置並賦給cs:ip,而儲存和恢復的工具必然是棧了;
iii. 因此call、ret其實是一組複合動作,其執行流程是:
call proc_addr:
push ++(cs:ip)
jmp -> proc_addr
ret:
pop (cs:ip)
4) 由於call和ret背後也是轉移,因此也是要分段內跳轉和段間元跳轉的:
i. 段內跳轉call:
*1. call near ptr proc_tag:等價於jmp near ptr,因此也是一種位移轉移,其中near ptr可以省略,但不建議這樣做,將near ptr寫上可以聯想到jmp near ptr,這樣不會導致記憶混亂,並且使程式清晰易讀;
*2. call 16-bit-register:子程式段內偏移地址存在16位暫存器中,等價於jmp 16-bit-register,因此是直接修改ip但不修改cs;
*3. call word ptr 記憶體單元:子程式段內偏移地址存在記憶體單元中,等價於jmp word ptr 記憶體單元,也是隻修改ip
*注意:它們都會在跳轉之前先push ip進行備份!沒有段內你短轉移!
ii. 遠跳轉call:直接同時修改cs:ip
*1. call far ptr proc_tag:等價於jmp far ptr
*2. call dword ptr 記憶體單元:等價於jmp dword ptr,第16位是偏移地址,高16位是段地址;
*注意:在跳轉之前都會先儲存cs:ip的值,順序是先push cs再push ip;
iii. 段內跳轉ret:直接ret即可,其實就等價於pop ip
iv. 遠跳retf:注意,遠跳是retf,即return far的縮寫,f不要忘了,等價於pop (ip, cs),注意和遠跳的call對應(棧是後進先出的!)
5) call和ret要對應使用,即段內跳的call就和ret配合使用,遠跳的call就和retf配合使用,千萬不能交叉配合使用,雖然這樣編譯不會報錯,但是一定會發生執行時錯誤或者是不可預料的錯誤,因為段內跳push和pop16位的地址,而遠跳push和pop32位的地址,交叉使用就會相差16位,從而導致cs:ip指向異常!
一定要牢記這點!
4. 乘法指令mul:
1) mul有兩種型別,一種是兩個8位相乘得到一個16位的結果,另一種是兩個16位相乘得到一個32位的結果;
2) 雙8位相乘:一個乘數預設放在al中,另一個乘數可以放在任意一個8位暫存器或者記憶體中,結果預設放在ax中;
3) 雙16位相乘:一個乘數預設放在ax中,另一個乘數可以放在任意一個16位暫存器或者記憶體中,結果的高16位預設放在dx中,低16位預設放在ax中;
4) 使用格式:
; 8-bit mul 8-bit
mov al, XXX
mov 8-bit-register/memory, XXX
mul 8-bit-register/memory
; -> ax
; 16-bit mul 16-bit
mov ax, XXX
mov 16-bit-register/memory, XXX
mul 16-bit-register/memory
; -> [dx:ax]
5) 示例:100 × 10和100 × 10000:
assume cs:code
code segment
dd 0
start:
mov al, 100
mov ah, 10
mul ah
mov ax, 100
mov dx, 10000
mul dx
mov word ptr cs:[0], ax
mov word ptr cs:[2], dx
mov ax, 4C00H
int 21H
code ends
end start
執行結果:
*1. ax -> 03E8H
*2. 將結果儲存在了程式碼段開頭定義的資料區中,結果是000F4240H