4.2 寄存器与程序员模型
从晶体管层面看,芯片内部有许多存储元件,但程序员不需要逐一命名它们。程序员模型(programmer's model)是指令能够直接访问的寄存器与状态集合。
在 8086 中,一个字(word)是 16 位,一个字节(byte)是 8 位。大多数程序员可见寄存器的宽度为一个字。
寄存器家族
| 家族 | 寄存器 | 本阶段的主要用途 |
|---|---|---|
| 通用数据 | AX、BX、CX、DX | 算术、逻辑、数据以及指令中的常见角色 |
| 指针和变址 | SP、BP、SI、DI | 栈位置以及带索引的存储器遍历 |
| 段 | CS、DS、SS、ES | 为地址形成提供段值 |
| 指令位置 | IP | 代码段中下一次顺序取指所使用的偏移量 |
| 机器状态与控制 | FLAGS | 记录运算结果,并控制某些行为 |
这些用途是默认规则与习惯,并不是绝对限制。例如,AX 常称为累加器,但很多指令也能使用其他寄存器。究竟允许哪些操作数、哪个寄存器具有隐含角色,要由具体指令定义。
一个字,三个名称
AX、BX、CX、DX 各自提供两个字节大小的别名:
| 16 位寄存器 | 高字节(15–8 位) | 低字节(7–0 位) |
|---|---|---|
| AX | AH | AL |
| BX | BH | BL |
| CX | CH | CL |
| DX | DH | DL |
这些别名不是数据副本。若 AX 为 1234H,那么 AH 是 12H,AL 是 34H。把 ABH 写入 AL 后,AX 变成 12ABH,AH 仍为 12H;之后若写整个 AX,则两个字节都会被替换。
在原始 8086 程序员模型中,SP、BP、SI、DI 没有 8 位的高低字节别名。
实验 1——编辑互相重叠的寄存器视图
分别通过 AX、AH 和 AL 视图写入同一寄存器。任务会要求你保留未修改的字节、通过两个字节别名拼出一个字,以及一次替换整个字。
检查理解
寄存器怎样在操作中协作
把寄存器名称与数据移动联系起来,就更容易记住它们:
- IP(Instruction Pointer,指令指针)提供当前代码段中下一次顺序取指所用的偏移量。BIU 取入字节时会推进 IP;控制转移会装入新的偏移量,并让队列中的旧字节失效。由于预取可能让队列领先于正在执行的指令,所以不能把芯片内部每一时刻的 IP 简单理解成“当前正在执行哪个字节”的计数器。
- SP(Stack Pointer,栈指针)标识栈段中的栈顶。8086 的栈向较小偏移量方向增长:压入一个字时先把 SP 减 2,再写入数据;弹出一个字时先读取,再把 SP 加 2。
- BP(Base Pointer,基址指针)常用于引用栈段中的数据,同时不改变 SP。
- SI(Source Index,源变址)和 DI(Destination Index,目的变址)常用于定位源数据与目的数据,尤其常见于串操作。
- CX常提供重复次数;DX常用于扩展算术结果或保存 I/O 端口地址;BX常用作基址寄存器。这些只是常见角色,并不是完整的指令规则。
寄存器变化就是状态变化。每一步都应从上一步状态计算,然后提交新状态;重复操作时,当前步骤的输出就是下一步骤的输入。
实验 2——编排寄存器状态轨迹
按任意顺序选择操作,观察每种操作只让相关寄存器发生变化。通过取指、重复复制、算术和栈活动完成任务;事件轨迹会记录每次状态转移。
检查理解
避免四个常见误解
1. 寄存器名称不是存储器地址。 寄存器只保存一个位模式;指令可能把它解释为数据、偏移量或其他信息。
2. AH、AL 并不独立于 AX。 它们只是同一组 16 位的不同视图。
3. 只有 IP 不能形成物理地址。 取指要把一个段值与 IP 配合使用;4.4 节将推导地址。
4. 习惯角色不会自动产生动作。 把 CX 称为计数寄存器并不会让它自己减小;只有更新 CX 的指令才会改变它。
下一节将研究 FLAGS。这个寄存器既能记录运算结果的特征,也能控制方向、中断识别和单步行为。