Keil下STM32的C语言调用汇编函数

对于ARM体系来说,不同语言撰写的函数之间相互调用(mix calls)遵循的是 ATPCS(ARM-Thumb Procedure Call Standard),ATPCS主要是定义了函数呼叫时参数的传递规则以及如何从函数返回。

一、准备工作

1)创建一个新项目。
在这里插入图片描述
2)根据自身情况选择硬件,配置一些参数。
在这里插入图片描述
在这里插入图片描述
3)为 SOURCE GROUP 1新建两个文件main.c(C程序)和Func.s(汇编程序)。
在这里插入图片描述
在这里插入图片描述

二、C语言调用汇编函数

C 程序调用汇编程序时,汇编程序的书写也要遵循ATPCS规则,以保证程序调用时参数正确传递。在C程序中调用汇编子程序的方法为:首先在汇编程序中使用 EXPORT伪指令声明被调用的子程序,表示该子程序将在其他文件中被调用;然后在C程序中使用extern关键字声明要调用的汇编子程序为外部函数。

1.C语言无参数调用汇编函数

Func.s代码

	AREA	MY_FUNCTION,CODE,READONLY
	EXPORT 	Init_1  ;//与在c文件中定义的Init_1函数关联起来
	
; //高级语言中的声明和使用变量其实是对板子寄存器的使用,所以我们只需要直接使用寄存器即可

Init_1

	MOV R1,#0     ; //设R1寄存器为i
	MOV R2,#0	  ; //设R2寄存器为j
	
LOOP	;// 写在最左边的是程序段的段名,执行跳转程序时用到
	CMP R1,#10	  ; //比较R1和10的大小
	BHS LOOP_END  	  ;// 如果R1大于等于10,则跳转到LOOP_END程序段,反之忽略该语句,直接执行下面的语句
	ADD R2,#1	  ; //j++
	ADD R1,#1     ; //i++
	B LOOP		  ; //循环
	
LOOP_END
	NOP	
	
	END  ; //必须空格后再写END,不然会被认为是段名,表示程序结束

main.c代码

# include<stdio.h>
extern void	Init_1(void);//与在汇编文件中定义的Init_1函数关联起来
int main()
{
	Init_1();
	return 0;
}

在图示处设置五个断点。
在这里插入图片描述
在这里插入图片描述
点击translatebuild,然后点击debug,选择第一个选项开始仿真调试。
在这里插入图片描述
点击单步运行,观察左边界面寄存器的变化状况。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
R0、R1从0开始,每次加1直到加到10,这与编写的代码相一致。

二、C语言有参数调用汇编函数

1.函数形参小于4个

修改Func.s代码

AREA  My_Function,CODE,READONLY;
	EXPORT Init_1;
;
		
Init_1
    ADD R0,#100;//将R0与100相加	
	END;

修改main.c代码

#include<stdio.h>

extern int Init_1(int x);

int main()
	{
	
	Init_1(10);

	return 0;
}

看到这里难免有点疑惑,这里似乎没有直接的语句写明形参x的值会存放到R0,那指令ADD R0,#100中传入的整型数x为什么可以和100相加?

设置两个断点。
在这里插入图片描述
在这里插入图片描述
然后开始仿真调试,点击单步运行,观察左边界面寄存器的变化状况。
在这里插入图片描述
在这里插入图片描述
可以看到传入的整型数x被传递到R0。在这里插入图片描述
且最后的运算结果为32位整数,则该由R0返回,这是ATPCS规定的参数的传递规则,最后R0的值为110。

2.函数形参多于4个

main.c代码(10个参数)

#include <stdio.h>
int fun(int n0,int n1,int n2,int n3,int n4,int n5,
int n6,int n7,int n8,int n9)
{
int m;
m=n0+n1+n2+n3+n4+n5+n6+n7+n8+n9;
return m;
}
void main(void)
{
int num;
num=fun(1,2,3,4,5,6,7,8,9,10);
}

为了搞清楚在ARM函数传递机制,在ubuntu18.04下安装arm-linux-gcc。安装好了后输入以下命令得到汇编文件。

arm-linux-gcc -s main.c -o main.s

输入以下命令查看汇编文件。

cat main.s

在这里插入图片描述

可以看出,子函数的参数值传递按顺序存放在R0,R1,R2,R3里,超过4个参数值传递放栈帧里。想详细了解C语言在ARM中函数调用时栈的变化过程,可参考下面的第一条链接。

三、汇编函数调用C函数

在汇编程序中调用C程序的方法为:首先在汇编程序中使用IMPORT伪指令事先声明将要调用的C语言函数;然后通过BL指令来调用C函数。

修改Func.s代码

	AREA	MY_Function,CODE,READONLY
	EXPORT 	Init_1  ;// 与在c文件中定义的Init_1函数关联起来
	IMPORT  get5    ; //声明get5 为外部引用

;// 高级语言中的声明和使用变量其实是对板子寄存器的使用,所以我们只需要直接使用寄存器即可

Init_1

	MOV R1,#0     ; //设R1寄存器为i
	MOV R2,#0	  ; //设R2寄存器为j
	
LOOP //写在最左边的是程序段的段名,执行跳转程序时用到
	CMP R1,#10	  ; //比较R1和10的大小
	BHS LOOP_END  	  ;// 如果R1大于等于10,则跳转到LOOP_END程序段,反之忽略该语句,直接执行下面的语句
	ADD R2,#1	  ; //j++
	ADD R1,#1     ; //i++
	BL get5  	  ; //调用get5,返回的值传入R0
	B LOOP		  ; //循环
	
LOOP_END
	NOP	
	END  ;// 必须空格后再写END,不然会被认为是段名,表示程序结束

修改main.c代码

# include<stdio.h>
extern void	Init_1(void);
int get5(void);
int main()
{
	Init_1();
	return 0;
}
int get5()
{
	return 5;
}

设置断点后进入仿真调试,观察左边界面寄存器的而变化状况。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从以上过程来看,R1、R2每次循环加1,而get5( )的返回值总是被返回到R0且均为5,这是由于该返回结果为一个32位整数。

四、总结C语言与汇编语言混合编程的规则

1.寄存器的使用规则

子程序之间通过寄存器R0-R3来传递参数,当参数个数多于4个时,使用堆栈来传递参数,此时R0-R3可记作A1-A4。在子程序中,使用寄存器R4-R11保存局部变量。因此当进行子程序调用时要注意对这些寄存器的保存和恢复,此时R4-R11可记作V1-V8。寄存器R12用于保存堆栈指针SP,当子程序返回时使用该寄存器出栈,记作IP。寄存器R13用作堆栈指针,记作SP。寄存器R14称为链接寄存器,记作LR,该寄存器用于保存子程序的返回地址。寄存器R15称为程序计数器,记作PC。

2.堆栈的使用规则

ATPCS规定堆栈采用满递减类型(FD,Full Descending),即堆栈通过减小存储器地址而向下增长,堆栈指针指向内含有效数据项的最低地址。

3.参数的传递规则

1)C语言在ARM中函数调用时,如果形参个数少于或等于4,则形参由R0,R1,R2,R3四个寄存器进行传递;若形参个数大于4,大于4的部分必须通过堆栈进行传递,且压入堆栈的顺序是与函数中形参的顺序相反。
2)对于X86平台,32位程序使用栈传递,而64位程序则根据参数的个数而不同。 当参数少于6,使用寄存器传递参数;当参数大于6,多出来的参数使用栈传递。可以参考以下第二条链接验证看看。
3)子程序的返回结果为一个32位整数时,通过R0返回;返回结果为一个64位整数时,通过R0和R1返回;依此类推。结果为浮点数时,通过浮点运算部件的寄存器F0、D0或者S0返回。

五、心得体会

学习C语言和汇编语言混合编程的过程中,了解了许多与自己固有认识不同的知识,像是这其中的参数传递机制,而且这个探索过程真的很有趣!

六、参考资料

C语言在ARM中函数调用时,栈是如何变化的?
C语言调用函数时参数是使用栈还是寄存器
arm汇编语言调用C函数

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值