在工作区新建如下两个文件:main.c;Func.s
分别输入以下代码
main.c
# include<stdio.h> extern void Init_1(void); int main(){ Init_1(); return 0; }
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是C语言程序,Func.s是汇编程序,Init在汇编文件里实现,在C文件里调用。
上面的代码是将寄存器R1的值与10相比,若小于10,则将R1自增至10。
注意 在C语言中调用汇编函数时,需要在.c文件中用extern 函数返回类型 函数名(); 声明,然后在main函数中调用,在汇编里面用EXPORT 函数名就可以与.c文件中的函数联系起来。
这里我们尝试将原汇编语言 Init_1函数的类型改为 int Init_1(init) ,此函数功能修改为 传入一个整型数x,函数运行后返回整型数 x+100。
根据要求将源码修改为以下内容
main.c
#include<stdio.h> extern int Init_1(int x); int main(){ Init_1(1); return 0; }
Func.s
AREA My_Function,CODE,READONLY EXPORT Init_1 ENTRY Init_1 ADD R0,R0,#100 BX LR END
可以看到R0的值由0变为了101,即16进制的65
小结 在这个部分中要明白ARM处理器中参数的传递和使用。
首先来看C语言:
C语言调用函数时,在32位程序中使用栈传递,而在64位程序中,传递方式和参数个数有关,当参数个数小于等于6个时,参数会使用寄存器被传递,当参数大于6个时,6个寄存器被分配后,会利用栈来传递多余的参数,且参数的传递都是从右到左压栈或是存入寄存器。
然后是ARM处理器中寄存器用法:
- r0-r3 用作传入函数参数,传出函数返回值。在子程序调用之间,可以将 r0-r3 用于任何用途。被调用函数在返回之前不必恢复 r0-r3。—如果调用函数需要再次使用 r0-r3 的内容,则它必须保留这些内容。
- r4-r11 被用来存放函数的局部变量。如果被调用函数使用了这些寄存器,它在返回之前必须恢复这些寄存器的值。r11 是栈帧指针 fp。
- r12 是内部调用暂时寄存器 ip。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复
r12。- 寄存器 r13 是栈指针 sp。它不能用于任何其它用途。sp 中存放的值在退出被调用函数时必须与进入时的值相同。
- 寄存器 r14 是链接寄存器 lr。如果您保存了返回地址,则可以在调用之间将 r14 用于其它用途,程序返回时要恢复寄存器 r15 是程序计数器 pc。它不能用于任何其它用途。
在c文件和汇编文件内分别输入以下代码
main.c
#include<stdio.h> extern int sum(int a,int b); int sum(int a,int b){ int c; a=1; b=1; c=a+b; return c; }
Func.s
AREA MYDATA, DATA IMPORT sum AREA MYCODE, CODE ENTRY EXPORT __main __main BL sum BX LR END
编译结果可以看到,R0的值变为2。在这个调用的过程中要注意导入一个C语言函数的格式。