- 一、题目要求
- 二、项目创建
- 三、C语言调用汇编-无参数调用
- 1、仿真设置
- 2、设置断点
- 3、编译并调试
- 四、C语言调用汇编语言-有参数调用
- 1、修改 三 中代码
- 2、设置断点
- 3、编译并调试
- 五、汇编语言调用C语言函数
- 1、修改 四 中代码
- 2、设置断点
- 3、编译并调试
- 六、其他
- 1、C与汇编之间函数调用ATPCS简介
- 2、参数如何传递
- 3、参数的清除
- 4、C语言与汇编语言混合程序
- 内联汇编
- 嵌入式汇编
- 七、总结
一、题目要求
修改代码,要求将原汇编语言 Init_1函数的类型改为 int Init_1(init) ,此函数功能修改为传入一个整型数x,函数运行后返回整型数 x+100。 请编程实现,并仿真跟踪调试;
如果要求在汇编函数中调用一个 C语言写的函数,应该如何修改汇编代码?
打开Keil ARM,Project->New μVision Project->选择STM32F103ZE
添加main.c和Func.s文件
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三、C语言调用汇编-无参数调用 1、仿真设置extern void Init_1(void); int main(){ Init_1(); return 0; }
打开魔法棒,选择Debug,勾选Use Simulator
将左侧的Dialog DLL中的内容改为DARMSTM.DLL,将Parameter的内容改为-pSTM32F103C8
在目标行前侧点击,生成小红点,即完成断点设置
编译build或rebuild后,进行调试
在左上角点击step或step over或Run To Cursor Line
观察R1、R2的寄存器值的变化
可以发现其由0逐步加到10
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,不然会被认为是段名,表示程序结束
main.c
# includeextern int Init_1(int x); int main(){ int xx = Init_1(10); printf("%d", xx); return 0; }
在Keil中,子函数的参数值传递按顺序存放到了R0、R1、R2、R3中,超过四个参数值传递放栈帧里。
由此,Init_1(10)传入的10放到了R0中,由MOV PC,LR返回110.
2、设置断点
操作同上
此时值为6E即110,调用成功!
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
# include2、设置断点 3、编译并调试extern void Init_1(void); int get5(void); int main(){ printf("Begin...n"); Init_1(); return 0; } int get5(){ return 5; }
执行get5后,R0变为了5,调用成功!
六、其他 1、C与汇编之间函数调用ATPCS简介- ARM-Thumb 过程调用标准 ATPCS(ARM-Thumb Procedure Call Standard)
- ATPCS 标准既是ARM 编译器使用的函数调用规则,也是设计可被 C 程序调用的汇编函数的编写规则
- ATPCS 规定,ARM 的数据堆栈为 FD 型堆栈,即满递减堆栈
- 函数是通过寄存器和堆栈来传递参数和返回函数值的,形参和返回值都应定义在具有暂存性质的寄存器和堆栈中
汇编程序调用C函数时,函数的入口参数使用栈来传送,参数的传递顺序是从右到左。即函数最后(最右边的)一个参数先入栈,而最左边的第一个参数最后入栈,然后执行 CALL 指令去调用C函数。
3、参数的清除在C函数返回后,汇编程序需要把先前压入栈中的函数参数清除掉,即调用者负责清除参数占用的栈空间。
4、C语言与汇编语言混合程序在C程序中内联或嵌入式汇编代码,以提高程序的效率
内联汇编- 在 C 程序中直接编写汇编程序段而形成一个语句块,这个语句块可以使用除了 BX 和 BLX之外的全部ARM指令来编写
- 可以使程序实现一些不能从C获得的底层功能
- 汇编语句块中,如果有两条指令占据了同一行,那么必须用分号“ ;”将它们分隔
- 如果一条指令需要占用多行,那么必须用反斜线符号“ ”作为续行符
- 可以在内联汇编语言块内的任意位置使用C/C++格式的注释
- 内联汇编代码中定义的标号可被用作跳转或C/C++ goto 语句的目标,同样,在C/C++代码中定义的标号,也可被用作内联汇编代码跳转指令的目标
- 在内联汇编语句块中最好使用 C 或 C++ 变量作为操作数
- 状态寄存器 PSR ,任何对 PSR 的引用总是执行指向物理 PSR
void enable_IRQ(void)
{
int tmp;
_ _asm //声名内联汇编代码
{
MRS tmp, CPSR
BIC tmp, tmp, #0x80
MSR CPSR_c, tmp
}
}
- 内联汇编的限制
- 它不支持 Thumb 指令;除了程序状态寄存器 PSR 之外,不能直接访问其他任何物理寄存器
- 如果在内联汇编程序指令中出现了以某个寄存器名称命名的操作数,那么它被叫做虚拟寄存器,而不是实际的物理寄存器。编译器在生成和优化代码的过程中,会给每个虚拟寄存器分配实际的物理寄存器,但这个物理寄存器可能与在指令中指定的不同。
- 在内联汇编代码中不能使用寄存器 PC(R15)、LR(R14)和SP(R13)
- 处理器模式会禁止使用 C 操作数或对已编译 C 代码的调用,直到将处理器模式恢复为原设置之后
- 嵌入式汇编程序是一个编写在C程序外的单独汇编程序,该程序段可以像函数那样被 C 程序调用
- 嵌入式汇编具有真实汇编的所有特性,数据交换符合 ATPCS 标准,同时支持 ARM 和Thumb,所以它可以对目标处理器进行不受限制的低级访问
- 不能直接引用 C/C++ 的变量
- 参数名只允许使用在参数列表中,不能用在嵌入式汇编函数体内
- 在 C 程序中调用嵌入式汇编程序的方法与调用 C 函数的方法相同
定义一个嵌入式汇编函数的语法格式为:
_ _asm return–type function–name(parameter-list)
{
汇编程序段
}
return–type:函数返回值类型,C语言中的数据类型
function–name:函数名
parameter-list:函数参数列表
| 项目 | 内联汇编 | 嵌入式汇编 |
|---|---|---|
| 指令集 | 仅限于ARM | ARM和Thumb |
| ARM汇编程序命令 | 不支持 | 支持 |
| C表达式 | 支持 | 仅支持常量表达式 |
| 优化代码 | 支持 | 不支持 |
| 内联 | 可能 | 从不 |
| 寄存器访问 | 不使用物理寄存器 | 使用物理寄存器 |
| 返回指令 | 自动生成 | 显式编写 |
ARM在C语言中调用汇编函数的方法
在C中调用汇编文件中的函数,要做的主要工作有两个:
一是在C中声明函数原型,并加extern关键字;
二是在汇编中用EXPORT导出函数名,并用该函数名作为汇编代码段的标识,最后用mov pc, lr返回。然后,就可以在C中使用该函数了。
参考:
[1] https://blog.csdn.net/longintchar/article/details/79511747
[2] https://blog.csdn.net/sinat_27421407/article/details/78829508



