TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Show HN: WIP NandToTetris Emulator in pure C – logic gates to ALU to CPU to PC

43 pointsby purple-leafy5 months ago
* OPEN TO CONTRIBUTIONS *<p>NandToTetris is a course which has you build a full computer from:<p>Logic gates -&gt; Chips -&gt; RAM -&gt; CPU -&gt; Computer -&gt; Assembler -&gt; Compiler -&gt; OS -&gt; Tetris<p>All this is done via software defined hardware emulation.<p>I&#x27;m building an emulator for this entire stack in C.<p>How is my approach different to other projects that build emulators?<p><pre><code> - No external dependencies (so far) - Start with a single software defined NAND gate in C - EVERYTHING is built from this base chip - Don&#x27;t use certain programming utilities: boolean logic operators, bitwise logic operators etc Instead we leverage the gates&#x2F;chips to implement such logic I build more and more base chips from the NAND gate - Simple gates: OR, AND, NOT, XOR (5 total &quot;gates&quot;) - Simple chips: DMux, Mux (11 so far, &quot;combinatorial&quot; chips - 16 bit variants </code></pre> For comparison, most emulator projects start right at the CPU level and don&#x27;t sequentially build primitive structures. So a lay-person, or person unfamiliar with PC architectures is missing some lower-level information.<p>Typical emulators look at CPU truth table &#x2F; Instruction set and implement that logic directly in code.<p>More straight forward, way fewer lines of code - but you skip all the gates&#x2F;chips fun.<p>------<p>Confused? Heres example code for my NAND gate:<p><pre><code> void nand_gate(Nand *nand) { nand-&gt;output.out = !(nand-&gt;input.a &amp; nand-&gt;input.b); } </code></pre> From this gate I build a NOT gate (note, no boolean operators)<p><pre><code> void not_gate(Not * not ) { Nand nand = { .input.a = not -&gt;input.in, .input.b = not -&gt;input.in, }; nand_gate(&amp;nand); not -&gt;output.out = nand.output.out; } </code></pre> Then OR &#x2F; AND &#x2F; XOR &#x2F; MUX &#x2F; DMUX ..... and their 16 bit versions.<p>Heres a more complex chip, a 16bit Mux-8-way chip<p><pre><code> &#x2F;* * out = a if sel = 000 * b if sel = 001 * c if sel = 010 * d if sel = 011 * e if sel = 100 * f if sel = 101 * g if sel = 110 * h if sel = 111 *&#x2F; void mux16_8_way_chip(Mux16_8_Way *mux16_8_way) { Mux16_4_Way mux16_4_way_chip_a, mux16_4_way_chip_b; Mux16 mux16_chip_c; &#x2F;&#x2F; Mux a memcpy(mux16_4_way_chip_a.input.sel, mux16_8_way- &gt;input.sel, sizeof(mux16_4_way_chip_a.input.sel)); memcpy(mux16_4_way_chip_a.input.a, mux16_8_way-&gt;input.a, sizeof(mux16_4_way_chip_a.input.a)); memcpy(mux16_4_way_chip_a.input.b, mux16_8_way-&gt;input.b, sizeof(mux16_4_way_chip_a.input.b)); memcpy(mux16_4_way_chip_a.input.c, mux16_8_way-&gt;input.c, sizeof(mux16_4_way_chip_a.input.c)); memcpy(mux16_4_way_chip_a.input.d, mux16_8_way-&gt;input.d, sizeof(mux16_4_way_chip_a.input.d)); mux16_4_way_chip(&amp;mux16_4_way_chip_a); &#x2F;&#x2F; Mux b memcpy(mux16_4_way_chip_b.input.sel, mux16_8_way-&gt;input.sel, sizeof(mux16_4_way_chip_b.input.sel)); memcpy(mux16_4_way_chip_b.input.a, mux16_8_way-&gt;input.e, sizeof(mux16_4_way_chip_b.input.a)); memcpy(mux16_4_way_chip_b.input.b, mux16_8_way-&gt;input.f, sizeof(mux16_4_way_chip_b.input.b)); memcpy(mux16_4_way_chip_b.input.c, mux16_8_way-&gt;input.g, sizeof(mux16_4_way_chip_b.input.c)); memcpy(mux16_4_way_chip_b.input.d, mux16_8_way-&gt;input.h, sizeof(mux16_4_way_chip_b.input.d)); mux16_4_way_chip(&amp;mux16_4_way_chip_b); &#x2F;&#x2F; Mux c mux16_chip_c.input.sel = mux16_8_way-&gt;input.sel[2]; memcpy(mux16_chip_c.input.a, mux16_4_way_chip_a.output.out, sizeof(mux16_chip_c.input.a)); memcpy(mux16_chip_c.input.b, mux16_4_way_chip_b.output.out, sizeof(mux16_chip_c.input.b)); mux16_chip(&amp;mux16_chip_c); memcpy(mux16_8_way-&gt;output.out, mux16_chip_c.output.out, sizeof(mux16_8_way-&gt;output.out)); } </code></pre> -----<p>Progress: I have only started this project yesterday, so have completed 1 out of 7 hardware projects so far

6 comments

duskwuff5 months ago
You may want to take a look at hardware definition languages like Verilog, if you haven&#x27;t before. They&#x27;re designed for this sort of thing.<p>(If you want to do your own thing that&#x27;s fine too - just wanted to make sure you&#x27;re aware of what&#x27;s out there.)
评论 #42546880 未加载
deosjr5 months ago
I did the same thing in Golang here: <a href="https:&#x2F;&#x2F;github.com&#x2F;deosjr&#x2F;nand2tetris">https:&#x2F;&#x2F;github.com&#x2F;deosjr&#x2F;nand2tetris</a><p>Be sure to check out the lispmachine folder; its essentially a fork I made after finishing the course. It implements a Lisp on the nand-gate level :)
purple-leafy5 months ago
For anyone who wants to challenge themselves to better understand the computers we use every day, I highly recommend the NandToTetris course [0]<p>I have learnt more about computers in 2 weeks of this course, than years of software development.<p>Particularly if you are a web-developer, I challenge you to look deeper into PC architectures and low-level programming.<p>C is a great language to get started with.<p>After I build this project, I want to move on to a more powerful emulator (maybe a GameBoy or similar, but probably starting at the CPU level rather than the logic gate level!)<p>[0] - <a href="https:&#x2F;&#x2F;www.nand2tetris.org&#x2F;" rel="nofollow">https:&#x2F;&#x2F;www.nand2tetris.org&#x2F;</a>
评论 #42545992 未加载
purple-leafy5 months ago
Open for contribution, its very simple code, and adding some testing logic would really help! I&#x27;m happy to guide and work with anyone on how to do this :)<p>A really simple way to start contributing would be to write some truth table tests for the nand_gate, then the other gates too.<p>EG: For the NAND gate, it would be as simple as the following (pseudocode):<p><pre><code> &#x2F;* TRUTH TABLE * | a | b | out | * --------------- * | 0 | 0 | 1 | * | 0 | 1 | 1 | * | 1 | 0 | 1 | * | 1 | 1 | 0 | *&#x2F; &#x2F;&#x2F; Define gate Nand nand_instance; &#x2F;&#x2F; Set inputs to either 0 or 1 nand_instance.input.a = 0; nand_instance.input.b = 0; &#x2F;&#x2F; Process logic nand_gate(&amp;nand_instance); &#x2F;&#x2F; Check output is correct if (nand_instance.output.out == 1) { &#x2F;&#x2F; PASSED } else { &#x2F;&#x2F; FAILED } </code></pre> Oh and for anyone who looks at this sort of low level project (hardware etc) and feels its outside their ability, its not!<p>I say this because thats how I have always felt seeing such projects.<p>Its actually very, very simple code to understand and start yourself, and working through the NandToTetris course, you could probably do better than my code within a few days.<p>You could write such an emulator in any programming language
pipes5 months ago
I have worked through this book. Well 11 out of the 12 chapters. I learn so much from it.<p>As an extra exercise for the reader, it suggests connecting it to the internet somehow. I&#x27;ve no idea how I would do this and I really wish they&#x27;d add it as a proper chapter.
signa115 months ago
are there plans to add gate level propagation delay as well ? that is one thing that i find missing, and would push things closer to reality perhaps ?<p>fwiw, i did the nand2tetris from coursera, and it is indeed great fun.
评论 #42546608 未加载