• 汇编语言用INVOKE和PROTO新建模块

    32 位模式中,可以用 Microsoft 的 INVOKE、PROTO 和扩展 PROC 伪指令新建多模块程序。与更加传统的 CALL 和 EXTERN 相比,它们的主要优势在于:能够将 INVOKE 传递的参数列表与 PROC 声明的相应列表进行匹配。

    现在用 INVOKE、PROTO 和高级 PROC 伪指令重新编写 ArraySum。为每个外部过程创建含有 PROTO 伪指令的头文件是很好的开始。每个模块都会包含这个文件 ( 用 INCLUDE 伪指令) 且不会增加任何代码量或运行时开销。

    如果一个模块不调用特定过程,汇编器就会忽略相应的 PROTO 伪指令。

    sum.inc 头文件本程序的 sum.inc 头文件如下所示:

    ; (sum.inc)
    INCLUDE Irvine32.inc
    
    PromptForIntegers PROTO,
        ptrPrompt:PTR BYTE,        ; 提示字符串
        ptrArray:PTR DWORD,        ; 数组指针
        arraySize:DWORD            ; 数组大小
    
    ArraySum PROTO,
        ptrArray:PTR DWORD,        ; 数组指针
        arraySize:DWORD            ; 数组大小
    
    DisplaySum PROTO,
        ptrPrompt:PTR BYTE,        ; 提示字符串
        theSum:DWORD               ; 数组之和

    _prompt 模块

    _prompt.asm 文件用 PROC 伪指令为 PromptForIntegers 过程声明参数,用 INCLUDE 将 sum.inc 复制到本文件:

    ; 提示整数输入请求          (_prompt.asm)
    INCLUDE sum.inc        ; 获得过程原型
    .code
    ;-----------------------------------------------------
    PromptForIntegers PROC,
      ptrPrompt:PTR BYTE,        ; 提示字符串
      ptrArray:PTR DWORD,        ; 数组指针
      arraySize:DWORD            ; 数组大小
    ;
    ; 提示用户输入数组元素值,并用用户输入
    ; 填充数组
    ; 返回:无
    ;-----------------------------------------------------
        pushad                 ; 保存所有寄存器
           
        mov  ecx,arraySize
        cmp  ecx,0             ; 数组大小 <= 0?
        jle  L2                ; 是: 退出
        mov  edx,ptrPrompt     ; 提示信息的地址
        mov  esi,ptrArray
    
    L1:    call WriteString    ; 显示字符串
        call ReadInt           ; 把整数读入EAX
        call Crlf              ; 换行
        mov  [esi],eax         ; 保存入数组
        add  esi,4             ; 下一个整数
        loop L1
    
    L2:    popad               ; 恢复所有寄存器
        ret
    PromptForIntegers ENDP
    END

    与前面的 PromptForIntegers 版本比较,语句 enter 0,0 和 leave 不见了,这是因为当 MASM 遇到 PROC 伪指令及其声明的参数时,会自动生成这两条语句。同样,RET 指令也不需要自带常数参数了,PROC 会处理好。

    _arraysum 模块

    接下来,_arraysum.asm 文件包含了 ArraySum 过程:

    ; ArraySum 过程                 (_arrysum.asm)
    INCLUDE sum.inc
    .code
    ;-----------------------------------------------------
    ArraySum PROC,
        ptrArray:PTR DWORD,    ; 数组指针
        arraySize:DWORD        ; 数组大小
    ;
    ; 计算 32 位整数数组之和
    ; 返回:  EAX = 和数
    ;-----------------------------------------------------
        push ecx              ; EAX 不入栈
        push esi
    
        mov  eax,0            ; 和数清零
        mov  esi,ptrArray
        mov  ecx,arraySize
        cmp  ecx,0            ; 数组大小 <= 0?
        jle  L2               ; 是: 退出
    
    L1:    add  eax,[esi]     ; 将每个整数加到和数中
        add  esi,4            ; 指向下一个整数
        loop L1               ; 按数组大小重复
    
    L2:    pop esi
        pop ecx               ; 用 EAX 返回和数
        ret
    ArraySum ENDP
    END

    _display 模块

    _display.asm 文件包含了 DisplaySum 过程:

    ; DisplaySum 过程        (_display.asm)
    INCLUDE Sum.inc
    .code
    ;-----------------------------------------------------
    DisplaySum PROC,
        ptrPrompt:PTR BYTE,    ; 提示字符串
        theSum:DWORD           ; 数组之和
    ;
    ; 控制台显示和数
    ; 返回:无
    ;-----------------------------------------------------
        push    eax
        push    edx
    
        mov    edx,ptrPrompt        ; 提示信息的指针
        call    WriteString
        mov    eax,theSum
        call    WriteInt            ; 显示 EAX
        call    Crlf
    
        pop    edx
        pop    eax
        ret
    DisplaySum ENDP
    END

    Sum_main 模块

    Sum_main.asm ( 启动模块 ) 包含主程序并调用所有其他的过程。它使用 INCLUDE 从 sum.inc 复制过程原型:

    ; 整数求和程序         (Sum_main.asm)
    INCLUDE sum.inc
    Count = 3
    .data
    prompt1 BYTE  "Enter a signed integer: ",0
    prompt2 BYTE  "The sum of the integers is: ",0
    array   DWORD  Count DUP(?)
    sum     DWORD  ?
    
    .code
    main PROC
        call Clrscr
    
        INVOKE PromptForIntegers, ADDR prompt1, ADDR array, Count
        INVOKE ArraySum, ADDR array, Count
        mov    sum,eax
        INVOKE DisplaySum, ADDR prompt2, sum
    
        call Crlf
        exit
    main ENDP
    END main

    小结 本节与上一节《用Extern伪指令新建模块》展示了在 32 位模式中新建多模块程序的两种方法:

    • 第一种使用 的是更传统的EXTERN伪指令;
    • 第二种使用的是INVOKE. PROTO和PROC的高级功能。

    后一种中的伪指令简化了很多细节,并为 Windows API 函数调用进行了优化。此外,它们还隐藏了一些细节,因此,编程者可能更愿意使用显式的堆栈参数和 CALL 及 EXTERN 伪指令。

更多...

加载中...