* OPEN TO CONTRIBUTIONS *<p>NandToTetris is a course which has you build a full computer from:<p>Logic gates -> Chips -> RAM -> CPU -> Computer
-> Assembler -> Compiler -> OS -> Tetris<p>All this is done via software defined hardware emulation.<p>I'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't use certain programming utilities: boolean logic
operators, bitwise logic operators etc
Instead we leverage the gates/chips to implement such logic
I build more and more base chips from the NAND gate
- Simple gates: OR, AND, NOT, XOR (5 total "gates")
- Simple chips: DMux, Mux (11 so far, "combinatorial"
chips
- 16 bit variants
</code></pre>
For comparison, most emulator projects start right at the CPU level and don'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 / Instruction set and implement that logic directly in code.<p>More straight forward, way fewer lines of code - but you skip all the gates/chips fun.<p>------<p>Confused? Heres example code for my NAND gate:<p><pre><code> void nand_gate(Nand *nand)
{
nand->output.out = !(nand->input.a & nand->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 ->input.in,
.input.b = not ->input.in,
};
nand_gate(&nand);
not ->output.out = nand.output.out;
}
</code></pre>
Then OR / AND / XOR / MUX / DMUX ..... and their 16 bit versions.<p>Heres a more complex chip, a 16bit Mux-8-way chip<p><pre><code> /*
* 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
*/
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;
// Mux a
memcpy(mux16_4_way_chip_a.input.sel, mux16_8_way- >input.sel, sizeof(mux16_4_way_chip_a.input.sel));
memcpy(mux16_4_way_chip_a.input.a, mux16_8_way->input.a, sizeof(mux16_4_way_chip_a.input.a));
memcpy(mux16_4_way_chip_a.input.b, mux16_8_way->input.b, sizeof(mux16_4_way_chip_a.input.b));
memcpy(mux16_4_way_chip_a.input.c, mux16_8_way->input.c, sizeof(mux16_4_way_chip_a.input.c));
memcpy(mux16_4_way_chip_a.input.d, mux16_8_way->input.d, sizeof(mux16_4_way_chip_a.input.d));
mux16_4_way_chip(&mux16_4_way_chip_a);
// Mux b
memcpy(mux16_4_way_chip_b.input.sel, mux16_8_way->input.sel, sizeof(mux16_4_way_chip_b.input.sel));
memcpy(mux16_4_way_chip_b.input.a, mux16_8_way->input.e, sizeof(mux16_4_way_chip_b.input.a));
memcpy(mux16_4_way_chip_b.input.b, mux16_8_way->input.f, sizeof(mux16_4_way_chip_b.input.b));
memcpy(mux16_4_way_chip_b.input.c, mux16_8_way->input.g, sizeof(mux16_4_way_chip_b.input.c));
memcpy(mux16_4_way_chip_b.input.d, mux16_8_way->input.h, sizeof(mux16_4_way_chip_b.input.d));
mux16_4_way_chip(&mux16_4_way_chip_b);
// Mux c
mux16_chip_c.input.sel = mux16_8_way->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(&mux16_chip_c);
memcpy(mux16_8_way->output.out, mux16_chip_c.output.out, sizeof(mux16_8_way->output.out));
}
</code></pre>
-----<p>Progress: I have only started this project yesterday, so have completed 1 out of 7 hardware projects so far