一、简介
C语言直观,易上手,且适用普及范围广,汇编语言效率高,内存占用少。两者结合,取长补短,是最佳的选择
二、实例题目要求
- 参考附件资料,完成C语言调用汇编函数
- 修改参考代码,要求将原汇编语言 Init_1函数的类型改为 int Init_1(init) ,此函数功能修改为传入一个整型数x,函数运行后返回整型数 x+100。 请编程实现,并仿真跟踪调试;
- 如果要求在汇编函数中调用一个 C语言写的函数,应该如何修改汇编代码?
参考代码如下:
C语言部分:
#include<stdio.h>
extern void Init_1(void);
int main()
{
Init_1();
return 0;
}
汇编语言部分:
AREA My_Function,CODE,READONLY ;这一行必要的除了My_Function可以自己取名以外,其他的都是模板
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,不然会被认为是段名,表示程序结束
三、实例第一部分
1.准备工作
- 首先应该在keil中新建工程并创建两个文件用于存放代码
如果有不熟悉如何在keil中新建工程的小萌新可以参考我之前的博客:基于Keil5创建STM32汇编语言工程以及hex文件分析
- 注:在创建两个文件时,一个应该选择C File(.c)文件,一个应该选择Asm File(.s)文件,如下图:
2.代码部分
- 将参考代码中的两部分代码分别存放在各自的文件中,如图,我将汇编语言存放在Funs.s文件下,C语言存放在main.c文件下
3.环境配置和调试
(1)环境配置
-
点击打开魔法棒,在
Debug
下选择Use Simulator
,并将左下的Dialog DLL
中的内容改为DARMSTM.DLL
,将Parameter
的内容改为-pSTM32F103C8
-
点击Rebuild,进行编译,显示0 error,表示代码无误
(2) 调试
-
编译无误后,点击
Start Debug Session
开始调试
-
在main.c中增加断点
-
点击Step进行单步调试
-
如图,寄存器R1、R2变为0,表明程序已经从C语言出调用到汇编语言中
-
如图,寄存器R1、R2逐步增加1,表明函数能执行其功能,说明C语言调用汇编程序成功!
四、实例第二部分(修改函数功能)
将原汇编语言 Init_1函数的类型改为 int Init_1(init),此函数功能修改为传入一个整型数x,函数运行后返回整型数x+100
1.代码部分
在Keil中,子函数的参数值传递按顺序存放到了R0、R1、R2、R3中,超过四个参数值传递放栈帧里。所以Init_1(10)传入的10放到了R0中,由MOV PC,LR返回110.
main.c代码:
#include<stdio.h>
extern int Init_1(int x);
int main(){
int y = Init_1(10);
printf("%d", y);
return 0;
}
func.s代码:
AREA MY_Function,CODE,READONLY
EXPORT Init_1 ; 与在c文件中定义的Init_1函数关联起来
; 高级语言中的声明和使用变量其实是对板子寄存器的使用,所以我们只需要直接使用寄存器即可
Init_1
ADD R0,#100 ; 将传入的值+100
MOV PC,LR ; 返回R0
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,不然会被认为是段名,表示程序结束
2.调试
- 设置断点,如图
- 点击
ctrl+F5
进入调试,并根据实例一调试步骤进行调试
如图显示:R0的寄存器的值为6E,将16进制转化为10进制,寄存器R0的值也就是110。在main函数调用Init_1函数,给的参数10,函数执行完时,值变为10+100,也是110,与寄存器R0的值相同,调用成功!
五、实例第三部分(汇编调用C语言函数)
在汇编中调用C语言函数XXX,则加上IMPORT XXX,注意Keil工具不允许汇编语句顶格写,否则会报错
1.代码部分
Funs.s代码:
AREA My_Function,CODE,READONLY
EXPORT Init_1 ; 与在c文件中定义的Init_1函数关联起来
IMPORT get8 ; 声明get8为外部引用
; 高级语言中的声明和使用变量其实是对板子寄存器的使用,所以我们只需要直接使用寄存器即可
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 get8 ; 调用get8,返回的值传入R0
B LOOP ; 循环
LOOP_END
NOP
END ; 必须空格后再写END,不然会被认为是段名,表示程序结束
main.c代码:
# include<stdio.h>
extern void Init_1(void);
int get8(void)
{
return 8;
}
int main()
{
Init_1();
return 0;
}
2.调试
- 设置断点,如图:
- 编译后,
ctrl+F5
进入调试
- 按实例一部分进行调试后,结果如图:
寄存器R0的值变为了8,表明C语言函数调用成功!
六、总结
通过本篇博客的学习,读者可以通过三个具体的实例了解到C语言和汇编语言的混合使用。采用C语言和汇编语言的混合使用在节约存储空间的同时,又能直观的读出程序的内容。读者可以多多使用这种混合的方法,通过多次的实际操作,真正掌握C语言和汇编的混合使用方法。文章仅供参考,若有纰漏,欢迎大家一起讨论纠正。