From 16fa19d0346b024877ceaaadb5049a7a02233a4d Mon Sep 17 00:00:00 2001 From: Felix Martin Date: Sun, 15 Nov 2020 14:38:56 -0500 Subject: [PATCH] Write documentation for first part of course --- .gitignore | 1 + README.md | 92 ++++++++++++++++++++++++++++++++++++++- gifs/vim_hack_syntax.png | Bin 0 -> 43399 bytes projects/06/assembler.py | 34 ++++++++------- vim/hackasm.vim | 77 ++++++++++++++++++++++++++++++++ 5 files changed, 186 insertions(+), 18 deletions(-) create mode 100644 gifs/vim_hack_syntax.png create mode 100644 vim/hackasm.vim diff --git a/.gitignore b/.gitignore index 6774fd6..40f21f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # ---> N2T tools/bin/Hardware Simulator.dat +tools/bin/CPU Emulator.dat # ---> Python # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/README.md b/README.md index eb9500e..2898952 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,94 @@ # N2T -Nand to Tetris solutions building a general-purpose computer from first -principles. +This repository contains my solutions for Nand to Tetris. +[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. ![Create HDL for 64-bit RAM in Vim](gifs/vim_ram64.gif) + +## 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. + +![Hack Asm syntax highlighting in Vim](./gifs/vim_hack_syntax.png) + +## 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. + + diff --git a/gifs/vim_hack_syntax.png b/gifs/vim_hack_syntax.png new file mode 100644 index 0000000000000000000000000000000000000000..35e8cbdd3d70034e1e9f731c3141be94a99afea0 GIT binary patch literal 43399 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

4dA`i3FFnceXXhwS%?9g#83-7fqddv5z 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 N002ovPDHLkV1mo0ZMpye literal 0 HcmV?d00001 diff --git a/projects/06/assembler.py b/projects/06/assembler.py index 6379838..2f25849 100755 --- a/projects/06/assembler.py +++ b/projects/06/assembler.py @@ -4,7 +4,6 @@ import sys import re - def preprocess(lines): lines = remove_whitespaces(lines) lines = replace_symbols(lines) @@ -36,7 +35,7 @@ def replace_symbols(lines): "R15": "15", "SCREEN": "16384", "KBD": "24576", - } + } # Find all labels, remove them, and add them to symbol table. address = 0 @@ -116,6 +115,7 @@ def assemble_c_instruction(line): ins_str = "111" + comp_lookup(comp) + dest_lookup(dest) + jump_lookup(jump) return ins_str + def comp_lookup(comp_str): return { "0": "0101010", @@ -176,26 +176,28 @@ def jump_lookup(jump_str): if __name__ == "__main__": - try: - hack_asm_file = sys.argv[1] + if not sys.argv[1:]: + sys.exit("Call: ./assembler.py ") + + for hack_asm_file in sys.argv[1:]: if not hack_asm_file.endswith(".asm"): sys.exit("Hack asm file must have a .asm file ending.") hack_asm_ns_file = hack_asm_file.replace(".asm", ".nosymbol.asm") hack_bin_file = hack_asm_file.replace(".asm", ".hack") - except IndexError: - sys.exit("Call: ./assembler.py ") - with open(hack_asm_file, 'r') as f: - assembly_lines = f.readlines() + with open(hack_asm_file, 'r') as f: + assembly_lines = f.readlines() - preprocessed_lines = preprocess(assembly_lines) - binary_lines = assemble(preprocessed_lines) + preprocessed_lines = preprocess(assembly_lines) + binary_lines = assemble(preprocessed_lines) - with open(hack_asm_ns_file, 'w') as f: - for line in preprocessed_lines: - f.write(line + "\n") + with open(hack_asm_ns_file, 'w') as f: + for line in preprocessed_lines: + f.write(line + "\n") - with open(hack_bin_file, 'w') as f: - for line in binary_lines: - f.write(line + "\n") + with open(hack_bin_file, 'w') as f: + for line in binary_lines: + f.write(line + "\n") + + print(f"{hack_asm_file} -> {hack_bin_file}") diff --git a/vim/hackasm.vim b/vim/hackasm.vim new file mode 100644 index 0000000..7c639e8 --- /dev/null +++ b/vim/hackasm.vim @@ -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"