← SEARCHABLE ARCHIVE
Nand2Tetris · 2026-05-23

Nand2Tetris 韩语教材 — 完整路线图

同时连接 12 个章节,从逻辑门到操作系统

INTERACTIVE READING MODE →

Nand2Tetris 韩文讲座资料

该博客上撰写的文章是出于教学目的而创建的。 另外,文章中的信息可能不正确,请谨慎。

Nand2Tetris 是从单个 NAND 门开始创建计算机、编译器和操作系统的过程。 如果单独看每一章,它看起来像是 HDL 代码或 Python 代码的集合,但实际上,上一章的结果成为下一章的材料。

晚安。 在本文中,我们将第 1 章到第 12 章的现有代码连接成一个流程。

全流

NAND
  ↓
逻辑门
  ↓
加法器和ALU
  ↓
寄存器和内存
  ↓
破解CPU和计算机
  ↓
汇编器
  ↓
虚拟机翻译器
  ↓
杰克编译器
  ↓
杰克操作系统

第 1-5 章创建硬件, 第 6-8 章创建了一个低级转换器。 第 9-12 章介绍了高级语言、编译器和操作系统。

第 1 章 布尔逻辑和基本门

目标

  • 仅使用 NAND 创建另一个逻辑门。
  • 查看真值表并思考 HDL 连接。
  • 了解 Mux 和 DMux 如何选择和分发数据。

仅当两个输入均为 1 时,NAND 才输出 0。 这可能看起来很简单,但将输入连接到同一行会创建 NOT,再次组合 NOT 可以创建 AND 和 OR。

Not(a) = Nand(a, a)
And(a, b) = Not(Nand(a, b))
Or(a, b) = Nand(Not(a), Not(b))

多路复用器是sel根据情况选择 a 或 b。 另一方面,DMux 将一个输入发送到 a 或 b。

【第一章查看代码】(#post/120001/ko)

验证问题

  1. 当我在 NAND 的两个输入上输入相同的值时,为什么不会发生?
  2. 我们来解释一下 Mux 和 DMux 在数据方向方面的区别。
  3. Mux16与 1 位复用器有何相同之处?

第 2 章布尔运算和 ALU

目标

  • 了解半加器和全加器的区别。
  • 我们看一下多位加法进位的过程。
  • 了解 ALU,它使用控制位执行多种操作。

半加法器将两个位相加以创建和和进位。 全加器接收来自前一位置的进位作为输入。

HalfAdder: a + b
FullAdder: a + b + carry-in
Add16:连接到FullAdder的16位加法器

ALU 的关键不是为每个操作创建单独的电路。 zx, nx, zy, ny, f, no通过改变信号的输入和输出,一个电路可以有多种用途。

第二章查看代码

验证问题

  1. 为什么用XOR作为加法之和?
  2. 如果在 16 位加法中丢弃最后一个进位会发生什么?
  3. 算术逻辑单元zr, ng下一章中输出将用于什么功能?

第 3 章 时序逻辑和内存

目标

  • 了解组合逻辑和时序逻辑之间的区别。
  • 我理解DFF是存储时间的最小单位。
  • 了解位、寄存器、RAM 和 PC 如何扩展。

在第 1 章和第 2 章的电路中,输出仅由电流输入决定。 然而,存储器必须记住前一个时钟的值。

DFF的输出不是当前的输入,而是前一个时间点的输入。 通过在此处附加一个 Mux,您可以创建一个仅在负载为 1 时存储新值的位。

DFF → Bit → Register → RAM8 → RAM64 → RAM512 → RAM4K → RAM16K

电脑是reset, load, inc优先事项很重要。 通常reset是最强的,然后是load,最后是inc。

【第三章查看代码】(#post/120003/ko)

验证问题

  1. 让我们用 Mux 来解释一下当负载为 0 时,一个位保持其现有值的过程。
  2. 为什么 RAM8 中需要 DMux 和 Mux?
  3. 如果PC的控制信号优先级颠倒,会出现什么样的bug?

第 4 章 Hack 机器语言

目标

  • 区分 A 命令和 C 命令。
  • 了解A、D、M寄存器的作用。
  • 了解分支和循环如何在汇编中表达。

A 指令将一个值放入 A 寄存器中。

@10

C 指令在一行中表达计算、存储位置和跳转条件。

D=M+1;JGT

M不是一个单独的寄存器。RAM[A]方法。 如果你忽略了这一点,很容易混淆 A 寄存器是用作值还是地址。

【第四章查看代码】(#post/120004/ko)

实践

  1. RAM[2] = RAM[0] + RAM[1]写 。
  2. 编写一个从 1 到 10 相加的循环。
  3. 检查Fill代码中为什么SCREEN地址是16384。

第 5 章破解计算机体系结构

目标

  • 将CPU、内存和ROM32K连接到一台计算机上。
  • 了解指令位如何控制 ALU 和寄存器。
  • 我们看到一个重复获取和执行的结构。

CPU 解释当前指令并决定将 ALU 结果存储在哪里:A、D 或 M。 如果跳转条件为真,则将A寄存器的值插入PC,否则移至下一条指令。

对于内存,根据地址范围选择 RAM16K、屏幕和键盘。 也就是说,只需读写内存地址就可以操作屏幕和键盘。

第五章代码查看

验证问题

  1. instruction[15]为什么 A/C 命令要除以 ?
  2. writeM当为1时,该值实际存储在哪个地址?
  3. 为什么键盘不需要负载信号?

第 6 章黑客汇编器

目标

  • .asm16位文件.hack转换为文件。
  • 过程标签和变量符号。
  • 理解为什么需要读两遍。

标签不是实际指令,不应增加 ROM 地址。 所以在第一遍中(LOOP)收集相同标签的地址,并在第二遍中翻译实际指令。

第一遍:标签→ROM地址
第二遍:A/C指令→16位代码
变量符号:从RAM[16]开始按顺序分配

【第六章查看代码】(#post/120006/ko)

实践

  1. 编写一个预处理函数来删除空格和注释。
  2. 即使同一个变量出现多次,也要检查该地址是否只分配了一次。
  3. dest, comp, jump当遇到表中没有的输入时,会输出错误。

第 7 章 VM 转换器 I

目标

  • 了解基于堆栈的虚拟机。
  • 将算术指令和内存段转换为 Hack 汇编。
  • 了解为什么比较操作需要唯一的标签。

大多数VM指令从堆栈中获取值,计算它们,然后将结果放回。

push constant 7
push constant 8
add

local, argument, this, that跟随基地址, temp, pointer, static每个都使用自己设定的地址规则。

【第七章查看代码】(#post/120007/ko)

验证问题

  1. 为什么push和pop改变SP的顺序不同?
  2. eq, gt, lt如果您不为每个标签创建唯一的标签,会发生什么情况?
  3. 为什么静态变量名应该包含文件名?

第 8 章 VM 转换器 II

目标

  • Label、goto 和 if-goto 在函数范围内进行处理。
  • 了解函数、调用和返回的堆栈框架。
  • 了解引导代码的作用。

调用函数时,必须保存返回地址和当前帧状态。

return-address
LCL
ARG
THIS
THAT

返回是FRAME = LCL, RET = *(FRAME - 5)首先保存呼叫者的段,然后恢复它。 由于LCL在恢复期间发生变化,因此必须首先保存RET。

【第八章查看代码】(#post/120008/ko)

实践

  1. 检查递归函数调用中的返回标签是否不重叠。
  2. 一些.vm检查文件的静态段没有冲突。
  3. 在引导程序中,将 SP 设置为 256 并Sys.init称呼 。

第 9 章 Jack 高级语言

目标

  • 区分 Jack 的类、构造函数、方法和函数。
  • 了解对象和数组如何存储在内存中。
  • 使用操作系统 API 处理输入和输出。

Jack看起来和Java很相似,但是它的功能要简单得多。 对象的字段存储在堆中,当前对象的地址存储在方法中。this就变成了。

构造函数分配所需大小的内存,然后this返回。 数组也更接近连续的内存块,而不是特殊的数据结构。

【第九章查看代码】(#post/120009/ko)

实践

  1. 创建两个 Counter 对象并确保值保持独立。
  2. 连续按下按键输入时防止重复处理。
  3. 解释为什么对于已使用的对象需要进行处理。

第 10 章 Jack 解析器

目标

  • 了解标记器将源代码划分为标记的过程。
  • Jack 语法是作为递归下降解析器实现的。
  • 将解析树输出为 XML。

字符串内部的符号和注释外部的符号必须以不同的方式处理。 因此,简单地按空格分割不会创建正确的分词器。

解析器查看当前标记并决定调用哪个语法函数。

class
 ├─ classVarDec
 ├─ subroutineDec
 └─ statements

第10章代码查看

验证问题

  1. 在字符串中//为什么我不能用评论删除它?
  2. 将表达式和术语分开有什么方便?
  3. 在使用令牌之前检查预期类型有什么好处?

第 11 章 Jack 编译器

目标

  • 将解析树转换为 VM 指令。
  • 区分类作用域和子例程作用域符号。
  • 了解方法调用和构造函数调用的隐藏行为。

符号表不仅仅存储名称。 类型、类型和索引存储在一起以确定要使用哪个VM 段。

static   → static
field    → this
argument → argument
var      → local

该方法接收当前对象作为第一个参数。pointer 0连接到 构造函数的数量与字段的数量一样多。Memory.alloc调用后返回地址为pointer 0把它放进去。

第11章代码查看

实践

  1. 检查当同名的局部变量遮盖字段时选择哪个符号。
  2. 在数组赋值中,我们组织地址和值在堆栈上存储的顺序。
  3. do查询返回值temp 0解释一下丢弃的原因。

第 12 章 Jack 操作系统

目标

  • 直接实现了Jack程序使用的基础库。
  • 将数学、字符串、数组和内存的职责分开。
  • 它填补了硬件和高级语言之间最后的空白。

操作系统并不是以巨大的内核启动的。 关键是用Jack语言实现Jack程序所期望的功能。

  • 数学:乘法、除法、绝对值
  • String:字符存储、长度、输出转换
  • 数组:连续的内存分配
  • 内存:堆分配和释放
  • 屏幕:像素和形状输出
  • 键盘:按键输入
  • 输出:输出字母和数字
  • 系统:初始化、待机、关闭

第十二章代码查看

验证问题

  1. 为什么不用CPU指令就可以实现乘法和除法?
  2. 为什么 String 对象有单独的长度和容量?
  3. 如果不释放内存,Jack程序会出现什么问题?

推荐学习顺序

最好按以下顺序阅读每一章。

  1. 在查看完整的代码之前,请先写下输入/输出和真值表。
  2. 首先测试小零件。
  3. 如果测试通过,则将其用作下一阶段的组件。
  4. 如果失败,不要只看输出,还要找出值在哪一层发生了变化。
  5. 最后,将现有博客代码与您自己的代码进行比较。

如果您按原样输入正确的代码,该项目将很快完成。 但在 Nand2Tetris 中重要的是理解答案本身下面的一层如何支持当前层。

一旦我们看到 NAND 通往操作系统的流程,计算机似乎就不再神奇了。