ARM架构-寄存器与汇编指令
2024年9月 · 预计阅读时间: 4 分钟
#
1.地址空间-RISC 与 CISC在 ARM 架构 CPU 看来,内存、IO 的操作是一样的。
在 X86 架构中,内存和 IO 是分开的。
在精简指令集 RISC 中,CPU 对内存只有读写操作,a=a*b 通过以下四步实现:
- 读取内存 a
- 读取内存 b
- 计算 a*b
- 把结果写入内存 a
而 x86 属于复杂指令集计算机(RISC:Complex Instruction Set Computing)它所用的指令比较复杂,比如某些复杂的指令,它是通过“微程序”来实现的。比如执行乘法指令时,实际上会去执行一个“微程序”,在“微程序”里,一样执行这四个步骤。
#
2.ARM 内部寄存器ARMv7(Cortex-M3/M4) - 32 位
无论是 Cortex-M3/M4 还是 Cortex-A7 都有 R0-R15 寄存器。(这里 A7 只展示了 User 模式下的寄存器,完整在 2.3)
#
2.1 通用寄存器R0-R12 为通用目的寄存器,用来保存数据和计算。
#
2.2 程序状态寄存器#
M3/M4xPSR 对应 3 个寄存器
可以使用下面的指令一次性操作三个寄存器:
#
A7只有一个 CPSR
#
2.3 A7 架构的所有寄存器#
3.ARM 汇编指令分为 Thumb 指令和 ARMv7 指令,Thumb 指令使用 16 位表示,ARMv7 指令用 32 位表示。
- ARM 指令集,这是 32 位的,每条指令占据 32 位,高效,但是太占空间
- Thumb 指令集,这是 16 位的,每条指令占据 16 位,节省空间
要节省空间时用 Thumb:指令,要效率时用 ARM 指令。
怎么区分当前指令是 Thumb 还是 ARM 指令呢?
程序状态寄存器中有一位,名为“T”,它等于 1 时表示当前运行的是 Thumb 指令。 假设函数 A 是使用 Thumb 指令写的,函数 B 是使用 ARM 指令写的,怎么调用 A/B? 我们可以往 PC 寄存器里写入函数 A 或 B 的地址,就可以调用 A 或 B, 但是怎么让 CPU 在执行 A 函数是进入 Thumb 状态,在执行 B 函数时进入 ARM 状态? 做个手脚: 调用函数 A 时,让 PC 寄存器的 BIT0 等于 1,即:PC=函数 A 地址+(1<<0); 调用函数 B 时,让 PC 寄存器的 BITO 等于 O, 即:PC=函数 B 地址
进一步,Thumb-2 在 Cotex-A 系列使用。它不需要手工跳转,每条指令可以直接分辨,支持 16 位指令、32 位指令混合编程。
#
3.1 UAL 汇编ARM 公司推出了:Unified Assembly Language (UAL),统一汇编语言,就不需要去区分这些指令集。
在程序前面用 CODE32/CODE16/THUMB:表示指令集:ARM/Thumb/Thumb2
日常工作中 只需要这么几条汇编指令,从名字就可以猜出含义:
MOV LDR/STR LDM/STM AND/OR ADD/SUB B/BL DCD ADR/LDR CMP
UAL 汇编格式:
- Operation 表示各类汇编指令,如 ADD, MOV;
- Cond 表示 condition,即该指令执行的条件;
- S 表示该指令执行后,会修改程序状态寄存器;
- Rd 为目的寄存器,用来存储运算结果;
- Rn,Operand2 是两个源操作数
如:
#
3.2 立即数如果就是想把任意数赋值给 R0,应该怎么办?
使用伪指令
#
3.3 伪指令#
LDR 加载数据“伪指令”,就是假的、不存在的指令。注意 LDR 作为“伪指令”时,指令中有一个“=”,否则它就是真实的 LDR(load regisgter)指令了。
编译器会把“伪指令”替换成真实的指令,比如:
LDR R0, =0x12
0x12 是立即数,那么替换为:MOV R0, #0x12
LDR R0, =0x12345678
0x12345678 不是立即数,那么替换为:
LDR R0, [PC, #offset]
// 2. 使用 Load Register 读内存指令读出值,offset 是链接程序时确定的
……
Label DCD 0x12345678
// 1. 编译器在程序某个地方保存有这个值
#
ADR 伪指令用来读某个标号的地址
示例:
ADR R0, Loop
Loop
ADD R0, R0, #1
它是“伪指令”,会被转换成某条真实的指令,比如:
ADD R0, PC, #val
; val 在链接时确定
Loop
ADD R0, R0, #1
#
3.4 ARM 编译器与 GCC 编译器语法差异#
4.汇编模拟器与汇编指令详解#
4.1 内存访问指令#
LDR 和 STR 指令#
LDM 和 STM 指令低标号寄存器对应低地址内存空间
示例:
#
栈的操作根据栈指针指向,可分为满(Full)/空(Empty):
- 满 SP 指向最后一个入栈的数据,需要先修改 SP 再入栈
- 空 SP 指向下一个空位置,先入栈再修改 SP
根据压栈时 SP 的增长方向,可分为增/减:
- 增(Ascending):SP 变大
- 减(Descending):SP 变小
组合后,就有 4 种方式:满增、满减,空增,空减
。
常用的“满减”:
- 入栈时用 STMDB,也可以用 STMFD,作用一样;出栈时用 LDMIA,也可以用 LDMFD,作用一样。
示例:源码为 “source\02_录制视频时现场编写的源码\02_VisUAL\stack.S”
注:配套使用
#
4.2 数据处理指令加法指令 ADD:
减法指令 SUB:
位操作:
VisUAL 里不支持(1<<4)这样的写法,写成:0x10
比较:
#
4.3 跳转指令B:Branch,跳转
BL:Branch with Link,跳转前先把返回地址保持在 LR 寄存器中
BX:Branch and eXchange,根据跳转地址的 BIT0 切换为 ARM 或 Thumb 状态(0:ARM 状态,1:Thumb 状态)
BLX:Branch with Link and eXchange 根据跳转地址的 BIT0 切换为 ARM 或 Thumb 状态(0:ARM 状态,1:Thumb 状态)
示例:
SUBS 会影响 CSPR 寄存器的值,BNE 表示不等于(Not Equal),即 Z=0,就会跳转回 Loop。
示例: