我看过一些用于Arduino和其他硬件的代码,其中C语言中嵌入了汇编语句,大致如下:
asm("movl %ecx %eax"); /* moves the contents of ecx to eax */
__asm__("movb %bh (%eax)"); /*moves the byte from bh to the memory pointed by eax */
这实际上是如何运作的?我知道每个编译器都不同,但通常为什么要这样做,以及有哪些方法可以利用它呢?
char x=6;
char y,z;
y=x; // y becomes 6
_asm
rrncf x, 1 // x becomes 3. Optimiser doesn't see this happen!
_endasm
z=y; // z should become 6, but actually gets
// the value of x, which is 3
volatile char x=6; // Tell the compiler that this variable could change
// all by itself, and any time, and therefore don't
// optimise with it.
char y,z;
y=x; // y becomes 6
_asm
rrncf x, 1 // x becomes 3. Optimiser doesn't see this happen!
_endasm
z=y; // z correctly gets the value of y, which is 6
volatile char y
。编译器会确保分配实际的 y
而不是优化后的“最近写入 x 然后写入 y 的值”。仅将 z
设为 volatile 是没有帮助的。 - SF.历史上,C编译器生成汇编代码,然后由汇编器将其转换为机器码。内联汇编作为一项简单的功能而出现,在中间汇编代码中,在该点,注入一些用户选择的代码。某些编译器直接生成机器码,在这种情况下,它们包含一个汇编器或调用外部汇编器来生成内联汇编片段的机器码。
汇编代码的最常见用途是使用专门的处理器指令,而编译器无法生成这些指令。例如,对于关键部分禁用中断,控制处理器功能(缓存、MMU、MPU、电源管理、查询CPU能力等),访问协处理器和硬件外设(例如,在x86上使用inb
/outb
指令等)。很少会找到asm("movl %ecx %eax")
这样的代码,因为它会影响C代码周围也在使用的通用寄存器,但像asm("mcr p15, 0, 0, c7, c10, 5")
这样的代码有其用途(在ARM上的数据内存屏障)。 OSDev wiki网站有几个示例,其中包含代码片段。
asm
、_asm
、__asm
或__asm__
引入。除了汇编代码本身外,内联汇编结构还可以包含其他代码,允许您在汇编和C之间传递值(例如,请求将局部变量的值复制到进入时的寄存器),或声明汇编代码破坏或保留某些寄存器。asm("")和__asm__都是有效用法。基本上,如果关键字asm在您的程序中与某些内容冲突,可以使用__asm__。如果您有多个指令,则可以在双引号中每行编写一个指令,并在指令后缀一个'\n' 和'\t'。这是因为gcc将每条指令作为字符串发送给as(GAS),通过使用换行符/制表符,您可以将正确格式的行发送到汇编器。您问题中的代码片段是基本内联。
在基本内联汇编中,只有指令。在扩展汇编中,您还可以指定操作数。它允许您指定输入寄存器、输出寄存器和一组被破坏的寄存器。不必指定要使用的寄存器,您可以将其留给GCC,并且那可能更适合于GCC的优化方案。扩展开头示例是:
__asm__ ("movl %eax, %ebx\n\t"
"movl $56, %esi\n\t"
"movl %ecx, $label(%edx,%ebx,$4)\n\t"
"movb %ah, (%ebx)");
volatile
说明符,例如__asm__ volatile("instrs");
,他们会感到非常荣幸。 - slugonamission