转载请注明作者及出处
一.嵌入式汇编关健字 asm
在所有C程序中,嵌入式汇编的写法有asm{} asm(), __asm__ __volatile__ ("");其中asm 和 __asm 含义是一样的.
而ARM的C编译器中,主流是两大类,一类ARM公司出品的ARMCC,另一类用得更广的是gnu 的汇编,两者嵌入式汇编语法主和格式,有着很多细节微差别,这里主要讨论 Gnu格式的.
ARM的嵌入式汇编是没有引号,用花括号包括进来,如下格式,多行之间直接采用回车分隔,语法采用ARM汇编格式.后文将不讨论
asm {
nop
mvn r0,#0
}
__voliatie 主要主要让编译不要对读写进行优化.
GNU 嵌入式汇编格式,用gcc编译的汇编必须满足如下格式
- 一般以 __asm __volatile打头,用()包括起来,
- 每句语句用""包括起来
- 多语句之间用 \n或\nt分隔
- 专用寄存器用%%的打头表示,
以下就是一个简单的嵌入式汇编格式
- __asm__ __volatile__ (
-
"mov %0, #55\n"
-
"mov %1, #66\n"
- "xor %%eax, %%eax\n"
-
)
- asm(
-
"mov r0, r0\n\t"
-
"mov r0, r0\n\t"
-
"mov r0, r0\n\t"
-
"mov r0, r0"
-
);
关于嵌入式汇编,可以参见如下文章
<>
二.嵌入式汇编与C的数据交互
一般嵌入式汇编是一段特定代码,因此有可以从C语言变量中读取数据,或者把处理后的数据交给C程序.
2.1 从C变量取值.
一般是从局域变量中取值,按定义的先后,用%0到%9表示,最多10个变量.
比如下代码
asm("add %1,%2");
2.2 向C程序返回结果.
这里要用更复杂的分段式表达式.完整的嵌入式汇编分为四大部分:
汇编代码部分(code),输出部分(output operand list ),输入部分(input operand list ),破坏段(clobber list)部分,每一部分用":"分开.
其中汇编语句段就一般的汇编指令,可以是多句.
其中输出部分即是向C变量赋值的部分.
我们看如下示例
asm("mov %0, %1, ror #1" : "=r" (result) : "r" (value));
这里的语句部分是 "mov %0,%1,ror 1",这里把%1的值赋给%0,并且与1作位与.
:后是输出部 "=r(result)",
而且"r" (value)是输入部,
这里破坏部省略.
2.3 输出部格式
在上例中 =r(result), =表示只用输出,r表示通用寄存器的值赋给变量,result表示输出C变量名字.
这个"=r(result)"的完整意思是把结果赋给result.
其中除 =外,还有其它保留字
- = 只写/输出变量
- + 可读可写变量
- & 该输出操作数不能使用输入操作数相同的寄存器
而输出部的关健字除r外,还有其它很多.它们用来表示输出变量与前一部分语句中操作寄存器的关系,
r 把r0~r15的值赋给变量
f 使用浮点的寄存器f0~f15
m 任意内存地址
a, b.c d 表示要求使用寄存器eax/ax/al, ebx/bx/bl, ecx/cx/cl或edx/dx/dl
这里有一个完整的表格
Constraint | Usage in ARM state | Usage in Thumb state |
f | Floating point registers f0 .. f7 | Not available |
h | Not available | Registers r8..r15 |
G | Immediate floating point constant | Not available |
H | Same a G, but negated | Not available |
I | Immediate value in data processing instructions e.g. ORR R0, R0, #operand | Constant in the range 0 .. 255 e.g. SWI operand |
J | Indexing constants -4095 .. 4095 e.g. LDR R1, [PC, #operand] | Constant in the range -255 .. -1 e.g. SUB R0, R0, #operand |
K | Same as I, but inverted | Same as I, but shifted |
L | Same as I, but negated | Constant in the range -7 .. 7 e.g. SUB R0, R1, #operand |
l | Same as r | Registers r0..r7 e.g. PUSH operand |
M | Constant in the range of 0 .. 32 or a power of 2 e.g. MOV R2, R1, ROR #operand | Constant that is a multiple of 4 in the range of 0 .. 1020 e.g. ADD R0, SP, #operand |
m | Any valid memory address |
N | Not available | Constant in the range of 0 .. 31 e.g. LSL R0, R1, #operand |
O | Not available | Constant that is a multiple of 4 in the range of -508 .. 508 e.g. ADD SP, #operand |
r | General register r0 .. r15 e.g. SUB operand1, operand2, operand3 | Not available |
w | Vector floating point registers s0 .. s31 | Not available |
X | Any operand
|
常见实例
:"=&r" (__val), "=m" (*mem)
2.4 输入部分格式
可以看是输入变量的声明,格式:关系关健字(变量名) ,关系关健字与输入段类型,,所以这一个段经常可以省略.
如 r(result)
可以还可以加上%的限定,表示可以变量对应的%0,%1之类变量可以交换位置,比如add加数,放前放后均可.
:"r"(result) 表示%0是C变量result的值
: "%r"(x),"r"(y)
还有常量0表示与第一个输出数相同
如下示例
asm("mov %0, %0, ror #1" : "=r" (value) : "0" (value))
2.5 破坏段(clobber list)格式
表示汇编代码产生什么样后果,一般是"memory",表示内存发现变化
:"memory"
除此之多,还有cc,表示改变条件代码寄存器(condition code register),如果一个寄存器名字,如r12,表示代码修改了r12.
参见下面的代码,其中省略了输出段和输入段
- asm volatile("mrs r12, cpsr\n\t"
-
"orr r12, r12, #0xC0\n\t"
-
"msr cpsr_c, r12\n\t" ::: "r12", "cc");
- asm volatile("mrs r12, cpsr\n"
-
"bic r12, r12, #0xC0\n"
-
"msr cpsr_c, r12" :: "X" (c) : "r12", "cc"
- __asm__ __volatile__ (
-
"loop:\n"
-
"ldrb r1, [%0], #1\n"
-
"strb r1, [%1], #1\n"
-
"cmp r1, #0\n"
-
"bne loop\n"
-
:
-
: "r" (a), "r" (b)
-
: "r0", "r1", "memory"
-
);
2.6 使用把汇编代码段定义成宏
这时建议使用 __asm__来代替 __asm ,__volatile__代替 __valatile,这样在ANSI C没有编译警告
- #define BYTESWAP(val) \
-
__asm__ __volatile__ ( \
-
"eor r3, %1, %1, ror #16\n\t" \
-
"bic r3, r3, #0x00FF0000\n\t" \
-
"mov %0, %1, ror #8\n\t" \
-
"eor %0, %0, r3, lsr #8" \
-
: "=r" (val) \
-
: "0"(val) \
-
: "r3", "cc" \
-
);
2.7 代码段内部标签,
这个可以在一个代码段使用标签,但是不能从一个段跳到另一个段的标签里.标签用1: 2:这样的定义方法.用b 语句跳转 ,如 bne 1b
参见如下实例
- #define __arch_compare_and_exchange_val_64_rel(mem, newval, oldval) \
-
({ \
-
__typeof (*(mem)) __tmp; \
-
-
__typeof (mem) __memp = (mem); \
-
-
__asm __volatile (__ARCH_REL_INSTR "\n" \
-
"1: ldarx %0,0,%1" MUTEX_HINT_REL "\n" \
-
" cmpd %0,%2\n" \
-
" bne 2f\n" \
-
" stdcx. %3,0,%1\n" \
-
" bne- 1b\n" \
-
"2: " \
-
: "=&r" (__tmp) \
-
: "b" (__memp), "r" (oldval), "r" (newval) \
-
: "cr0", "memory"); \
-
__tmp; \
-
})