开发Arm程序的时候,大多数时候使用C/C++语言就可以了,但汇编语言在某些情况下能够实现一些C语言无法实现的功能,这时候就要调用一些汇编语言的程序.我们需要大概了解一下在C语言中如何嵌入汇编语言.
1.内嵌汇编语言的语法:
__asm
{
指令[;指令]
......
[指令]
}
2.举例:使能/禁止IRQ中断
__inline void enable_IRQ(void)
{
int tmp;
__asm //嵌入汇编代码
{
MRS tmp,CPSR //读取CPSR的值
BIC tmp,tmp,#0x80 //将IRQ中断禁止位I清零,即允许IRQ中断
MSR CPSR_c,tmp //设置CPSR的值
}
}
__inline void disable_IRQ(void)
{
int tmp;
__asm
{
MRS tmp,CPSR
ORR tmp,tmp,#Ox80
MSR CPSR_c,tmp
}
}
3.举例:字符串复制
void my-strcpy(const char *src,char *dst)
{
int ch;
__asm
{
loop:
#ifndef __thumb
LDRB ch,[src],#1
STRB ch,[dst],#1
#else
LDRB ch,[src]
ADD src,#1
STRB ch,[dst]
ADD dst,#1
#endif
CMP ch,#0
BNE loop
}
}
int main(void)
{
const char *a="Hello world!";
char b[20];
__asm
{
MOV R0,a
MOV R1,b
BL my_strcpy,{R0,R1}
}
return(0);
}
4.内嵌汇编的指令用法:
.操作书: 内嵌的汇编指令中作为操作数的寄存器和常量可以是C表达式.这些表达式可以是char,short或int等类型,而且这些表达式都是作为无符号数进行操作的.若要有符号数,用户需要自己处理与符号有关的操作.编译器将会计算这些表达式的值,并为其分配寄存器.
.物理寄存器:内嵌汇编中使用物理寄存器是有限制的:
_ 不能直接向PC(程序计数器)寄存器中赋值,程序跳转只能通过B或BL指令来实现.
_ 使用物理寄存器的指令中,不要使用过于复杂的C表达式
_ 尽可能少的使用物理寄存器
.常量: 在内嵌汇编指令中,常量前面的"#"可以省略
.标号: C程序中的标号可以被内嵌的汇编指令使用.但是只有指令B可以使用C程序中的标号,而指令BL则不能使用.
.内存单元的分配:所有内存分配均由C编译器完成,分配的内存单元通过变量供内嵌汇编器使用.内嵌汇编器不支持内嵌汇编程序中用于内存分配的伪指令.
5.内嵌汇编注意事项:
.必须小心使用物理寄存器,如R0~R3,IP,LR,CPSR中的标志位,避免发生冲突.
例如:
__asm
{
MOV R0,x
ADD y,R0,x/y
}
改成下面代码会比较妥当:
__asm
{
MOV var,x
ADD y,var,x/y
}
.不要使用寄存器代替变量.
.使用内嵌汇编无需保存和恢复寄存器.事实上,除了CPSR,SPSR寄存器,对物理寄存器先读后写都会引起汇编报错.
.汇编语言中","号作为操作数分隔符.如果有C表达式作为操作数,若表达式中包含有",",则必须使用()将其规约为一个汇编操作数,例如:
__asm
{
ADD x,y,(f(),z) //"f(),z"为带有","的C表达式.
}
6.不同数据类型对应的汇编指令:
unsigned char LDRB/STRB
unsigned short LDRH/STRH
unsigned int LDR/STR
char LDRSB/STRSB
short LDRSH/STRSH
7.访问C程序的变量:
AREA globals,CODE,READONLY
EXPORT asmsubroutine
IMPORT globalvar ;声明的外部变量
asmsubroutine
LDR R1,=blobalval
LDR R0,[R1]
ADD R0,R0,#1
STR R0,[R1]
MOV PC,LR
END