- ARM 汇编基础(语法、寄存器等)
- 核心数据处理指令(算术、逻辑、移位)
- 内存访问指令(加载与存储)
- 分支与跳转指令(程序流程控制)
- 其他重要指令
- 伪指令
ARM 汇编基础
语法风格
ARM 汇编主要有两种语法风格:UAL (Unified Assembly Language) 和传统的 ARM/Thumb 语法。UAL 是现代标准,强烈推荐使用,它的特点是:

- 操作码(助记符)不区分大小写(如
ADD,add,Add都可以)。 - 寄存器名使用
R前缀(如R0)或 前缀(如%r0),也可以省略前缀(如0)。 - 第二操作数前必须有 符号(立即数)或 ` `(寄存器)。
核心寄存器
ARM 处理器有 37 个 32 位通用寄存器,但在任何时刻,程序员可以访问其中 16 个,这 16 个寄存器根据处理器的工作模式(如用户模式、中断模式等)而有不同的别名,以便于记忆。
| 别名 | 编号 | 描述 |
|---|---|---|
r0 - r3 |
0 - 3 | 暂存寄存器:用于函数参数传递和临时存储。 |
r4 - r11 |
4 - 11 | 变量寄存器:用于保存函数内的局部变量。r7 在 Linux 系统调用中常被用作系统调用号。r11 (或 fp) 通常用作帧指针。 |
r12 |
12 | 内部调用暂存器:用作函数调用间的临时寄存器。 |
r13 |
13 | 堆栈指针:指向当前栈顶,别名 sp。 |
r14 |
14 | 链接寄存器:保存函数返回地址,别名 lr。 |
r15 |
15 | 程序计数器:指向下一条要执行的指令地址,别名 pc。 |
核心数据处理指令
这类指令通常在寄存器之间操作,结果也存放在寄存器中。
算术指令
ADD(加法)ADD Rd, Rn, Operand2 ; Rd = Rn + Operand2 ADD R0, R1, #10 ; R0 = R1 + 10 ADD R2, R3, R4 ; R2 = R3 + R4
SUB(减法)SUB Rd, Rn, Operand2 ; Rd = Rn - Operand2 SUB R0, R1, #5 ; R0 = R1 - 5
MUL(乘法)MUL Rd, Rn, Rm ; Rd = Rn * Rm (结果只使用低32位) MUL R0, R1, R2 ; R0 = R1 * R2
UDIV(无符号除法, ARMv7-A/R 及以后)UDIV Rd, Rn, Rm ; Rd = Rn / Rm (无符号) UDIV R0, R1, R2 ; R0 = R1 / R2
逻辑指令
AND(按位与)AND Rd, Rn, Operand2 ; Rd = Rn & Operand2 AND R0, R1, #0xFF ; R0 = R1 & 0xFF (屏蔽高24位)
ORR(按位或)ORR Rd, Rn, Operand2 ; Rd = Rn | Operand2 ORR R0, R1, #0x100 ; R0 = R1 | 0x100 (设置第8位)
EOR(按位异或)EOR Rd, Rn, Operand2 ; Rd = Rn ^ Operand2 EOR R0, R1, R1 ; R0 = R1 ^ R1 = 0 (清零寄存器)
BIC(位清除,即按位与反)BIC Rd, Rn, Operand2 ; Rd = Rn & (~Operand2) BIC R0, R1, #0x20 ; R0 = R1 & ~0x20 (清除第5位)
移位指令
移位是 ARM 的强大功能,可以嵌入到几乎所有数据处理指令的 Operand2 中。
LSL(逻辑左移)LSR(逻辑右移)ASR(算术右移,保持符号位)ROR(循环右移)
示例:

; 将 R1 的值逻辑左移 2 位,结果存入 R0 ADD R0, R1, R1, LSL #2 ; R0 = R1 + (R1 << 2) <=> R0 = R1 * 4 ; 将 R2 的值算术右移 1 位,结果存入 R2 (相当于除以2) ASR R2, R2, #1 ; 移位也可以作为独立指令 LSL R3, R4, #5 ; R3 = R4 << 5
内存访问指令 (Load/Store)
ARM 是加载/存储架构,意味着所有内存操作都必须通过专门的指令来完成,不能直接在内存之间操作数据。
加载指令 (从内存到寄存器)
LDR(Load Register)LDR Rd, [Rn] ; Rd = [Rn] (读取Rn地址处的32位数据) LDR Rd, [Rn, #offset] ; Rd = [Rn + offset] LDR Rd, [Rn, #offset]! ; Rd = [Rn + offset], 然后更新 Rn = Rn + offset (!) LDR Rd, [Rn], #offset ; 先读取,再更新 Rn = Rn + offset
LDRB(Load Register Byte)LDRB R0, [R1] ; R0 = [R1] (读取R1地址处的8位数据,零扩展到32位)
存储指令 (从寄存器到内存)
STR(Store Register)STR Rd, [Rn] ; [Rn] = Rd STR Rd, [Rn, #offset] ; [Rn + offset] = Rd
STRB(Store Register Byte)STRB R0, [R1] ; [R1] = R0 (将R0的低8位存入R1地址)
示例:
; 假设 R1 指向一个结构体,R2 是偏移量 LDR R0, [R1, #4] ; 将 R1+4 地址处的数据加载到 R0 STR R3, [R4, #-8] ; 将 R3 的值存入 R4-8 的地址 ; 使用堆栈 STR R5, [SP, #-4]! ; 将 R5 压入栈中,并让 SP 减 4 LDR R6, [SP], #4 ; 从栈中弹出数据到 R6,并让 SP 加 4
分支与跳转指令 (程序流程控制)
-
B(Branch / 跳转)B label ; 无条件跳转到 label BNE label ; Z 标志位为0 (不相等/非零),则跳转 BEQ label ; Z 标志位为1 (相等/零),则跳转 BGT label ; 如果大于 (无符号),则跳转 BLT label ; 如果小于 (无符号),则跳转
条件码是 ARM 的另一个强大特性,大部分指令都可以根据条件码执行或跳过。
(图片来源网络,侵删) -
BL(Branch with Link / 带链接的跳转)BL my_function ; 跳转到 my_function,并将返回地址 (下一条指令地址) 保存到 LR (r14)
这是函数调用的标准方式。
-
BX(Branch and Exchange / 跳转并交换)BX R0 ; 跳转到 R0 中存放的地址
常用于从 ARM 状态切换到 Thumb 状态(R0 的最低位为 1)。
其他重要指令
MOV(Move)MOV Rd, Operand2 ; Rd = Operand2 (将立即数或寄存器值移入 Rd) MOV R0, #0 ; R0 = 0 MOV R1, R2 ; R1 = R2
MVN(Move Negative / 按位取反后移动)MVN R0, #0 ; R0 = ~0 = 0xFFFFFFFF
CMP(Compare)CMP Rn, Operand2 ; Rn - Operand2,并根据结果设置标志位,但不保存结果 CMP R0, #10 ; 比较 R0 是否等于 10
PUSH/POP(压栈/出栈,是伪指令,但非常常用)PUSH {R0-R3, LR} ; 将 R0, R1, R2, R3 和 LR 压入栈中 POP {R4-R7, PC} ; 从栈中弹出数据到 R4, R5, R6, R7,并跳转到弹出的地址
伪指令
伪指令不是真正的处理器指令,它们在汇编阶段被汇编器转换成一条或多条真实的指令。
LDR(用于加载地址/大立即数)LDR R0, =0x12345678 ; 伪指令,0x12345678 可以在指令中表示,则编码为立即数;否则,汇编器会将其放入一个常量池,并生成一条加载该常量的指令。 LDR R0, =my_label ; 伪指令,加载 my_label 的地址到 R0。
NOP(No Operation)NOP ; 什么都不做,通常用于延时或对齐指令。
ALIGNALIGN 4 ; 确保下一条指令的地址是 4 字节对齐的。
综合示例:一个简单的函数
下面是一个将两个 32 位整数相加并返回结果的函数示例。
;-----------------------------------------------------------------------------
; 函数: int add(int a, int b);
; 描述: 将两个整数相加并返回结果。
; 输入: R0 = a, R1 = b (根据 AAPCS 调用约定)
; 输出: R0 = 结果
;-----------------------------------------------------------------------------
add_function:
ADD R0, R0, R1 ; R0 = R0 + R1 (结果存入 R0)
BX LR ; 从 LR 中加载返回地址,并返回调用者
;-----------------------------------------------------------------------------
; 主程序
;-----------------------------------------------------------------------------
.global main
main:
; 准备参数
MOV R0, #10 ; R0 = a = 10
MOV R1, #20 ; R1 = b = 20
; 调用函数
BL add_function ; 跳转到 add_function,返回地址存入 LR
; R0 的值应该是 30
; ... 程序继续 ...
; 退出程序 (在嵌入式系统中可能不同)
MOV R7, #1 ; 系统调用号 1 (exit) for Linux
SWI 0 ; 触发软中断,进入内核态
希望这份详细的梳理能帮助你全面了解 ARM 汇编指令!如果你有具体的应用场景或想深入了解某个方面,可以随时提问。
