Keil下STM32的C与汇编语言混合编程
1、函数调用时的规则
1、父函数与子函数间的入口参数依次通过R0–R3这4个寄存器传递。父函数在调用子函数前先将参数存入到R0–R3中,若只有一个参数则使用R0传递,2个则使用R0和R1传递,依次类推,当超过4个参数时,其它参数通过栈传递。当子函数运行时,根据自身参数个数自动从R0~R3或者栈中读取参数。 2、子函数通过R0寄存器将返回值传递给父函数。子函数返回时,将返回值存入R0,当返回到父函数时,父函数读取R0获得返回值。 3、发生函数调用时,R0—R3是传递参数的寄存器,即使是父函数没有参数需要传递,子函数也可以任意更改R0–R3寄存器,无需考虑会破坏它们在父函数中保存的数值,返回父函数前无需恢复其值。AAPCS规定,发生函数调用前,由父函数将R0–R3中有用的数据压栈,然后才能调用子函数,以防止父函数R0–R3中的有用数据被子函数破坏。 4、R4——R11为普通的通用寄存器,若子函数需要使用这些寄存器,则需要将这些寄存器先压栈然后再使用,以免破坏了这些寄存器中保存的父函数的数值,子函数返回父函数前需要先出栈恢复其数值,然后再返回父函数。AAPCS规定,发生函数调用时,父函数无需对这些寄存器进行压栈处理,若子函数需要使用这些寄存器,则由子函数负责压栈,以防止父函数R4R11中的数据被破坏。 5、编译器在编译时就确定了函数间的调用关系,它会使函数间的调用遵守3、4条规定。但编译器无法预知中断函数的调用,被中断的函数无法提前对R0–R3进行压栈处理,因此需要在中断函数里对它所使用的R0–R11压栈。对于中断函数,不遵守第3条规定,遵守第5条规定。 6、R12寄存器在某些版本的编译器下另有它用,用户程序不能使用,因此我们在编写汇编函数时也必须对它进行压栈处理,确保它的数值不能被破坏。 7、R13寄存器是堆栈寄存器(SP),用来保存堆栈的当前指针。 8、R14寄存器是链接寄存器(LR),用来保存函数的返回地址。 9、R15寄存器是程序寄存器(PC),指向程序当前的地址。
原文链接:https://blog.csdn.net/STCNXPARM/article/details/108742047
2、C语言调用汇编
main.c
# include<stdio.h>
extern int add(int x);
int sum=0;
int main()
{
sum=add(7);
return 0;
}
汇编函数fun.s
AREA Add,CODE,READONLY
EXPORT add
add
mov R1,#100
ADD R0,R0,R1
mov pc,lr
end
程序的入口是main,在main里调用汇编的函数.
在C语言中,要extern 一个函数声明即可,然后这个函数在汇编里面实现。
在汇编里面,用EXPORT 把C语言定义的函数名引进来,再开始编写函数名开始的段
由函数调用规则可知,4个以内的参数,直接存放在R0~R3 这4个寄存器里面。
4个以后的参数放在堆栈里。
如果函数有返回值,那么返回值放在R0里。
通过调试来验证:
由寄存器R0的值可知,c函数的参数值直接保存在R0中
通过汇编函数,将R1赋值为100,然后通过add,将R0与R1寄存器中的值求和重新放入R0中,作为函数的返回值
可以验证的确R0是存放返回值的
3、汇编调用C函数
汇编程序的书写要遵循ATPCS规则,以保证程序调用时参数正确传递。在汇编程序中调用C程序的方法为:首先在汇编程序中使用IMPORT伪指令事先声明将要调用的C语言函数;然后通过BL指令来调用C函数。
汇编代码ss.s
IMPORT ADD
AREA Add,CODE,READONLY
EXPORT add
add
BL ADD
mov pc,lr
end
main.c
# include<stdio.h>
extern int add(int a);
int main()
{
add(3);
return 0;
}
add.c
int ADD(int a)
{
return a+100;
}
程序入口为main函数,通过main函数调用汇编函数add,在通过汇编函数add调用c函数ADD
通过调试来验证是否成功
通过主函数中add的参数值赋给R0寄存器,因为只有一个参数,所以只用一个寄存器R0
汇编语言执行BL ADD,调用C函数,此时程序进入ADD函数中,寄存器R0的值还未发生改变。
通过函数ADD,寄存器R0的值发生改变。
|