在Keil下用C语言调用汇编函数

一、在C函数中调用汇编函数

1.1 调用不带参数的汇编函数

关于建立MDK工程的具体操作,可参考我的另一篇博文:
基于MDK创建纯汇编语言的STM32工程

  1. 新建一个main.c文件
#include <stdio.h>
extern void Init_1(void);
int main()
{
	Init_1();
	return 0;
}
  1. 新建一个func.s文件
    AREA	My_Function,CODE,READONLY
	EXPORT Init_1
 
Init_1
	MOV R1,#0
	MOV R2,#0
	
LOOP
	CMP R1,#10
	BHS LOOP_END
	ADD R2,#1
	ADD R1,#1
	B LOOP
	
LOOP_END
	NOP
	
	END

关于上述汇编代码的解释:

  • EXPORT:将c文件中定义的函数相关联;
  • Init_1LOOPLOOP_END:程序段名,是跳转程序的参照;
  • MOV R1,#0MOV R2,#0:将R1,R2寄存器的初值设置为0;
  • CMP R1,#10BHS LOOP_END:将R1的值与10相比较,若R1大于10,就调到LOOP_END
  • ADD R2,#1ADD R1,#1:R1,R2寄存器加1;
  • B LOOP:再次进入循环的标志;
  • END:程序结束。

关于汇编代码格式的注意事项:

  1. 开头部分:AREA My_Function ,CODE,READONLY
    这个指令一定不要顶格写!
  2. 中间部分:程序名必须顶格写;
  3. 末尾部分:必须空格后再写END,不然会被认为是段名,表示程序结束;

在这里插入图片描述

  1. 仿真调试
    使用单步调试,可以看到R1从0变到9,再变为A(10在16进制下为A)时 ,跳出汇编程序,具体过程如下所示:
    初始状态:
    在这里插入图片描述
    R1变为0:
    在这里插入图片描述
    R1变为1:
    在这里插入图片描述
    R1变为2:
    在这里插入图片描述
    一直执行,到9:
    在这里插入图片描述
    最后到A:
    在这里插入图片描述
    跳出循环:
    在这里插入图片描述

1.2调用带形参的汇编函数

  1. 修改代码

将原汇编语言 Init_1函数的类型改为 int Init_1(init) ,此函数功能修改为 传入一个整型数x,函数运行后返回整型数 x+100;

  • 修改main.c
#include <stdio.h>
extern int Init_1(int a);
int main()
{
	Init_1(5);
	return 0;
}
  • 修改func.s
Init_1
    ADD R0,R0,#100	;R0记录传入的形参,并且执行R0=R0+100;
	BX LR           ;跳转的LR的地址执行,在这里,LR记录main函数调用子函数的返回地址

在这里插入图片描述

之所以让R0加100,是因为在arm编程里,函数调用过程中,子函数的参数值传递按顺序存放在R0、R1、R2、R3里,超过4个参数值传递放栈帧里,所以我们给定的参数值默认是放在R0中的,要想实现x+100,就要对R0寄存器加100。

  1. 进入调试:
    在这里插入图片描述
    可以看到R0初始值为十六进制下的78,进入单步运行后,R0变为5:
    在这里插入图片描述
    再次点击进入汇编文件,R0加了100,变为16进制下的105为69
    在这里插入图片描述

二、在汇编函数中调用c函数

在c文件中写入函数Init_1,将主函数放入汇编文件当中,可以发现,将以前的导入函数EXPORT改为了导入INPORT

2.1 代码文件

  1. main.c
#include<stdio.h>
extern int Init_1();
int Init_1()
{
	int x=5;
	return x+100;
}
  1. func.s
    IMPORT Init_1
	AREA MYCODE, CODE
 
	EXPORT __main
 
__main
    BL Init_1
		
	END

在这里插入图片描述

注意:ARM汇编指令不支持顶格写,否则不能识别;声明变量时不要有空格,不然会出现奇奇怪怪的错误。

2.2 仿真调试

调试方法依旧如上,编译无误后进入仿真调试
初始状态:
在这里插入图片描述

F5跳入主函数的断点处
在这里插入图片描述

再经过单步调试后,可以看到寄存器R0变为16进制的105,即69 ,证明试验无误。
在这里插入图片描述

三、汇编函数与c函数混合调用

3.1 代码文件

  • main.c
extern int SUM_ASM(void);
int sum(int a,int b)
{
	int c;
	a=100;
	b=200;
	c=a+b;
	return c;
}
int main(void)
{
	SUM_ASM();
	return 0;
}
  • func.s
    AREA	 My_Function ,CODE,READONLY  	
	IMPORT sum
	EXPORT SUM_ASM
		
	ENTRY

SUM_ASM
	LDR R0,=0X3
	LDR R1,=0X4
	BL sum 
	MOV PC,LR
	
	
	END

在这里插入图片描述

3.2 仿真调试

初始状态:
在这里插入图片描述
运行结果:
在这里插入图片描述

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

4.1寄存器的使用规则

  • R1~R3通常用来传递函数参数,当参数个数多于4个时,使用堆栈来传递参数,此时R0-R3可记作A1-A4;
  • R4~R11用来保存程序运算的中间结果或函数的局部变量,因此当进行子程序调用时要注意对这些寄存器的保存和恢复,此时R4-R11可记作V1-V8;
  • R12通常用来作为函数调用过程中的临时寄存器,用于保存堆栈指针SP,当子程序返回时使用该寄存器出栈,记作IP;
  • R13寄存器是堆栈寄存器(SP),用来保存堆栈的当前指针
  • R14寄存器是链接寄存器(LR),用来保存函数的返回地址
  • R15寄存器是程序寄存器(PC),指向程序当前的地址

4.2 堆栈的使用规则

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

4.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程序中调用汇编程序,或者是二者相互嵌套,都涉及到子程序的调用、参数的传递、子程序的返回等问题,以及寄存器、堆栈的存储规则。在了解这些具体调用规则后,再通过设计程序进行仿真试验,更能理解每一步的执行过程,掌握其运行原理。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值