16 位和 32 位模式中,MASM 使用 .MODEL 伪指令确定若干重要的程序特性:内存模式类型、过程命名模式以及参数传递规则。若汇编代码被其他编程语言程序调用,那么后两者就尤其重要。
.MODEL 伪指令的语法如下:
下表列出了 memorymodel 字段可选择的模式。除了平坦模式之外,其他所有模式都可以用于 16 位实地址编程。
模式 | 说明 |
---|---|
微模式 | 一个既包含代码又包含数据的段。文件扩展名为 .com 的程序使用该模式 |
小模式 | 一个代码段和一个数据段。默认情况下,所有代码和数据都为近属性 |
中模式 | 多个代码段,一个数据段 |
紧凑模式 | 一个代码段,多个数据段 |
大模式 | 多个代码段和数据段 |
巨模式 | 与大模式相同,但是各个数据项可以大于单个段 |
平坦模式 | 保护模式。代码与数据使用 32 位偏移量。所有的数据和代码(包括系统资源)都在一个 32 位段内 |
32 位程序使用平坦内存模式,其偏移量为 32 位,代码和数据最大可达 4GB。比如,Irvine32.inc 文件包含了如下 .MODEL 伪指令:
.MODEL 伪指令中的 ModelOptions 字段可以包含一个语言说明符和一个栈距离。语言说明符指定过程与公共符号的调用和命名规范。栈距离可以是 NEARSTACK(默认值)或者 FARSTACK。
伪指令 .MODEL 有几种不同的可选语言说明符,其中的一些很少使用(比如 BASIC、FORTRAN 和 PASCAL)。反之,C 和 STDCALL 则十分常见。结合平坦内存模式,示例如下:
语言说明符 STDCALL 用于 Windows 系统函数调用。本章在链接汇编代码和 C 与 C++ 程序时,使用 C 语言说明符。
STDCALL 语言说明符将子程序参数按逆序(从后往前)压入堆栈。为了便于说明,首先用高级语言编写如下函数调用:
若 STDCALL 被选为语言说明符,则等效的汇编语言代码如下:
另一个重要的考虑是,过程调用后如何从堆栈中移除参数。STDCALL 要求在 RET 指令中带一个常量操作数。返回地址从堆栈中弹出后,该常数为 RET 执行与 ESP 相加的数值:
堆栈指针加上 8 后,就指回了主调程序参数入栈之前指针的位置。
最后,STDCALL 通过将输出(公共)过程名保存为如下格式来修改这些名称:
前导下划线添加到过程名,@ 符号后面的整数指定了过程参数的字节数(向上舍入到 4 的倍数)。例如,假设过程 AddTwo 带有两个双字参数,那么汇编器传递给链接器的名称就为 _AddTwo@8。
Microsoft 链接器是区分大小写的,因此 _MYSUB@8 和 _MySub@8 是两个不同的名称。要查看 OBJ 文件中所有的过程名,使用 Visual Studio 中的 DUMPBIN 工具,选项为 /SYMBOLS。
和 STDCALL 一样,C 语言说明符也要求将过程参数按从后往前的顺序压入堆栈。对于过程调用后从堆栈中移除参数的问题,C 语言说明符将这个责任留给了主调方。在主调程序中,ESP 与一个常数相加,将其再次设置为参数入栈之前的位置:
C 语言说明符在外部过程名的前面添加前导下划线。示例如下: