- UID
- 1029342
- 性别
- 男
|
高级语言可以实现大部分编程功能,但是当我们需要对特定代码进行优化,写启动代码,或者操作特定
硬件,或需要直接用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指令)
内嵌汇编的通用形式:
r0-r3
asm (
代码
r0-r3
: 输出操作数列表
: 输入操作数列表
: 破坏列表
);
在一个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__ (); |
|