Win32ASM学习[20]:子程序

关于函数调用约定 :函数调用约定

这是以前的一个求和函数的例子
----------------------------------------------------------------------------------------------------------------
.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
----------------------------------------------------------------------------------------------------------------
把上面的例子改为用寄存器传递参数:
----------------------------------------------------------------------------------------------------------------
.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 提前声明:
----------------------------------------------------------------------------------------------------------------
.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 是从左到右压栈参数.
----------------------------------------------------------------------------------------------------------------
.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 的下偏移? 因为是先压栈参数在调用函数.
----------------------------------------------------------------------------------------------------------------
.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 能更好地理解压参顺序:
----------------------------------------------------------------------------------------------------------------
.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 保护寄存器:
----------------------------------------------------------------------------------------------------------------
 所谓保护就是在子过程执行前先压栈, 执行后在出栈.
----------------------------------------------------------------------------------------------------------------
.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 来得简洁:
----------------------------------------------------------------------------------------------------------------
.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 都可以代替了.

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值