arm gcc内嵌汇编

时间:2022-01-10 00:57:31


arm gcc内嵌汇编

=============================

高级语言可以实现大部分编程功能,但是当我们需要对特定代码进行优化,写启动代码,或者操作特定硬件,或需要直接用CPU指令等等操作的时候我们需要用到汇编。但是我们只想在高级语言(比如C语言)中的某些特定部分插入某些汇编指令,这时候,我们就需要用高级语言提供的内嵌汇编功能。我们以arm gcc为例,用arm gcc进行c语言和汇编语言混合编程。

最简单的内嵌汇编:

 #include <stdio.h>

 int main( int argc, char ** argv )
 {
         asm("nop");
 }

上面就一条内嵌汇编指令:asm("nop");nop指令在汇编上表示执行空操作,消耗一个CPU指令周期。(实际上nop是一条伪指令,会被编译起翻译成: mov r0, r0这条真实cpu指令)


内嵌汇编的通用形式:

 asm (
   代码
   : 输出操作数列表
   : 输入操作数列表
   : 破坏列表
 );

在一个asm语句中可以写一条或者多条汇编代码,一条汇编代码结束采用\n进行换行,有时候,也采用\n\t进行换行。C语言通过变量的形式访问数据,而汇编语言是通过内存地址或者直接访问寄存器的方式访问数据,如果要在内嵌汇编中访问C语言中的变量,并且修改它的值的话,那么就需要用一种方式用于相互转换C语言的变量与内嵌汇编能访问的寄存器或内存地址形式。这种方式就是通过输入操作数列表和输出操作数列表实现的。

输入和输出操作数列表的格式是一样的,都是由一个或者多个操作数组成,中间采用逗号进行分开,单个操作的格式如下:

 [C变量在汇编中的访问名称] "限制性字符“ (C传递进来的变量名称)


例如,下面程序是实现从C语言中传递2个变量a,b进入汇编中,然后在汇编中实现a+b,把结果存回变量C中:

#include <stdio.h>

int main( int argc, char ** argv )
{
        int a = 20, b = 30;
        int c;

        asm (
                "add %[cc], %[aa], %[bb]"
                : [cc] "=r" (c)
                : [aa] "r" (a), [bb] "r" (b)
  : "memory"
    );

        printf("c = %d\n", c );
        return 0;
}
~      
上面代码没有破坏列表,可以省略不写。上面汇编中,输入了C语言中的变量a和b,执行结果输出到变量c中,在汇编中访问C语言中的变量a,b,c通过%[aa],%[bb],%[cc]进行访问的。当传递常量,指针,或者变量到内嵌汇编中,内嵌汇编必须知道如何在汇编代码中表示他们,这个就是由中间“限制性字符”来指定。

常用限制性字符:

 I: 立即数   例如: [changliang] "I" (120), 表示把120作为立即数的方式传递进去。
 m: 任何有效内存 例如: [nc] "m" (a) , 表示把变量a的地址传递到内嵌汇编中,str r0, %[nc], 就表示把r0的值存入变量a。
 r: 通常的寄存器(r0-r15)。最常用。

上面这些限制性字符如果不带修饰符的话,表示是只读操作数,只能用于输入操作数列表中。对于输出操作数列表中的操作数我们需要加上相应的写权限。常用修饰符包括如下:

 = 只写操作数,通常用于所有的输出操作数。
 + 读写操作。
 & 只用作输出的寄存器。(表示不能与输入寄存器用同样一个寄存器)


破坏列表:

 一般就是一个“memory",或者寄存器列表。如果在汇编代码中修改了内存中的值,我们就需要在破坏列表中加上"memory"字段,告诉编译器,我们修改了内存中的值。如果在汇编代码中修改了某些寄存器,那么我们就需要把在代码中所修改过的寄存器列在破坏列表中。

 
防止编译器优化:

 为了不让编译器优化我们的代码,比如,本来我们想在汇编中采用nop指令进行延时,然后,通常情况下编译起会把nop当成一条无用指令而优化掉它,为了防止编译器优化,我们需要在asm语句符号后面加上__volatile__,这样就可以防止编译器优化我们的汇编代码。

 asm __volatile__ ();