From( ARM 寄存器详解 ):https://blog.csdn.net/sandeldeng/article/details/52954781
ARM 汇编基础教程:2.数据类型和寄存器:https://www.52pojie.cn/thread-797306-1-1.html
ARM 的 寄存器
ARM 工作状态 和 工作模式
- 工作状态:arm 支持大小端(默认小端 --- 低地址地位字节数据)、arm支持16bit thumb指令和32bit arm指令(默认arm指令)
- 工作模式:不同 工作模式 下 可访问的寄存器和指令集有差异,主要用于区分运行权限保护系统(由运行异常、中断或者软件主动触发切换模式)
工作模式分类(7种):用户模式usr(低权限-用户进程工作)、快速中断FIQ、普通中断IRQ、保护模式supervisior(高权限-操作系统运行)、地址或内存访问错误Abort、指令未定义Undefined、系统特权任务模式system(较少用)
ARM 寻址方式(处理器根据指令信息找到操作数的方式)
- 1、立即数寻址( 操作数直接在指令中 mov R1,#3:将3放到R0)
- 2、寄存器寻址 ( 操作数放在寄存器中 mov R0,R1:将R0的值放到R1中)
- 3、寄存器间接寻址
(操作数在寄存器值为地址指向的内存中 mov R0,[R2]:将R2中的值为地址指向内存中的数放到R0中) - 4、寄存器位移寻址
(当第二个数为位移方式时、将寄存器的值先位移处理得到操作数 mov R0,R1,lsl#3:将R1中值左移3位放到R0中) - 5、寄存器基址寻址(也叫基址变址寻址)
由间接寻址发展而来,先对寄存器中的值进行计算,再以结果为地址,取其指向内存值为操作数。
示例: LDR R0, [R1, #4]:先将R1中的值加4 然后以结果为地址 对应的内存操作数放到R0 - 6、多寄存器寻址
一条指令传送多个(最多16个)寄存器值
示例: STMIA R0!,{R2-R7,R12}:将 R2 到 R7 和 R12 放到 R0 指向的地址中 - 7、相对寻址
以程序计数器 PC 的当前值为基地址,指令中的地址标号作为偏移量,
将两者相加之后得到操作数的有效地址 BL NEXT:跳转到NEXT标签处 - 8、拷贝寻址。将连续的寄存器值进行操作。
STMIA R0! ,{R1-R7}:将R1~R7的数据保存到R0指向的地址中 - 9、堆栈寻址。将栈用于操作数保存或者导出的操作。
STMFD SP!,{R1-R7,LR}:将R1~R7,LR入栈,SP更新。满递减堆栈
要介绍 ARM 寄存器之前我们要先了解一下 ARM 处理器的工作模式:
ARM 处理器有 7 种 工作模式( 使用级别 ),以防造成对系统的破坏。
不同模式可以访问的 寄存器 不同,可以运行的指令不同
(1)USR(10000):正常用户模式,程序正常执行模式。
(2)FIQ(10001):快速中断模式,以处理快速情况,支持高速数据传输或通道处理。
(3)IRQ(10010):外部中断模式,普通中断处理
(4)SVC(10011):操作系统保护模式(管理模式),即操作系统使用的特权模式(内核),处理软件中断swi reset
(5)abt(10111):数据访问中止模式,用于 虚拟存储器 和 存储器 保护
(6)und(11011):未定义指令终止模式,用于支持通过软件仿真硬件的协处理器
(7)sys(11111):系统模式,用于运行特权级的操作系统任务( armv4 以上版本才具有)
注意:
- usr 是 普通模式( 即 用户模式 ),其他 六种 是 特权模式,而除了usr 和 sys 模式以外的五种模式是异常模式。
- ARM 微处理器的运行模式可以通过软件改变,也可以通过外部中断或异常处理改变。大多数的应用程序运行在用户模式下,当处理器运行在用户模式下时,某些被保护的系统资源是不能被访问的。除用户模式以外,其余的所有6种模式称之为非用户模式,或特权模式(Privileged Modes);其中除去 用户模式 和 系统模式 以外的 5 种又称为 异常模式(Exception Modes),常用于处理中断或异常,以及需要访问受保护的系统资源等情况。
- ARM 处理器在每一种处理器模式下均有一组相应的寄存器与之对应。即在任意一种处理器模式下,可访问的寄存器包括15个通用寄存器(R0~R14)、1 到 2 个状态寄存器 和 程序计数器。在所有的寄存器中,有些是在7种处理器模式下共用的同一个物理寄存器,而有些寄存器则是在不同的处理器模式下有不同的物理寄存器。
ARM 处理器有 32位的ARM 和 16位的Thumb 两种工作状态。
- 在 32 位 ARM 状态下执行 字对齐 的 ARM 指令,
当处理器执行在ARM状态:
1)所有指令32bits宽
2)所有指令必须 word 对齐
3)所有 pc 值由 [31:2] 决定,bits[1:0] 未定义( 所以指令不能 halfword/byte对齐 )
- 在 16位 Thumb 状态下,执行半字对齐的Thumb指令。
在 Thumb 状态下,程序计数器 PC (Program Counter) 使用位[1]选择另一个半字。
- ARM 指令和 THUMB指令的关系:
THUMB 指令是 ARM 指令的子集,可以相互调用,只要遵循一定的调用规则
Thumb 指令 与 ARM 指令 的时间效率和空间效率关系为:
存储空间约为ARM代码的60%~70%
指令数比ARM代码多约30%~40%
存储器为32位时ARM代码比Thumb代码快约40%
存储器为16位时Thumb比ARM代码快约40~50%
使用Thumb代码,存储器的功耗会降低约30%
注:当处理器处于Thumb状态时发生异常(如IRQ、FIQ、Undef、Abort、SWI等),则异常处理返回时,自动切换到 Thumb 状态。ARM 处理器总是 从 ARM 工作状态开始执行的。因此,如果要在调试器重运行 Thumb 程序,必须为 该 Thumb 程序添加一个 ARM程序头,然后再切换到Thumb工作状态,调用该 Thumb程序。
在实际系统中,内核状态需要经常的切换(Interworkong)来满足系统性能要求。具体的切换是通过 Branch Exchange,即 BX 指令来实现的。指令格式为:
Thumb 工作状态 BX Rn
ARM 工作状态 BX Rn
其中 Rn 可以是寄存器 R0 ~ R15 中的任意一个。指令可以通过将寄存器Rn的内容,拷贝到程序计数器 PC 来完成在 4GB地址空间中的绝对跳转,如果操作数寄存器的状态位 Bit0 = 0,则进入 ARM 工作状态;如果 Bit0 = 1,则进入 Thumb 工作状态。
ARM 处理器在两种工作状态之间可以切换,切换不影响处理器的模式或寄存器的内容。
- (1) 当操作数寄存器的状态位 ( 位[0] ) 为 1 时,执行 BX 指令进入 Thumb 状态。如果处理器在 Thumb 状态进入异常,则当异常处理 ( IRQ、FIQ、Undef、Abort 和 SWI ) 返回时,自动转换到 Thumb 状态。
- (2) 当操作数寄存器的状态位 ( 位[0] ) 为 0 时,执行 BX 指令进入ARM状态,处理器进行异常处理 ( IRQ、FIQ、Reset、Undef、Abort 和 SWI )。在此情况下,把PC放入异常模式链接寄存器中。从异常向量地址开始执行也可以进入ARM状态。
在资料《 ARM Architecture Reference Manual 》在其中的 Programmers’Model 一章中,我们可以轻松的找到官方文档对寄存器的说明,建议大家去看看官方的英文文档,我将其中的重要内容简单的总结一下。
ARM 框架文档
图:
31 个通用寄存器,6 个状态寄存器 ( 1 个 cpsr,5 个 spsr)。
Arm 处理器总共有 37 个寄存器,其可以分为以下 2 类:
- 1. 通用寄存器( 31 个)
1. 不分组寄存器( R0 — R7 ),共 8 个。
2. 分组寄存器( R8 — R14 )
3. PC 指针( R15 ) - 2. 程序状态寄存器( 6个 )
1. CPSR( 1个 )
2. SPSR( 5个 )
所谓 不分组 就是在七种模式下的任意一种模式都访问同一个物理寄存器地址。就是不分组寄存器没有特权模式,任意一种模式都可以使用未分组寄存器。
ARM 框架图( ARM 工作状态寄存器 ):
ARM 处理器 工作模式 下的 寄存器:
图 3:
图 4:
THUMB 状态下的 寄存器 组织
Thumb 与 ARM 状态下的 寄存器 关系
图:
ARM 状态下 各模式 寄存器 小结
ARM 框架 的 寄存器 解析
下面将会结合上图和上面列出的框架对寄存器进行解析:
未分组寄存器:R0 - R7 R15 cpsr,cpu 在任何模式下看到的 这几个寄存器都是一样的。在所有的运行模式下,未分组寄存器都指向同一个物理寄存器,他们未被系统用作特殊的用途,因此,在中断或异常处理进行运行模式转换时,由于不同的处理器运行模式均使用相同的物理寄存器,可能会造成寄存器中数据的破坏,这一点在进行程序设计时应引起注意。
分组寄存器 R8 - R14 在不同模式下看到的这几个寄存器是不一样的
分组寄存器R8~R12
寄存器功能说明
- R0 - R12 存取数据,临时数据
- R13:sp 用于指向不同模式的栈顶。栈,每种模式都需要开辟一块内存,用于在该模式下 函数调用,临时分配的数据存放在此处。
Stack Pointer (SP)
,栈指针寄存器,该寄存器始终保存着一个指向栈顶的值,值得注意的地方;
SP寄存器的Bit[1:0](最低两位)始终为0,因此这个寄存器是按照字对齐的,也就是四个字节;M3有两个堆栈指针,并且同一时刻只能使用其中的一个;
主堆栈指针(MSP/SP_main):复位后默认使用的堆栈指针寄存器,用于操作系统内核以及异常处理例程(包括中断服务例程);
进程堆栈指针(PSP/SP_process):由用户的应用程序代码使用。
寄存器R13通常被用作堆栈指针寄存器,另外究竟使用哪个寄存器,由CPU的控制寄存器来决定;Handler mode :即系统发生异常(中断等)的情况会进入该模式,通常使用SP_main;
Thread mode:用户程序正常运行时处于该模式,可以选择使用SP_main 或 SP_process;
CPU的Configuration Control Register,如下图所示; M3
有两个堆栈指针,并且同一时刻只能使用其中的一个;- 主堆栈指针(
MSP/SP_main
):复位后默认使用的堆栈指针寄存器,用于操作系统内核以及异常处理例程(包括中断服务例程); - 进程堆栈指针(
PSP/SP_process
):由用户的应用程序代码使用。
- 主堆栈指针(
- R14 : lr 程序跳转的时候,返回到的地址就保存到此处
- R15 :pc 要执行的下一条指令地址,就存放在此处,每次指令执行完,就自动+4
- CPSR:程序状态寄存器。程序执行的时候,有很多临时标记位,结果是0 是否溢出,是否有借位,是否有 进位,当前cpu模式,
- SPSR:用于模式切换,将切换前的 cpsr 保存到 新的模式的 spsr,模式切换回去的时候,再将spsr的内容还原到cpsr。
1. 未分组寄存器 R0 ~ R7,共 8 个。在所有的运行模式下都使用同一个物理寄存器,它们未被系统用作特殊的用途。
2. 分组寄存器 R8 ~ R12,R13 ~ R14
R8 ~ R12:( 总共10个 )
每次所访问的物理寄存器与处理器当前的运行模式有关,R8~R12:每个寄存器对应两个不同的物理寄存器
当使用fiq模式时,访问寄存器R8_fiq~R12_fiq,当使用除fiq模式以外的其他模式时,访问寄存器R8_usr~R12_usr。
其中 FIQ 模式有单独的一组 R8 ~ R12,共5个;( FIQ ( 快速中断模式 ) 时访问寄存器 R8_fiq ~ R12_fiq )
另外 6 种模式共用一组 R8 ~ R12,共5个;(当使用除 FIQ 模式以外的其他模式时,访问寄存器 R8 ~ R12)
R13 ~ R14:( 总共12个 )
其中 USR 和 SYS 模式(表格的第一列)共用一组 R13 ~ R14 共2个,
另外 5 种模式下各有独自的一组 R13 ~ R14,并采用以下记号来区分不同的物理寄存器,分别
为 fiq、irq、svc、abt、und。共10个。( mode为以下几种之一:usr、fiq、irq、svc、abt、und。 )
i. R13 在 ARM 指令中常用作堆栈指针 SP
特别注意:由于每一种模式都有自己的 R13,所以我们在自己初始化的时候 一般都要初始化每种模式下的R13,
使其指向该运行模式的栈空间。
ii. R14 称为子程序链接寄存器 LR (Link Register)
有两个特殊功能,一种是每一种模式下都可以用于 保存函数的返回地址,
另外就是异常处理后的返回地址,如中断
3. PC 指针( R15 )
R15 用作程序计数器 ( PC ),对应一个物理寄存器,由于 ARM 体系结构采用了多级流水线技术
(不了解多级流水线的可以访问这个博客:http://blog.csdn.net/abclixu123/article/details/7471822 )
对于 ARM 指令集而言,PC 总是指向当前指令的下两条指令的地址,可以通过向 pc 赋值,来控制程序跳转。
即 PC 的值为当前指令的地址值加8个字节。
4. CPSR (1 个 状态寄存器。CPSR 表示:当前程序状态寄存器 )
CPSR(当前程序状态寄存器)
在官方文档中我们可以找到
下面介绍其中几个比较重要的位,其他位,大家可以参考官方手册:
- 0 ~ 7 控制
- 8 ~ 27 保留
- 28 ~ 31 条件标志
图
图
图
- N:当两个表示的有符号整数运算时,1表示运算结果为负数,0表示结果为正或零。
- Z:1表示运算的结果为零,0表示运算的结果不为零。对于CMP指令,1表示进行比较的两个数大小相等。
- C:下面分四种情况讨论C的设置方法:
a) 加法运算(包括比较指令CMN):当运算产生了进位时(无符号数溢出),C=1,否则C=0。
b) 减法运算(包括比较指令CMP):当运算时产生了借位(无符号数溢出),C=0,否则C=1。
c) 对于包含移位操作的非加/减运算指令,C为移出值的最后一位。
d) 对于其他的非加/减运算指令,C的值通常不改变。 - V:下面分两种情况讨论V的设置方法
a) 对于加/减法运算指令,当操作数和运算结果为二进制的补码表示的带符号数时,V=1表示符号位溢出。
b) 对于其他的非加/减运算指令,C的值通常不改变。 - I: 1 表示禁止外部(硬件)中断(IRQ)
- F:1 表示禁止快速中断(FIQ)
- T:1 表示为 thumb 状态。 0 为 ARM 状态。
- M[4:0]:用来设置处理器的工作模式具体数据见本文开始的介绍。
5. SPSR ( 5 个 备份状态寄存器 )
SPSR(备份的程序状态寄存器,或者叫 影子寄存器)
SPSR 除 usr、sys 外,对应用于异常保护的 CPSR 的备份,异常时,保存CPSR值,异常退出时,将该值恢复到CPSR,以保证程序的正常运行,每一中异常运行模式(除usr和sys)有各自的物理寄存器。
至此,共有 37 个寄存器:其中 31 个为通用寄存器,6 个位状态寄存器。
37个寄存器是指 通用寄存器 和一些 状态寄存器,这是 ARM 架构定义好的寄存器,事实上 ARMv7 已经不止这些寄存器了,37 个 寄存器是 ARMv6 以前的架构,典型的就是基于 ARMv4T 的 ARM9 有这么多寄存器,之后 ARM 引入了 TrustZone 以及虚拟化就又引入了一些 banked 寄存器。现在到了 ARMv8,通用寄存器又不一样了,具体可以看 ARM 的 spec。
下图是在 Keil MDK
的调试环境下的寄存器列表,如下所示;
从图中可知,
- 用户 模式 和 系统 模式
使用相同的物理寄存器,R0-R15,CPSR,共17个物理寄存器; - FIQ 模式(快速中断请求模式)中:
R0-R7,R15,CPSR 是 和 用户模式 相同的物理寄存器,R8-R14是FIQ模式专有的; - IRQ 模式(中断请求模式)中:
R0-R12,R15,CPSR 和 用户模式 共用相同的物理寄存器,R13,R14,SPSR 是 IRQ 模式专有的 - SVC 模式(操作系统保护模式)中:
R0-R12,R15,CPSR 和 用户模式 共用相同的物理寄存器,R13,R14,SPSR 是 SVC 模式专有的 - Undef 模式(未定义模式)中:
R0-R12,R15,CPSR 和 用户模式 共用相同的物理寄存器,R13,R14,SPSR 是 Undef 模式专有的 - Abort 模式(终止模式)中:
R0-R12,R15,CPSR 和 用户模式 共用相同的物理寄存器,R13,R14,SPSR 是 Abort 模式专有的
注:R13:寄存器 R13 通常用做堆栈指针SP。
R14:link return 寄存器,在子程序调用和异常发生时保存返回地址。(保存调用子函数的返回地址,和中断的返回地址)
R15:寄存器R15被用做程序计数器,也称为PC。其值等于当前执行的指令的地址+8(因为在取址和执行之间多了一个译码的阶段)。PC总是指向正在运行的后两条指令地址,即是当前执行指令的地址+8.
CPSR:ARM 的状态寄存器
SPSR:CPSR 的备份寄存器,( 或者 影子寄存器 )
ARM 所有工作模式下都可以访问程序状态寄存器 CPSR。CPSR 包含 条件标志位、中断控制位、当前处理器模式 以及其他状态和控制信息。CPSR 在每种异常模式下都有一个对应的物理寄存器 ---- 程序状态保存寄存器 SPSR。当异常发生的时候,SPSR用于保存 CPSR 的值,以便异常返回后恢复异常发生时的工作状态。
CPSR 状态寄存器的示意图:
r0 - r3 用作传入函数参数,传出函数返回值。在子程序调用之间,可以将 r0-r3 用于任何用途。
被调用函数在返回之前不必恢复 r0-r3。如果调用函数需要再次使用 r0-r3 的内容,则它必须保留这些内容。
r4 - r11 被用来存放函数的局部变量。如果被调用函数使用了这些寄存器,它在返回之前必须恢复这些寄存器的值。
r12 是内部调用暂时寄存器 ip。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。
在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复 r12。
13 是栈指针 sp。它不能用于任何其它用途。sp 中存放的值在退出被调用函数时必须与进入时的值相同。
r14 是链接寄存器 lr。如果您保存了返回地址,则可以在调用之间将 r14 用于其它用途,程序返回时要恢复
r15 是程序计数器 PC。它不能用于任何其它用途。
注意:在中断程序中,所有的寄存器都必须保护,编译器会自动保护 R4~R11
寄存器编号 | 可选名字 | ATPCS寄存器用法 |
---|---|---|
R0~R3 | A1~A4 | 参数寄存器,在调用函数时,用来存放前4个函数参数和返回值,多出的存堆栈中。(易失的) |
R4~R8 | V1~V5 | 通用变量寄存器。(非易失性的) |
R9 | V6 sb | 通用变量寄存器。在与读/写位置无关的编译情况下,用作基址寄存器。(非易失性的) |
R10 | V7 sl | 通用变量寄存器。在使用堆栈边界检查的编译情况下,R10保存堆栈边界的地址。(非易失性的) |
R11 | V8 fp | 通用变量寄存器。在使用结构指针的情况下,必须保存这个寄存器中调用函数的变量值。(非易失性的) |
R12 | Ip | 通用临时过渡寄存器(易失的) |
R13 | sp | 堆栈指针,指向满递减堆栈 |
R14 | lr | 链接寄存器,在函数调用时用以保存返回地址 |
R15 | pc | 程序计数器 |
注意:带四个或更少的参数的函数,要比多于四个参数的效率到很多,多于四个,参数要存放在堆栈中,调用函数,须通过堆栈。
LR
Link Register (LR)
,连接寄存器,即在调用子程序的时候,可以将当前的程序地址存入LR
寄存器,这样就方便了函数的返回;
通常LR
会配合BL
和BLX
来使用,下面简单举一个函数调用的例子;
BL func01BL func02B .func01MOV R5, #05BX LRfunc02MOV R6, #06BX LR
5 PC
Program Counter(PC)
,程序计数寄存器,这个寄存器的Bit [0]
始终为0
,所以指令的对齐方式是按照四个字节(一个字)或者两个字节(半字)来对齐的。
LDR LR, =func01LDR PC, =func03B .func01MOV R5, #05BX LRfunc02MOV R6, #06BX LRfunc03MOV R7, #07MOV R8, #08 BX LR
这个程序程序为直接将func03
标签的地址装载到PC
寄存器,因此程序会直接跳转到func03
,因为之前将func01
装载到LR
寄存器,因此最终会调用函数func01
。
ARM 64 经常用到的汇编指令
MOV X1,X0 ;将寄存器X0的值传送到寄存器X1
ADD X0,X1,X2 ;寄存器X1和X2的值相加后传送到X0
SUB X0,X1,X2 ;寄存器X1和X2的值相减后传送到X0
AND X0,X0,#0xF ; X0的值与0xF相位与后的值传送到X0
ORR X0,X0,#9 ; X0的值与9相位或后的值传送到X0
EOR X0,X0,#0xF ; X0的值与0xF相异或后的值传送到X0
LDR X5,[X6,#0x08] ;X6寄存器加0x08的和的地址值内的数据传送到X5
STR X0, [SP, #0x8] ;X0寄存器的数据传送到SP+0x8地址值指向的存储空间
STP x29, x30, [sp, #0x10] ;入栈指令
LDP x29, x30, [sp, #0x10] ;出栈指令
CBZ ;比较(Compare),如果结果为零(Zero)就转移(只能跳到后面的指令)
CBNZ ;比较,如果结果非零(Non Zero)就转移(只能跳到后面的指令)
CMP ;比较指令,相当于SUBS,影响程序状态寄存器CPSR
B/BL ;绝对跳转#imm, 返回地址保存到LR(X30)
RET ;子程序返回指令,返回地址默认保存在LR(X30)
其中 MOV
指令只能用于寄存器之间传值,寄存器和内存之间传值通过 LDR
和 STR
ARM指令又一个重要特点就是所有指令都是带有条件的,就是说汇编中就需要根据状态寄存器中的一些状态来控制分支的执行。例如:
图中指令 b
是跳转指令,后边跟着跳转的条件 eq
,那么这个 eq
是什么意思呢?
eq
英文单词equal
的意思,注意这里equal
并不是c语言当中==
的意思,这里根据状态寄存器的条件标志位Z
来判断,如果Z = 1
则eq
成立,如果Z = 0
则eq
不成立,就是NE
。
ARM 指令包含4位的条件码列表:
操作码 | 条件码助记符 | 标志 | 含义 |
---|---|---|---|
0000 | EQ | Z=1 | 相等 |
0001 | NE(Not Equal) | Z=0 | 不相等 |
0010 | CS/HS(Carry Set/High or Same) | C=1 | 无符号数大于或等于 |
0011 | CC/LO(Carry Clear/LOwer) | C=0 | 无符号数小于 |
0100 | MI(MInus) | N=1 | 负数 |
0101 | PL(PLus) | N=0 | 正数或零 |
0110 | VS(oVerflow set) | V=1 | 溢出 |
0111 | VC(oVerflow clear) | V=0 | 没有溢出 |
1000 | HI(HIgh) | C=1,Z=0 | 无符号数大于 |
1001 | LS(Lower or Same) | C=0,Z=1 | 无符号数小于或等于 |
1010 | GE(Greater or Equal) | N=V | 有符号数大于或等于 |
1011 | LT(Less Than) | N!=V | 有符号数小于 |
1100 | GT(Greater Than) | Z=0,N=V | 有符号数大于 |
1101 | LE(Less or Equal) | Z=1,N!=V | 有符号数小于或等于 |
1110 | AL | 任何 | 无条件执行(默认) |
1111 | NV | 任何 | 从不执行 |
ARM指令所有指令都是带有条件的,默认是AL
即无条件执行,当指令带有默认条件时不需要明确写出。