# 3.2 CPU中的核心部件与Simple-MIPS指令（1）

## 阅读入口

* [CPU的核心部件](/knowledge/cs/computer-architecture/cpu-components/)
* [Simple-MIPS教学指令集](/knowledge/cs/computer-architecture/simple-mips/)

## 各部件的职责

### 程序计数器PC

PC存储下一条要执行的指令的内存地址。可以把它想成读书时的书签，标记下一次从哪里继续。顺序执行时，它在取指后增加4：一条Simple-MIPS指令是32位，即4字节，下一条指令正好在相邻的4字节之后。分支和跳转指令会直接改写PC，顺序执行、循环和条件分支都靠这一点实现。

PC只负责提供地址，不判断该地址上是什么内容；取回指令之后的工作由IR和控制单元接手。

### 指令寄存器IR

取指时，内存按PC给出的地址送回32位指令，这条指令先放进IR，再由控制单元译码并发出执行信号。IR只保存当前这一条指令，取下一条时会覆盖它的内容。

一条Simple-MIPS指令的32位通常划分为几个字段：

* **操作码**：指出指令类型，例如加载、存储、减法；
* **寄存器编号**：指出源寄存器和目标寄存器；
* **立即数或地址偏移**：供内存访问或算术运算使用的常数。

### 算术逻辑单元ALU

ALU是执行运算的部件，通常接收两个操作数，输出一个结果。它处理的运算包括：

* 算术运算：加、减、乘、除；
* 逻辑运算：与、或、非、异或；
* 移位运算：左移、右移，包含逻辑移位和算术移位；
* 比较运算：比较两个数的大小，设置条件标志。

执行哪一种运算由控制单元根据操作码决定。ALU本身不区分当前是哪条指令，它只对送进来的操作数执行被指定的运算。

### 寄存器组

寄存器是CPU内部的高速存储单元，用来临时存放数据和地址。它的访问通常在1个时钟周期内完成，比内存快得多；数量有限，Simple-MIPS有32个通用寄存器；每个寄存器固定32位。

寄存器按用途有习惯命名：

| 寄存器         | 用途          |
| :---------- | :---------- |
| `$zero`     | 恒为0         |
| `$at`       | 汇编器临时使用     |
| `$v0`–`$v1` | 函数返回值       |
| `$a0`–`$a3` | 函数参数        |
| `$t0`–`$t9` | 临时寄存器       |
| `$s0`–`$s7` | 需要跨调用保留的寄存器 |

`$zero`的值由硬件固定，写入也会被丢弃，因此常用来提供常数0。以上是常见命名，完整编号共32个；寄存器编号如何出现在指令里，见[Simple-MIPS教学指令集](/knowledge/cs/computer-architecture/simple-mips/)。

### 一条指令的取指与执行

1. PC把地址送到内存，内存返回该地址上的32位指令；顺序执行时PC同时增加4。
2. 指令进入IR，控制单元按操作码译码，确定要读哪些寄存器、做什么运算、是否访问内存。
3. 寄存器组把操作数送入ALU，ALU完成运算；如果指令是load或store，还要由内存完成读写。
4. 结果写回寄存器组，PC已经指向下一条指令。

> [!NOTE]
> PC加4的单位是字节：一条指令4字节，所以相邻两条指令的地址相差4。把“加4”理解成“跳4条指令”会差4倍。

[来源：CPU的核心部件](/knowledge/cs/computer-architecture/cpu-components/#properties)

读这几段时先记每个部件保存什么：PC存地址，IR存当前这条指令，寄存器组给ALU送数。有了这张对照表，下面读指令的写法就不必来回翻页。

## 操作数与指令形式

Simple-MIPS是为教学目的设计的简化指令集。它参考MIPS等真实架构，但并不完全等同于真实的MIPS指令集，目的是更直观地讲解计算机组成原理的核心概念。本页的指令都按Simple-MIPS的写法给出；PC、IR、ALU和寄存器组如何配合执行这些指令，见[CPU的核心部件](/knowledge/cs/computer-architecture/cpu-components/)。

每条Simple-MIPS指令固定32位，每个通用寄存器也固定32位。常见指令的基本形式是

```text
操作码 目标, 源1, 源2
```

操作数按角色分为以下几类：

| 记号       | 含义                         |
| :------- | :------------------------- |
| `$rd`    | 目标寄存器，结果写在这里               |
| `$rs`    | 第一个源寄存器；访存时兼作基址寄存器         |
| `$rt`    | 第二个源寄存器；在load/store中承担另一角色 |
| `offset` | 立即数偏移，与 `$rs`中的地址相加        |
| `shamt`  | 移位位数，取0–31                 |
| `label`  | 标签，代表一条指令的地址               |

寄存器编号的具体分工（`$zero`、`$t0`–`$t9`、`$s0`–`$s7`等）由寄存器组的约定决定，不在指令本身的语义里体现。

[来源：Simple-MIPS教学指令集](/knowledge/cs/computer-architecture/simple-mips/#definition)

`操作码 目标, 源1, 源2`是本节所有指令的共同形式，`$rd`、`$rs`、`$rt`的角色由这张表固定。3.3的控制流指令继续使用同一套记号，两节可以对照着读。

## 学习安排

教材先拆开CPU的内部部件，再引入指令，因为读一条指令时必须知道每个操作数落在哪个部件：PC提供地址，IR暂存指令，寄存器组给ALU送数，load/store把寄存器与内存连起来。按这个顺序读，部件表不是要背的清单，而是读指令时的对照表。

* 3.2只讲数据传送、算术和移位指令，控制流指令留到3.3；两节合起来才能写出循环。
* 教材的示例主要用 `$t0`、`$t1`、`$t2`和 `$s1`；读例子时先认寄存器编号，再看操作码。
* 本节聚焦汇编写法和每条指令的语义；指令字段的划分回到CPU部件页对照即可。

## 课程约定

* Simple-MIPS是教学指令集，名称与真实MIPS有差异：教学集写`mov`、`shiftl`、`shiftr`，标准汇编分别写`move`、`sll`、`srl`。作业按题目采用的指令集作答。
* 访存一律写成`offset($rs)`，读题时先分清哪一个是基址、哪一个是偏移，不要漏掉括号。
* PC和地址都按字节计：一条指令4字节，顺序执行时下一条是当前地址加4，不要写成加1。

## 易错点

* 真实MIPS没有独立的`mov`，但Simple-MIPS把复制当作基本指令；不要把“真实MIPS没有”当成“Simple-MIPS不能用”。
* 逻辑右移`srl`左侧补0，只有对非负整数才等同于除以 $2^{shamt}$ 后向下取整；不要拿它处理带符号数的除法。
* `sll`、`srl`的`shamt`取0–31。

## 读指令的检查顺序

拿到一条指令，先看它是否访问内存：`lw`和`sw`读写内存，其余指令只改寄存器或PC。再看三个寄存器分别站在`rd`、`rs`、`rt`的哪个位置，最后才代数值计算。这个顺序能避免把`rs`和`rt`的角色对调。
