一、伪操作
(一)微操作的概念
伪指令:本身不是指令,编译器可以将其替换成若干条指令
伪操作:不会生成代码,只是在编译阶段告诉编译器怎么编译
(二)C语言中的伪操作
在C语言中一般带‘ ;’是可以编译的指令,带‘ # ’的是预编译指令,是伪指令,预处理时处理,不会编译,不会生成汇编代码。例如
#define PI 3.14
#if --- #endif
(三)汇编中的伪操作
汇编代码中的伪操作一般都以‘ . ’开头
1、声明全局变量
.global symbol
将symbol声明成一个全局变量
未声明不能引用:
声明后引用:
2、声明局部变量
.local symblo
将symbol声明成一个局部变量
3、宏定义
.equ DATA,0xFF
MOV R1,#DATA
相当于C语言中的宏定义
4、封装代码
.macro FUNC
MOV R1,#1
MOV R2,#2
.endm
FUNC
5、条件编译
#if 1
MOV R1,#1
MOV R2,#2
#endif
#if 0
MOV R1,#1
MOV R2,#2
#endif
6、重复语句
.rept 3
MOV R1,#1
MOV R2,#2
.endr
7、弱化一个符号
如果程序执行一个没有定义的符号,使用.weak修饰可以通过编译,但这条指令会变成一条NOP空指令
@.weak symbol
.weak func
B func
8、申请空间
在当前地址申请一个字的空间并将其初始化
MOV R1,#1
.word 0xFFFFFFFF
MOV R2,#2
9、按2的n次对齐操作
.align 2
@数字的含义是按2的几次方对齐
10、 申请任意n个字节的内存地址空间
.space 12,0x12
11、其他常见的伪操作
.text
@进入代码段
.end
@结束代码段
指令集:
.arm
@下面执行的都是arm指令
.thumb
@下面执行的都是thumb指令
汇编指令集取决于处理器,而伪操作取决于编译器,不同编译器伪操作语法不同,本节我们使用的是GNU编译器,因此伪操作的语法和GNU编译器相对应。
二、C和汇编混合编程
(一)背景知识
操作系统的作用:向下管理硬件、向上提供接口(API)。
驱动是在操作系统层次。
数据处理指令、跳转指令、内存访问指令是通用指令,任何一个处理器都具备这些通用指令,通用指令一般在C语言中都有相应的语句对应。
状态寄存器传送指令(读写CPSR),软中断(系统调用)、协处理器指令,是专用指令,ARM有其他处理器不一定有。
在ARM处理器上装Linux操作系统,当linux操作系统要进行软中断等专用指令时(内核代码),C语言没有对用的指令,此时使用C语言和汇编指令混合编程,能用C的用C。
所有的CPU都离不开汇编语言。所有CPU在刚上电时的代码都是汇编语言。C语言直接使用栈,在这之前要初始化SP,单片机之前的启动代码都是汇编语言。初始化栈和CPU模式都要通过汇编指令。
(二)C和汇编混合编程格式
1、汇编语言调用(跳转)C语言
在CPU上电完成后,需要跳转到C语言。
arm-asm.s
MOV R1,#1
MOV R2,#2
BL func_c
MOV R3,#3
test.s
func_c(void){
int a;
a++;
a--;
}
执行到第三行时自动跳转:
2、C语言调用(跳转)汇编语言
arm-asm.s
MOV R1,#1
MOV R2,#2
BL func_c
.global FUNC_ASM
FUNC_ASM:
MOV R4,#4
MOV R5,#5
test.s
func_c(void){
int a = 1;
FUNC_ASM();
}
3、C内联汇编
func_c(void){
int a = 1;
asm
(
"MOV R1,#1\n"
"MOV R2,#2\n"
);
a++;
}
4、C语言和汇编混合编程的原则
在哪种语言环境下符合哪种语言的语法规则
1)在汇编中,将C语言当做标号来处理
2)在C语言中将汇编语言的编号当做函数来处理
三、ATPCS协议
(一)背景知识
混合编程时,我们自己写的栈有可能会库函数的栈有冲突(压栈出栈方式、方向)。又或者我们写的C语言编译和库函数的编译方式也不同,可能会导致内存覆盖。
ATPCS协议用于规定不同编译器编译出来的代码怎么做到兼容。
ATPCS:arm thumb program call(调用) standard
(二)ATPCS协议的内容
1、栈的种类
使用满减栈
2、寄存器的使用
R15(LR):程序计数器,只能用于存储程序的指针,不能用于其他用途
R14(PC):链接寄存器,只能用于返回地址,不能用作其他用途
R13(SP):栈指针,只能用于存储栈指针,不能用于其他用途(实际上我们就是在ATPCS协议规定下将R13当作栈指针)
R0-R3:当函数的参数少于4个的时候使用R0-R3,多出的4个部分用栈传递。
main向其他函数传参时,函数会从R0-R3依次读取第一个参数、第二个参数、第三个参数...
多余4个的参数压入栈中,函数直接从栈中去拿。因此在写代码时,能少于4个参数,就少于4个参数。
3、返回值
返回值使用R0回传。
int a,b,c,d,e,f,g;
func_c(int a,int b,int c, int d,int e,int f){
return a+b+c+d+e+f;
}
int main(){
g = func_c(a,b,c,d,e,f);
}
4、其余的寄存器用于存储局部变量。
四、ARM体系结构总结
1、操作系统的作用
向下管理硬件,向上提供接口,研究操作系统就是研究底层,直接对接CPU和其他硬件设备。
2、ARM的多种模式主要是为了适应操作系统的需求。
3、栈的效率比堆要高。
4、系统调用的内涵,应用调用write()函数,需要到操作系统调用Liunx的一些机制,此时需要更高权限的模式,所以进行软中断,CPU从用户模式到SVC模式。
5、通过汇编知道C语言代码在实际CPU中是怎样执行的。越高级的语言用起来越方便,越偏离原理。C语言就是在汇编之上进行封装。可以去写Linux下的一些应用程序。shell脚本是shell去封装C,不需要关心内存。
6、驱动方向->Device control
五、作业
1.简述ATPCS协议的主要内容是什么
ATPCS规定了应用程序的函数可以如何分开地写,分开地编译,最后将它们连接在一起,所以它实际上定义了一套有关过程(函数)调用者与被调用者之间的协议。PCS强制实现如下约定:调用函数如何传递参数(即压栈方法,以何种方式存放参数),被调用函数如何获取参数,以何种方式传递函数返回值。
1、栈的种类
使用满减栈
2、寄存器的使用
R15(LR):程序计数器,只能用于存储程序的指针,不能用于其他用途
R14(PC):链接寄存器,只能用于返回地址,不能用作其他用途
R13(SP):栈指针,只能用于存储栈指针,不能用于其他用途(实际上我们就是在ATPCS协议规定下将R13当作栈指针)
R0-R3:当函数的参数少于4个的时候使用R0-R3,多出的4个部分用栈传递。
main向其他函数传参时,函数会从R0-R3依次读取第一个参数、第二个参数、第三个参数...
多余4个的参数压入栈中,函数直接从栈中去拿。因此在写代码时,能少于4个参数,就少于4个参数。
3、返回值
返回值使用R0回传。
4、其余的寄存器用于存储局部变量。