可以编写汇编程序来调用 C 和 C++ 函数。这样做的理由至少有两个:
- C 和 C++ 有丰富的输入-输出库,因此输入-输出有更大的灵活性。处理浮点数时,这是相当有用的。
- 两种语言都有丰富的数学库。
调用标准 C 库(或 C++ 库)函数时,必须从 C 或 C++ 的 main() 过程启动程序,以便运行库初始化代码。
1) 函数原型
汇编语言代码调用的 C++ 函数,必须用“C”和关键字 extern 定义。其基本语法如下:
extern "C" returnType funcName(paramlist)
{...}
示例如下:
extern "C" int askForlnteger()
{
cout << "Please enter an integer:";
//...
}
与其修改每个函数定义,把多个函数原型放在一个块内显得更容易。然后,还可以省略单个函数实现的 extern 和“C”:
extern "C" {
int askForlnteger();
int showInt(int value, unsigned outwidth);
//etc.
}
2) 汇编语言模块
如果汇编语言模块调用 Irvine32 链接库过程,就要使用如下 .MODEL 伪指令:
.model flat, STDCALL
虽然 STDCALL 与 Win32 API 兼容,但是它与 C 程序的调用规范不匹配。因此,在声明由汇编模块调用的外部 C 或 C++ 函数时,必须给 PROTO 伪指令加上 C 限定符:
INCLUDE Irvine32.inc
askForlnteger PROTO C
showInt PROTO C, value:SDWORD, outWidth:DWORD
C 限定符是必要的,因为链接器必须把函数名与 C++ 模块输出的参数列表匹配起来。此外,使用了 C 调用规范,汇编器必须生成正确的代码以便在函数调用后清除堆栈。
C++ 程序调用的汇编过程也必须使用 C 限定符,这样汇编器使用的命名规则将能被链接器识别。比如,下面的 SetTextColor 过程有一个双字参数:
SetTextOutColor PROC C,
color:DWORD
...
SetTextOutColor ENDP
最后,如果汇编代码调用其他汇编过程,C 调用规范要求在每个过程调用后,把参数从堆栈中移除。
如果汇编代码不调用 Irvine32 过程,就可以在 .MODEL 伪指令中使用 C 调用规范:
;(do not INCLUDE Irvine32.inc)
.586
.model flat,C
此时不再需要为 PROTO 和 PROC 伪指令添加 C 限定符:
askForInteger PROTO
showInt PROTO, value:SDWORD, outWidth:DWORD
SetTextOutColor PROC,
color:DWORD
...
SetTextOutColor ENDP
3) 函数返回值
C++ 语言规范没有提及代码实现细节,因此没有规定标准方法让 C 和 C++ 函数返回数值。当编写的汇编代码调用这些语言的函数时,要检查编译器文件以便了解它们的函数是如何返回数值的。
下面列出了一些可能的情况,但并非全部:
- 整数用单个寄存器或寄存器组返回。
- 主调程序可以在堆栈中为函数返回值预留空间。函数在返回前,可以将返回值存入堆栈。
- 函数返回前,浮点数值通常被压入处理器的浮点数堆栈。
下面列出了 Microsoft Visual C++ 函数怎样返回数值:
- bool 和 char 值用 AL 返回。
- short int 值用 AX 返回。
- int 和 long int 值用 EAX 返回。
- 指针用 EAX 返回。
- float、double 和 long double 值分别以 4 字节、8 字节和 10 字节数值压入浮点堆栈。