💻 第二章 试着制造一台计算机吧
📄 一句话概括

MREQ 和 IORQ 两根引脚一开一关,同一条地址总线上的点名就分流给内存或 I/O——一趟地址,两个去向。

📝 本节和前面几节的关系

两套门牌(内存地址、端口号)早就挂好了,这一节接上裁决它们的开关线。

一、两根请求线

MREQ(内存请求):有效表示【这趟地址是给内存的】; IORQ(I/O 请求):有效表示【这趟地址是给 I/O 的】。 注意这些控制线是【低电平有效】——0 表示生效,1 表示休息。别被绕晕:硬件这么设计有电路上的理由(抗干扰),记住约定就好。

二、一趟访问的完整表态

CPU 要读内存某格:地址总线放地址 -> MREQ 拉低(找内存)-> RD 拉低(读)-> 内存把内容放上数据总线。 CPU 要写数码管:地址总线放端口号 -> IORQ 拉低(找 I/O)-> WR 拉低(写)-> 数据总线上的内容进端口。 【去哪】加【干什么】,四个信号一组,一次访问的表态就完整了。

三、译码器:硬件中的分流员

光有请求线还不够,得有电路【看线办事】:译码器盯着 MREQ/IORQ 和地址高位,决定把读写信号递给哪块芯片。 这一步在真实硬件里由译码器芯片(或几根门电路)完成,一纳秒都不犹豫——软件里的 if 分支,硬件里有它的孪生兄弟。

四、动手 Demo:译码分流模拟器

模拟两趟访问,看地址如何按 MREQ/IORQ 的表态分流到内存或 I/O。

# 译码分流:一趟地址两个去向
def decode(addr, mreq, iorq):
    if mreq == 0 and iorq == 1:
        return "内存[0x%04X]" % addr
    if mreq == 1 and iorq == 0:
        return "I/O 端口[0x%02X]" % addr
    return "非法状态"
print(decode(0x0100, 0, 1))   # 访问内存
print(decode(0x02, 1, 0))     # 访问 I/O

实跑输出:

--------------------------------------------------------------------
区分内存和 I/O 的引脚——一趟地址两个去向
--------------------------------------------------------------------

Z80 有两根关键的请求线:MREQ(内存请求) 和 IORQ(I/O 请求)。
同一趟点名,两根线一开一关,地址就流进不同的门:

  MREQ     IORQ     这趟点名给谁     例子
  ----------------------------------------------------------
  1        0        I/O              端口号 2 的数码管
  0        1        内存             地址 0x0100 的一条指令

用 Python 模拟一趟译码:
    地址 = 0x0100, MREQ=0(有效), IORQ=1(无效)
    -> 判定:访问内存[0x0100]
    地址 = 0x02,   MREQ=1(无效), IORQ=0(有效)
    -> 判定:访问 I/O 端口[0x02]

硬件的译码器芯片干的就是这个【看线分流】的活,一纳秒都不犹豫。

函数里的判断就是译码器的逻辑——硬件把它做进了硅片,时序上比软件快几个量级。 两个 1 同时出现(都不请求)是空闲态;两个 0 同时出现是设计错误。

30 秒实验

  1. 把 decode 函数抄下来改一改:两个 0 时返回【设计错误】。
  2. 对照 2.4 节的两套门牌表,把每行配上 MREQ/IORQ 的取值。

常见误区

  1. 以为低电平有效是笔误——硬件控制线大量使用低有效,是约定不是错误。
  2. 以为译码靠软件——分流由译码器电路硬件完成,CPU 根本不用操心。
自测三问(点开对答案) 1. MREQ 和 IORQ 各管什么? —— MREQ 有效找内存,IORQ 有效找 I/O——同趟地址的去向开关。 2. 一次访问的完整表态是哪几个信号? —— 去哪(MREQ/IORQ)+干什么(RD/WR),四个信号一组。 3. 译码器的职责是什么? —— 盯着请求线和地址高位,把读写信号递给正确的那块芯片。

相关笔记