本专栏总结王利涛《C语言嵌入式Linux高级编程》第二期课程
一、ATPCS规则
- ATPCS: ARM-Thumb Procedure Call Standard
- 堆栈使用规则
- 使用满递减堆栈,入栈出栈操作使用LDMFD/STMFD
- 子程序调用基本规则
- ①子程序间通过寄存器R0-R3 传递参数和返回结果,参数多于4个,其余的使用堆栈传参。
- ②子程序中使用R4-R11保存局部变量。
- ③R12作为过程调用中的临时寄存器,用于保存SP,记作IP。
- ④R13作为数据栈指针,记作SP。
- ⑤R15作为程序计数器,记作PC。
二、ARM编译器内嵌汇编
-
关键字 __asm
-
ARM 编译器对ANSI C进行扩展,使用__asm在C语言中内嵌汇编
__asm
{
指令
…
} -
实例
int src[10] = {1,2,3,4,5,6,7,8,9,10};
int dst[10] = {0};
int i = 0;
int main(void)
{
for (i = 0; i < 10; i++)
dst[i] = src[i];
return 0;
}
----------------------------------------------
内嵌汇编方式(只能适用于ARM的编译器)
int main(void)
{
__asm
{
ldr r0,=src
ldr r1,=dst
mov r2,#10
loop:
ldr r3,[r0],#4
str r3,[r1],#4
subs r2,r2,31
bne loop
}
return 0;
}
三、GNU内嵌汇编
- 关键字
__asm__
- GNU编译器对ANSI C 进行扩展,使用
__asm__
修饰,表示后面的代码为内嵌汇编,其后可选择使用__volatile__
告诉编译器不要优化代码:
__asm__ __volatile__
{
汇编语句;
...
}
- 内嵌单行汇编代码示例
asm(“mov r0, r0”); - 内嵌多行汇编代码示例
__asm__ __volatile__
{
"ldr r0,=src;"
"ldr r1,=dst;"
"mov r2,#10;"
"loop:"
"ldr r3,[r0],#4;"
"str r3,[r1],#4"
"subs r2,r2,#1"
"bne loop;"
};
四、汇编中调用C子程序
- 使用方法
- 根据ATPCS参数规则,完成参数传递。
- 满足C语言能够参数残敌和保存局部变量的堆栈环境。
- 使用BL func 即可(带链接的跳转)
int sum(int a, int b)
{
int result = 0;
printf("result = %d\n",result);
return result;
}
int main(void)
{
SUM_ASM(); //在C中调用汇编函数
return 0;
}
-------------------------------------------
IMPORT sum; 先引入要调用函数
AREA SUM_ASM, CODE, READONLY
EXPORT SUM_ASM
SUM_ASM
MOV R0,0x03; 传参,传入的参数少于4,用R0-R3
MOV R1,0x04;
BL sum ; 调用C语言函数,跳转之前,保存下一条指令到LR里,执行完sum后,就调回来,sum翻译成汇编包含了这条语句:MOV PC,LR.所以才能返回来。
MOV PC, LR; 返回到main函数
END
- 大于4个参数的函数调用,采用压栈方式
IMPORT sum; 先引入要调用函数
AREA SUM_ASM, CODE, READONLY
EXPORT SUM_ASM
SUM_ASM
MOV R0,0x00; 传入参数 0,即a=0
MOV R1,0x01; 传入参数 1,即b=1
MOV R2,0X02; 传入参数 2,即c=2
MOV R3,0X03; 传入参数 3,即d=3
MOV R4,0X05 ; 传入参数 5,即e=0
STR R4,[SP,#-4]; 压栈操作,把R4压入栈中,用R4来搬运
MOV R4,0X04 ; 传入参数 4,即f=0
STR R4,[SP,#-4];
BL sum ; ;最后,调用sum函数,同时将参数也传进去
MOV PC, LR; 返回到main函数
END
sum的c语言接口:
int sum(int a, int b, int c, int d, int e, int f);