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



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

    这是以前的一个求和函数的例子
    ; 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 都可以代替了.

    该学模块化编程了.

  • 相关阅读:
    flex 内嵌js文件
    LOADRUNNER 录制脚本的问题
    loadrunner windows 2003 添加性能计数器的问题
    其他的网站能上,微软网站不能上是由病毒引起的
    sql express 静默安装的方方法
    javascript 附件事件时带参数的处理方法
    java 面试题 转载
    windows 域共享的问题
    winform listbox 没有listitem的问题
    对象相交检测示例
  • 原文地址:https://www.cnblogs.com/del/p/1721193.html
Copyright © 2020-2023  润新知