Nand2Tetris 韩语教材 — 完整路线图
同时连接 12 个章节,从逻辑门到操作系统
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)
验证问题
- 当我在 NAND 的两个输入上输入相同的值时,为什么不会发生?
- 我们来解释一下 Mux 和 DMux 在数据方向方面的区别。
Mux16与 1 位复用器有何相同之处?
第 2 章布尔运算和 ALU
目标
- 了解半加器和全加器的区别。
- 我们看一下多位加法进位的过程。
- 了解 ALU,它使用控制位执行多种操作。
半加法器将两个位相加以创建和和进位。 全加器接收来自前一位置的进位作为输入。
HalfAdder: a + b
FullAdder: a + b + carry-in
Add16:连接到FullAdder的16位加法器
ALU 的关键不是为每个操作创建单独的电路。 zx, nx, zy, ny, f, no通过改变信号的输入和输出,一个电路可以有多种用途。
验证问题
- 为什么用XOR作为加法之和?
- 如果在 16 位加法中丢弃最后一个进位会发生什么?
- 算术逻辑单元
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)
验证问题
- 让我们用 Mux 来解释一下当负载为 0 时,一个位保持其现有值的过程。
- 为什么 RAM8 中需要 DMux 和 Mux?
- 如果PC的控制信号优先级颠倒,会出现什么样的bug?
第 4 章 Hack 机器语言
目标
- 区分 A 命令和 C 命令。
- 了解A、D、M寄存器的作用。
- 了解分支和循环如何在汇编中表达。
A 指令将一个值放入 A 寄存器中。
@10
C 指令在一行中表达计算、存储位置和跳转条件。
D=M+1;JGT
M不是一个单独的寄存器。RAM[A]方法。 如果你忽略了这一点,很容易混淆 A 寄存器是用作值还是地址。
【第四章查看代码】(#post/120004/ko)
实践
RAM[2] = RAM[0] + RAM[1]写 。- 编写一个从 1 到 10 相加的循环。
- 检查Fill代码中为什么SCREEN地址是16384。
第 5 章破解计算机体系结构
目标
- 将CPU、内存和ROM32K连接到一台计算机上。
- 了解指令位如何控制 ALU 和寄存器。
- 我们看到一个重复获取和执行的结构。
CPU 解释当前指令并决定将 ALU 结果存储在哪里:A、D 或 M。 如果跳转条件为真,则将A寄存器的值插入PC,否则移至下一条指令。
对于内存,根据地址范围选择 RAM16K、屏幕和键盘。 也就是说,只需读写内存地址就可以操作屏幕和键盘。
验证问题
instruction[15]为什么 A/C 命令要除以 ?writeM当为1时,该值实际存储在哪个地址?- 为什么键盘不需要负载信号?
第 6 章黑客汇编器
目标
.asm16位文件.hack转换为文件。- 过程标签和变量符号。
- 理解为什么需要读两遍。
标签不是实际指令,不应增加 ROM 地址。 所以在第一遍中(LOOP)收集相同标签的地址,并在第二遍中翻译实际指令。
第一遍:标签→ROM地址
第二遍:A/C指令→16位代码
变量符号:从RAM[16]开始按顺序分配
【第六章查看代码】(#post/120006/ko)
实践
- 编写一个预处理函数来删除空格和注释。
- 即使同一个变量出现多次,也要检查该地址是否只分配了一次。
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)
验证问题
- 为什么push和pop改变SP的顺序不同?
eq,gt,lt如果您不为每个标签创建唯一的标签,会发生什么情况?- 为什么静态变量名应该包含文件名?
第 8 章 VM 转换器 II
目标
- Label、goto 和 if-goto 在函数范围内进行处理。
- 了解函数、调用和返回的堆栈框架。
- 了解引导代码的作用。
调用函数时,必须保存返回地址和当前帧状态。
return-address
LCL
ARG
THIS
THAT
返回是FRAME = LCL, RET = *(FRAME - 5)首先保存呼叫者的段,然后恢复它。 由于LCL在恢复期间发生变化,因此必须首先保存RET。
【第八章查看代码】(#post/120008/ko)
实践
- 检查递归函数调用中的返回标签是否不重叠。
- 一些
.vm检查文件的静态段没有冲突。 - 在引导程序中,将 SP 设置为 256 并
Sys.init称呼 。
第 9 章 Jack 高级语言
目标
- 区分 Jack 的类、构造函数、方法和函数。
- 了解对象和数组如何存储在内存中。
- 使用操作系统 API 处理输入和输出。
Jack看起来和Java很相似,但是它的功能要简单得多。 对象的字段存储在堆中,当前对象的地址存储在方法中。this就变成了。
构造函数分配所需大小的内存,然后this返回。 数组也更接近连续的内存块,而不是特殊的数据结构。
【第九章查看代码】(#post/120009/ko)
实践
- 创建两个 Counter 对象并确保值保持独立。
- 连续按下按键输入时防止重复处理。
- 解释为什么对于已使用的对象需要进行处理。
第 10 章 Jack 解析器
目标
- 了解标记器将源代码划分为标记的过程。
- Jack 语法是作为递归下降解析器实现的。
- 将解析树输出为 XML。
字符串内部的符号和注释外部的符号必须以不同的方式处理。 因此,简单地按空格分割不会创建正确的分词器。
解析器查看当前标记并决定调用哪个语法函数。
class
├─ classVarDec
├─ subroutineDec
└─ statements
验证问题
- 在字符串中
//为什么我不能用评论删除它? - 将表达式和术语分开有什么方便?
- 在使用令牌之前检查预期类型有什么好处?
第 11 章 Jack 编译器
目标
- 将解析树转换为 VM 指令。
- 区分类作用域和子例程作用域符号。
- 了解方法调用和构造函数调用的隐藏行为。
符号表不仅仅存储名称。 类型、类型和索引存储在一起以确定要使用哪个VM 段。
static → static
field → this
argument → argument
var → local
该方法接收当前对象作为第一个参数。pointer 0连接到 构造函数的数量与字段的数量一样多。Memory.alloc调用后返回地址为pointer 0把它放进去。
实践
- 检查当同名的局部变量遮盖字段时选择哪个符号。
- 在数组赋值中,我们组织地址和值在堆栈上存储的顺序。
do查询返回值temp 0解释一下丢弃的原因。
第 12 章 Jack 操作系统
目标
- 直接实现了Jack程序使用的基础库。
- 将数学、字符串、数组和内存的职责分开。
- 它填补了硬件和高级语言之间最后的空白。
操作系统并不是以巨大的内核启动的。 关键是用Jack语言实现Jack程序所期望的功能。
- 数学:乘法、除法、绝对值
- String:字符存储、长度、输出转换
- 数组:连续的内存分配
- 内存:堆分配和释放
- 屏幕:像素和形状输出
- 键盘:按键输入
- 输出:输出字母和数字
- 系统:初始化、待机、关闭
验证问题
- 为什么不用CPU指令就可以实现乘法和除法?
- 为什么 String 对象有单独的长度和容量?
- 如果不释放内存,Jack程序会出现什么问题?
推荐学习顺序
最好按以下顺序阅读每一章。
- 在查看完整的代码之前,请先写下输入/输出和真值表。
- 首先测试小零件。
- 如果测试通过,则将其用作下一阶段的组件。
- 如果失败,不要只看输出,还要找出值在哪一层发生了变化。
- 最后,将现有博客代码与您自己的代码进行比较。
如果您按原样输入正确的代码,该项目将很快完成。 但在 Nand2Tetris 中重要的是理解答案本身下面的一层如何支持当前层。
一旦我们看到 NAND 通往操作系统的流程,计算机似乎就不再神奇了。