5.3 间接、基址与变址寻址
直接寻址在指令中内嵌一个固定偏移量。程序经常需要遍历数组、选择记录,或访问位置会变化的栈数据。8086 可以使用寄存器计算存储器偏移量。
计算得到的 16 位偏移量称为有效地址(Effective Address,EA)。方括号表示要访问 EA 对应的存储器。
8086 只支持一组特定的地址表达式
原始 8086 可以使用 BX 或 BP 作为基址(base),使用 SI 或 DI 作为变址(index),再加一个可选常量位移(displacement):
并非任意寄存器组合都能编码。八种 r/m 存储器模式为:
| r/m | 地址寄存器 |
|---|---|
000 | BX + SI |
001 | BX + DI |
010 | BP + SI |
011 | BP + DI |
100 | SI |
101 | DI |
110 | BP,但 mod=00 时表示直接地址 |
111 | BX |
以下形式不是普通 8086 有效地址:[AX]、[DX]、[SP]、[BX+BP] 和 [SI+DI]。一个寄存器可以保存有用的数,并不表示它一定能出现在该编码字段中。
“间接”表示指令命名一个参与地址计算的寄存器,而不是只内嵌最终地址。“基址”强调 BX 或 BP,“变址”强调 SI 或 DI;一个表达式可以同时采用基址和变址。
位移既改变地址,也改变编码
mod 字段控制位移长度:
mod=00:通常没有位移;
mod=01:一个有符号 8 位位移,范围 −128 到 +127;
mod=10:一个 16 位位移;
mod=11:寄存器操作数,不访问存储器。
负 8 位位移在相加前会做符号扩展。若 SI=1000H,则 [SI-10H] 的 EA=0FF0H;位移 −16 编码为字节 F0H。
[BP] 需要特殊处理。由于 mod=00,r/m=110 保留给 16 位直接地址,所以 [BP] 必须编码成带零位移的 [BP+0],即 mod=01 与位移字节 00H。
实验 1——搭建合法与非法地址表达式
切换四个候选地址寄存器,并调节正位移或负位移。实验台会诊断非法组合,同时为合法组合生成准确的 mod、r/m 和位移字段。
检查理解
有效地址算术按 16 位回绕
EA 是一个 16 位偏移量。若寄存器加法超过 FFFFH,只保留低 16 位。例如:
这是偏移量回绕。5.4 节会把得到的 EA 与段值组合,形成 20 位物理地址。
等效地址可能具有不同编码成本
假设 BX=1000H、SI=0020H,目标 EA 为 1020H:
[BX+SI]不使用位移即可到达目标;
[BX+20H]也能到达1020H,但需要一个位移字节。
对于同一个指令家族,第一种形式更短,因为其 ModR/M 后面不需要位移。
更短并不总是唯一目标;表达式还必须符合程序当前能够安全使用哪些寄存器。编码成本只是工程约束之一,正确性和寄存器可用性同样重要。
实验 2——在字节预算内命中目标 EA
从所有合法 8086 寄存器路线中选择一种,并调节位移。部分任务只有一个最短解;另一些任务故意接受多种策略,只要它们在预算内到达相同 EA。
检查理解
有效地址检查表
1. 确认寄存器组合存在于 8086 r/m 表中。
2. 把当前寄存器内容与有符号或 16 位位移相加。
3. 保留低 16 位作为 EA。
4. 选择能表示所需数值的最短位移形式。
5. 记住 [BP] 编码特例。
EA 只是偏移量。下一节将决定哪个段提供基址,并展示段超越如何修复——或制造——存储器访问故障。