在C代码中实现汇编语言的方法有内联汇编和嵌入型汇编两种,使用它们可以在C语言中实现C语言不能完成的一些工作。例如,在下面几种情况必须使用内联汇编或嵌入式型汇编。
(1)、程序中使用饱和算术运算,如SSAT16和USAT16指令
(2)、程序需要对协处理器进行操作
(3)、在C语言中完成对程序状态寄存器的操作
1、 内联汇编
a) 语法:
__asm
{
汇编语句
}
因为内联汇编嵌入在C或C++程序中,所以在用法上有其自身的一些特点:
i. 如果同一行中包含多条指令,用分号隔开。
ii. 如果一条指令不能在一行中完成,使用“/”将其连接。
iii. 内联汇编中使用逗号“,”作为指令操作数的分隔符,所以如果在C语言中使用逗号必须用圆括号括起来。如”__asm {ADD x, y, (f(),z)}”。
iv. 内联汇编语言中的寄存器名被编译器视为C或C++语言中的变量,所以内联汇编中出现的寄存器名不一定和同名的物理寄存器相对应。这些寄存器咋使用前必须声明,否则编译器将提示警告信息。
v. 内联汇编中的寄存器(除程序状态寄存器CPSR和SPSR外)在读取前必须先赋值,否则编译器将产生错误信息。
例如:
int f(int x) // 函数声明的方式和C语言一样 { int R0; // 声明R0寄存器 __asm { ADD R0,x,1 // 对R0赋值,之后才能对其读取操作 EOR x,R0,x } return x; }
b) 例:中断使能
该例只能运行于系统模式下,因为用户模式是无权修改程序状态寄存器的。
参考文件(enable_irq.c)
// 内联汇编示例----中断使能 // 该例只能运行于系统模式下,因为用户模式是无权修改程序状态寄存器的。 __inline void enable_IRQ(void) { int tmp; __asm { MRS tmp,CPSR BIC tmp,tmp,#0x80 MRS CPSR_c,tmp } } __inline void disable_IRQ(void) { int tmp; __asm { MRS tmp,CPSR ORR tmp,tmp,#0x80 MRS CPSR_c,tmp } } int main(void) { disable_IRQ(); enable_IRQ(); return 0; }
2、 嵌入型汇编
a) 语法:
__asm return_type function_name(parameter_list) // 函数声明的方式和C语言一样 { 汇编语句 }
另外,如果嵌入型汇编器使用—asm选线,则初始状态为ARM状态,若使用—thumb选项,则初始状态为Thumb状态。参数是通过R0-R3传递的,不能使用参数列表中的变量。
例:字符串复制(str_cpy.c)
// 嵌入型汇编示例:字符串复制 #include__asm void my_strcpy(const char *src, const char *dst) { loop LDRB R3,[R0],#1 STRB R3,[R1],#1 CMP R3, #1 BNE loop MOV pc,lr } void main() { const char *a = "Hello world"; char b[20]; my_strcpy(a,b); printf("Original string: '%s'\n", a); printf("Copied string: '%s'\n", b); }
b) 嵌入式汇编程序表达式和C表达式之间的差异
i. 汇编程序表达式总是无符号的。相同的表达式在汇编和C语言总有不同值。
例如:
MOV R0,#(-33554432 / 2) // 结果为0x7f000000
MOV R0,#__cpp(-33554432 / 2) // 结果为0xff000000,cpp指明的部分表示访问的是C表达式
ii. 以0开头的汇编程序编码仍然是十进制。
汇编程序运算符优先级顺序与C不同。例如:
MOV R0,#(0x23 :AND: 0Xf + 1) // ((0x23 & 0xf) + 1) =>4
MOV R0, #__cpp(0x23 :AND: 0Xf + 1) // (0x23 & (0xf + 1)) =>0
iii. 汇编程序字符串不是以空字符为终止标志的。
c) 关键字__cpp
可用__cpp关键字从汇编代码中访问C或C++ 编译时的常量表达式,其中包括含有外部链接的数据或函数地址。
3、 汇编代码访问C全局变量
在汇编代码中访问C全局变量,只能通过 地址间接访问全局变量。例如:
IMPORT globvar
Start
LDR R1,=globvar ;从内存池中读取globvar变量的地址,加载到R1中
STR R0, [R1]