C与汇编混编

本文主要讲讲如何在C语言中嵌入汇编语言。

为什么要在C中写汇编?

  1. 速度快、效率高(但现在编译器技术这么高级,这种情况已经很少见了
  2. 实现C语言也无法解决的底层操作,如调入扇区、显存显示等,这是主要原因

内联汇编

基本的内联汇编语法是这样的

asm ( assembler template 
    : output operands                  /* optional */
    : input operands                   /* optional */
    : list of clobbered registers      /* optional */
    );

所谓clobbered寄存器就是指在此段代码段会被使用/修改值的寄存器,在这里声明表示让编译器不要占用(输入输出寄存器可以不用)。

volatile表示编译器不要对此段代码进行优化。 __asm__asm__volatile__volatile的写法都是可以的。

注意汇编语句均用双引号括起来,同时每行最末要添加\r\n以告诉汇编器换行。

常用的限制如下

  • 通用寄存器(r),如asm ("movl %%eax, %0\n" :"=r"(myval));
r Register(s)
a %eax, %ax, %al
b %ebx, %bx, %bl
c %ecx, %cx, %cl
d %edx, %dx, %dl
S %esi, %si
D %edi, %di
  • 内存限制m
  • 匹配01、…、9:表示用它限制的操作数与某个指定的操作数匹配,如用0去描述%1
  • =只写的(输出操作数)
  • +操作数在指令中是读写类型的(输入输出操作数)

Intel语法解决方案

gcc编译器默认的内联(inline)汇编是AT&T语法,但OS课上我们都采用Intel/nasm的语法,故这里需要做出一点修改,在汇编语句前添加

asm (".intel_syntax noprefix");

即可,同时注意添加编译选项-masm=intel(注意这种方法不能使用m限制)。

如果想换回AT&T语法,则输入

asm (".att_syntax noprefix");

完整可运行的例子如下

#include <stdio.h>

#ifdef ATT // -DATT
int att_add(int foo, int bar)
{// ".att_syntax noprefix"
    __asm__ __volatile__("addl  %%ebx,%%eax"
                         :"=a"(foo)
                         :"a"(foo), "b"(bar)
                         );
    return foo;
}
#else // -masm=intel
int intel_add(int foo, int bar)
{
    asm volatile(".intel_syntax noprefix\r\n"
                 "add  eax, ebx\r\n"
                 :"=a"(foo)
                 :"a"(foo), "b"(bar)
                 :);
    return foo;
}
#endif

int main(void)
{
    int foo = 10, bar = 15;
#ifdef ATT
    printf("%d + %d = %d\n", foo, bar, att_add(foo,bar));
#else
    printf("%d + %d = %d\n", foo, bar, intel_add(foo,bar));
#endif
}

参考资料