计组与微机控制
3.6 load 与 store 编码的示例
3.6 load ? store ?????
ARM load/store 指令编码
单寄存器访存指令 LDR/STR
ARM 是典型 Load/Store 架构:
> 普通算术逻辑指令通常只能处理寄存器,访问内存要用 LDR/STR。
| 指令 | 含义 || `LDR Rd, [Rn]` | 从 Rn 指向的地址读一个字到 Rd |
| `STR Rd, [Rn]` | 把 Rd 的值写到 Rn 指向的地址 |
| `LDRB/STRB` | 按字节读写 |
| `LDRH/STRH` | 按半字读写 |
| `LDRSB/LDRSH` | 读字节/半字并符号扩展 |
例子:
LDR R0, [R1]STR R0, [R2]含义:R0 = *(uint32_t *)R1
*(uint32_t *)R2 = R0
本页开始讲 `LDR/STR` 的机器码格式。
重点:
- ARM 指令是 32 位;
- `LDR/STR` 字段中包含条件执行位;
- 包含基址寄存器 Rn;
- 包含目的/源寄存器 Rd;
- 包含偏移字段;
- 包含若干控制位决定寻址方式。

理解指令编码的目的:
> 不是背每一位,而是知道汇编语法最终会落成硬件可识别的字段。
本页继续展示 Load/Store 指令格式。
读图重点:

- 哪些位选择条件执行;
- 哪些位选择 Load 还是 Store;
- 哪些位选择字节还是字;
- 哪些位选择前索引/后索引;
- 哪些位选择是否写回;
- 哪些位决定偏移是立即数还是寄存器。
这些字段共同决定:
访问方向 + 数据大小 + 有效地址形成方式 + 是否更新基
ARM也可以对偏移做加法或减法。
例如:
LDR r0, [r1, +r2]LDR r0, [r1, -r2]有效地址分别是:
EA = [r1] + [r2]
EA = [r1] - [r2]
这使得同一套寻址机制既能向高地址方向访问,也能向低地址方向访问。
栈向低地址增长时,负偏移和递减寻址非常常见。
Load/Store 的巴科斯范式
本页用形式化语法描述 ARM 的 Load/Store 指令。
要理解符号:
- `{}`:可选字段;- `|`:二选一;- `{B}`:可选字节访问;- `!`:写回;
- `+/-`:偏移方向;- 移位:对寄存器偏移进行缩放。
示例:
LDR r0, [r1, r2, LSL #4]!STRB r9, [r5, -r7, ASR #2]
第一条表示:
EA = r1 + r2 x 16
访问后写回 r1
第二条表示:
EA = r5 - (r7 算术右移 2 位)
传送 1 字节(也就是本节一开始就有的指令)
本页把一个 32 位二进制串分解成 ARM 指令:
STRPL r4, [r2, -r6, LSL #2]!
这页的价值是建立“汇编语句和机器码字段”的对应:
- `STR` 由方向位决定;
- `PL` 来自条件字段;
- `r4` 是源寄存器;
- `r2` 是基址寄存器;
- `-r6, LSL #2` 来自偏移字段和 U 位/移位字段;
- `!` 来自写回位。
做这类题的顺序:
1. 先看条件字段;
2. 再判断指令类别;
3. 判断 Load/Store;
4. 找 Rn、Rd、Rm;
5. 判断偏移方向;
6. 判断是否写回;
7. 还原汇编语句。
本章统一总结
ARM 寻址方式可以压成一张表:
| 形式 | 有效地址 | 是否更新基址 | 用途 |
| `[r1]` | r1 | 否 | 指针访问 |
| `[r1,#d]` | r1+d | 否 | 固定字段、固定偏移 |
| `[r1,r2]` | r1+r2 | 否 | 动态偏移 |
| `[r1,r2,LSL #2]` | r1+4r2 | 否 | 32 位数组下标 |
| `[r1,#d]!` | r1+d | 是,先加后访问 | 预递增 |
| `[r1],#d` | r1 | 是,访问后加 | 后递增 |
| `[pc,#d]` | pc+d | 否 | 常量池、位置无关 |
最容易错的点:
- `LDR r0,[r1,#4]` 不改变 r1;
- `LDR r0,[r1,#4]!` 改变 r1;
- `LDR r0,[r1],#4` 先访问旧 r1,再改变 r1;
- 数组下标要乘元素大小;
- ARM 按字节编址,所以 32 位字地址差 4。
