Show HN: คอมพิวเตอร์ที่โปรแกรมได้ซึ่งสร้างจาก NAND gate
(github.com/ArhanChaudhary)- NAND คือ คอมพิวเตอร์ 16-bit ที่เทียบเท่าทัวริง ซึ่งจำลองบนเว็บ โดยยึดสมมติฐานเชิงการศึกษาว่าประกอบขึ้นจาก clock และ NAND gate เท่านั้น และครอบคลุมตั้งแต่ CPU ไปจนถึง IDE และ UI
- สแตกของตัวเองอิงจาก แพลตฟอร์ม Jack-VM-Hack โดยมี CPU, ภาษาเครื่อง, assembly, assembler, ภาษา VM, VM translator, ภาษา Jack และ compiler ให้มาด้วย
- โปรแกรมตัวอย่างมีทั้ง Average, Pong, 2048 รวมถึงเดโม VM escape ที่ใช้ stack overflow และ stack smashing ตลอดจน GeneticAlgorithm ที่ใช้ machine learning แบบง่าย
- Jack เป็นภาษาเชิงวัตถุแบบ weakly typed ที่มีไวยากรณ์คล้าย Java แต่ภายในพึ่งพา signed 16-bit integer เพียงชนิดเดียว และมีข้อจำกัดมาก เช่น ไม่มีลำดับความสำคัญของ operator, ต้องจัดการหน่วยความจำเอง และมีพฤติกรรมที่ไม่ได้กำหนดไว้
- โปรเจกต์นี้ไม่ได้หมายความว่าการคำนวณทั้งหมดทำงานบน NAND gate ทางกายภาพจริง ๆ แต่เป็น การใช้งานเพื่อการศึกษาและทฤษฎี ที่ใช้ TypeScript compiler และ VM translator, NAND gate logic simulator ที่เขียนด้วย Rust และ WebAssembly
สแตกคอมพิวเตอร์ทั้งหมดที่ NAND มีให้
- NAND เป็นคอมพิวเตอร์ 16-bit บนเว็บ ซึ่งแนะนำว่าเป็นตัวย่อของ Not A Nand-powered Device
- จำลองคอมพิวเตอร์ที่เทียบเท่าทัวริงซึ่งสร้างจาก clock และ NAND gate และมีองค์ประกอบต่อไปนี้อยู่ในตัว
- CPU
- ภาษา machine code
- ภาษา assembly
- assembler
- ภาษา virtual machine
- virtual machine translator
- ภาษาโปรแกรมมิง
- compiler
- IDE
- user interface
- มีพื้นฐานจาก คอร์ส Nand to Tetris และ แพลตฟอร์ม Jack-VM-Hack ในหนังสือที่เกี่ยวข้อง
- มี วิดีโอเดโม NAND ให้ดู
โปรแกรมตัวอย่างและเดโม
-
Average
- โปรแกรมง่าย ๆ สำหรับรับตัวเลขและคำนวณค่าเฉลี่ย
- แสดง control flow, การคำนวณเชิงเลขคณิต, I/O และการจัดสรรหน่วยความจำแบบไดนามิก
- เป็นโปรแกรมที่ให้มากับ Nand to Tetris software suite
-
Pong
- เกม Pong ที่แสดงโมเดลเชิงวัตถุ
- ใช้ปุ่มลูกศรเลื่อน paddle ไปซ้ายขวาเพื่อเด้งลูกบอล
- ทุกครั้งที่ลูกบอลเด้ง paddle จะเล็กลง และเมื่อบอลแตะด้านล่างของหน้าจอ เกมจะจบ
- เป็นโปรแกรมที่ให้มากับ Nand to Tetris software suite
-
2048
- เกม 2048 ที่แสดง recursion และ application logic ที่ซับซ้อน
- เลื่อนตัวเลขใน grid 4x4 ด้วยปุ่มลูกศร และตัวเลขที่เหมือนกันจะรวมกัน
- เมื่อไปถึง tile 2048 จะชนะ แต่ยังเล่นต่อได้จนกว่าจะแพ้
-
Overflow
- ตั้งใจทำให้เกิด stack overflow ด้วย recursion แบบไม่สิ้นสุด เพื่อทำ virtual machine escape
- ใช้ประโยชน์จากการที่ runtime ไม่มีการตรวจสอบป้องกัน stack overflow
- เมื่อค่า stack pointer เกิน 2048 stack จะหลุดออกจากพื้นที่หน่วยความจำที่ตั้งใจไว้และไหลเข้าไปใน heap memory space
- หากรันบน RAM ที่ว่างอยู่ โปรแกรมอาจถูก reset กลางคันเพราะ instruction ที่ตั้งค่า program counter เป็น 0
- หากรันทันทีหลังจากรัน GeneticAlgorithm จะสามารถอ่าน RAM memory เดิมที่ยังไม่ถูกเขียนทับได้
-
SecretPassword
- ใช้ประโยชน์จากการที่ runtime ไม่ป้องกัน stack smashing เพื่อเรียกฟังก์ชันที่เดิมเข้าถึงไม่ได้
- ผู้ใช้สามารถเขียนทับ memory address หนึ่งใน RAM ด้วยค่าที่ต้องการได้
- หากเขียนทับ return address ของ stack frame ด้วย address ของฟังก์ชันอื่น ก็สามารถรันโค้ดใด ๆ ภายในโปรแกรมได้
- ค่าตัวอย่างคือ memory location
267, overwrite value1743 - ช่องโหว่ประเภทเดียวกันนี้มีอยู่ใน buffer overflow ของ C ด้วย
-
GeneticAlgorithm
- เป็น creature simulation ที่ใช้ machine learning แบบง่าย
- dot แต่ละตัวมี “brain” ของตัวเองซึ่งประกอบด้วย acceleration vector และวิวัฒนาการผ่าน natural selection เพื่อให้ไปถึง goal
- dot ที่ตายในตำแหน่งใกล้ goal มากกว่าจะมีโอกาสสูงกว่าที่จะถูกเลือกเป็น parent ของ generation ถัดไป
- ในขั้นตอน reproduction บางส่วนของ brain จะเกิด mutation เพื่อจำลองวิวัฒนาการตามธรรมชาติ
- มี วิดีโอเดโม Genetic Algorithm ให้ดู
ข้อจำกัดด้านฮาร์ดแวร์ที่ GeneticAlgorithm เผชิญ
- GeneticAlgorithm เป็นโปรแกรมเดี่ยวที่ใช้เวลาพัฒนานานที่สุดในบรรดาองค์ประกอบต่าง ๆ ของ NAND
- ด้วยเหตุผลด้านประสิทธิภาพ ปัจจัยที่ dot ใช้ในการวิวัฒนาการมีเพียงว่าตอนตายนั้นอยู่ใกล้ goal แค่ไหน ทำให้ entropy ของอัลกอริทึม natural selection ต่ำ
- ด้วยปริมาณการใช้หน่วยความจำ จึงมีข้อจำกัดที่ไม่น่าพอใจทั้งจำนวน dot และขนาด brain
- ด้วยความซับซ้อนทางเทคนิค แม้จะจัดวาง obstacle ใหม่ระหว่าง simulation ก็ไม่อาจรับประกันได้ว่า brain ของ dot จะใหญ่พอที่จะไปถึง goal
- brain size ถูกกำหนดเฉพาะตอนเริ่มโปรแกรมเท่านั้น
- การ implement ใช้การปรับแต่งหลายอย่างเพื่อหลบเลี่ยงข้อจำกัดของ NAND
- ROM instruction memory space มีจำกัด หากโค้ดมากเกินไปจะ compile ไม่ผ่าน
- GeneticAlgorithm เวอร์ชันสุดท้ายใช้ 99.2% ของ instruction memory space
- RAM memory space มีจำกัด จึงต้องปรับการใช้ heap memory ให้เหมาะสม
- เหตุผลที่หน้าจอถูกเติมด้วย static ระหว่าง generation คือมีการใช้ screen memory space เป็น temporary swap memory สำหรับ generation ถัดไป
- NAND ไม่มี floating point type และช่วง integer ที่แทนค่าได้คือ -32768 ถึง 32767
- ความแม่นยำของการคำนวณ fitness ลดลง และต้องคำนึงถึง integer overflow ด้วย
- การปรับแต่งที่เกี่ยวข้องและ insight เพิ่มเติมมีบันทึกไว้ใน codebase ของ GeneticAlgorithm
การเขียนโปรแกรม NAND ด้วย Jack
- ใน Jack มีการเน้นว่าเหตุผลสำคัญที่สุดที่ทำให้โปรแกรมไม่ทำงานคือ ไม่มีลำดับความสำคัญของตัวดำเนินการ
4 * 2 + 3ต้องเขียนเป็น(4 * 2) + 3if (~x & y)ต้องเขียนเป็นif ((~x) & y)- evaluation value ของ expression ที่กำกวมและไม่มีวงเล็บถือเป็น undefined
- Jack เป็น weakly typed object-oriented programming language ของ NAND
- ตามคำอธิบายแล้วใกล้เคียงกับ “C ที่มี Java syntax”
- เนื่องจาก NAND มี complete tech stack เป็นของตัวเอง จึงสามารถเขียนโปรแกรมได้ด้วย Jack เท่านั้น
- Jack OS พื้นฐานจะถูก bundle ไปกับโปรแกรมตอน compile
- ให้ interface กับ strings, memory และ hardware
- มีฟังก์ชันอย่าง
Keyboard.readLine,Keyboard.readInt,Output.printString,Output.println
- Jack รองรับ primitive type 3 ชนิด ได้แก่
int,char,boolean- สามารถนิยาม abstract data type ผ่าน class ได้
- variable แบบ
fieldใช้ประกาศ attribute แยกตาม instance - variable แบบ
fieldมี private scope และการเข้าถึงจากภายนอกต้องผ่าน method
functionและmethodมีวิธีเรียกใช้ต่างกัน- method ของ current object สามารถเรียกใน class เดียวกันได้แบบ
do g(); - function call ต้องใส่ class name นำหน้า
- object method เรียกผ่าน object เช่น
do b.q();
- method ของ current object สามารถเรียกใน class เดียวกันได้แบบ
Weak typing และการจัดการหน่วยความจำของ Jack
- ต่างจาก Java ตรงที่ Jack ไม่รองรับ strong type, down casting, polymorphism และ inheritance
- ภายในจริง ๆ แล้ว type ที่มีอยู่มีเพียง signed 16-bit integer เท่านั้น
- compiler ไม่สนใจแม้จะผสม type ใน assignment และ operation
- ถ้าใส่
65ลงในcharก็สามารถ扱うได้เทียบเท่ากับ'A' - ถ้าใส่
5000ลงในตัวแปรArrayแล้วรันa[100] = 77จะกลายเป็นRAM[5100] = 77 - array entry สามารถเก็บ data type ต่างชนิดกันได้
- หาก memory layout ตรงกัน ก็สามารถใช้
Arrayเหมือนเป็น instance ของ class อื่นได้
- Jack เป็นภาษาที่ต้อง จัดการหน่วยความจำด้วยตนเอง
- หากไม่ deallocate memory ที่ไม่ต้องการแล้ว จะเกิด memory leak
- heap overflow แสดงเป็น
ERR6 - Jack OS เก็บ array และ string ไว้ใน heap ไม่ใช่ stack
- class ที่แทน object ควรมี method
disposeตาม best practice- เรียก
disposeของ field variable ก่อน - ท้ายสุด deallocate ตัว object instance เองด้วย
do Memory.deAlloc(this);
- เรียก
- loop ที่สร้างและพิมพ์ string literal ซ้ำ ๆ อาจทำให้เกิด heap overflow ได้
- dispose string ในแต่ละรอบ หรือ
- allocate string เพียงครั้งเดียวแล้วนำมาใช้ซ้ำ ก็จะพิมพ์ต่อไปได้เรื่อย ๆ
พฤติกรรมที่ไม่ถูกนิยามและข้อควรระวัง
-
ตัวดำเนินการเปรียบเทียบ
a > bและa < bไม่ได้ถูกต้องทางคณิตศาสตร์เสมอไป- การ implement ของ VM แปลง
a > bเป็นa - b > 0 a - bอาจ overflow ได้ ทำให้20000 > -20000กลายเป็นfalse- หาก absolute distance ของ
aและbมากกว่า 32767 เครื่องหมาย>และ<อาจผิดได้ - พฤติกรรมนี้ไม่ถูกแก้ไขเพื่อ compatibility กับ Nand to Tetris
-
-32768
-32768เป็นค่าเดียวที่-(-32768) = -32768- ไม่มี positive counterpart จึงอาจทำให้เกิด unsoundness และ logic error
Output.printIntภายในคาดว่าMath.absจะคืน positive number แต่ในกรณี-32768ไม่เป็นเช่นนั้น ทำให้ Jack OS ทำงานผิดพลาด
-
การเรียกฟังก์ชันโดยมี argument น้อยเกินไป
- หากเรียก function ที่มี parameter โดยไม่ส่ง argument อาจเกิด undefined behavior ได้
- ในทางกลับกัน function call ที่มี argument มากเกินไปถือว่า valid และสามารถ index extra argument ได้ด้วย keyword
arguments - ไม่มี argument count indicator
-
type casting ที่ไม่เหมาะสม
- สามารถใช้
Arrayเพื่อ cast ตัวแปรเป็น type อื่นได้ - หากเรียก instance method ที่ไม่มีอยู่จริงบน variable ที่ถูก cast แล้ว จะเป็น undefined behavior
- compiler ไม่ smart พอที่จะตรวจจับสิ่งนี้
- สามารถใช้
-
การแก้ไข stack frame และ internal register
- หากแก้ไข stack frame ที่ memory address
256~2047หรือ internal register ที่1~15อาจเกิด undefined behavior ได้ - ระบุว่าโดยทั่วไปทำได้ยากหากไม่มีการใช้
Memory.pokeผิดวิธีหรือ negative array indexing
- หากแก้ไข stack frame ที่ memory address
-
การโหลดไฟล์ VM ของผู้ใช้
- NAND มี program validation สำหรับไฟล์
.jackแต่ไม่มีสำหรับไฟล์.vm - ในไฟล์
.vmสามารถเรียก nonexistent function, อ้างถึง unassigned variable และทำ memory operation ที่ invalid ทางตรรกะได้ - ในกรณีส่วนใหญ่จะเกิด virtual machine escape และอาจไม่แสดงอะไรบน screen
- NAND มี program validation สำหรับไฟล์
สเปกฮาร์ดแวร์และการจัดวางหน่วยความจำ
- RAM ของ NAND ประกอบด้วย 32,768 words โดยแต่ละ word เก็บ 16-bit binary number
- hardware จอง memory address 8,192 ตำแหน่งสำหรับ screen
- แต่ละ bit ของแต่ละ address ถูก map แบบ linear ไปยัง pixel ที่สอดคล้องกันบน screen ขนาด 512x256
- bit numbering เป็นแบบ LSb 0
- keyboard ถูก map ไปที่ memory address
24576- key ที่ถูกกดอยู่ในปัจจุบันจะสะท้อนที่ตำแหน่งนี้
- แนะนำให้ใช้ class
Keyboardของ Jack OS มากกว่าจัดการ user input โดยตรงผ่าน address นี้
- keyboard รู้จัก ASCII character และ key พิเศษ
- new line =
128 - backspace =
129 - left/up/right/down arrow =
130~133 - home/end/page up/page down/insert/delete/ESC =
134~140 - F1~F12 =
141~152
- new line =
- hardware จอง memory address 240 ตำแหน่งสำหรับ static class variable และ 1,792 ตำแหน่งสำหรับ global stack
- ระบุว่าข้อจำกัดนี้โดยทั่วไปไม่เป็นปัญหา ตราบใดที่ไม่ได้ทำ deep recursion
ก้าวข้าม Jack OS ไปสู่การพัฒนา OS ของตัวเอง
- โดยพื้นฐานแล้ว Jack OS จะถูก bundle รวมกับโปรแกรมตอน compile เพื่อให้บริการ string, memory และ hardware interface
- สามารถจัดเตรียม OS implementation ของตัวเองที่มี hardware interface เฉพาะได้
- IDE จะปฏิบัติกับไฟล์ Jack OS เหมือนไฟล์ program ทั่วไป
- ไฟล์ OS ก็สามารถ delete หรือ overwrite ได้เช่นกัน
- แม้จะใช้ OS ของตัวเอง ก็ยังมี core function บางอย่างที่ต้อง implement เพื่อให้ compile ได้
Sys.init: ไม่ใช่Main.mainแต่เป็น entry point จริงที่ hardcoded อยู่ใน VM implementationMemory.alloc: เป็น heap memory allocator ที่ class constructor ใช้ภายในเมื่อสร้าง objectString.newWithStr: เป็น internal constructor สำหรับ string literalMath.multiply: ถูกเรียกภายในแทน Jack expressionx * yMath.divide: ถูกเรียกภายในแทน Jack expressionx / y
Sys.initของ Jack OS ที่ให้มาจะ initialize memory, math, screen และ output จากนั้นเรียกMain.main()แล้วเรียกSys.halt()
การทำงานภายในของ NAND
- คอมพิวเตอร์ NAND ใช้ Harvard architecture
- ROM ซึ่งเป็น instruction memory และ RAM ซึ่งเป็น data memory ถูกแยกออกจากกัน
- CPU ทำให้ทั้งสองส่วนทำงานร่วมกัน
- CPU เป็น accumulator machine
- ใน control flow จะพึ่งพา built-in register อย่างมาก
- ในที่นี้ accumulator คือ register
D
- CPU instruction set มี opcode เพียง สองตัว
- instruction set ค่อนข้างเรียบง่าย แต่ให้ความสามารถหลากหลาย
- ALU ถูกระบุด้วย expression ที่คำนวณได้ใน instruction เดียว
- compiler และ virtual machine ไม่ใช่แนวคิดเฉพาะของ NAND จึงกล่าวถึงแบบสั้น ๆ
- syntax feature แปลก ๆ บางอย่างเป็นผลจากการทำให้ implement compiler ได้ง่ายขึ้น
- compiler เป็น recursive descent parser บน LL(1) grammar
- compiler สร้าง VM code และ VM ถูกใช้เป็น simple stack machine
- VM instruction แต่ละตัวถูก mapping ไปยัง assembly และ machine code
- ดู implementation code ได้ที่ core และ compiler implementation
สาระสำคัญของภาษา Jack และ OS Reference
- โปรแกรม Jack ประกอบด้วย collection ของ class
- class ถูกนิยามในไฟล์แยกต่างหาก
- ต้องมี class อย่างน้อยหนึ่งตัว และหนึ่งในนั้นต้องเป็น
Main - ตาม Jack OS แล้ว entry point คือ function
mainของ classMain
- class สามารถมี declaration ของ
field,static,constructor,method,functionได้- ลำดับของ declaration
fieldและstaticเป็นแบบ arbitrary - ลำดับของ subroutine declaration ก็เป็นแบบ arbitrary เช่นกัน
- type เป็นหนึ่งใน
void,int,boolean,char, class name
- ลำดับของ declaration
- ลักษณะของ syntax
- whitespace และ comment จะถูก ignored
&และ|เป็น bitwise operator และไม่ทำ short-circuittrue,false,nullมีค่าเป็น-1,0,0ตามลำดับ- string constant ไม่สามารถมี newline หรือ quotation mark โดยตรง และ escape ก็ไม่ได้
- quotation mark และ newline มีให้ผ่าน
String.doubleQuote()และString.newLine()ของ OS - identifier เป็นแบบ case-sensitive
- class หลักของ Jack OS
Array: การสร้าง array และ disposeKeyboard: key press, character, line, integer inputMath: abs, multiply, divide, sqrt, max, minMemory:peek,poke,alloc,deAllocOutput: การแสดงผล text screen และการย้าย cursorScreen: การวาด pixel, line, rectangle, circleString: การสร้าง string, dispose, การเข้าถึง character, append, การแปลง integerSys: halt, error, wait
- invalid state จะแสดง error code ในรูปแบบ
"ERR[N]"และยุติ program executionERR3: division by zeroERR6: heap overflowERR15,ERR16: string index out of boundsERR17: string is fullERR20: illegal cursor location
ไม่ใช่โปรเจกต์ที่สร้างจาก NAND gate จริง ๆ เท่านั้น
- FAQ ยอมรับว่าคำอธิบายและ title ที่ว่า “everything made from NAND gates” นั้น misleading แต่เป็นไปโดย good faith
- compiler และ virtual machine translator เขียนด้วย TypeScript
- emulated kernel และ emulated hardware ไม่ได้แทนวิธีทำงานของ real computer แบบตรงตัว
- NAND gate logic simulator จริงเขียนด้วย Rust และเป็นเพียงส่วนเล็ก ๆ ของ codebase ทั้งหมด
- Rust code ถูก compile เป็น WebAssembly เพื่อให้รันใน browser
- มีการระบุว่าประเด็นนี้ทำให้สมมติฐานที่ว่า computation ทั้งหมดรันบน NAND gate ถูกตัดออกไปโดยพฤตินัย
- NAND ทำหน้าที่เป็น educational and theoretical project
- ในทางทฤษฎี CPU logic เดียวกันสามารถทำงานได้ใน real-world manifestation ของ emulated hardware ด้วย
- มีการยกตัวอย่าง FPGA hardware project ที่อิงกับ nand2tetris คือ https://gitlab.com/x653/nand2tetris-fpga/
ขอบเขตการ implement และข้อจำกัดของ IDE
- NAND ทำตาม specification ของคอร์ส Nand to Tetris และหนังสือที่เกี่ยวข้อง
- ผู้ implement ได้ implement specification ของ CPU, assembler, virtual machine translator และ compiler เอง และเมื่อ port platform ไปยัง web ก็เพิ่ม IDE และ UI ของตัวเองเข้าไป
- เหตุผลที่ Jack ต้องระบุ type คือเพื่อให้ compiler ตัดสินได้ว่า instance method เป็นของ class ใด
s.appendChar(33)ของsที่ประกาศเป็น typeStringจะถูกแปลงระหว่าง compile เป็นString.appendChar(s, 33)
- IDE ยอมสละ user experience เพื่อความเรียบง่ายของ implementation
- ใช้
contenteditableและ cursor positioning logic เพื่อทำ syntax highlighting - ผลคือทำงานช้า มี bug ที่เห็นได้ชัด และ common keybind ไม่ทำงาน
- ใช้
- หากต้องการ compile และรัน code ให้กด “Start”
- โดยทั่วไป OS ใช้เวลาน้อยกว่า 1 วินาทีเล็กน้อยในการ initialize memory และ setup service
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
เป็นโปรเจกต์เสริมที่ยอดเยี่ยมมาก และ README ก็ดีมากด้วย กำลังคิดว่าจะลองทำตาม Nand to Tetris หลังจากได้ลองเล่นกับ 6502 Computer ของ Ben Eater(https://eater.net/) มาบ้าง
แค่จากสื่อนี้อย่างเดียวก็น่าจะทำเป็น วิชาในมหาวิทยาลัยได้หลายวิชา เลย เป็นสื่อที่ทำมาดีมาก
ทำได้ดีมากจริง ๆ เหมือนได้เดินผ่าน ชั้นของ abstraction ด้วยตัวเอง ซึ่งโปรแกรมเมอร์ส่วนใหญ่ทั้งอาชีพอาจไม่มีวันได้เห็น
งานเจ๋งมาก NAND to Tetris ช่วยให้ฉันได้งานแรกหลังเรียนจบมหาวิทยาลัย
ตอนสอบ คุณวุฒิด้านฮาร์ดแวร์คอมพิวเตอร์ ของ UC Berkeley ช่วงต้นทศวรรษ 1990 ก็มีโจทย์แกนหลักที่คล้ายกันมาก
พูดให้เฉพาะเจาะจงคือเป็นโจทย์ให้ออกแบบ โปรเซสเซอร์ RISC แบบ pipeline ที่ใช้ microcode ตั้งแต่พื้นฐานโดยใช้แค่ NAND gate เท่านั้น ไม่ต้องสร้างจริงแต่ต้องส่งแบบออกแบบอย่างละเอียดบนกระดาษ
งานนี้น่าทึ่งจริง ๆ ตอนที่เรียนคอร์ส Nand2Tetris ฉันก็อยากลองทำ implementation แบบเสมือน คล้าย ๆ กันเหมือนกัน
ที่น่าประทับใจคือคุณทำมันสำเร็จจริง และตอนนี้ก็น่าจะเข้าใจการทำงานของคอมพิวเตอร์ได้ลึกมากแล้ว
แต่ก็น่าทึ่งที่มีคนทำสิ่งที่ยิ่งใหญ่กว่าที่ฉันจินตนาการไว้อีกเป็นระดับหนึ่งแล้ว
งานยอดเยี่ยมมาก ช่วงนี้ฉันก็เพิ่งเริ่ม Nand2Tetris และอยากจะเรียนส่วนที่ 1 ซึ่งเป็นพาร์ตฮาร์ดแวร์ของคอร์สให้จบภายในไม่กี่เดือนข้างหน้า
ฉันเขียนความคืบหน้าไว้ในบล็อกนี้: https://gurudas.dev/blog/2024/04/13/nand-to-tetris-2024-proj...
ไม่จริงหรอก คุณใช้ทั้ง NAND gate และ clock นี่นา
งานเจ๋งมาก เดี๋ยวจะบุ๊กมาร์กไว้กลับมาดูแบบละเอียดทีหลัง
ฉันชอบ NAND-to-Tetris แต่ยังไม่เคยทำจนจบ เลยตั้งตารอที่จะได้ดูโปรเจกต์นี้
สงสัยว่าจำนวน NAND gate ทั้งหมดมีเท่าไหร่?