4.4 分段、物理地址与字节序
8086 的寄存器宽度为 16 位,所以一个寄存器最多直接编码 65,536 个不同值。但处理器提供 20 位地址总线,可以访问 个字节单元,也就是 1 MiB。
8086 使用分段寻址(segmented addressing)衔接这两种宽度。
段值加偏移量形成物理地址
一次存储器引用使用两个 16 位量:
- 段值(segment)以 16 字节为单位选择一个基址;
- 偏移量(offset)选择相对于该基址的字节。
原始 8086 按下式形成总线地址:
乘以 16 等价于二进制左移四位,也等价于在十六进制数后追加一个 0。
对于 1234H:0010H:
段基址对齐到一个 paragraph(节,或称小段),这是 8086 中的 16 字节单位。段值本身不是物理基址,必须先乘以 16。
默认的段值—偏移量搭配
| 活动 | 常用搭配 | 含义 |
|---|---|---|
| 取指 | CS:IP | 代码段加指令指针 |
| 大多数数据访问 | DS:有效偏移量 | 数据段加指令计算出的偏移量 |
| 栈访问 | SS:SP 或 SS:BP | 栈段加栈指针或基址指针 |
| 串操作目的地址 | ES:DI | 附加段加目的变址 |
这些默认搭配可以减少指令需要编码的信息。部分指令允许段超越,而精确的有效地址规则将在指令集内容中介绍。
段的重叠与 20 位回绕
不同段可以互相重叠。1000H:0020H 与 1001H:0010H 都会形成物理地址 10020H:
因此,“段值:偏移量”并不是物理字节的一一对应名称。
未经截断的最大和会超过 20 位。在原始 8086 上,FFFFH:0010H 的算术结果是 100000H,但芯片只有 A19 到 A0 地址引脚。引出的低 20 位为 00000H,所以地址发生回绕。后来的 PC 兼容机引出了 A20 相关问题;本节规则专门描述原始 8086 的总线行为。
实验 1——构造、比较并回绕地址
调节段值与偏移量,观察每一步十六进制计算实时变化。任务包括普通地址组合、重叠逻辑地址、最高地址以及原始 8086 的回绕行为。
检查理解
字节具有地址顺序
存储器按字节寻址:每个地址命名一个 8 位单元。因此,一个 16 位字会占据两个相邻地址。
8086 采用小端序(little-endian):
- 低位字节存放在较低地址;
- 高位字节存放在下一个地址。
从地址 1000H 开始存放字 1234H,结果如下:
| 地址 | 保存的字节 | 在字中的角色 |
|---|---|---|
1000H | 34H | 低字节 |
1001H | 12H | 高字节 |
读取这个字时,会重新组合为 12H × 100H + 34H = 1234H。字节序改变的是存储器中的字节排列,并不会把寄存器中的值变成 3412H。
偶地址字与奇地址字
8086 的 16 位数据总线由两个字节存储体组织而成。从偶地址开始的字能够在一个对齐总线周期内传送两个字节。从奇地址开始的字跨越了对齐的字边界,因此原始 8086 需要两个总线周期。
这是性能差异,并不是地址合法性规则。1001H 处的字完全合法,只是它跨越了 1000H–1001H 与 1002H–1003H 两个边界。
实验 2——修复小端存储布局
存入、解释、交换或直接编辑相邻存储单元。其中一个任务故意给出颠倒的字节;另一个任务把字放在奇地址,并揭示相应的总线周期代价。
检查理解
分析存储器的三步检查表
1. 形成地址:把段值左移四位,加上偏移量,并在原始 8086 上保留总线结果的低 20 位。
2. 确定宽度:一个字节占一个存储单元,一个字占两个相邻单元。
3. 排列字节:在 8086 上,把低字节放在较低地址。
现在,你已经能够把指令流、程序员可见寄存器、FLAGS 和物理存储器联系起来。下一章可以在这套机器状态之上,继续解释 8086 指令如何编码并完成有用的工作。