--- title: ARM汇编基础 tags: [ARM, 汇编, 嵌入式Linux, IMX6ULL, STM32, RK3568] created: 2026-09-17 updated: 2026-09-17 pdf_ref: "正点原子 I.MX6U嵌入式Linux驱动开发指南V2.0.1 第七章 ARM汇编基础" --- # ARM汇编基础 > **前置知识**: [[01-Cortex-A7架构详解]] | [[STM32学习笔记/01-STM32开发环境搭建]] ## 一、ARM指令集概述 > ⚠️ **来源说明**:本节(指令集对比、ARM 指令编码格式、寻址模式)不属于《I.MX6U嵌入式Linux驱动开发指南》第七章内容,为扩展知识。原书第七章只讲解 GNU 汇编语法与最常用的 Cortex-A7 汇编指令。 ### 1.1 ARM指令集 vs Thumb指令集 | 特性 | ARM指令集 | Thumb指令集 | Thumb-2指令集 | | ---------- | ------------------ | ------------- | ------------- | | 指令宽度 | 32位 | 16位 | 16/32位混合 | | 寄存器访问 | 全部R0-R15 | 低寄存器R0-R7 | 全部R0-R15 | | 条件执行 | 所有指令 | 仅部分指令 | 所有指令 | | 性能 | 最高 | 较低 | 接近ARM | | 代码密度 | 低 | 高 | 高 | | 典型应用 | 启动代码、关键路径 | 低功耗应用 | 通用嵌入式 | ### 1.2 指令格式 ARM 32位指令格式: ``` | 31-28 | 27-25 | 24-21 | 20 | 19-16 | 15-12 | 11-0 | |-------|-------|-------|----|-------|-------|------| | cond | 001 | opcode| S | Rn | Rd | Operand2 | ``` - **cond**: 条件码(4位) - **opcode**: 操作码(4位) - **S**: 状态标志更新(1位) - **Rn**: 第一源寄存器(4位) - **Rd**: 目标寄存器(4位) - **Operand2**: 第二操作数(12位) ### 1.3 寻址模式 | 寻址模式 | 语法 | 示例 | 说明 | | -------------- | -------------- | ------------------- | -------------- | | 立即数寻址 | #immediate | `MOV R0, #0x10` | 立即数在指令中 | | 寄存器寻址 | Rm | `ADD R0, R1, R2` | 操作数在寄存器 | | 寄存器间接寻址 | [Rm] | `LDR R0, [R1]` | 地址在寄存器 | | 基址变址寻址 | [Rn, #offset] | `LDR R0, [R1, #4]` | 基址+偏移 | | 前索引寻址 | [Rn, #offset]! | `LDR R0, [R1, #4]!` | 先更新基址 | | 后索引寻址 | [Rn], #offset | `LDR R0, [R1], #4` | 先访问后更新 | | 相对寻址 | label | `B loop` | PC相对跳转 | ## 二、数据处理指令 ### 2.0 处理器内部数据传输指令(MOV / MRS / MSR) 原书第 7.2.1 节重点讲解的三个数据传输指令: | 指令 | 目的 | 源 | 描述 | | ------------- | ---- | ---- | ------------------------------------------ | | `MOV R0,R1` | R0 | R1 | 将 R1 里面的数据复制到 R0 中 | | `MRS R0,CPSR` | R0 | CPSR | 将特殊寄存器 CPSR 里面的数据复制到 R0 中 | | `MSR CPSR,R1` | CPSR | R1 | 将 R1 里面的数据复制到特殊寄存器 CPSR 里中 | ```asm MOV R0, R1 @ R0 = R1 MOV R0, #0X12 @ R0 = 0x12,立即数传递到寄存器 MRS R0, CPSR @ 读特殊寄存器只能用 MRS:R0 = CPSR MSR CPSR, R0 @ 写特殊寄存器只能用 MSR:CPSR = R0 ``` > 要点:**读** CPSR/SPSR 只能用 `MRS`,**写** CPSR/SPSR 只能用 `MSR`,不能使用 MOV。 ### 2.1 数据传输指令 ```asm MOV R0, #10 @ R0 = 10 MOV R1, R2 @ R1 = R2 MOV R0, R1, LSL #2 @ R0 = R1 << 2(左移2位) MVN R0, #0 @ R0 = 0xFFFFFFFF (-1) MVN R1, #0xFF @ R1 = 0xFFFFFF00 ``` ### 2.2 算术指令 ```asm ADD R0, R1, R2 @ R0 = R1 + R2 ADD R0, R1, #10 @ R0 = R1 + 10 ADC R0, R1, R2 @ R0 = R1 + R2 + C(带进位加法) SUB R0, R1, R2 @ R0 = R1 - R2 SBC R0, R1, R2 @ R0 = R1 - R2 - !C(带借位减法) MUL R0, R1, R2 @ R0 = R1 * R2(32位乘法) UDIV R0, R1, R2 @ R0 = R1 / R2(无符号除法) SDIV R0, R1, R2 @ R0 = R1 / R2(有符号除法) ``` > 原书第 7.2.5 节列出的算术指令为:ADD、ADC、SUB、SBC、MUL、UDIV、SDIV;其中乘除指令在嵌入式配置寄存器时基本用不到,最常用的是加减。 ### 2.3 逻辑指令 ```asm AND R0, R1, #0xFF @ R0 = R1 & 0xFF(按位与) ORR R0, R1, #0xFF @ R0 = R1 | 0xFF(按位或) ORN R0, R1, #0xFF @ R0 = R1 | (~0xFF)(按位或非) EOR R0, R1, #0xFF @ R0 = R1 ^ 0xFF(按位异或) BIC R0, R1, #0xFF @ R0 = R1 & ~0xFF(位清除) ``` > 原书第 7.2.6 节列出的逻辑指令为:AND、ORR、BIC、ORN、EOR。 ### 2.4 比较指令 ```asm CMP R0, R1 @ 更新标志位:R0 - R1 CMN R0, R1 @ 更新标志位:R0 + R1(负数比较) TST R0, #0xFF @ 更新标志位:R0 & 0xFF(位测试) TEQ R0, R1 @ 更新标志位:R0 ^ R1(等值测试) ``` ### 2.5 乘法指令 ```asm MUL R0, R1, R2 @ R0 = R1 * R2 MLA R0, R1, R2, R3 @ R0 = R1 * R2 + R3(乘加) ``` ## 三、Load/Store指令 ### 3.1 单寄存器传输 ```asm LDR R0, [R1] @ R0 = *R1 LDR R0, [R1, #4] @ R0 = *(R1+4) LDR R0, [R1, #4]! @ 先更新R1=R1+4,再R0=*R1(前索引) LDR R0, [R1], #4 @ 先R0=*R1,再更新R1=R1+4(后索引) LDR R0, =0x0209C000 @ 伪指令:加载32位地址 STR R0, [R1] @ *R1 = R0 STR R0, [R1, #4] @ *(R1+4) = R0 LDRB R0, [R1] @ R0 = *(uint8_t*)R1(字节加载) STRB R0, [R1] @ *(uint8_t*)R1 = R0(字节存储) LDRH R0, [R1] @ R0 = *(uint16_t*)R1(半字加载) STRH R0, [R1] @ *(uint16_t*)R1 = R0(半字存储) ``` > LDR/STR 默认按字(32位)读写,加后缀 **B** 按字节(LDRB/STRB)、加后缀 **H** 按半字(LDRH/STRH)读写(原书 7.2.2 节)。 ### 3.2 批量寄存器传输 ```asm LDM R0, {R1, R2, R3} @ R1=*R0, R2=*(R0+4), R3=*(R0+8) STM R0, {R1, R2, R3} @ *R0=R1, *(R0+4)=R2, *(R0+8)=R3 LDM R0!, {R1-R3} @ 先加载,然后R0=R0+12 ``` ### 3.3 堆栈操作 ```asm PUSH {R0-R3, LR} @ 压栈(满递减FD):STMDB SP!, {R0-R3, LR} POP {R0-R3, PC} @ 出栈:LDMIA SP!, {R0-R3, PC} ``` **原书第 7.2.3 节**指出,PUSH/POP 的另一种等价写法是 `STMFD SP!` 和 `LDMFD SP!`: ```asm STMFD SP!, {R0~R3, R12} @ R0~R3,R12 入栈 STMFD SP!, {LR} @ LR 入栈 LDMFD SP!, {LR} @ 先恢复 LR LDMFD SP!, {R0~R3, R12} @ 再恢复 R0~R3,R12 ``` 其中 `STM` = 多存储(Store Multiple),`LDM` = 多加载(Load Multiple),`FD` = Full Descending(满递减)。ATPCS 规定 ARM 使用 FD 类型堆栈:SP 指向最后一个入栈的数据,堆栈由高地址向低地址增长。STM/LDM 的寄存器列表中**编号小的对应低地址,编号高的对应高地址**。 **原书堆栈示例**:设当前 SP = 0x80000000,向下增长,执行 `PUSH {R0~R3, R12}` 后: ``` 地址 内容 0x80000000 (原栈底,SP 初始处) 0x7FFFFFFC R12 0x7FFFFFF8 R3 0x7FFFFFF4 R2 0x7FFFFFF0 R1 0x7FFFFFEC R0 ← SP = 0x7FFFFFEC ``` 再执行 `PUSH {LR}` 后 SP = 0x7FFFFFE8,LR 存放于 0x7FFFFFE8。出栈时按 `POP {LR}`、`POP {R0~R3,R12}` 顺序从栈顶(SP 当前指向处)地址依次**减小**提取数据。 > 说明:`PUSH`/`POP` 是 `STMFD SP!`/`LDMFD SP!` 的别名,二者编码相同;`STMFD` 与 `STMDB` 亦为同一指令的两种写法。 ### 3.4 批量传输寻址模式 > ⚠️ **来源说明**:本小节不属于原书第七章内容,为扩展知识;原书只讲解了 STM/LDM 与 STMFD/LDMFD。 | 后缀 | 模式 | 用途 | | ---- | ------ | ------------ | | IA | 后递增 | 连续内存读取 | | IB | 前递增 | 连续内存写入 | | DA | 后递减 | 逆序内存读取 | | DB | 前递减 | 堆栈压入 | > 堆栈类型后缀(FD/FA/ED/EA)用于描述堆栈模型,其中 **FD(满递减)为 ARM 默认**,对应 `STMFD`/`LDMFD`;不要把 FD 与 IA/IB/DA/DB 混为同类后缀。x86 使用的是空递减(ED)模型。 ## 四、跳转指令 ### 4.1 跳转指令详解 ```asm B loop @ 无条件跳转到loop(跳转范围 ±2KB 时可改用 B.W 32位版本) B.W loop @ 32位版本跳转指令,跳转范围更大 BX LR @ 间接跳转,跳转到 Rm 中保存的地址,并切换指令集 BL function @ 跳转并保存返回地址到LR(用于函数调用) BLX function @ 结合BX和BL:跳转到Rm指定地址、保存返回地址、可切换指令集 ``` 原书第 7.2.4 节跳转指令表: | 指令 | 描述 | | ------------ | -------------------------------------------------------------------- | | `B