📄 一句话概括
CPU 的一生只有四步循环:取指令、译码、执行、PC 前进——跳转不过是把最后一步换成【改 PC】。
📝 本节和前面几节的关系
寄存器认全了,这一节让它们动起来:逐条追踪一段真实程序的执行。
一、执行四步循环
取指令:按 PC 的地址从内存里读出一条指令——PC 就是 CPU 的眼睛; 译码:看操作码,弄清这条指令要干什么(3E 是装载、D3 是输出、76 是停机); 执行:干活——改寄存器、读写内存或端口; PC 前进:加上本条指令的长度,指向下一条。 四步一个循环,一节拍一节拍地进行——计算机的一生就是这四步的无限重复。
二、追踪一段真程序
程序(第二章那 5 个字节):0x0000: 3E 80(LD A,128)、0x0002: D3 02(OUT (2),A)、0x0004: 76(HALT)。 第 1 步:PC=0x0000 取到 3E,译码【装载】,执行把下一字节 80 装进 A,PC 前进到 0x0002; 第 2 步:取到 D3,译码【输出】,把 A 写端口 2,PC 到 0x0004; 第 3 步:取到 76,译码【停机】,CPU 收工。
三、循环与分支怎么来
全是这四步的变体:循环=执行时把 PC 改回前面某地址(时间倒流);分支=满足条件才改 PC。 所以第四、五章讲的程序流程(顺序、循环、分支)在硬件层面只有一件事:PC 往哪走。
四、动手 Demo:逐步执行跟踪器
用 Python 模拟 CPU 的四步循环,每步打印 PC、A、端口的值。
# 四步循环: 取指 -> 译码 -> 执行 -> PC 前进
mem = {0: 0x3E, 1: 0x80, 2: 0xD3, 3: 0x02, 4: 0x76}
pc, a, port = 0, 0, None
while True:
op = mem[pc]
if op == 0x3E:
a = mem[pc+1]; pc += 2
elif op == 0xD3:
port = a; pc += 2
elif op == 0x76:
break
print("PC=0x%04X A=%d 端口=%s" % (pc, a, port))
实跑输出:
--------------------------------------------------------------------
逐条追踪 5 字节程序的一生
--------------------------------------------------------------------
把第二章那 5 个字节装进内存,然后一条条跟踪执行,
每一步都打印 PC、A 和端口的当前值:
[第 1 步] PC=0x0000 取指=0x3E (LD A, 128)
执行: A <- 128
A=128 端口2=None
[第 2 步] PC=0x0002 取指=0xD3 (OUT (2), A)
执行: 端口 2 <- A(128)
A=128 端口2=128
[第 3 步] PC=0x0004 取指=0x76 (HALT)
执行: 停机
追踪完这一遍,CPU 的整个工作循环就现形了:
取指令 -> 译码(看操作码) -> 执行 -> PC 前进
四步一节拍一节拍地循环,计算机的一生就是这四步的无限重复。
代码里的 if/elif 就是【译码】这一步——真实 CPU 用电路实现,速度是纳秒级。 PC 前进的步长等于指令长度(2 字节的指令加 2)——所以指令表里每条的字节数很重要。
30 秒实验
- 把跟踪器代码抄来跑,改一改内存里第一个字节(把 3E 改成 76)——程序立刻变了行为。
- 给跟踪器加一条【JP 0x0000】支持——你会看到 PC 被改回 0x0000,程序无限循环。
常见误区
- 以为译码是单独的硬件阶段就以为它很慢——译码就在取指后的那个节拍里完成。
- 以为循环是【特殊机制】——循环只是 PC 被改回前面,硬件上毫无特殊之处。
自测三问(点开对答案)
1. CPU 执行的四步循环是什么? —— 取指令、译码、执行、PC 前进——循环往复。 2. 循环与分支在硬件层面是什么操作? —— 都是改 PC:循环改回前地址,分支满足条件才改。 3. 为什么 PC 前进的步长因指令而异? —— 不同指令长度不同(1~4 字节),要跳过整条指令才指向下一条。- ☐ 下一节(3.5 尝试手工汇编)
相关笔记
- 输入测试程序并进行调试 — 上一章亲手装填的那 5 个字节
- 23 课 3.1.1 每个包都是独立传输的 — 那边是网络的分组思想,与【逐条执行】对照有趣
- 💻 计算机是怎样跑起来的 · 索引