<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>数字电路 on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/tags/%E6%95%B0%E5%AD%97%E7%94%B5%E8%B7%AF/</link>
    <description>AI 视频、AI Agent 与工程实践笔记</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Wed, 08 Nov 2023 10:40:00 +0800</lastBuildDate><atom:link href="https://ftmao.com/blog/tags/%E6%95%B0%E5%AD%97%E7%94%B5%E8%B7%AF/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>《编码》笔记：Petzold 教你从手电筒开始，亲手搭出一台计算机</title>
      <link>https://ftmao.com/blog/bookstore/code-petzold-book-notes/</link>
      <pubDate>Wed, 08 Nov 2023 10:40:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/bookstore/code-petzold-book-notes/</guid>
      <description>&lt;h1 id=&#34;编码笔记petzold-教你从手电筒开始亲手搭出一台计算机&#34;&gt;《编码》笔记：Petzold 教你从手电筒开始，亲手搭出一台计算机&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;目标读者：写了多年代码、对&amp;quot;计算机底层怎么跑起来的&amp;quot;只有模糊印象的工程师；也适合所有想给 CS 基础补课的非科班同行。
参考书籍：Charles Petzold《编码：隐匿在计算机软硬件背后的语言》（Code: The Hidden Language of Computer Hardware and Software，1999 年初版；2022 年第 2 版对章节做了扩充并配了在线交互演示，主线结构不变）。
阅读目标：不是抄一份计算机组成原理提纲，而是跟着 Petzold 的搭积木路径走一遍——每一层只用上一层的东西，绝不凭空引入新概念——把&amp;quot;CPU 在执行指令&amp;quot;这句话从信仰变成直觉。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一全书主线一次不跳步的推导&#34;&gt;一、全书主线：一次不跳步的推导&lt;/h2&gt;
&lt;p&gt;大多数计算机教材的讲法是&amp;quot;切横断面&amp;quot;：第一章讲数制，第二章讲门电路，第三章讲组成原理，每章都是成品快照。Petzold 的讲法是&lt;strong&gt;纵向钻一口井&lt;/strong&gt;：从两个小孩隔着窗户用手电筒闪摩尔斯电码开始，一路向下——电筒换成电线，电线换成电报，电报里拆出继电器，继电器组成逻辑门，逻辑门搭出加法器，加法器加上触发器变成内存，内存配上指令码变成自动计算机。全书二十多章，&lt;strong&gt;每一步的零件都来自上一步，没有一处&amp;quot;此处略去一万字&amp;quot;&lt;/strong&gt;。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[&amp;#34;手电筒 + 摩尔斯电码&amp;lt;br/&amp;gt;编码即信息表示&amp;#34;] --&amp;gt; B[&amp;#34;电报 + 继电器&amp;lt;br/&amp;gt;电流的可控开关&amp;#34;]
    B --&amp;gt; C[&amp;#34;布尔代数 + 逻辑门&amp;lt;br/&amp;gt;开关做判断&amp;#34;]
    C --&amp;gt; D[&amp;#34;加法器&amp;lt;br/&amp;gt;门电路做算术&amp;#34;]
    D --&amp;gt; E[&amp;#34;触发器 + 振荡器&amp;lt;br/&amp;gt;电路有了记忆与节拍&amp;#34;]
    E --&amp;gt; F[&amp;#34;内存阵列&amp;lt;br/&amp;gt;可寻址的存储&amp;#34;]
    F --&amp;gt; G[&amp;#34;自动操作&amp;lt;br/&amp;gt;指令码 + 累加器 + 跳转&amp;#34;]
    G --&amp;gt; H[&amp;#34;微处理器 + 操作系统&amp;lt;br/&amp;gt;进入我们熟悉的世界&amp;#34;]
    style A fill:#e8f5e9
    style G fill:#fff3cd&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这种写法对工程师有一个特别的价值：&lt;strong&gt;它把&amp;quot;底层&amp;quot;从黑话变成了具体的东西&lt;/strong&gt;。我们日常说&amp;quot;这代码编译成指令在 CPU 上跑&amp;quot;，多数人（包括多年前的我）其实只是在复述一个信仰。跟着这本书走完一遍，&amp;ldquo;指令&amp;quot;就是你亲手设计过的那几个二进制编码，&amp;ldquo;CPU&amp;quot;就是你亲手连过的那几个加法器和触发器的组合——再没有神秘空间留给想象。&lt;/p&gt;
&lt;p&gt;Petzold 在全书贯串的那句话，也是这篇笔记的主线：&lt;strong&gt;计算机里没有魔法，一切都建立在二元选择与电路上。&lt;/strong&gt; 剩下的全是组合与封装。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二编码的本质信息表示与二元选择&#34;&gt;二、编码的本质：信息表示与二元选择&lt;/h2&gt;
&lt;p&gt;书的前几章一个电子元件都没有，全在讲&amp;quot;码&amp;rdquo;：摩尔斯电码、布莱叶盲文、手电筒信号。Petzold 这么安排是在立全书的第一个支柱——&lt;strong&gt;编码不依赖任何具体载体，它只依赖&amp;quot;可区分的不同状态&amp;rdquo;&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;摩尔斯电码&lt;/strong&gt;证明：只用两种信号（点、划），加上时序规则，就能表达全部字母和数字。它还顺手展示了编码设计的精妙：常用字母（E、T）用短码，生僻字母用长码——这就是一百多年前的变长编码和频率统计。Petzold 还画出了那张著名的&lt;strong&gt;解码二叉树&lt;/strong&gt;：从树根出发，点是向左、划是向右，顺着信号一路走下去就唯一落到某个字母上——译码本质上是二分查找，这是&amp;quot;二元选择组合出任意复杂度&amp;quot;的第一个具体例子。书里有个诚实的补充也值得记住：摩尔斯电码严格说不是纯二元码，因为&amp;quot;点与划之间的停顿、字母之间的停顿、单词之间的停顿&amp;quot;本身就携带信息——&lt;strong&gt;时序也是一种码元&lt;/strong&gt;。这个细节在后面讲串行通信和时钟的章节里会一再回来。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;布莱叶盲文&lt;/strong&gt;证明：6 个凸点位置，每个位置只有&amp;quot;凸/平&amp;quot;两种状态，2 的 6 次方等于 64 个码字，足够装下字母、数字、标点，还留了余量给&amp;quot;数字转义符&amp;quot;&amp;ldquo;大写转义符&amp;quot;这类&lt;strong&gt;转义码（shift/escape code）&lt;/strong&gt;——码不够用时，不扩格子，用前缀切换语义。更进一步，二级布莱叶盲文（Grade 2）还引入了缩写字——用一个码字表示整个常用词——用空间（码表复杂度）换速度。这个思路后来在 ASCII 扩展、Unicode、协议设计、甚至 Huffman 编码里反复出现。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;手电筒闪码&lt;/strong&gt;证明：编码可以是纯行为，不需要任何纸张和机器。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这三章合起来说的其实是一句话：&lt;strong&gt;只要一个系统能提供足够多的&amp;quot;可区分的二选一&amp;rdquo;，它就能表示任何信息。&lt;/strong&gt; 二进制不是一种&amp;quot;计算机语言&amp;quot;，它是最小成本的区分方案——两个状态最容易造、最抗干扰、最不容易读错。计算机用二进制不是因为它&amp;quot;喜欢&amp;quot;0 和 1，而是因为造一个可靠区分两档的物理装置（开/关、高/低电压）远比造可靠区分十档的装置便宜。&lt;/p&gt;
&lt;h3 id=&#34;数制二进制是机器的语言十六进制是人的速记&#34;&gt;数制：二进制是机器的语言，十六进制是人的速记&lt;/h3&gt;
&lt;p&gt;书里对数制的处理也很克制：二进制是电路的天然语言，八进制和十六进制&lt;strong&gt;只是人类给二进制发明的速记法&lt;/strong&gt;——一位十六进制数字恰好对应 4 个二进制位，换算零成本。这解释了为什么我们至今还在和 &lt;code&gt;0xFF&lt;/code&gt; 打交道：不是情怀，是十六进制在&amp;quot;人类可读&amp;quot;和&amp;quot;机器对齐&amp;quot;之间的最佳折中。字节（8 位）成为标准单位后，两位十六进制数恰好表示一个字节，这个巧合让十六进制的地位彻底锁死。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;感悟&lt;/strong&gt;：编码这一层想通之后，再看日常的东西眼光会变。字符集是编码、颜色是编码、文件格式是编码、协议报文是编码——全是&amp;quot;约定一组状态，再约定每个状态意味着什么&amp;quot;。出 bug 的时候，一半是逻辑错，另一半是&lt;strong&gt;两边对同一份编码的理解不一致&lt;/strong&gt;（编码声明错了、转义没做、字节序反了）。Petzold 把编码放在第一章不是怀旧，是在定义全书的词汇表。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三电报与继电器一切从一根电线开始&#34;&gt;三、电报与继电器：一切从一根电线开始&lt;/h2&gt;
&lt;p&gt;从手电筒到计算机之间隔着一个关键跳跃：&lt;strong&gt;让电替你传递信号，并且让电替你控制电&lt;/strong&gt;。Petzold 借电报史完成了这一步。&lt;/p&gt;
&lt;p&gt;电报机（Samuel Morse 那一脉）的原理朴素得让人不好意思：一根导线、一个电池、一个能通断电路的按键、另一头一个电磁铁吸动的发声器。按下按键，远处&amp;quot;咔嗒&amp;quot;一声——摩尔斯电码就此摆脱了视线距离的限制。1844 年莫尔斯在华盛顿和巴尔的摩之间架通了第一条实验电报线，发出的第一封电文《圣经》语句&amp;quot;What hath God wrought&amp;quot;，Petzold 引用它时也带着点敬意：这是人类第一次让信息的传播速度超过了马和火车。&lt;/p&gt;
&lt;p&gt;真正的问题出在工程上：导线有电阻，信号传几十公里就衰减得不成样子。解决办法是&lt;strong&gt;继电器（relay）&lt;/strong&gt;——用微弱电流驱动电磁铁，电磁铁吸动一个新的开关，接通一段新的、电力充足的电路，把信号&amp;quot;原样转发&amp;quot;出去。&lt;/p&gt;
&lt;p&gt;继电器就是这条主线上最重要的零件，它有两个身份：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;信号放大器/中继器&lt;/strong&gt;：弱信号进，强信号出，电报线路因此可以无限接力延长；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用电流控制的开关&lt;/strong&gt;：这是它的本质——一个不需要人手的、可以被另一路电流开合的开关。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第二个身份是整座大厦的地基。Petzold 在这里点破了一件事：&lt;strong&gt;继电器输出的电，可以反过来去控制别的继电器&lt;/strong&gt;。这句话听起来平淡，但它意味着电路第一次可以&amp;quot;自己操作自己&amp;quot;——后面所有的逻辑门、加法器、存储器，都是这句话的展开。把&amp;quot;人手按开关&amp;quot;换成&amp;quot;电流控制开关&amp;quot;，就是硬件开始脱离人工的瞬间。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四布尔代数与逻辑门用开关做判断&#34;&gt;四、布尔代数与逻辑门：用开关做判断&lt;/h2&gt;
&lt;p&gt;19 世纪中叶，George Boole 发表了他的逻辑代数：把&amp;quot;真/假&amp;quot;映射成 1/0，把&amp;quot;并且/或者/否定&amp;quot;映射成代数运算。Boole 本人大概没想过这东西有什么用——直到 1938 年，Claude Shannon 在 MIT 的硕士论文里证明：&lt;strong&gt;开关电路和布尔代数是同一件事的两面&lt;/strong&gt;。串联的开关就是&amp;quot;与&amp;quot;，并联的开关就是&amp;quot;或&amp;quot;，反向接通就是&amp;quot;非&amp;quot;。Petzold 把这段历史搬出来，是为了让逻辑门的出现显得不可避免：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;与门（AND）&lt;/strong&gt;：两个继电器串联，两路都有电流，输出才通。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;或门（OR）&lt;/strong&gt;：两个继电器并联，任一路有电流，输出就通。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反相器（NOT）&lt;/strong&gt;：电磁铁吸动一个&amp;quot;平时闭合&amp;quot;的开关，有输入反而断开。&lt;/li&gt;
&lt;li&gt;由这三样再组合出&lt;strong&gt;与非（NAND）、或非（NOR）、异或（XOR）&lt;/strong&gt;。其中异或——&amp;ldquo;两者不同才为真&amp;rdquo;——马上要成为加法器的主角。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里有个对程序员很有亲切感的转折：&lt;strong&gt;从这一章开始，继电器这个具体的电磁装置就退场了，剩下的书里只剩抽象的门符号。&lt;/strong&gt; Petzold 特意提醒你：门可以用继电器做，后来用真空管做，再后来用晶体管做，今天用 CMOS 做——&lt;strong&gt;实现换了一代又一代，逻辑结构一行都不用改&lt;/strong&gt;。这是我们软件工程师天天享受却未必意识到其来历的红利：抽象层一旦正确，底下怎么换天都无所谓。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;感悟&lt;/strong&gt;：Shannon 那篇论文常被引用为&amp;quot;信息论的开端&amp;quot;，但在这本书的语境里它更像一份&lt;strong&gt;接口定义文档&lt;/strong&gt;——一边是数学家的布尔代数，一边是工程师的开关电路，两边从此可以各自演化、互相对接。好的抽象层都是这个形态：两边的人都不用懂对方的全部细节，只要共同遵守那个映射。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五搭加法器全书的第一个工程高潮&#34;&gt;五、搭加法器：全书的第一个工程高潮&lt;/h2&gt;
&lt;p&gt;有了门，Petzold 立刻提出第一个像样的工程问题：&lt;strong&gt;用门电路算加法&lt;/strong&gt;。这一步的推导漂亮得值得记下来：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先看一位加法的结果表：0+0=0、0+1=1、1+0=1、1+1=0（进位 1）。结果位恰好是&lt;strong&gt;异或&lt;/strong&gt;；进位位恰好是&lt;strong&gt;与&lt;/strong&gt;。一个 XOR 门加一个 AND 门，就是&lt;strong&gt;半加器（half adder）&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;半加器处理不了低位传上来的进位，所以真正的每一位要用&lt;strong&gt;全加器（full adder）&lt;/strong&gt;：两个半加器加一个或门——先加两个输入，再把结果与进位输入相加，两路进位用或门合并输出。&lt;/li&gt;
&lt;li&gt;把 8 个全加器首尾相接（低位的进位输出接高位的进位输入），就是一台 &lt;strong&gt;8 位行波进位加法器（ripple-carry adder）&lt;/strong&gt;。进位像波浪一样从右往左传播，最坏情况要传满 8 级——这是书里第一次出现&amp;quot;性能&amp;quot;概念：电路的正确性和电路的速度是两回事。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[&amp;#34;输入 A&amp;#34;] --&amp;gt; HA1[&amp;#34;半加器 1&amp;lt;br/&amp;gt;XOR 出和&amp;lt;br/&amp;gt;AND 出进位&amp;#34;]
    B[&amp;#34;输入 B&amp;#34;] --&amp;gt; HA1
    CI[&amp;#34;低位进位输入&amp;#34;] --&amp;gt; HA2[&amp;#34;半加器 2&amp;#34;]
    HA1 --&amp;gt;|&amp;#34;中间和&amp;#34;| HA2
    HA1 --&amp;gt;|&amp;#34;进位 1&amp;#34;| OR1[&amp;#34;或门&amp;#34;]
    HA2 --&amp;gt;|&amp;#34;进位 2&amp;#34;| OR1
    HA2 --&amp;gt; SUM[&amp;#34;本位和输出&amp;#34;]
    OR1 --&amp;gt; CO[&amp;#34;进位输出&amp;lt;br/&amp;gt;送往更高一位&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;8 个这样的全加器级联，接上 8 个开关作输入、8 个灯泡作输出，你就有一台能算 8 位加法的机器——开关拨出被加数和加数，灯泡亮灭就是结果，输入输出全是二进制，但&lt;strong&gt;这已经是&amp;quot;人机界面&amp;quot;的雏形&lt;/strong&gt;：机器内部的表示和呈现给人的形式，从第一天起就是两回事。Petzold 在这一章结尾的成就感是全书第一个峰值：&lt;strong&gt;减法也不用另造&lt;/strong&gt;——把减数按位取反再加一（补码），扔回同一台加法器就算完了。补码这个设计让减法、负数表示全部复用加法硬件，是&amp;quot;编码设计省掉硬件&amp;quot;的典范——一个纯粹的数制约定，换掉了整整一套减法电路。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;感悟&lt;/strong&gt;：读这一章的时候我专门停下来想了一下：我写了这么多年 &lt;code&gt;a + b&lt;/code&gt;，这是第一次真正&amp;quot;看见&amp;quot;那个加号。不是比喻意义上的看见，是逐位地、进位一级一级传过去地看见。这个体验本身值回书价。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六反馈与触发器电路学会了记忆&#34;&gt;六、反馈与触发器：电路学会了记忆&lt;/h2&gt;
&lt;p&gt;到加法器为止，所有电路都是&lt;strong&gt;组合的&lt;/strong&gt;——输出完全由当前输入决定，输入撤了，输出就没了。这样的电路不会&amp;quot;记住&amp;quot;任何东西。Petzold 用整整一章讲那个改变一切的结构：&lt;strong&gt;反馈（feedback）&lt;/strong&gt;——把输出接回输入。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;振荡器（oscillator）&lt;/strong&gt;：把反相器的输出接回自己的输入，电路就在 0 和 1 之间自动来回翻转。不需要任何人操作，它自己产生了节拍——这就是时钟（clock）的雏形，后来计算机里一切&amp;quot;按节拍推进&amp;quot;的源头。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;R-S 触发器（flip-flop）&lt;/strong&gt;：两个或非门交叉耦合——每个门的输出接回另一个门的输入。S（置位）给一个脉冲，输出变 1 并保持；R（复位）给一个脉冲，输出变 0 并保持。两个输入都撤掉之后，&lt;strong&gt;输出停在最后的状态&lt;/strong&gt;。电路记住了&amp;quot;上一次谁碰过我&amp;quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;D 触发器（电平/边沿触发）&lt;/strong&gt;：给 R-S 触发器加一路&amp;quot;时钟&amp;quot;输入，数据端（D）决定&amp;quot;存什么&amp;quot;，时钟端决定&amp;quot;什么时候存&amp;quot;。时钟跳变的瞬间，D 的值被锁存下来，之后 D 再怎么变都不影响输出。&lt;strong&gt;这就是 1 位存储器。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    S[&amp;#34;置位 S&amp;#34;] --&amp;gt; N1[&amp;#34;或非门 1&amp;#34;]
    R[&amp;#34;复位 R&amp;#34;] --&amp;gt; N2[&amp;#34;或非门 2&amp;#34;]
    N1 --&amp;gt;|&amp;#34;输出 Q&amp;#34;| OUT[&amp;#34;Q&amp;lt;br/&amp;gt;电路记住的状态&amp;#34;]
    N1 -.-&amp;gt;|&amp;#34;交叉反馈&amp;#34;| N2
    N2 -.-&amp;gt;|&amp;#34;交叉反馈&amp;#34;| N1
    N2 --&amp;gt;|&amp;#34;反相输出&amp;#34;| NQ[&amp;#34;Q 反&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;反馈结构还有一个直接的红利：&lt;strong&gt;计数器&lt;/strong&gt;。把触发器的反相输出接回自己的数据端，每来一个时钟脉冲它就翻转一次——输出波形的频率恰好是输入的一半。把 n 个这样的触发器串起来，每级的输出频率依次减半，把各级输出当作二进制的各位来读，你就得到一个 n 位二进制计数器，时钟每跳 2 的 n 次方次循环一轮。Petzold 在这里埋了一个很妙的呼应：前几章刚教会你读二进制数，这一章电路自己就&amp;quot;数&amp;quot;出了二进制数——&lt;strong&gt;数制不是抽象数学，是电路的自然行为&lt;/strong&gt;。计数器在后面的自动操作里会扮演&amp;quot;程序计数器&amp;quot;的角色：&lt;strong&gt;让电路自己记住&amp;quot;执行到第几条指令了&amp;quot;&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;感悟&lt;/strong&gt;：这一章是全书概念密度最高的一章，也是最值得慢读的一章。软件里&amp;quot;状态&amp;quot;是理所当然的（变量往那一放就有了），硬件里&amp;quot;状态&amp;quot;是要专门设计的——没有反馈回路就没有记忆，没有记忆就没有程序计数器、没有寄存器、没有一切。&amp;ldquo;存储&amp;quot;不是自然存在的，是用两个或非门互相咬尾咬出来的。理解这一点之后，&amp;ldquo;时序电路&amp;quot;和&amp;quot;组合电路&amp;quot;的分野、为什么信号完整性和时钟频率会成为硬件的命门，全都顺理成章。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;七内存阵列与自动操作积木的最后两格&#34;&gt;七、内存阵列与自动操作：积木的最后两格&lt;/h2&gt;
&lt;h3 id=&#34;内存把触发器组织成可寻址的阵列&#34;&gt;内存：把触发器组织成可寻址的阵列&lt;/h3&gt;
&lt;p&gt;一个 D 触发器存 1 位。8 个一排存 1 个字节。接下来是纯粹的工程组织问题：怎么让&amp;quot;写入&amp;quot;动作精确命中阵列里的某一个字节？答案是我们今天无比熟悉的三件套：&lt;strong&gt;地址、译码器、选择器&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;16 位地址就是 16 根信号线，经译码器展开成 65536 条选择线，每条线唯一选中一个字节——64K 字节的 RAM 阵列。&lt;/li&gt;
&lt;li&gt;读数据时，选择器（一堆与门加或门）把被选中的字节送到输出；写数据时，只有被选中的那一排触发器的时钟端被放行。&lt;/li&gt;
&lt;li&gt;触发器太贵？书上接着讲了省继电器的折衷（锁存结构的演进、以及后来电容式 DRAM 的思路），但逻辑骨架不变：&lt;strong&gt;可寻址 = 译码 + 选择&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;自动操作让电路按清单自己干活&#34;&gt;自动操作：让电路按清单自己干活&lt;/h3&gt;
&lt;p&gt;全书第十七章&amp;quot;自动化&amp;quot;是积木封顶的地方，Petzold 的目标明确：造一台&lt;strong&gt;不用人盯着拨开关&lt;/strong&gt;的计算机器。零件都已备齐：加法器、RAM 阵列、计数器、触发器。&lt;/p&gt;
&lt;p&gt;在进入自动化之前，书里先让你体会了一把&amp;quot;手工操作&amp;quot;的滋味：要算两个数的乘积，你得自己坐在控制面板前，扳开关把被乘数写进 RAM、扳开关触发加法、把中间结果抄下来再写回去——每算一步人都在场。这个铺垫很关键，它让&amp;quot;自动化&amp;quot;的必要性变成了体感而不是概念：&lt;strong&gt;瓶颈从来不是电路算得慢，是人操作得慢。&lt;/strong&gt; 组装的思路是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;代码进内存&lt;/strong&gt;：把数据和指令都编码成字节放进 RAM——指令就是约定俗成的字节值，比如某个字节表示&amp;quot;Load&amp;rdquo;（把某地址的值装进累加器），某个表示&amp;quot;Add&amp;rdquo;，某个表示&amp;quot;Store&amp;quot;，某个表示&amp;quot;Jump&amp;quot;，某个表示&amp;quot;Halt&amp;quot;。书里这台机器的指令表还包括带进位加（Add with Carry）、带借位减（Subtract with Borrow）、零跳转（Jump If Zero）等变体——多字节运算靠进位标志串起来，这正是后来 x86 里 &lt;code&gt;ADC/SBB&lt;/code&gt; 这类指令的原型。&lt;strong&gt;指令集就是一张编码表&lt;/strong&gt;，回到第二章的主题。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;累加器（accumulator）&lt;/strong&gt;：一个带锁存的寄存器，挂在加法器上，是所有运算的集散地——Load 装进来，Add 累加，Store 写回内存。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自动取指&lt;/strong&gt;：用一个计数器（程序计数器）依次生成地址，每执行完一条指令自动加一，取下一条。RAM 的一部分存指令、一部分存数据，同一套读写机构伺候两者。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跳转（Jump）&lt;/strong&gt;：Jump 指令把指定地址直接装进计数器，下一条指令就换地方取了。配上&amp;quot;条件跳转&amp;quot;（结果为零才跳、有进位才跳），程序有了分支；分支加循环，通用计算的全部要件就此凑齐。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    PC[&amp;#34;程序计数器&amp;lt;br/&amp;gt;自动加一&amp;#34;] --&amp;gt; RAM[&amp;#34;RAM 阵列&amp;lt;br/&amp;gt;指令与数据共处一室&amp;#34;]
    RAM --&amp;gt;|&amp;#34;取出指令字节&amp;#34;| DEC[&amp;#34;译码与控制&amp;lt;br/&amp;gt;查表: 这是什么操作?&amp;#34;]
    DEC --&amp;gt;|&amp;#34;Load / Store&amp;#34;| ADDR[&amp;#34;地址送往 RAM 读写&amp;#34;]
    DEC --&amp;gt;|&amp;#34;Add / Subtract&amp;#34;| ALU[&amp;#34;加法器 + 累加器&amp;#34;]
    ADDR --&amp;gt; ACC[&amp;#34;累加器&amp;#34;]
    ALU --&amp;gt; ACC
    ACC --&amp;gt;|&amp;#34;Store 写回&amp;#34;| RAM
    DEC --&amp;gt;|&amp;#34;Jump: 改写计数器&amp;#34;| PC
    DEC --&amp;gt;|&amp;#34;Halt&amp;#34;| STOP[&amp;#34;停机&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这就是冯·诺依曼结构的全部实质：&lt;strong&gt;指令和数据用同一种编码、住在同一片存储里，机器按计数器自动取指执行&lt;/strong&gt;。书里这台&amp;quot;玩具计算机&amp;quot;只有几条指令，但 Petzold 会算给你看：用 Load/Add/Store/Jump 这几条，循环累加做乘法、乘法套乘法做乘方，都能跑——慢，但没有做不到。&lt;strong&gt;通用性不是来自指令多，是来自跳转。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;八从玩具机到真实世界总线微处理器与字符编码&#34;&gt;八、从玩具机到真实世界：总线、微处理器与字符编码&lt;/h2&gt;
&lt;p&gt;登顶之后，书的最后几章把我们熟悉的世界一件件接回来：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;微处理器&lt;/strong&gt;：把前面搭的所有东西——运算器、寄存器、控制逻辑——集成进一枚芯片。Petzold 用 Intel 8080 和 Motorola 6800 做标本：你会发现它们的指令集就是自己搭的那台玩具机的豪华版，Load/Add/Store/Jump 一条不少，只是更多、更快、更小。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;总线（bus）&lt;/strong&gt;：处理器、内存、外设之间不能每两个都拉专线，于是大家共享一组公共信号线——地址总线、数据总线、控制总线——挂在上面的设备按地址认领属于自己的请求。总线本质上是&lt;strong&gt;用时间换空间的多路复用&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;存储层次&lt;/strong&gt;：书里的 RAM 阵列有个致命弱点——断电即失（触发器是电路，不是石刻）。于是需要更慢但持久的介质兜底：纸带、磁芯、磁盘、闪存，一代代接力。这一慢一快的落差逼出了存储层次结构：越快越贵越小的离 CPU 越近，越慢越便宜越大的垫底。&lt;strong&gt;寄存器、缓存、内存、磁盘的金字塔，不是设计出来的品味，是物理约束逼出来的妥协。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;操作系统&lt;/strong&gt;：有了能自动执行指令的机器，就需要一个&amp;quot;先跑起来、再替别的程序干活&amp;quot;的程序——管理内存、管理设备、把文件抽象成字节流。从自动操作到 OS，差的只是一层软件约定。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高级语言与汇编&lt;/strong&gt;：机器码太难写，于是有人写了一个把助记符翻译成机器码的程序——汇编器；再往上，一个把接近数学表达的文本翻译成机器码的程序——编译器。&lt;strong&gt;软件开始生产软件&lt;/strong&gt;，这是全书暗线的必然终点：每一层抽象一旦稳固，就变成下一层的建材。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;定点与浮点&lt;/strong&gt;：要表示小数和极大极小的数，又得回到编码问题——约定哪些位是整数部分、哪些是小数部分（定点），或者约定一套&amp;quot;符号 + 尾数 + 指数&amp;quot;的科学计数法格式（浮点）。浮点的一切怪异（0.1 加 0.2 不等于 0.3）都是这套编码约定的直接推论。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ASCII 与字符编码&lt;/strong&gt;：兜兜转转回到编码——计算机要处理文字，于是约定 7 位二进制数对应字符：95 个可打印字符加 33 个控制字符。这套约定和第一章的摩尔斯电码、布莱叶盲文在结构上一模一样：&lt;strong&gt;有限状态、人为约定、带转义&lt;/strong&gt;。全书在这里闭环：从编码出发，绕电路一大圈，最后又回到编码。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;第 2 版（2022）补充的在线交互演示值得单独提一句：书里搭的继电器、逻辑门、加法器都能在网页上拨开关亲眼看它跑。对&amp;quot;不亲手拨一遍就记不住&amp;quot;型学习者（比如我），这个配套资源把书的完成度又推高了一截。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;九落到工程实践这本书改变了什么&#34;&gt;九、落到工程实践：这本书改变了什么&lt;/h2&gt;
&lt;p&gt;这本书不是工具书，但它在我日常工作中留下了几个具体的使用痕迹：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;排障时下意识&amp;quot;降层&amp;quot;。&lt;/strong&gt; 遇到玄学 bug，以前的第一反应是往上翻日志；现在会先问一句&amp;quot;这一层底下是什么&amp;quot;——字符乱码降一层是编码声明错位，再降一层是字节序；接口超时降一层是重试放大，再降一层是队头阻塞。Petzold 的书本质上训练的就是这种&amp;quot;每一层都是上一层的编码&amp;quot;的世界观：&lt;strong&gt;任何一层的魔法，降一层都是约定。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对&amp;quot;性能&amp;quot;有了物理直觉。&lt;/strong&gt; 行波进位加法器那一节是全书第一次讲&amp;quot;正确但慢&amp;quot;：进位要一级一级传，n 位加法器的延迟随位数线性增长，真实 CPU 用超前进位（carry lookahead）用空间换时间。这个&amp;quot;信号要在物理结构里传播&amp;quot;的直觉，换个尺度就是缓存行、内存墙、网络 RTT——&lt;strong&gt;所有性能问题归根结底都是&amp;quot;距离&amp;quot;问题&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设计编码和协议时更克制。&lt;/strong&gt; 布莱叶盲文的转义码、补码省掉减法器、ASCII 的控制字符，讲的都是同一件事：&lt;strong&gt;好的编码用最小状态数表达最大语义，余量留给扩展&lt;/strong&gt;。自己设计消息格式、状态机、错误码时，我会先问：状态数能不能再砍一半？需不需要留一个&amp;quot;转义&amp;quot;出口？&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;给新人讲底层时有了最短路径。&lt;/strong&gt; 组里有同学问&amp;quot;程序到底怎么跑起来的&amp;quot;，与其扔一本组成原理，不如复述 Petzold 的积木链：手电筒 → 继电器 → 门 → 加法器 → 触发器 → 内存 → 取指执行。十五分钟讲完，对方的眼神从&amp;quot;敬畏&amp;quot;变成&amp;quot;就这？&amp;quot;——&lt;strong&gt;&amp;ldquo;就这？&amp;ldquo;就是这本书想达到的效果&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动手验证的习惯被强化。&lt;/strong&gt; 这本书的写法本身就是示范：每个论断都跟着一个&amp;quot;不信你自己连一遍&amp;quot;的电路。受它影响，我现在学任何底层概念（调度、GC、索引结构）都会逼自己写一个最小可跑的实现——写一个 50 行的玩具调度器，比读十篇调度原理文章管用。第 2 版的在线交互演示把这个门槛降到零：浏览器里就能拨开关看加法器跑，没有借口不动手。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十积木依赖速查表&#34;&gt;十、积木依赖速查表&lt;/h2&gt;
&lt;p&gt;把全书的搭积木链条压成一张表。每一行回答三个问题：这一层用什么搭的、获得了什么新能力、对应我们今天熟悉的什么。这张表也是我给新人讲底层的提纲——从上到下念一遍，就是计算机的简史。&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;层级&lt;/th&gt;
					&lt;th&gt;用什么搭的&lt;/th&gt;
					&lt;th&gt;获得的新能力&lt;/th&gt;
					&lt;th&gt;今天的对应物&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;编码（摩尔斯/布莱叶）&lt;/td&gt;
					&lt;td&gt;可区分的两种状态&lt;/td&gt;
					&lt;td&gt;任何信息都可表示&lt;/td&gt;
					&lt;td&gt;字符集、协议、文件格式&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;电报&lt;/td&gt;
					&lt;td&gt;电池 + 导线 + 电磁铁&lt;/td&gt;
					&lt;td&gt;信息超越视线距离传播&lt;/td&gt;
					&lt;td&gt;一切通信的祖先&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;继电器&lt;/td&gt;
					&lt;td&gt;电磁铁吸动开关&lt;/td&gt;
					&lt;td&gt;电流控制电流&lt;/td&gt;
					&lt;td&gt;晶体管的逻辑祖先&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;逻辑门&lt;/td&gt;
					&lt;td&gt;继电器串并联&lt;/td&gt;
					&lt;td&gt;用开关做布尔判断&lt;/td&gt;
					&lt;td&gt;CPU 里的门电路&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;半加器/全加器&lt;/td&gt;
					&lt;td&gt;XOR + AND + OR&lt;/td&gt;
					&lt;td&gt;电路做算术&lt;/td&gt;
					&lt;td&gt;ALU&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;行波进位加法器&lt;/td&gt;
					&lt;td&gt;8 个全加器级联&lt;/td&gt;
					&lt;td&gt;多位加法&lt;/td&gt;
					&lt;td&gt;整数运算单元&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;振荡器&lt;/td&gt;
					&lt;td&gt;反相器输出接回输入&lt;/td&gt;
					&lt;td&gt;自动产生节拍&lt;/td&gt;
					&lt;td&gt;时钟信号&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;R-S/D 触发器&lt;/td&gt;
					&lt;td&gt;两个或非门交叉耦合&lt;/td&gt;
					&lt;td&gt;电路有了记忆&lt;/td&gt;
					&lt;td&gt;寄存器、锁存器&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;计数器&lt;/td&gt;
					&lt;td&gt;触发器串联分频&lt;/td&gt;
					&lt;td&gt;电路自己会数数&lt;/td&gt;
					&lt;td&gt;程序计数器&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;RAM 阵列&lt;/td&gt;
					&lt;td&gt;触发器 + 译码器 + 选择器&lt;/td&gt;
					&lt;td&gt;可寻址的海量存储&lt;/td&gt;
					&lt;td&gt;内存&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;自动计算机&lt;/td&gt;
					&lt;td&gt;加法器 + RAM + 计数器 + 指令码&lt;/td&gt;
					&lt;td&gt;不用人盯的通用计算&lt;/td&gt;
					&lt;td&gt;CPU + 冯·诺依曼结构&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;微处理器/OS/总线&lt;/td&gt;
					&lt;td&gt;上述一切的集成与封装&lt;/td&gt;
					&lt;td&gt;进入我们熟悉的世界&lt;/td&gt;
					&lt;td&gt;今天的一切&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;看这张表有一个额外的收获：&lt;strong&gt;每一层都极其简单，复杂的只有组合方式。&lt;/strong&gt; 这也是面对任何庞大系统时该有的心态——别怕它大，找到它的&amp;quot;继电器层&amp;rdquo;，从那里开始读。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;本篇小结&#34;&gt;本篇小结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;全书主线一句话：&lt;strong&gt;计算机没有魔法，一切都建立在二元选择与电路上&lt;/strong&gt;；其余全是组合与封装。&lt;/li&gt;
&lt;li&gt;编码的本质是信息表示：摩尔斯电码、布莱叶盲文、ASCII 是同一结构的三个实例——有限状态、人为约定、转义扩展。&lt;/li&gt;
&lt;li&gt;继电器是地基零件：用电控制的开关，让电路第一次能自己操作自己。&lt;/li&gt;
&lt;li&gt;布尔代数给开关赋予逻辑：与、或、非三件套搭出一切判断；异或加与门就是半加器，两个半加器加或门就是全加器，八个级联就是行波进位加法器；补码让减法复用加法。&lt;/li&gt;
&lt;li&gt;反馈让电路有了记忆：反相器咬尾成振荡器（时钟），或非门交叉耦合出 R-S 触发器，加时钟成 D 触发器（1 位存储），串联成计数器（程序计数器）。&lt;/li&gt;
&lt;li&gt;自动操作的全部要件：指令编码表 + 累加器 + 程序计数器 + 条件跳转；指令与数据同住一片 RAM，就是冯·诺依曼结构。&lt;/li&gt;
&lt;li&gt;往上走的每一层——微处理器、总线、操作系统、高级语言——都只是同一套积木的规模化封装；积木依赖速查表见第十节。&lt;/li&gt;
&lt;li&gt;本书的副产物是一种读系统的方法论：面对任何庞大系统，找到它的&amp;quot;继电器层&amp;rdquo;，从那里开始读。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这本书我常推荐给两类人：刚入行想补计算机地基的，和写了很多年上层代码、开始好奇底下发生了什么的。对前者它是最平滑的入门路径，对后者它是一次&amp;quot;祛魅&amp;quot;——读完你会发现，所谓底层，不过是一群被理解得很透的简单东西。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Charles Petzold, &lt;em&gt;Code: The Hidden Language of Computer Hardware and Software&lt;/em&gt;, Microsoft Press, 1999（第 1 版）/ 2022（第 2 版，含 codehiddenlanguage.com 在线交互演示）。&lt;/li&gt;
&lt;li&gt;中译本：《编码：隐匿在计算机软硬件背后的语言》，左飞、薛佟佟 译，电子工业出版社。&lt;/li&gt;
&lt;li&gt;Claude E. Shannon, &amp;ldquo;A Symbolic Analysis of Relay and Switching Circuits&amp;rdquo;, 1938（书中第四章主线的原始论文，继电器与布尔代数的联姻）。&lt;/li&gt;
&lt;li&gt;George Boole, &lt;em&gt;An Investigation of the Laws of Thought&lt;/em&gt;, 1854（布尔代数的原始出处）。&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
  </channel>
</rss>
