为了使C语言程序具有更高的效率和更多的功能,需在C语言程序里嵌入用汇编语言编写的子程序。一方面是为提高子程序的执行速度和效率;另一方面,可解决某些用C语言程序无法实现的机器语言操作。而C语言代码与汇编语言代码的接口是任何C编译器毋庸置疑要解决的问题。
通常,有两种方法可将汇编语言代码与C语言代码联合在一起。一种是把独立的汇编语言程序用C函数连接起来,通过API (Application Program Interface) 的方式调用;另一种就是我们下面要讲的在线汇编方法,即将直接插入式汇编指令嵌入到C函数中。
C语言的嵌入式汇编
为了使C语言程序具有更高的效率和更多的功能,需在C语言程序里嵌入用汇编语言编写的子程序。一方面是为提高子程序的执行速度和效率;另一方面,可解决某些用C语言程序无法实现的机器语言操作。而C语言代码与汇编语言代码的接口是任何C编译器毋庸置疑要解决的问题。
通常,有两种方法可将汇编语言代码与C语言代码联合在一起。一种是把独立的汇编语言程序用C函数连接起来,通过API (Application Program Interface) 的方式调用;另一种就是我们下面要讲的在线汇编方法,即将直接插入式汇编指令嵌入到C函数中。
采用GCC规定的在线汇编指令格式进行指令的输入,是GCC实现将?’nSP?汇编指令嵌入C函数中的方法。GCC在线汇编指令格式规定如下:
asm (“汇编指令模板”:输出参数:输入参数:clobbers参数);
若无clobber参数,则在线汇编指令格式可简化为:
asm (“汇编指令模板”:输出参数:输入参数);
下面,将对在线汇编指令格式中的各种成分之内容进行介绍。
1) 汇编指令模板
模板是在线汇编指令中的主要成分,GCC据此可在当前位置产生汇编指令输出。例如,下面一条在线汇编指令:
asm ("%0 += %1" : "+r" (foo) : "r" (bar));
此处,"%0 += %1"就是模板。其中,操作数"%0"、"%1"作为一种形式参数,分别会由第一个冒号后面实际的输出、输入参数取代。带百分号的数字表示的是第一个冒号后参数的序号。
如下例:
asm ("%0 = %1 + %2" : "=r" (foo) : "r" (bar), "i" (10));
"%0"会由参数foo取代,"%1"会由参数bar取代,而"%2"则会由数值10取代。
在汇编输出中,一个汇编指令模板里可以挂接多条汇编指令。其方法是用换行符'\n'来结束每一条指令,并用Tab键符'\t'将同一模板产生在汇编输出中的各条指令在换行显示时缩进到同一列,以使汇编指令显示清晰。如下例:
asm ("%0 += %1\n\t%0 += %1" : "+r" (foo) : "r" (bar));
2) 操作数
在线汇编指令格式中,第一冒号后的参数为输出操作数,第二冒号后的参数为输入操作数,第三冒号后跟着的则是clobber操作数。在各类操作数中,引号里的字符代表的是其存储类型约束符;括弧里面的字符串表示的是实际操作数。
如果输出参数有若干个,可用逗号“,”将每个参数隔开。同样,该法则适用于输入参数或clobber参数。
3) 操作数约束符
约束符的作用在于指示GCC,使用在汇编指令模板中的操作数的存储类型。表4.9列出了一些约束符和它们分别代表的操作数不同的存储类型,也列出了用在操作数约束符之前的两个约束符前缀。
4) GCC在线汇编指令举例
例1:asm ("%0 = %1 + %2" : "=m" (foo) : "r" (bar), "i" (10));
操作数foo和bar都是局部变量。bar的值会分配给寄存器(此例中寄存器为R1),而foo的值会置入存储器中,其地址在此由BP寄存器指出。GCC对此会产生如下代码:
// GCC在线汇编起始
[BP] = R1 + 10
// GCC在线汇编结束
注意,本在线汇编指令产生的汇编代码不能被正确汇编。正确的在线汇编指令应当是:
asm ("%0 = %1 + %2" : "=r" (foo) : "r" (bar), "i" (10));
它产生如下的汇编代码:
// GCC在线汇编起始
R1 = R4 + 10
// GCC在线汇编结束
例2:
int a;
int b;
#define SEG(A,B) asm("%0 = seg %1" : "=r" (A) : "p" (&B));
int main(void)
{
int foo;
int bar;
SEG(foo, a);
SEG(bar, b);
return foo;
}
例3:asm ("%0 += %1" : "+r" (foo) : "r" (bar));