DAY10. 伪操作与混合编程
如果出现图片无法查看可能是网络问题,我用的GitHub+图床保存的图片,可以参考我另外一篇文章GitHub的使用方法含网络问题解决
GitHub使用教程含网络问题_github加速器_肉丸子QAQ的博客-CSDN博客
1. 伪操作
GNU的伪操作一般都以.
开
.global
.global symbol
@ 将symbol声明成全局符
- 当一个项目需要多个.s文件时,没有声明全局符是无法调用的
![image-20230731180918088](https://cdn.jsdelivr.net/gh/ybm2002329/mdlmage/202308020953225.png)
.local
.local symbol
@ 将symbol声明成局部符号,只能在当前.s文件使用
.equ
.equ DATA, 0xFF @类似于C语言的宏定义
MOV R1, #DATA
![image-20230731181533220](https://cdn.jsdelivr.net/gh/ybm2002329/mdlmage/202308020953226.png)
通过反编译可以看出
MOV
指令占用的是0地址,说明.equ
是不会生成指令的
.macro
将语句封装
@ 封装开始
.macro FUNC
MOV R1, #1
MOV R2, #2
.endm
@封装结束
FUNC
![image-20230731182103571](https://cdn.jsdelivr.net/gh/ybm2002329/mdlmage/202308020953227.png)
将程序复位后可以看到指针指向FUNC
![]()
条件编译.if .endif
.if 0 @1会编译机器码,0不编译
MOV R1, #1
MOV R2, #2
.endif
![image-20230731182430887](https://cdn.jsdelivr.net/gh/ybm2002329/mdlmage/202308020953229.png)
程序复位后指针直接指向了B
.rept
.rept 3 @重复编译的次数
MOV R1, #1
MOV R2, #2
.endr
可以看到编译了3次指令出来
.weak
弱化一个符号,即告诉编译器即便没有这个符号也不要报错
@ .weak symbol
@ 弱化一个符号,即告诉编译器即便没有这个符号也不要报错
.weak func
B func
.word
在当前地址申请一个字的空间并将其初始化为VALUE
@ .word VALUE
@ 在当前地址申请一个字的空间并将其初始化为VALUE
MOV R1, #1
.word 0xFFFFFFFF
MOV R2, #2
![image-20230731183800082](https://cdn.jsdelivr.net/gh/ybm2002329/mdlmage/202308020953232.png)
可以看到04的地址被占用掉了,只是用来放的数据
.byte
在当前地址申请一个字节的空间并将其初始化为VALUE
@ .byte VALUE
@ 在当前地址申请一个字节的空间并将其初始化为VALUE
MOV R1, #1
.byte 0xFF
MOV R2, #2
![image-20230731184135440](https://cdn.jsdelivr.net/gh/ybm2002329/mdlmage/202308020953233.png)
- 因为指令都是4的整数倍,当使用
.byte
后会占用04的地址,第二条MOV指令会从05开始不满足条件无法编译成功将5-7的地址空出来,从08开始就能解决这个问题
- 如何空出地址需要用到另外一个伪操作
.align
.align
告诉编译器后续的代码2的N次方对齐
@ .byte VALUE
@ 在当前地址申请一个字节的空间并将其初始化为VALUE
MOV R1, #1
.byte 0xFF
@ .align N
@ 告诉编译器后续的代码2的N次方对其
.align 4
MOV R2, #2
![image-20230731184750276](https://cdn.jsdelivr.net/gh/ybm2002329/mdlmage/202308020953235.png)
当N取4时就是16,可以看到第二条MOV指令存放在16(16进制就是10)的位置
.arm
告诉编译器后续的代码是ARM指令
@ .arm
@ 告诉编译器后续的代码是ARM指令
.thumb
告诉编译器后续的代码是Thumb指令
@ .thumb
@ 告诉编译器后续的代码是Thumb指令
.text
定义一个代码段
@ .text
@ 定义一个代码段
.data
定义一个数据段
@ .data
@ 定义一个数据段
.space
在当前地址申请N个字节的空间并将其初始化为VALUE
@ .space N, VALUE
@ 在当前地址申请N个字节的空间并将其初始化为VALUE
MOV R1, #1
.space 12, 0x12
MOV R2, #2
不同的编译器伪操作的语法不同
2. C与汇编的混合编程
- 数据处理指令、跳转指令、内存访问指令是CPU的通用指令,在ARM中能用,在其他也能用
- MSR状态寄存器传送指令、SWI指令、协处理器指令,ARM专用指令,其他CPU不一定能用
C和汇编的混合编程原则
在哪种语言环境下符合哪种语言的语法规则:
-
在汇编中将C中的函数当做标号处理:汇编没有函数的概念,调用C语言就要去掉括号
-
在C中将汇编中的标号当做函数处理:C语言的函数后面是有括号的,调用汇编的话就需要补上
-
在C中内联的汇编当做C的语句来处理
两种混合编程方式
- 方式一:汇编语言调用(跳转)C语言
.s文件
MOV R1, #1
MOV R2, #2
BL func_c
MOV R3, #3
.c文件
void func_c(void)
{
int a;
a ++;
a --;
}
- 通过调试我们可以发现当执行到BL后LR会保存下一条的地址
![]()
- 当执行完C语言后跳转BL的后一条指令
![]()
- 要想从C语言跳转回来的话就需要将LR值给PC,所以C语言最后一句话会自己写一条指令
![]()
- 方式二:C语言调用(跳转)汇编语言
.c文件
void func_c(void)
{
int a;
a ++;
FUNC_ASM();
a --;
}
.s文件
MOV R1, #1
MOV R2, #2
BL func_c
MOV R3, #3
.global FUNC_ASM @使用伪操作声明全局符
FUNC_ASM:
MOV R4, #4
MOV R5, #5
![image-20230731205219708](https://cdn.jsdelivr.net/gh/ybm2002329/mdlmage/202308020953240.png)
当C语言结束后跳转到相应的汇编代码位置
以上两种都是需要创建.s .c文件,通过两个文件之间的跳转来实现混合编程
-
C内联(内嵌)汇编
在C语言中插入汇编
void func_c(void) { int a; a ++; //C内联(内嵌)汇编 asm ( "MOV R6, #6\n" "MOV R7, #7\n" ); //C语言调用(跳转)汇编语言 FUNC_ASM(); a --; }
3. ATPCS协议
ATPCS协议:(ARM-THUMB Procedure Call Standard)
ATPCS协议主要内容
-
栈的种类
- 使用满减栈
-
寄存器的使用
R15
用作程序计数器,只能用于储存程序的指针,不能作其他用途R14
用作链接寄存器,只能用于存储返回地址,不能作其他用途R13
用作栈指针,只能用于存储栈指针不能作其他用途- 当函数的参数不多于4个时使用
R0-R3
传递,当函数的参数多于4个时,多出的部分用栈传递
- 函数的返回值使用
R0
传递
- 其它寄存器主要用于存储局部变量
int a=1,b=1,c=1,d=1,e=1,f=1,g;
int func(int a,int b,int c,int d,int e,int f)
{
return a+b+c+d+e+f;
}
int main(void)
{
g = func(a,b,c,d,e,f);
return 0;
}
4. 作业
1.简述ATPCS协议的主要内容是什么
1.栈的种类 1.1 使用满减栈 2.寄存器的使用 2.1 R15用作程序计数器,不能作其他用途 2.2 R14用作链接寄存器,不能作其他用途 2.3 R13用作栈指针,不能作其他用途 2.4 当函数的参数不多于4个时使用R0-R3传递,当函数的参数多于4个时,多出的部分用栈传递 2.5 函数的返回值使用R0传递 2.6 其它寄存器主要用于存储局部变量