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 通往作業系統的流程,電腦似乎就不再神奇了。