修改代码,要求将原汇编语言 Init_1函数的类型改为 int Init_1(init) ,此函数功能修改为传入一个整型数x,函数运行后返回整型数 x+100。 请编程实现,并仿真跟踪调试;
如果要求在汇编函数中调用一个 C语言写的函数,应该如何修改汇编代码?
打开Keil ARM,Project
->New μVision Project
->选择STM32F103ZE
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
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
# include
extern 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.
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
extern void Init_1(void);
int get5(void);
int main(){
printf("Begin...\n");
Init_1();
return 0;
}
int get5(){
return 5;
}
执行get5后,R0变为了5,调用成功!
汇编程序调用C函数时,函数的入口参数使用栈来传送,参数的传递顺序是从右到左。即函数最后(最右边的)一个参数先入栈,而最左边的第一个参数最后入栈,然后执行 CALL 指令去调用C函数。
在C函数返回后,汇编程序需要把先前压入栈中的函数参数清除掉,即调用者负责清除参数占用的栈空间。
在C程序中内联或嵌入式汇编代码,以提高程序的效率
void enable_IRQ(void)
{
int tmp;
_ _asm //声名内联汇编代码
{
MRS tmp, CPSR
BIC tmp, tmp, #0x80
MSR CPSR_c, tmp
}
}
定义一个嵌入式汇编函数的语法格式为:
_ _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