汇编语言调用C语言/C++函数

可以编写汇编程序来调用 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 字节数值压入浮点堆栈。