Write documentation for first part of course
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
# ---> N2T
|
# ---> N2T
|
||||||
tools/bin/Hardware Simulator.dat
|
tools/bin/Hardware Simulator.dat
|
||||||
|
tools/bin/CPU Emulator.dat
|
||||||
# ---> Python
|
# ---> Python
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
|||||||
92
README.md
92
README.md
@@ -1,6 +1,94 @@
|
|||||||
# N2T
|
# N2T
|
||||||
|
|
||||||
Nand to Tetris solutions building a general-purpose computer from first
|
This repository contains my solutions for Nand to Tetris.
|
||||||
principles.
|
[N2T](https://www.nand2tetris.org/) is a course that teaches how
|
||||||
|
to build a fully functioning general-purpose computer from first
|
||||||
|
principles. It starts with the hardware design, including its own
|
||||||
|
instruction set for which you program an assembler. You finish the
|
||||||
|
course with a two-step compiler that translates a high-level programming
|
||||||
|
language called Jack into assembly code.
|
||||||
|
|
||||||
|
Even though I have a solid understanding of microcontrollers based on my
|
||||||
|
experience in the embedded industry, I still learned a couple of new concepts
|
||||||
|
and improved my general knowledge of how a computer works. Going through all
|
||||||
|
the steps to build a computer starting from first principles helps to facilitate
|
||||||
|
a deep understanding. If you are a person that needs hands-on examples to grasp
|
||||||
|
a concept, you will love this course as much as I do.
|
||||||
|
|
||||||
|
In the following, I explain how to use my solutions, mainly if I want to revisit
|
||||||
|
this class later. If you haven't done the course yet, you should not look at the
|
||||||
|
answers, but you can try to play the game I wrote in the Jack programming
|
||||||
|
language. It is not a game but a 1D cellular automaton simulator.
|
||||||
|
|
||||||
|
## Project 1: Boolean Logic
|
||||||
|
|
||||||
|
In this project, we start building the basic gates required for the computer.
|
||||||
|
Our basic building block is a Nand gate from which we make 15 additional gates.
|
||||||
|
We implement the gates in a simplified hardware description language (HDL). To
|
||||||
|
test the HDL files, run `./tools/HardwareSimulator.sh` and open one of the test
|
||||||
|
scripts located in `./projects/01`.
|
||||||
|
|
||||||
|
## Project 2: Boolean Arithmetic
|
||||||
|
|
||||||
|
Based on the previous projects' basic gates, we build arithmetic chips:
|
||||||
|
a half-adder, a full adder, a 16-bit adder, and a 16-bit incrementer
|
||||||
|
based on the simple adders. Finally, we create the ALU (arithmetic-logic
|
||||||
|
unit), which is the heart of the CPU that we make in the later projects.
|
||||||
|
The ALU takes two 16-bit inputs and computes an output depending on a
|
||||||
|
couple of control-bits' status. To test the arithmetic chips, use the
|
||||||
|
hardware simulator equally to the first project.
|
||||||
|
|
||||||
|
## Project 3: Memory
|
||||||
|
|
||||||
|
Till this point, all gates are stateless. To build a computer, we need
|
||||||
|
memory. For this purpose, the course introduces a DFF (data flip-flop).
|
||||||
|
We can create a one-bit register and build up from there to a 16k chip
|
||||||
|
with the DFF.
|
||||||
|
|
||||||
|
It found it rewarding to build memory from first principles, but even more
|
||||||
|
rewarding was how easy Vim makes it to write the HDL code for these chips. The
|
||||||
|
following picture shows how I create a 64-bit register from 8-bit registers in a
|
||||||
|
matter of seconds.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## Project 4: Machine Language Programming
|
||||||
|
|
||||||
|
In project 4, we get familiar with the Hack machine language - our
|
||||||
|
computer's assembly language. We write two basic projects: fill the
|
||||||
|
screen when the user presses a button, and a second one that
|
||||||
|
multiplicates two input arguments. To try the scripts, start the CPU
|
||||||
|
emulator by executing `./tools/CPUEmulator.sh`. You can then open the
|
||||||
|
script located in `./projects/04/fill` or `./projects/04/mult`.
|
||||||
|
|
||||||
|
I have created a Vim syntax file for the Hack assembly language. Copy the file
|
||||||
|
`hackasm.vim` from the vim directory into your Vim installation's syntax
|
||||||
|
directory. You can then set the filetype to hackasm by running `:set ft=hackasm`
|
||||||
|
from within Vim, and you should see highlighting as shown on the following
|
||||||
|
screenshot.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Project 5: Computer Architecture
|
||||||
|
|
||||||
|
In this project, we assemble all prior building blocks into the main memory,
|
||||||
|
CPU, and finally into the full hack computer. There are test scripts similar to
|
||||||
|
project one to three to validate that the computer works as designed. Seeing it
|
||||||
|
all come together is incredibly rewarding. Even if you stop the course at this
|
||||||
|
point, you have developed a great intuition of how a computer works.
|
||||||
|
|
||||||
|
## Project 6: The Assembler
|
||||||
|
|
||||||
|
With the computer working, we now need a way to assemble the hackasm code into
|
||||||
|
machine instructions. The purpose of this project is to build the assembler in
|
||||||
|
the programming language of our choice.
|
||||||
|
|
||||||
|
My Python version has 203 lines of code and relies on Python 3.8 features. We
|
||||||
|
can test the assembler by changing the directory to `./projects/06` and then
|
||||||
|
running `python assembler.py pong/*.asm`. Note that my assembler can only
|
||||||
|
translate individual asm-files and does not search a directory.
|
||||||
|
|
||||||
|
Load the resulting hack file into the CPU emulator to verify that the
|
||||||
|
assembler works correctly.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
BIN
gifs/vim_hack_syntax.png
Normal file
BIN
gifs/vim_hack_syntax.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@@ -4,7 +4,6 @@ import sys
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def preprocess(lines):
|
def preprocess(lines):
|
||||||
lines = remove_whitespaces(lines)
|
lines = remove_whitespaces(lines)
|
||||||
lines = replace_symbols(lines)
|
lines = replace_symbols(lines)
|
||||||
@@ -36,7 +35,7 @@ def replace_symbols(lines):
|
|||||||
"R15": "15",
|
"R15": "15",
|
||||||
"SCREEN": "16384",
|
"SCREEN": "16384",
|
||||||
"KBD": "24576",
|
"KBD": "24576",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Find all labels, remove them, and add them to symbol table.
|
# Find all labels, remove them, and add them to symbol table.
|
||||||
address = 0
|
address = 0
|
||||||
@@ -116,6 +115,7 @@ def assemble_c_instruction(line):
|
|||||||
ins_str = "111" + comp_lookup(comp) + dest_lookup(dest) + jump_lookup(jump)
|
ins_str = "111" + comp_lookup(comp) + dest_lookup(dest) + jump_lookup(jump)
|
||||||
return ins_str
|
return ins_str
|
||||||
|
|
||||||
|
|
||||||
def comp_lookup(comp_str):
|
def comp_lookup(comp_str):
|
||||||
return {
|
return {
|
||||||
"0": "0101010",
|
"0": "0101010",
|
||||||
@@ -176,26 +176,28 @@ def jump_lookup(jump_str):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
try:
|
if not sys.argv[1:]:
|
||||||
hack_asm_file = sys.argv[1]
|
sys.exit("Call: ./assembler.py <hack_asm_file>")
|
||||||
|
|
||||||
|
for hack_asm_file in sys.argv[1:]:
|
||||||
if not hack_asm_file.endswith(".asm"):
|
if not hack_asm_file.endswith(".asm"):
|
||||||
sys.exit("Hack asm file must have a .asm file ending.")
|
sys.exit("Hack asm file must have a .asm file ending.")
|
||||||
hack_asm_ns_file = hack_asm_file.replace(".asm", ".nosymbol.asm")
|
hack_asm_ns_file = hack_asm_file.replace(".asm", ".nosymbol.asm")
|
||||||
hack_bin_file = hack_asm_file.replace(".asm", ".hack")
|
hack_bin_file = hack_asm_file.replace(".asm", ".hack")
|
||||||
except IndexError:
|
|
||||||
sys.exit("Call: ./assembler.py <hack_asm_file>")
|
|
||||||
|
|
||||||
with open(hack_asm_file, 'r') as f:
|
with open(hack_asm_file, 'r') as f:
|
||||||
assembly_lines = f.readlines()
|
assembly_lines = f.readlines()
|
||||||
|
|
||||||
preprocessed_lines = preprocess(assembly_lines)
|
preprocessed_lines = preprocess(assembly_lines)
|
||||||
binary_lines = assemble(preprocessed_lines)
|
binary_lines = assemble(preprocessed_lines)
|
||||||
|
|
||||||
with open(hack_asm_ns_file, 'w') as f:
|
with open(hack_asm_ns_file, 'w') as f:
|
||||||
for line in preprocessed_lines:
|
for line in preprocessed_lines:
|
||||||
f.write(line + "\n")
|
f.write(line + "\n")
|
||||||
|
|
||||||
with open(hack_bin_file, 'w') as f:
|
with open(hack_bin_file, 'w') as f:
|
||||||
for line in binary_lines:
|
for line in binary_lines:
|
||||||
f.write(line + "\n")
|
f.write(line + "\n")
|
||||||
|
|
||||||
|
print(f"{hack_asm_file} -> {hack_bin_file}")
|
||||||
|
|
||||||
|
|||||||
77
vim/hackasm.vim
Normal file
77
vim/hackasm.vim
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
if exists("b:current_syntax")
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Computation instructions
|
||||||
|
syntax match hackasmFunction "\v0"
|
||||||
|
syntax match hackasmFunction "\v1"
|
||||||
|
syntax match hackasmFunction "\vM"
|
||||||
|
syntax match hackasmFunction "\vD"
|
||||||
|
syntax match hackasmFunction "\vA"
|
||||||
|
syntax match hackasmFunction "\v-"
|
||||||
|
syntax match hackasmFunction "\v\+"
|
||||||
|
syntax match hackasmFunction "\v\|"
|
||||||
|
syntax match hackasmFunction "\v!"
|
||||||
|
syntax match hackasmFunction "\v\&"
|
||||||
|
|
||||||
|
" Jump instructions
|
||||||
|
syntax match hackasmConditional "\v;null"
|
||||||
|
syntax match hackasmConditional "\v;JGT"
|
||||||
|
syntax match hackasmConditional "\v;JEQ"
|
||||||
|
syntax match hackasmConditional "\v;JGE"
|
||||||
|
syntax match hackasmConditional "\v;JLT"
|
||||||
|
syntax match hackasmConditional "\v;JNE"
|
||||||
|
syntax match hackasmConditional "\v;JLE"
|
||||||
|
syntax match hackasmConditional "\v;JMP"
|
||||||
|
|
||||||
|
" Assignment instructions
|
||||||
|
syntax match hackasmOperator "\vnull\s*\="
|
||||||
|
syntax match hackasmOperator "\vM\s*\="
|
||||||
|
syntax match hackasmOperator "\vD\s*\="
|
||||||
|
syntax match hackasmOperator "\vMD\s*\="
|
||||||
|
syntax match hackasmOperator "\vA\s*\="
|
||||||
|
syntax match hackasmOperator "\vAM\s*\="
|
||||||
|
syntax match hackasmOperator "\vAD\s*\="
|
||||||
|
syntax match hackasmOperator "\vAMD\s*\="
|
||||||
|
|
||||||
|
syntax match hackasmNumber "\v\@[0-9]+"
|
||||||
|
|
||||||
|
syntax match hackasmIdentifier "\v\@[a-z][a-z_]*"
|
||||||
|
|
||||||
|
syntax match hackasmLabel "\v\@[A-Z][A-Za-z0-9:._]*"
|
||||||
|
syntax match hackasmLabel "\v\([A-Z][A-Za-z0-9:._]*\)"
|
||||||
|
|
||||||
|
syntax match hackasmError "\v\t+"
|
||||||
|
syntax match hackasmError "\v\([A-Z].*[^A-Za-z0-9:._].*\)"
|
||||||
|
|
||||||
|
syntax match hackasmError "\v\@[A-Z]+[^A-Za-z0-9:._].*"
|
||||||
|
syntax match hackasmError "\v\@[a-z]+[^a-za-z0-9:._].*"
|
||||||
|
|
||||||
|
" Computation instruction errors
|
||||||
|
syntax match hackasmError "\vM\s*\+\s*D"
|
||||||
|
syntax match hackasmError "\vA\s*\+\s*D"
|
||||||
|
" TODO Missing invalid computation instructions
|
||||||
|
|
||||||
|
syntax match hackasmKeyword "\v\@R[0-9]"
|
||||||
|
syntax match hackasmKeyword "\v\@R1[0-5]"
|
||||||
|
syntax match hackasmKeyword "\v\@SCREEN"
|
||||||
|
syntax match hackasmKeyword "\v\@KBD"
|
||||||
|
syntax match hackasmKeyword "\v\@SP"
|
||||||
|
syntax match hackasmKeyword "\v\@LCL"
|
||||||
|
syntax match hackasmKeyword "\v\@ARG"
|
||||||
|
syntax match hackasmKeyword "\v\@THIS"
|
||||||
|
syntax match hackasmKeyword "\v\@THAT"
|
||||||
|
|
||||||
|
syntax match hackasmComment "\v//.*$"
|
||||||
|
|
||||||
|
highlight link hackasmError Error
|
||||||
|
highlight link hackasmComment Comment
|
||||||
|
highlight link hackasmFunction Function
|
||||||
|
highlight link hackasmConditional Conditional
|
||||||
|
highlight link hackasmOperator Operator
|
||||||
|
highlight link hackasmKeyword Keyword
|
||||||
|
highlight link hackasmNumber Number
|
||||||
|
highlight link hackasmIdentifier Identifier
|
||||||
|
highlight link hackasmLabel Label
|
||||||
|
|
||||||
|
let b:current_syntax = "hackasm"
|
||||||
Reference in New Issue
Block a user