CPU 的寄存器

寄存器是 CPU 里唯一能存住东西的部件。上一节的结构图去掉所有寄存器,剩下的 ALU、译码器、CU 全是组合逻辑——只要输入一撤,输出立刻消失。是寄存器把”时刻”变成了”状态”,让机器能跨越时钟周期记住事情。

教材把寄存器分成”用户可见”和”用户不可见”两组。这个分法的名字起得不好,但它背后的判据是本节最有用的东西,而且它有一个非常具体的后果:进程切换的时候,操作系统到底要保存哪些寄存器。

机制

分类表

寄存器全称存什么位数可见性
PC程序计数器下一条指令的地址主存地址位数✅
通用寄存器组GPRs(含 ACC)操作数、中间结果、地址机器字长✅
PSW程序状态字寄存器ZF、SF、CF、OF、中断允许位、模式位按标志位数✅
SP堆栈指针栈顶地址(通常是 GPRs 中的某一个)主存地址位数✅
IR指令寄存器当前正在执行的指令指令字长❌
MAR存储器地址寄存器要访问的主存单元地址主存地址位数❌
MDR存储器数据寄存器与主存交换的数据存储字长❌
暂存器 T / Y / Z——总线上传来的中间值机器字长❌
微指令寄存器 μIR、μPC——当前微指令、下一条微地址见 5.4.3❌

位数那一列比可见性那一列考得更多。 四个高频陷阱:

  • MAR 的位数 = 主存地址位数,不是机器字长。地址空间 个单元 → MAR 至少 位。
  • MDR 的位数 = 存储字长,不是机器字长。主存按字节编址但一次存取 16 位,MDR 就是 16 位。
  • IR 的位数 = 指令字长。变长指令集里按最长的算,或按题目给的”定长指令字格式”直接取。
  • PC 的位数 = 主存地址位数,与 MAR 同宽——因为 PC 的内容最终要送进 MAR。

PC 的”加 1”到底加多少

PC 具有自动加”1”的能力,这个”1”指的是一条指令的长度,不是数字 1。

指令字长存储字长(按编址单位算)

按字节编址、指令 32 位 → PC 加 4;按字编址、一条指令占一个字 → PC 加 1。4.1.3 说过,定长指令格式的一个直接好处就是这个增量是常数,控制器不必等译码结果就能算下一条指令地址。

关键时序:PC 的自增发生在取指阶段内,也就是说指令还没执行完,PC 已经指向下一条了。这是 4.2.2 里 PC 相对寻址”基准是已经加过的 PC”的硬件来源,也是中断响应时”保存断点”保存的正是这个已经加过的值的原因(5.5.3)。

「用户可见」这个说法的准确含义

疑问点:PC 也算用户可见?可怎么对它编程——靠跳转指令吗?那 ACC 为什么反倒不可见了?

前半句的方向完全对:PC 的”可见”确实就是通过转移指令兑现的。后半句的前提要更正——ACC 是用户可见的。

「可见」不等于「能用 MOV 直接写」,它的准确含义是三条:

  1. ISA 定义了它——指令集手册里写明了它的存在和语义
  2. 程序能观察到它的值——哪怕只能间接观察
  3. 有指令能改变它——哪怕只能间接改变

PC 三条全中:JMP / Jcc / CALL / RET 都在写 PC,CALL 压栈的返回地址就是 PC 的值被程序读到了。而 ARM 更直接——R15 就是 PC,可以像普通通用寄存器一样参与运算。

ACC(累加器)是通用寄存器,当然可见。 之所以会觉得它”不可见”,是因为在一地址指令 ADD X 里,ACC 是隐含操作数——指令里根本没写它。但**“隐含”说的是”不必在指令里写出来”,“不可见”说的是”ISA 里根本没有它”,这是两个完全不同的意思**。隐含寻址见 4.2.2,一地址指令格式见 4.1.2。

反过来看什么才叫真的不可见:暂存器 T 没有编号、任何指令都无法命名它、它的值在两条指令之间不携带任何信息。换一个数据通路结构,它可以根本不存在——多总线结构里就常常不需要它。

疑问点:「用户可见 / 不可见」这个边界划得不太合理,应该按具体功能和行为分

这个批评抓对了一半:判据确实应该是行为的。但结论要反过来——“用户可见”本身就是一条行为判据,只是名字起得差。

它的准确名字是**「是否属于 ISA 状态」**,判据是 4.1.1 那条判据的直接套用:

换一个微体系结构实现,这个寄存器还必须存在、还必须有相同的值吗?

  • PC / GPRs / PSW:必须。任何一款兼容实现,程序读到的 PC 和标志位都得一样,否则老程序跑不对。→ ISA 状态
  • IR / MAR / MDR / 暂存器:不必。可以多设几个、可以合并、可以流水线化成一排,程序察觉不到。→ 微体系结构状态

按功能分类(存地址的 / 存数据的 / 存状态的)也是一个有用的维度,它决定这个寄存器在数据通路上接在哪。但两个维度回答的是不同问题,不能互相取代:

维度回答什么问题
可见性(ISA 状态?)能不能出现在指令里?进程切换时要不要保存?
功能(地址 / 数据 / 状态)在结构图上接在哪?位数按什么取?

让可见性这条线”合理”的,是它右边那个后果。 操作系统做上下文切换时,保存的恰好就是 ISA 状态那一组:通用寄存器、PC、PSW,一个不多一个不少(见 OS 2.1.4 进程控制、OS 1.3.1 处理器运行模式)。

为什么 IR、MAR、暂存器不用保存? 因为切换只发生在指令边界——上一条指令已经执行完、下一条还没开始。那个时刻,这些寄存器里没有任何跨指令有用的信息:IR 里的指令已经做完了,暂存器里的中间值已经用掉了。

所以这条边界不是分类学上的偏好,而是一条工程约束:它精确划出了”必须被保存和恢复的那一组状态”。 换句话说——如果按你说的”具体行为”来分,分出来的结果和这条线是重合的。

三组寄存器的功能视角

既然功能维度也有用,把它一并摆出来:

功能组成员共同点
存地址PC、MAR、SP位数 = 主存地址位数;内容参与地址运算
存数据GPRs、ACC、MDR、暂存器位数 = 字长或存储字长;内容送 ALU
存状态PSW、IR内容不是”值”而是”情况”:IR 存的是”要做什么”,PSW 存的是”刚才发生了什么”

PSW 的双重身份值得单独说:它既是运算的结果(ALU 写入 ZF/SF/CF/OF),又是转移指令的输入(Jcc 读它决定转不转),还是控制器的输入(5.1.2 说过 CU 的三类输入之一)。它是全 CPU 唯一一个同时被数据通路和控制通路读写的寄存器——这就是它在结构图上总被单独画出来的原因。

PSW 里除了四个算术标志,还有中断允许位(IF)和模式位。中断允许位在中断响应时被硬件清零(关中断),在 IRET 时恢复——见 5.5.3。模式位区分用户态和内核态,跨到 OS 那边就是 OS 1.3.1 的全部内容。

边界

边界辨析:隐含 / 不可见

  • 隐含:指令里不写出来,但 ISA 定义了它。ADD X 隐含 ACC,PUSH 隐含 SP。→ 可见
  • 不可见:ISA 里根本没有它,程序无法命名也无法观察。暂存器 T、MAR。

判据:指令手册里有没有它的名字。

边界辨析:MDR 位数 / 机器字长

MDR 的位数 = 存储字长(主存一次读写的位数),不是机器字长(ALU 一次处理的位数)。二者常相等,但题目一旦给出”按字节编址、存取单位 16 位、机器字长 16 位”这类条件,就要逐项对号。三个字长的完整区分见 4.1.2。

层次辨析:PC / MAR

二者位数相同、内容都是地址,区别在语义:

  • PC 存的一定是指令地址,且是下一条的
  • MAR 存的是本次要访问的任意主存单元地址——取指时它装 PC 的值,取数时它装操作数地址

一道题问”取指周期中 MAR 的内容”,答PC 的内容(当前指令地址);问”取指周期结束后 PC 的内容”,答下一条指令的地址。

对照速查

寄存器位数取自一句话记法
PC主存地址位数装地址的,且要能覆盖整个主存
MAR主存地址位数同上,是 PC 的下游
MDR存储字长主存一次吞吐多少,它就多宽
IR指令字长装得下一条完整指令
GPRs / ACC机器字长ALU 一次算多宽,它就多宽
PSW标志位个数存”情况”不存”值”
场景要保存的寄存器理由
进程切换 / 中断处理GPRs、PC、PSW全部 ISA 状态;切换发生在指令边界,其余寄存器无跨指令信息
中断隐指令(硬件)PC、PSW只保存断点和状态字,GPRs 由软件在服务程序开头保存

考点

  • 位数计算:给”按字节编址、地址空间 、存储字长 32 位、指令字长 16 位”,问各寄存器位数。逐条对号:MAR 24,MDR 32,IR 16,PC 24。
  • 可见性判断:选择题问”下列哪个寄存器用户不可见”,答案在 IR / MAR / MDR / 暂存器里选。PSW 和 PC 都是可见的,这两个是最常见的误答。
  • PC 加多少:按字节编址 + 32 位定长指令 → 加 4。别机械地答 1。
  • 取指周期各寄存器内容追踪:见 5.2.2,是本章最稳定的选择题题型。
  • “为什么设暂存器”:见 5.1.2,答”单总线一拍只传一个值 + 避免 ALU 输入输出成环”。

链接