Hack CPU emulator: from NAND gates to a running program
A whole computer can be built from one kind of logic gate. Here is the Hack computer from the nand2tetris course: write assembly, watch the assembler turn it into 16-bit machine words, and step the CPU through it while registers, memory and a 512 × 256 screen change. Below it, the ALU at the heart of the CPU, evaluated one NAND gate at a time.
| Addr | Instruction | Machine word |
|---|---|---|
| 0 | @R2 | 0000000000000010 |
| 1 | M=0 | 1110101010001000 |
| 2 | @R1 | 0000000000000001 |
| 3 | D=M | 1111110000010000 |
| 4 | @n | 0000000000010000 |
| 5 | M=D | 1110001100001000 |
| 6 | @n | 0000000000010000 |
| 7 | D=M | 1111110000010000 |
| 8 | @END | 0000000000010010 |
| 9 | D;JLE | 1110001100000110 |
| 10 | @R0 | 0000000000000000 |
| 11 | D=M | 1111110000010000 |
| 12 | @R2 | 0000000000000010 |
| 13 | M=D+M | 1111000010001000 |
| 14 | @n | 0000000000010000 |
| 15 | M=M-1 | 1111110010001000 |
| 16 | @LOOP | 0000000000000110 |
| 17 | 0;JMP | 1110101010000111 |
| 18 | @END | 0000000000010010 |
| 19 | 0;JMP | 1110101010000111 |
- A
- 0
- D
- 0
- M = RAM[A]
- 0
- PC
- 0
Next: @R2
| RAM[0] SP / R0 | 0 |
| RAM[1] LCL / R1 | 0 |
| RAM[2] ARG / R2 | 0 |
| RAM[3] THIS / R3 | 0 |
| RAM[4] THAT / R4 | 0 |
| RAM[5] R5 | 0 |
| RAM[16] n | 0 |
out = 7 (0000000000000111), zr = 0, ng = 0. Computed through 654 NAND gates.
How to use the emulator
Pick a program or write your own in Hack assembly; the listing on the left shows each instruction's address and 16-bit machine word as you type. Set the inputs in RAM[0] and RAM[1], then Step one instruction at a time or Run. The CPU panel shows the A and D registers, the memory word A points at, and the program counter; the RAM table highlights the last word written. Programs that write to addresses from 16384 draw on the screen. Copy machine code gives the assembled program in the .hack text format, one binary word per line.
Everything from NAND
A NAND gate outputs 0 only when both inputs are 1. It is functionally complete: any Boolean function can be built from NANDs alone, so in principle a whole processor can be too. That is the premise of The Elements of Computing Systems by Noam Nisan and Shimon Schocken, whose nand2tetris course builds a computer called Hack from a NAND gate upward. The constructions used on this page, counted by evaluating them, take:
| Chip | NAND gates |
|---|---|
| Not | 1 |
| And | 2 |
| Or | 3 |
| Xor | 4 |
| Mux | 4 |
| Half adder | 5 |
| Full adder | 9 |
| 16-bit adder | 144 |
| 16-bit Hack ALU | 654 |
The counts follow from the constructions: NOT is one NAND with its inputs joined, AND is NAND then NOT, OR is a NAND of two NOTs, and a full adder takes the classic nine NANDs. A 16-bit adder chains sixteen full adders so that each carry ripples into the next, which is why its delay grows with the word size. Real chip designs use other gate types too and optimise across chip boundaries, so these are counts for this construction, not for any product.
The Hack ALU
The Hack ALU computes a function of two 16-bit inputs, x and y, chosen by six control bits applied in order: zx sets x to 0, nx inverts x bitwise, zy and ny do the same to y, f selects x + y or x & y, and no inverts the result. Two flags report whether the output is zero (zr) or negative (ng). These six bits produce 18 useful functions, including 0, 1, −1, x, −x, x + 1, x − y and x | y; x − y, for example, is !(!x + y), since in two's complement !x = −x − 1. The explorer above evaluates the ALU gate by gate through 654 NANDs, and its results are checked against ordinary arithmetic for every function.
The Hack machine language
Hack has two instructions. The A-instruction @value loads a 15-bit constant into A; its word starts with 0. The C-instruction dest=comp;jump starts with 111, followed by one bit choosing whether the ALU's y input is A or M = RAM[A], the six ALU control bits, three dest bits (A, D, M) and three jump bits (less than, equal to, greater than zero). So @17, D=D+M, M=D and 0;JMP assemble to:
0000000000010001 1111000010010000 1110001100001000 1110101010000111
Because A both holds addresses and selects M, most work is a pair: @x to point at a variable, then a C-instruction to read or write it. A jump goes to the address in A, so @LOOP comes before 0;JMP.
The assembler
The assembler makes two passes. The first walks the program counting instructions and records each label (LOOP) as the address of the instruction after it. The second translates each line: C-instructions by table lookup, and A-instructions by number, predefined symbol (R0 to R15, SP, LCL, ARG, THIS, THAT, SCREEN = 16384, KBD = 24576), label, or, if none of those, a new variable placed at the next free address from 16. In the multiplication program, n is such a variable and lands at RAM[16].
The CPU cycle
Every clock cycle the CPU fetches the instruction at the program counter and executes it: an A-instruction loads A; a C-instruction feeds D and A or M through the ALU, writes the result to the chosen destinations, and sets the PC either to A (if the jump condition holds) or to PC + 1. Writes to M and the jump use A's value from the start of the instruction. Counting instructions shows what a loop costs: adding two numbers takes 6 instructions, multiplying 6 by 7 by repeated addition 94, and summing 1 to 100 takes 1,410 to reach 5,050. The cost of the last two grows with their input, which is why real CPUs have hardware multipliers.
Memory and the screen
Hack has no I/O instructions. The screen and keyboard are memory-mapped: RAM from 16384 to 24575 is the screen, 256 rows of 32 words, one bit per pixel, with the least significant bit of each word the leftmost pixel; RAM[24576] holds the code of the key being pressed. Drawing is writing memory, so the "Draw on the screen" program writes −1 (sixteen 1 bits) to one word per row, adding 32 to the address to move down a row.
NAND to CPU questions and answers
Why can every computer be built from NAND gates?
NAND is functionally complete: NOT is a NAND with both inputs tied together, AND is a NAND followed by a NOT, and OR is a NAND of two NOTs, and every Boolean function can be written with AND, OR and NOT. The constructions on this page use 1, 2, 3 and 4 NAND gates for NOT, AND, OR and XOR, and 654 for the whole 16-bit ALU.
What is the Hack computer?
The 16-bit computer students build in "The Elements of Computing Systems" by Noam Nisan and Shimon Schocken, the nand2tetris course. It has two registers, A and D, a selected memory register M = RAM[A], a 32K instruction ROM, data RAM with a memory-mapped 512 × 256 black-and-white screen and keyboard, and just two instruction types.
What are A-instructions and C-instructions?
@value is an A-instruction: it loads a 15-bit constant or a symbol's address into A, and its machine word starts with 0. dest=comp;jump is a C-instruction: it computes one of 28 ALU functions of D and A or M, stores the result in any of A, D and M, and optionally jumps to the address in A if the result is negative, zero or positive as the jump field asks. Its word starts with 111.
How does the Hack assembler handle symbols?
In two passes. The first records each label (XXX) as the address of the next instruction. The second translates instructions; an @symbol that is neither predefined (R0 to R15, SP, LCL, ARG, THIS, THAT, SCREEN, KBD) nor a label becomes a new variable, allocated in RAM from address 16 upward.
How does a Hack program stop?
It does not: the CPU fetches an instruction every cycle forever. By convention a program ends with an infinite loop, (END) @END 0;JMP, and this emulator stops when it reaches one.
How does the Hack screen work?
The screen is memory-mapped: RAM addresses 16384 to 24575 hold 256 rows of 32 sixteen-bit words, one bit per pixel, with the least significant bit of each word the leftmost pixel. Writing -1 (all ones) to address 16384 turns the first 16 pixels of the top row black.