程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 學 Win32 匯編[32] - 子程序進階

學 Win32 匯編[32] - 子程序進階

編輯:Delphi

 這是以前的一個求和函數的例子

; Test32_1.asm 
.386 
.model flat, stdcall 
 
include  Windows.inc 
include  kernel32.inc 
include  masm32.inc 
include  debug.inc 
includelib kernel32.lib 
includelib masm32.lib 
includelib debug.lib 
 
.code 
sum proc v1:dword, v2:dword, v3:dWord 
   mov eax, v1 
   add eax, v2 
   add eax, v3 
   ret 
sum endp 
; 
main proc 
   invoke sum, 11, 22, 33 
   PrintDec eax; 66 
   ret 
main endp 
end main 

  把上面的例子改為用寄存器傳遞參數:

; Test32_2.asm 
.386 
.model flat, stdcall 
 
include  Windows.inc 
include  kernel32.inc 
include  masm32.inc 
include  debug.inc 
includelib kernel32.lib 
includelib masm32.lib 
includelib debug.lib 
 
.code 
sum proc 
   add eax, ecx 
   add eax, edx 
   ret 
sum endp 
; 
main proc 
   mov eax, 11 
   mov ecx, 22 
   mov edx, 33 
   invoke sum 
   PrintDec eax; 66 
   ret 
main endp 
end main 

  如果調用的函數在之後實現, 須用 PROTO 提前聲明:

; Test32_3.asm 
.386 
.model flat, stdcall 
 
include  Windows.inc 
include  kernel32.inc 
include  masm32.inc 
include  debug.inc 
includelib kernel32.lib 
includelib masm32.lib 
includelib debug.lib 
 
;sum proto v1:dword, v2:dword, v3:dWord 
sum proto :dword, :dword, :dWord ;函數聲明的主要是參數類型, 一般省略參數名 
 
.code 
main proc 
   invoke sum, 11, 22, 33 ;現在調用的是之後的函數 
   PrintDec eax; 66 
   ret 
main endp 
; 
sum proc v1, v2, v3 
   mov eax, v1 
   add eax, v2 
   add eax, v3 
   ret 
sum endp 
end main 

測試 StdCall 模式下的參數壓棧順序:

  子程序可以指定語言模式(StaCall、C、SysCall、Basic、Fortran、Pascal);

  如果不指定則默認使用在 .model 中指定的語言模式.

  StaCall、C、SysCall 是從右到左壓棧參數;

  Basic、Fortran、Pascal 是從左到右壓棧參數.

; Test32_4.asm 
.386 
.model flat, stdcall 
 
include  Windows.inc 
include  kernel32.inc 
include  masm32.inc 
include  debug.inc 
includelib kernel32.lib 
includelib masm32.lib 
includelib debug.lib 
 
.code 
sum proc stdcall v1, v2, v3 
  ;查看參數壓棧順序(StdCall 是從右到左 push) 
   mov edx, [ebp+16] 
   PrintHex edx    ;33 
   
   mov edx, [ebp+12] 
   PrintHex edx    ;22 
   
   mov edx, [ebp+8] 
   PrintHex edx    ;11 
   
   PrintLine 
   
  ;下面求和代碼 
   mov eax, v1 
   add eax, v2 
   add eax, v3 
   ret 
sum endp 
; 
main proc 
   invoke sum, 11h, 22h, 33h 
   PrintDec eax; 66 
   ret 
main endp 
end main 

  測試 Pascal 模式下的參數壓棧順序:

  這是和上面的對比練習, 它們的壓棧參數的順序是反的.

  其中的 EBX+8 是最後壓棧參數(DWord)的地址, 同樣 EBX 向上偏移 12、16 就分別是另外兩個參數的地址.


 地址 EBX+4 是 RET 將要返回的地址.

  為什麼參數不是在 EBX 的下偏移? 因為是先壓棧參數在調用函數.

; Test32_5.asm 
.386 
.model flat, stdcall 
 
include  Windows.inc 
include  kernel32.inc 
include  masm32.inc 
include  debug.inc 
includelib kernel32.lib 
includelib masm32.lib 
includelib debug.lib 
 
.code 
sum proc pascal v1, v2, v3 
  ;查看參數壓棧順序(pascal 是從左到右 push) 
   mov edx, [ebp+16] 
   PrintHex edx    ;11 
   
   mov edx, [ebp+12] 
   PrintHex edx    ;22 
   
   mov edx, [ebp+8] 
   PrintHex edx    ;33 
   
   PrintLine 
   
  ;下面求和代碼 
   mov eax, v1 
   add eax, v2 
   add eax, v3 
   ret 
sum endp 
; 
main proc 
   invoke sum, 11h, 22h, 33h 
   PrintDec eax; 66 
   ret 
main endp 
end main 

  如果用 Call 代替 invoke 能更好地理解壓參順序:

; Test32_6.asm 
.386 
.model flat, stdcall 
 
include  Windows.inc 
include  kernel32.inc 
include  masm32.inc 
include  debug.inc 
includelib kernel32.lib 
includelib masm32.lib 
includelib debug.lib 
 
.code 
VIEwParam proc C v1, v2, v3 ;把這裡的 C 換為 pascal 會有完全不同的結果 
   PrintDec v1 ;11 
   PrintDec v2 ;22 
   PrintDec v3 ;33 
   ret 
VIEwParam endp 
; 
main proc 
   push 33 
   push 22 
   push 11 
   call VIEwParam 
   leave ;leave 是上面幾個 push 的反操作, 省了不少 pop 
   ret 
main endp 
end main 

 子過程使用 uses 保護寄存器:

  所謂保護就是在子過程執行前先壓棧, 執行後在出棧.

; Test32_7.asm 
.386 
.model flat, stdcall 
 
include  Windows.inc 
include  kernel32.inc 
include  masm32.inc 
include  debug.inc 
includelib kernel32.lib 
includelib masm32.lib 
includelib debug.lib 
 
.data 
   dwVal dd ? 
.code 
sum proc stdcall uses eax ecx edx, v1, v2, v3 ;這其中的 stdcall 可省略 
   mov eax, v1 
   mov ecx, v2 
   mov edx, v3 
   add eax, ecx 
   add eax, edx 
   mov dwVal, eax 
   ret 
sum endp 
; 
main proc 
  ;sum 對這三個寄存器進行的保護, 先給些測試值 
   mov eax, 7 
   mov ecx, 8 
   mov edx, 9 
   
   invoke sum, 11, 22, 33 
   PrintDec dwVal ;66 
   
   PrintDec eax ;7 
   PrintDec ecx ;8 
   PrintDec edx ;9 
   ret 
main endp 
end main 

  使用 uses 不如使用 pushad 和 popad 來得簡潔:

; Test32_8.asm 
.386 
.model flat, stdcall 
 
include  Windows.inc 
include  kernel32.inc 
include  masm32.inc 
include  debug.inc 
includelib kernel32.lib 
includelib masm32.lib 
includelib debug.lib 
 
.data 
   dwVal dd ? 
.code 
sum proc v1, v2, v3 
   pushad 
   mov eax, v1 
   mov ecx, v2 
   mov edx, v3 
   add eax, ecx 
   add eax, edx 
   mov dwVal, eax 
   popad 
   ret 
sum endp 
; 
main proc 
   mov eax, 7 
   mov ecx, 8 
   mov edx, 9 
   
   invoke sum, 11, 22, 33 
   PrintDec dwVal ;66 
   
   PrintDec eax ;7 
   PrintDec ecx ;8 
   PrintDec edx ;9 
   ret 
main endp 
end main 

  和子程序密切相關的有兩個指令: call 和 ret

  call 相當於 push+jmp;

  ret 相當於 pop+jmp;

  有些 ret 後面還有個數字, 如 ret 8, 這相當於 ret 後再 esp+8(這是清理 8 字節的堆棧).

  另外程序可以同 public 和 private 指定是否能跨模塊使用, 默認是 public, 極少用到 private.

  聲明其他模塊成員的 extrn、extern、public 關鍵字, 現在用 proto 都可以代替了.

  該學模塊化編程了.



  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved