学 Win32 汇编[32] - 子程序进阶


接: http://www.cnblogs.com/del/archive/2010/04/05/1704864.html

这是以前的一个求和函数的例子
; Test32_1.asm.386.model flat, stdcallinclude    windows.incinclude    kernel32.incinclude    masm32.incinclude    debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.codesum proc v1:dword, v2:dword, v3:dword    mov eax, v1    add eax, v2    add eax, v3    retsum endp;main proc    invoke sum, 11, 22, 33    PrintDec eax; 66    retmain endpend main

把上面的例子改为用寄存器传递参数:
; Test32_2.asm.386.model flat, stdcallinclude    windows.incinclude    kernel32.incinclude    masm32.incinclude    debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.codesum proc    add eax, ecx    add eax, edx    retsum endp;main proc    mov eax, 11    mov ecx, 22    mov edx, 33    invoke sum    PrintDec eax; 66    retmain endpend main

如果调用的函数在之后实现, 须用 PROTO 提前声明:
; Test32_3.asm.386.model flat, stdcallinclude    windows.incinclude    kernel32.incinclude    masm32.incinclude    debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib;sum proto v1:dword, v2:dword, v3:dwordsum proto :dword, :dword, :dword ;函数声明的主要是参数类型, 一般省略参数名.codemain proc    invoke sum, 11, 22, 33 ;现在调用的是之后的函数    PrintDec eax; 66    retmain endp;sum proc v1, v2, v3    mov eax, v1    add eax, v2    add eax, v3    retsum endpend main

测试 StdCall 模式下的参数压栈顺序:
子程序可以指定语言模式(StaCall、C、SysCall、Basic、Fortran、Pascal);
如果不指定则默认使用在 .model 中指定的语言模式.

StaCall、C、SysCall 是从右到左压栈参数;
Basic、Fortran、Pascal 是从左到右压栈参数.
; Test32_4.asm.386.model flat, stdcallinclude    windows.incinclude    kernel32.incinclude    masm32.incinclude    debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.codesum 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    retsum endp;main proc    invoke sum, 11h, 22h, 33h    PrintDec eax; 66    retmain endpend main

测试 Pascal 模式下的参数压栈顺序:
这是和上面的对比练习, 它们的压栈参数的顺序是反的.
其中的 EBX+8 是最后压栈参数(DWORD)的地址, 同样 EBX 向上偏移 12、16 就分别是另外两个参数的地址.
地址 EBX+4 是 RET 将要返回的地址.
为什么参数不是在 EBX 的下偏移? 因为是先压栈参数在调用函数.
; Test32_5.asm.386.model flat, stdcallinclude    windows.incinclude    kernel32.incinclude    masm32.incinclude    debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.codesum 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    retsum endp;main proc    invoke sum, 11h, 22h, 33h    PrintDec eax; 66    retmain endpend main

如果用 Call 代替 invoke 能更好地理解压参顺序:
; Test32_6.asm.386.model flat, stdcallinclude    windows.incinclude    kernel32.incinclude    masm32.incinclude    debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.codeViewParam proc C v1, v2, v3 ;把这里的 C 换为 pascal 会有完全不同的结果    PrintDec v1 ;11    PrintDec v2 ;22    PrintDec v3 ;33    retViewParam endp;main proc    push 33    push 22    push 11    call ViewParam    leave ;leave 是上面几个 push 的反操作, 省了不少 pop    retmain endpend main

子过程使用 uses 保护寄存器:
所谓保护就是在子过程执行前先压栈, 执行后在出栈.
; Test32_7.asm.386.model flat, stdcallinclude    windows.incinclude    kernel32.incinclude    masm32.incinclude    debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.data    dwVal dd ?.codesum 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    retsum 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    retmain endpend main

使用 uses 不如使用 pushad 和 popad 来得简洁:
; Test32_8.asm.386.model flat, stdcallinclude    windows.incinclude    kernel32.incinclude    masm32.incinclude    debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.data    dwVal dd ?.codesum proc v1, v2, v3    pushad    mov eax, v1    mov ecx, v2    mov edx, v3    add eax, ecx    add eax, edx    mov dwVal, eax    popad    retsum 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    retmain endpend main

和子程序密切相关的有两个指令: call 和 ret
call 相当于 push+jmp;
ret 相当于 pop+jmp;
有些 ret 后面还有个数字, 如 ret 8, 这相当于 ret 后再 esp+8(这是清理 8 字节的堆栈).

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

声明其他模块成员的 extrn、extern、public 关键字, 现在用 proto 都可以代替了.

该学模块化编程了.

posted on 2010-04-26 14:28 万一 阅读(2654) 评论(3) 编辑 收藏©著作权归作者所有:来自51CTO博客作者JLee79的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 0428作业-函数
  2. 使用 IntraWeb (33) - Cookie
  3. Delphi 正则表达式之TPerlRegEx 类的属性与方法(7): Split 函数
  4. 异常 - 虚拟机初始化错误 - Error occurred during initializati
  5. $()的四种类型参数的应用场景实例演示;以及jq转js的方法
  6. 冷月手撕408之数据结构(3)-顺序表
  7. 使用binlog2sql工具来恢复数据库
  8. 快递100集成多家快递公司同城配送下单API接口代码
  9. 快递100集成多家快递同城配送公司查询订单API接口案例

随机推荐

  1. Android 主题切换/换肤方案 研究(一) -
  2. ListView常用属性、方法
  3. EditView属性介绍
  4. 关于android的单位dp,dip
  5. 文字跑马灯效果
  6. Audio and Video
  7. Hello TWaver Android
  8. Android(安卓)Gesture 之触摸屏手势识别
  9. android 程序 发布加密
  10. Linearlayout和relativeLayout的属性的一