跳到主要内容位置

ARM架构-寄存器与汇编指令

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)

image-20240902224740097

2.1 通用寄存器#

R0-R12 为通用目的寄存器,用来保存数据和计算。

2.2 程序状态寄存器#

M3/M4#

xPSR 对应 3 个寄存器

可以使用下面的指令一次性操作三个寄存器:

MRS R0, PSR ; 读组合状态寄存器, Move to Register from Status
MRS PSR, R0 ; 写组合状态寄存器, Move to Status from Register

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{cond}{S} Rd, Rn, Operand2
  • Operation 表示各类汇编指令,如 ADD, MOV;
  • Cond 表示 condition,即该指令执行的条件;
  • S 表示该指令执行后,会修改程序状态寄存器;
  • Rd 为目的寄存器,用来存储运算结果;
  • Rn,Operand2 是两个源操作数

如:

CMP R1, R3
MOVEQ R1, R2 ;表示如果上一条指令相等条件成立的话,才执行这条指令

EQ: 等于(Equal),即 Z=1
NE: 不等于(Not Equal),即 Z=0
CSHS: 带进位(Carry Set)或 高于或等于(Higher or Same),即 C=1
CCLO: 无进位(Carry Clear)或 低于(Lower),即 C=0
MI: 负数(Minus),即 N=1
PL: 正数或零(Plus or Zero),即 N=0
VS: 溢出(Overflow Set),即 V=1
VC: 无溢出(Overflow Clear),即 V=0
HI: 高于(Higher),即 C=1Z=0
LS: 低于或等于(Lower or Same),即 C=0Z=1
GE: 大于等于(Greater Than or Equal),即 N=V
LT: 小于(Less Than),即 NV
GT: 大于(Greater Than),即 Z=0N=V
LE: 小于或等于(Less Than or Equal),即 Z=1NV
AL: 总是(Always),即无条件执行

3.2 立即数#

如果就是想把任意数赋值给 R0,应该怎么办?

使用伪指令

3.3 伪指令#

LDR 加载数据#

LDR R0, =VAL

“伪指令”,就是假的、不存在的指令。注意 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.汇编模拟器与汇编指令详解#

11_汇编模拟器_示例2

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”

STMFD sp!, {r0-r5} ; Push onto a Full Descending Stack
LDMFD sp!, {r0-r5} ; Pop from a Full Descending Stack

注:配套使用

4.2 数据处理指令#

加法指令 ADD:

ADD R1, R2, R3 ; R1 = R2 + R3
ADD R1, R2, #0x12 ; R1 = R2 + 0x12

减法指令 SUB:

SUB R1, R2, R3 ; R1 = R2 - R3
SUB R1, R2, #0x12 ; R1 = R2 - 0x123.

位操作:

VisUAL 里不支持(1<<4)这样的写法,写成:0x10

AND R1, R2, #(1<<4) ; 位与,R1 = R2 & (1<<4)
AND R1, R2, R3 ; 位与,R1 = R2 & R3
BIC R1, R2, #(1<<4) ; 清除某位,R1 = R2 & ~(1<<4)
BIC R1, R2, R3 ; 清除某位,R1 = R2 & ~R3
ORR R1, R2, R3

比较:

CMP R0, R1 ; 比较R0-R1的结果
CMP R0, #0x12 ; 比较R0-0x12的结果
TST R0, R1 ; 测试 R0 & R1的结果
TST R0, #(1<<4) ; 测试 R0 & (1<<4)的结果

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。

示例: