1. 程式人生 > >十八、使用call和ret指令實現子程式的呼叫和返回

十八、使用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