目录

一、新建一个工程

1.新建工程 

2.代码 

 二、C语言中调用汇编函数

 1.无参数的调用

 2.带有参数的调用

 三、汇编语言中调用C语言函数

 四、寄存器的使用规则

 五、参考文献


 

一、新建一个工程

1.新建工程 

打开下载好的MDK5,点击project,创建一个新的工程,命名文件并且保存,选择芯片,我选择的是STM32F103C8,然后点击CMSIS,勾选CORE选项,点击Device,勾选Starup选项,点击OK完成设置。

汇编STA和lda_stm32

 然后添加源main.c和func.s文件至Source Group 1

2.代码 

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);

int main(){
	
	Init_1();
	
	return 0;
}

 二、C语言中调用汇编函数

 1.无参数的调用

上述代码进行编译运行,没有错误

汇编STA和lda_c语言_02

 然后设置断点,并进行调试,如图:

汇编STA和lda_汇编STA和lda_03

 

汇编STA和lda_汇编STA和lda_04

 程序进入循环,R1与R2不断加一,最终加到10

汇编STA和lda_stm32_05

 如上图,均变成0x00……00A即10。

 2.带有参数的调用

修改后的代码:

main.c

# include<stdio.h>

extern int Init_1(int x);

int main(){
	
	int xx = Init_1(10);
	printf("%d", xx);
	
	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,不然会被认为是段名,表示程序结束

 编译调试

汇编STA和lda_汇编STA和lda_06

此时,Init_1(10)10被自动传入R0。 

 

汇编STA和lda_c语言_07

汇编STA和lda_stm32_08

如上图,xx的值为0x6E,即110,调用成功。

ARM中,子函数的参数值传递按顺序存放在r0,r1,r2,r3里,超过4个参数值传递放栈帧里。
因此Init_1(10)传入的10放到了R0,由MOV PC,LR返回110。

 三、汇编语言中调用C语言函数

修改后的代码:

main.c 

# include<stdio.h>

extern void	Init_1(void);

int get5(void);

int main(){
	
	printf("Begin...\n");
	Init_1();

	return 0;
}

int get5(){
	return 5;
}

 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,不然会被认为是段名,表示程序结束

 设置断点之后调试,如图

 

汇编STA和lda_汇编STA和lda_09

 执行get5后,R0变为了5,成功调用。

 四、寄存器的使用规则

在ARM编程里,函数调用过程中,子函数的参数值传递按顺序存放在r0,r1,r2,r3里,超过4个参数值传递放栈帧

r0-r3 用作传入函数参数,传出函数返回值。在子程序调用之间,可以将 r0-r3 用于任何用途。被调用函数在返回之前不必恢复 r0-r3。—如果调用函数需要再次使用 r0-r3 的内容,则它必须保留这些内容。
r4-r11 被用来存放函数的局部变量。如果被调用函数使用了这些寄存器,它在返回之前必须恢复这些寄存器的值。r11 是栈帧指针 fp。
r12 是内部调用暂时寄存器 ip。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复 r12。
寄存器 r13 是栈指针 sp。它不能用于任何其它用途。sp 中存放的值在退出被调用函数时必须与进入时的值相同。
寄存器 r14 是链接寄存器 lr。如果您保存了返回地址,则可以在调用之间将 r14 用于其它用途,程序返回时要恢复
寄存器 r15 是程序计数器 pc。它不能用于任何其它用途。