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.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.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.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.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 位模式中新建多模块程序的两种方法:
后一种中的伪指令简化了很多细节,并为 Windows API 函数调用进行了优化。此外,它们还隐藏了一些细节,因此,编程者可能更愿意使用显式的堆栈参数和 CALL 及 EXTERN 伪指令。