汇编语言用INVOKE和PROTO新建模块
32 位模式中,可以用 Microsoft 的 INVOKE、PROTO 和扩展 PROC 伪指令新建多模块程序。与更加传统的 CALL 和 EXTERN 相比,它们的主要优势在于:能够将 INVOKE 传递的参数列表与 PROC 声明的相应列表进行匹配。
现在用 INVOKE、PROTO 和高级 PROC 伪指令重新编写 ArraySum。为每个外部过程创建含有 PROTO 伪指令的头文件是很好的开始。每个模块都会包含这个文件 ( 用 INCLUDE 伪指令) 且不会增加任何代码量或运行时开销。
如果一个模块不调用特定过程,汇编器就会忽略相应的 PROTO 伪指令。
sum.inc 头文件本程序的 sum.inc 头文件如下所示:
后一种中的伪指令简化了很多细节,并为 Windows API 函数调用进行了优化。此外,它们还隐藏了一些细节,因此,编程者可能更愿意使用显式的堆栈参数和 CALL 及 EXTERN 伪指令。
现在用 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 伪指令。