mirror of
https://github.com/avinal/The-VM-to-HACK-Translator.git
synced 2026-01-11 23:58:32 +05:30
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57dfa048cf | ||
|
|
df2c013ab3 | ||
|
|
07a7f480a8 | ||
|
|
56b35b12ca | ||
|
|
a11558120b | ||
|
|
291b239065 | ||
|
|
4332eae357 | ||
|
|
68748fed47 | ||
|
|
25304b4662 | ||
|
|
4ef8187412 | ||
|
|
b4b19da495 | ||
|
|
30921da66c | ||
|
|
d3e3f8f701 | ||
|
|
5a864e1a78 | ||
|
|
4f577ea7c6 | ||
|
|
5444102bef | ||
|
|
7d85cebda1 | ||
|
|
236fe8e556 | ||
|
|
274bbde1c4 | ||
|
|
c59bd2122c | ||
|
|
1a8a9d38ac |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -35,4 +35,3 @@ VMTranslator
|
||||
*.asm
|
||||
.vscode
|
||||
*.zip
|
||||
|
||||
|
||||
9
Main.cpp
9
Main.cpp
@@ -1,9 +0,0 @@
|
||||
#include "code_writer.hpp"
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
code_writer toassembly(argv[1]);
|
||||
toassembly.write_assembly();
|
||||
return 0;
|
||||
}
|
||||
22
Makefile
22
Makefile
@@ -1,29 +1,25 @@
|
||||
CXX = g++
|
||||
STDVERSION = -std=c++17
|
||||
STDVERSION = -std=c++17 -lstdc++fs
|
||||
WARNINGS = -pedantic -Wall -Wfatal-errors -Wextra -Wno-unused-parameter -Wno-unused-variable
|
||||
|
||||
|
||||
TARGET = VMTranslator
|
||||
OBJ_DIR = build
|
||||
OBJECTS = code_writer.o parser.o vutility.o
|
||||
TEST_OBJS = parser_test.o
|
||||
|
||||
all:$(TARGET)
|
||||
|
||||
$(TARGET): $(OBJECTS)
|
||||
$(CXX) $(WARNINGS) $(STDVERSION) -o $(TARGET) Main.cpp $(OBJECTS)
|
||||
$(CXX) $(WARNINGS) -o $(TARGET) src/Main.cpp $(OBJECTS) $(STDVERSION)
|
||||
|
||||
vutility.o: vutility.cpp vutility.hpp
|
||||
$(CXX) $(WARNINGS) $(STDVERSION) -c vutility.cpp -o vutility.o
|
||||
vutility.o: src/vutility.cpp include/vutility.hpp
|
||||
$(CXX) $(WARNINGS) $(STDVERSION) -c src/vutility.cpp -o vutility.o
|
||||
|
||||
parser.o: parser.cpp parser.hpp
|
||||
$(CXX) $(WARNINGS) $(STDVERSION) -c parser.cpp -o parser.o
|
||||
parser.o: src/parser.cpp include/parser.hpp
|
||||
$(CXX) $(WARNINGS) $(STDVERSION) -c src/parser.cpp -o parser.o
|
||||
|
||||
code_writer.o:code_writer.cpp code_writer.hpp
|
||||
$(CXX) $(STDVERSION) -c code_writer.cpp -o code_writer.o
|
||||
code_writer.o: src/code_writer.cpp include/code_writer.hpp
|
||||
$(CXX) $(WARNINGS) $(STDVERSION) -c src/code_writer.cpp -o code_writer.o
|
||||
|
||||
|
||||
test:
|
||||
$(CXX) $(STDVERSION) tests/*.cpp tests/catch/catch_main.cpp parser.cpp code_writer.cpp -o test
|
||||
|
||||
clean:
|
||||
rm -rf *.o $(TARGET) *.asm
|
||||
63
README.md
63
README.md
@@ -1,2 +1,65 @@
|
||||
# The-VM-to-HACK-Translator
|
||||
Translates Virtual Machine Language into HACK Assembly language.
|
||||
|
||||
**Important: If you are taking this course then I suggest you to try to write the assembler yourself first, it is not very easy and not very tough too. At least not impossible, because I did it 😘**
|
||||
|
||||
## Get Started with the project
|
||||
1. Directory Structure
|
||||
```shell
|
||||
.
|
||||
|_include # Contains headers
|
||||
|_src # Contains Source Code and Main
|
||||
|_Test-Files # Example HACK VM Files
|
||||
```
|
||||
2. Clone this repo
|
||||
```shell
|
||||
git clone https://github.com/avinal/The-VM-to-HACK-Translator.git
|
||||
```
|
||||
3. Build and run, provide only [HACK VM Language](https://www.nand2tetris.org/project08) file. Don't confuse with `.vm` extesion.
|
||||
```shell
|
||||
cd The-VM-to-HACK-Translator
|
||||
make
|
||||
./VMTranslator file.vm
|
||||
```
|
||||
4. Start over after modification
|
||||
```shell
|
||||
make clean
|
||||
```
|
||||
|
||||
|
||||
## Tips
|
||||
In case you want to modify this project, you can use [Boost C++ Libraries](https://www.boost.org/), they can simplify many tasks needed by this project.
|
||||
|
||||
## Some Words
|
||||
This was a nice project and taugh me many things. Given below are some miraculous code snippets that proved to be really useful and are well researched. Hope you will find them useful.
|
||||
|
||||
1. Trim spaces, comments, carrige returns, newline, tab and verticle tabs:
|
||||
```cpp
|
||||
inline std::string trim(std::string str)
|
||||
{
|
||||
if (str.find('/') != std::string::npos) // comments
|
||||
{
|
||||
str.erase(str.find('/'));
|
||||
}
|
||||
str.erase(0, str.find_first_not_of(" \r\t\v\n")); //prefixing
|
||||
str.erase(str.find_last_not_of(" \r\t\v\n") + 1); //surfixing}
|
||||
return str;
|
||||
}
|
||||
```
|
||||
2. Tokenize a string using C++ with respect to a delimiter, space by defalt (string split):
|
||||
```cpp
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
std::vector<std::string> split(std::string str, char delimiter = ' ')
|
||||
{
|
||||
std::vector<std::string> tokens;
|
||||
std::stringstream tokenizer(str);
|
||||
std::string intermediate;
|
||||
while (std::getline(tokenizer, intermediate, delimiter))
|
||||
{
|
||||
tokens.push_back(intermediate);
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
```
|
||||
3. **Fascinating** Using Lambdas with Map to implement a function or function pointers. [See here](https://github.com/avinal/The-VM-to-HACK-Translator/blob/291b239065fc5cc0921b8592dfcd69c6f3022e52/src/code_writer.cpp#L29)
|
||||
22
Test-Files/BasicLoop.vm
Normal file
22
Test-Files/BasicLoop.vm
Normal file
@@ -0,0 +1,22 @@
|
||||
// This file is part of www.nand2tetris.org
|
||||
// and the book "The Elements of Computing Systems"
|
||||
// by Nisan and Schocken, MIT Press.
|
||||
// File name: projects/08/ProgramFlow/BasicLoop/BasicLoop.vm
|
||||
|
||||
// Computes the sum 1 + 2 + ... + argument[0] and pushes the
|
||||
// result onto the stack. Argument[0] is initialized by the test
|
||||
// script before this code starts running.
|
||||
push constant 0
|
||||
pop local 0 // initializes sum = 0
|
||||
label LOOP_START
|
||||
push argument 0
|
||||
push local 0
|
||||
add
|
||||
pop local 0 // sum = sum + counter
|
||||
push argument 0
|
||||
push constant 1
|
||||
sub
|
||||
pop argument 0 // counter--
|
||||
push argument 0
|
||||
if-goto LOOP_START // If counter > 0, goto LOOP_START
|
||||
push local 0
|
||||
2
Test-Files/FibonacciElement/FibonacciElement.cmp
Normal file
2
Test-Files/FibonacciElement/FibonacciElement.cmp
Normal file
@@ -0,0 +1,2 @@
|
||||
| RAM[0] |RAM[261]|
|
||||
| 262 | 3 |
|
||||
18
Test-Files/FibonacciElement/FibonacciElement.tst
Normal file
18
Test-Files/FibonacciElement/FibonacciElement.tst
Normal file
@@ -0,0 +1,18 @@
|
||||
// This file is part of www.nand2tetris.org
|
||||
// and the book "The Elements of Computing Systems"
|
||||
// by Nisan and Schocken, MIT Press.
|
||||
// File name: projects/08/FunctionCalls/FibonacciElement/FibonacciElement.tst
|
||||
|
||||
// FibonacciElement.asm results from translating both Main.vm and Sys.vm into
|
||||
// a single assembly program, stored in the file FibonacciElement.asm.
|
||||
|
||||
load FibonacciElement.asm,
|
||||
output-file FibonacciElement.out,
|
||||
compare-to FibonacciElement.cmp,
|
||||
output-list RAM[0]%D1.6.1 RAM[261]%D1.6.1;
|
||||
|
||||
repeat 6000 {
|
||||
ticktock;
|
||||
}
|
||||
|
||||
output;
|
||||
17
Test-Files/FibonacciElement/FibonacciElementVME.tst
Normal file
17
Test-Files/FibonacciElement/FibonacciElementVME.tst
Normal file
@@ -0,0 +1,17 @@
|
||||
// This file is part of www.nand2tetris.org
|
||||
// and the book "The Elements of Computing Systems"
|
||||
// by Nisan and Schocken, MIT Press.
|
||||
// File name: projects/08/FunctionCalls/FibonacciElement/FibonacciElementVME.tst
|
||||
|
||||
load, // Load all the VM files from the current directory
|
||||
output-file FibonacciElement.out,
|
||||
compare-to FibonacciElement.cmp,
|
||||
output-list RAM[0]%D1.6.1 RAM[261]%D1.6.1;
|
||||
|
||||
set sp 261,
|
||||
|
||||
repeat 110 {
|
||||
vmstep;
|
||||
}
|
||||
|
||||
output;
|
||||
30
Test-Files/FibonacciElement/Main.vm
Normal file
30
Test-Files/FibonacciElement/Main.vm
Normal file
@@ -0,0 +1,30 @@
|
||||
// This file is part of www.nand2tetris.org
|
||||
// and the book "The Elements of Computing Systems"
|
||||
// by Nisan and Schocken, MIT Press.
|
||||
// File name: projects/08/FunctionCalls/FibonacciElement/Main.vm
|
||||
|
||||
// Computes the n'th element of the Fibonacci series, recursively.
|
||||
// n is given in argument[0]. Called by the Sys.init function
|
||||
// (part of the Sys.vm file), which also pushes the argument[0]
|
||||
// parameter before this code starts running.
|
||||
|
||||
function Main.fibonacci 0
|
||||
push argument 0
|
||||
push constant 2
|
||||
lt // checks if n<2
|
||||
if-goto IF_TRUE
|
||||
goto IF_FALSE
|
||||
label IF_TRUE // if n<2, return n
|
||||
push argument 0
|
||||
return
|
||||
label IF_FALSE // if n>=2, returns fib(n-2)+fib(n-1)
|
||||
push argument 0
|
||||
push constant 2
|
||||
sub
|
||||
call Main.fibonacci 1 // computes fib(n-2)
|
||||
push argument 0
|
||||
push constant 1
|
||||
sub
|
||||
call Main.fibonacci 1 // computes fib(n-1)
|
||||
add // returns fib(n-1) + fib(n-2)
|
||||
return
|
||||
16
Test-Files/FibonacciElement/Sys.vm
Normal file
16
Test-Files/FibonacciElement/Sys.vm
Normal file
@@ -0,0 +1,16 @@
|
||||
// This file is part of www.nand2tetris.org
|
||||
// and the book "The Elements of Computing Systems"
|
||||
// by Nisan and Schocken, MIT Press.
|
||||
// File name: projects/08/FunctionCalls/FibonacciElement/Sys.vm
|
||||
|
||||
// Pushes a constant, say n, onto the stack, and calls the Main.fibonacii
|
||||
// function, which computes the n'th element of the Fibonacci series.
|
||||
// Note that by convention, the Sys.init function is called "automatically"
|
||||
// by the bootstrap code.
|
||||
|
||||
|
||||
function Sys.init 0
|
||||
push constant 4
|
||||
call Main.fibonacci 1 // computes the 4'th fibonacci element
|
||||
label WHILE
|
||||
goto WHILE // loops infinitely
|
||||
49
Test-Files/FibonacciSeries.vm
Normal file
49
Test-Files/FibonacciSeries.vm
Normal file
@@ -0,0 +1,49 @@
|
||||
// This file is part of www.nand2tetris.org
|
||||
// and the book "The Elements of Computing Systems"
|
||||
// by Nisan and Schocken, MIT Press.
|
||||
// File name: projects/08/ProgramFlow/FibonacciSeries/FibonacciSeries.vm
|
||||
|
||||
// Puts the first argument[0] elements of the Fibonacci series
|
||||
// in the memory, starting in the address given in argument[1].
|
||||
// Argument[0] and argument[1] are initialized by the test script
|
||||
// before this code starts running.
|
||||
|
||||
push argument 1
|
||||
pop pointer 1 // that = argument[1]
|
||||
|
||||
push constant 0
|
||||
pop that 0 // first element in the series = 0
|
||||
push constant 1
|
||||
pop that 1 // second element in the series = 1
|
||||
|
||||
push argument 0
|
||||
push constant 2
|
||||
sub
|
||||
pop argument 0 // num_of_elements -= 2 (first 2 elements are set)
|
||||
|
||||
label MAIN_LOOP_START
|
||||
|
||||
push argument 0
|
||||
if-goto COMPUTE_ELEMENT // if num_of_elements > 0, goto COMPUTE_ELEMENT
|
||||
goto END_PROGRAM // otherwise, goto END_PROGRAM
|
||||
|
||||
label COMPUTE_ELEMENT
|
||||
|
||||
push that 0
|
||||
push that 1
|
||||
add
|
||||
pop that 2 // that[2] = that[0] + that[1]
|
||||
|
||||
push pointer 1
|
||||
push constant 1
|
||||
add
|
||||
pop pointer 1 // that += 1
|
||||
|
||||
push argument 0
|
||||
push constant 1
|
||||
sub
|
||||
pop argument 0 // num_of_elements--
|
||||
|
||||
goto MAIN_LOOP_START
|
||||
|
||||
label END_PROGRAM
|
||||
2
Test-Files/NestedCall/NestedCall.cmp
Normal file
2
Test-Files/NestedCall/NestedCall.cmp
Normal file
@@ -0,0 +1,2 @@
|
||||
| RAM[0] | RAM[1] | RAM[2] | RAM[3] | RAM[4] | RAM[5] | RAM[6] |
|
||||
| 261 | 261 | 256 | 4000 | 5000 | 135 | 246 |
|
||||
65
Test-Files/NestedCall/NestedCall.tst
Normal file
65
Test-Files/NestedCall/NestedCall.tst
Normal file
@@ -0,0 +1,65 @@
|
||||
// Test file for NestedCall test.
|
||||
|
||||
load NestedCall.asm,
|
||||
output-file NestedCall.out,
|
||||
compare-to NestedCall.cmp,
|
||||
output-list RAM[0]%D1.6.1 RAM[1]%D1.6.1 RAM[2]%D1.6.1 RAM[3]%D1.6.1 RAM[4]%D1.6.1 RAM[5]%D1.6.1 RAM[6]%D1.6.1;
|
||||
|
||||
set RAM[0] 261,
|
||||
set RAM[1] 261,
|
||||
set RAM[2] 256,
|
||||
set RAM[3] -3,
|
||||
set RAM[4] -4,
|
||||
set RAM[5] -1, // test results
|
||||
set RAM[6] -1,
|
||||
set RAM[256] 1234, // fake stack frame from call Sys.init
|
||||
set RAM[257] -1,
|
||||
set RAM[258] -2,
|
||||
set RAM[259] -3,
|
||||
set RAM[260] -4,
|
||||
|
||||
set RAM[261] -1, // Initialize stack to check for local segment
|
||||
set RAM[262] -1, // being cleared to zero.
|
||||
set RAM[263] -1,
|
||||
set RAM[264] -1,
|
||||
set RAM[265] -1,
|
||||
set RAM[266] -1,
|
||||
set RAM[267] -1,
|
||||
set RAM[268] -1,
|
||||
set RAM[269] -1,
|
||||
set RAM[270] -1,
|
||||
set RAM[271] -1,
|
||||
set RAM[272] -1,
|
||||
set RAM[273] -1,
|
||||
set RAM[274] -1,
|
||||
set RAM[275] -1,
|
||||
set RAM[276] -1,
|
||||
set RAM[277] -1,
|
||||
set RAM[278] -1,
|
||||
set RAM[279] -1,
|
||||
set RAM[280] -1,
|
||||
set RAM[281] -1,
|
||||
set RAM[282] -1,
|
||||
set RAM[283] -1,
|
||||
set RAM[284] -1,
|
||||
set RAM[285] -1,
|
||||
set RAM[286] -1,
|
||||
set RAM[287] -1,
|
||||
set RAM[288] -1,
|
||||
set RAM[289] -1,
|
||||
set RAM[290] -1,
|
||||
set RAM[291] -1,
|
||||
set RAM[292] -1,
|
||||
set RAM[293] -1,
|
||||
set RAM[294] -1,
|
||||
set RAM[295] -1,
|
||||
set RAM[296] -1,
|
||||
set RAM[297] -1,
|
||||
set RAM[298] -1,
|
||||
set RAM[299] -1,
|
||||
|
||||
repeat 4000 {
|
||||
ticktock;
|
||||
}
|
||||
|
||||
output;
|
||||
70
Test-Files/NestedCall/NestedCallVME.tst
Normal file
70
Test-Files/NestedCall/NestedCallVME.tst
Normal file
@@ -0,0 +1,70 @@
|
||||
// Test file for NestedCall test.
|
||||
|
||||
load Sys.vm,
|
||||
output-file NestedCall.out,
|
||||
compare-to NestedCall.cmp,
|
||||
output-list RAM[0]%D1.6.1 RAM[1]%D1.6.1 RAM[2]%D1.6.1 RAM[3]%D1.6.1 RAM[4]%D1.6.1 RAM[5]%D1.6.1 RAM[6]%D1.6.1;
|
||||
|
||||
set RAM[0] 261,
|
||||
set RAM[1] 261,
|
||||
set RAM[2] 256,
|
||||
set RAM[3] -3,
|
||||
set RAM[4] -4,
|
||||
set RAM[5] -1, // test results
|
||||
set RAM[6] -1,
|
||||
set RAM[256] 1234, // fake stack frame from call Sys.init
|
||||
set RAM[257] -1,
|
||||
set RAM[258] -2,
|
||||
set RAM[259] -3,
|
||||
set RAM[260] -4,
|
||||
|
||||
set RAM[261] -1, // Initialize stack to check for local segment
|
||||
set RAM[262] -1, // being cleared to zero.
|
||||
set RAM[263] -1,
|
||||
set RAM[264] -1,
|
||||
set RAM[265] -1,
|
||||
set RAM[266] -1,
|
||||
set RAM[267] -1,
|
||||
set RAM[268] -1,
|
||||
set RAM[269] -1,
|
||||
set RAM[270] -1,
|
||||
set RAM[271] -1,
|
||||
set RAM[272] -1,
|
||||
set RAM[273] -1,
|
||||
set RAM[274] -1,
|
||||
set RAM[275] -1,
|
||||
set RAM[276] -1,
|
||||
set RAM[277] -1,
|
||||
set RAM[278] -1,
|
||||
set RAM[279] -1,
|
||||
set RAM[280] -1,
|
||||
set RAM[281] -1,
|
||||
set RAM[282] -1,
|
||||
set RAM[283] -1,
|
||||
set RAM[284] -1,
|
||||
set RAM[285] -1,
|
||||
set RAM[286] -1,
|
||||
set RAM[287] -1,
|
||||
set RAM[288] -1,
|
||||
set RAM[289] -1,
|
||||
set RAM[290] -1,
|
||||
set RAM[291] -1,
|
||||
set RAM[292] -1,
|
||||
set RAM[293] -1,
|
||||
set RAM[294] -1,
|
||||
set RAM[295] -1,
|
||||
set RAM[296] -1,
|
||||
set RAM[297] -1,
|
||||
set RAM[298] -1,
|
||||
set RAM[299] -1,
|
||||
|
||||
set sp 261,
|
||||
set local 261,
|
||||
set argument 256,
|
||||
set this 3000,
|
||||
set that 4000;
|
||||
|
||||
repeat 50 {
|
||||
vmstep;
|
||||
}
|
||||
output;
|
||||
63
Test-Files/NestedCall/Sys.vm
Normal file
63
Test-Files/NestedCall/Sys.vm
Normal file
@@ -0,0 +1,63 @@
|
||||
// Sys.vm for NestedCall test.
|
||||
|
||||
// Sys.init()
|
||||
//
|
||||
// Calls Sys.main() and stores return value in temp 1.
|
||||
// Does not return. (Enters infinite loop.)
|
||||
|
||||
function Sys.init 0
|
||||
push constant 4000 // test THIS and THAT context save
|
||||
pop pointer 0
|
||||
push constant 5000
|
||||
pop pointer 1
|
||||
call Sys.main 0
|
||||
pop temp 1
|
||||
label LOOP
|
||||
goto LOOP
|
||||
|
||||
// Sys.main()
|
||||
//
|
||||
// Sets locals 1, 2 and 3, leaving locals 0 and 4 unchanged to test
|
||||
// default local initialization to 0. (RAM set to -1 by test setup.)
|
||||
// Calls Sys.add12(123) and stores return value (135) in temp 0.
|
||||
// Returns local 0 + local 1 + local 2 + local 3 + local 4 (456) to confirm
|
||||
// that locals were not mangled by function call.
|
||||
|
||||
function Sys.main 5
|
||||
push constant 4001
|
||||
pop pointer 0
|
||||
push constant 5001
|
||||
pop pointer 1
|
||||
push constant 200
|
||||
pop local 1
|
||||
push constant 40
|
||||
pop local 2
|
||||
push constant 6
|
||||
pop local 3
|
||||
push constant 123
|
||||
call Sys.add12 1
|
||||
pop temp 0
|
||||
push local 0
|
||||
push local 1
|
||||
push local 2
|
||||
push local 3
|
||||
push local 4
|
||||
add
|
||||
add
|
||||
add
|
||||
add
|
||||
return
|
||||
|
||||
// Sys.add12(int n)
|
||||
//
|
||||
// Returns n+12.
|
||||
|
||||
function Sys.add12 0
|
||||
push constant 4002
|
||||
pop pointer 0
|
||||
push constant 5002
|
||||
pop pointer 1
|
||||
push argument 0
|
||||
push constant 12
|
||||
add
|
||||
return
|
||||
2
Test-Files/SimpleFunction/SimpleFunction.cmp
Normal file
2
Test-Files/SimpleFunction/SimpleFunction.cmp
Normal file
@@ -0,0 +1,2 @@
|
||||
| RAM[0] | RAM[1] | RAM[2] | RAM[3] | RAM[4] |RAM[310]|
|
||||
| 311 | 305 | 300 | 3010 | 4010 | 1196 |
|
||||
29
Test-Files/SimpleFunction/SimpleFunction.tst
Normal file
29
Test-Files/SimpleFunction/SimpleFunction.tst
Normal file
@@ -0,0 +1,29 @@
|
||||
// This file is part of www.nand2tetris.org
|
||||
// and the book "The Elements of Computing Systems"
|
||||
// by Nisan and Schocken, MIT Press.
|
||||
// File name: projects/08/FunctionCalls/SimpleFunction/SimpleFunction.tst
|
||||
|
||||
load SimpleFunction.asm,
|
||||
output-file SimpleFunction.out,
|
||||
compare-to SimpleFunction.cmp,
|
||||
output-list RAM[0]%D1.6.1 RAM[1]%D1.6.1 RAM[2]%D1.6.1
|
||||
RAM[3]%D1.6.1 RAM[4]%D1.6.1 RAM[310]%D1.6.1;
|
||||
|
||||
set RAM[0] 317,
|
||||
set RAM[1] 317,
|
||||
set RAM[2] 310,
|
||||
set RAM[3] 3000,
|
||||
set RAM[4] 4000,
|
||||
set RAM[310] 1234,
|
||||
set RAM[311] 37,
|
||||
set RAM[312] 1000,
|
||||
set RAM[313] 305,
|
||||
set RAM[314] 300,
|
||||
set RAM[315] 3010,
|
||||
set RAM[316] 4010,
|
||||
|
||||
repeat 300 {
|
||||
ticktock;
|
||||
}
|
||||
|
||||
output;
|
||||
16
Test-Files/SimpleFunction/SimpleFunction.vm
Normal file
16
Test-Files/SimpleFunction/SimpleFunction.vm
Normal file
@@ -0,0 +1,16 @@
|
||||
// This file is part of www.nand2tetris.org
|
||||
// and the book "The Elements of Computing Systems"
|
||||
// by Nisan and Schocken, MIT Press.
|
||||
// File name: projects/08/FunctionCalls/SimpleFunction/SimpleFunction.vm
|
||||
|
||||
// Performs a simple calculation and returns the result.
|
||||
function SimpleFunction.test 2
|
||||
push local 0
|
||||
push local 1
|
||||
add
|
||||
not
|
||||
push argument 0
|
||||
add
|
||||
push argument 1
|
||||
sub
|
||||
return
|
||||
29
Test-Files/SimpleFunction/SimpleFunctionVME.tst
Normal file
29
Test-Files/SimpleFunction/SimpleFunctionVME.tst
Normal file
@@ -0,0 +1,29 @@
|
||||
// This file is part of www.nand2tetris.org
|
||||
// and the book "The Elements of Computing Systems"
|
||||
// by Nisan and Schocken, MIT Press.
|
||||
// File name: projects/08/FunctionCalls/SimpleFunction/SimpleFunctionVME.tst
|
||||
|
||||
load SimpleFunction.vm,
|
||||
output-file SimpleFunction.out,
|
||||
compare-to SimpleFunction.cmp,
|
||||
output-list RAM[0]%D1.6.1 RAM[1]%D1.6.1 RAM[2]%D1.6.1
|
||||
RAM[3]%D1.6.1 RAM[4]%D1.6.1 RAM[310]%D1.6.1;
|
||||
|
||||
set sp 317,
|
||||
set local 317,
|
||||
set argument 310,
|
||||
set this 3000,
|
||||
set that 4000,
|
||||
set argument[0] 1234,
|
||||
set argument[1] 37,
|
||||
set argument[2] 9,
|
||||
set argument[3] 305,
|
||||
set argument[4] 300,
|
||||
set argument[5] 3010,
|
||||
set argument[6] 4010,
|
||||
|
||||
repeat 10 {
|
||||
vmstep;
|
||||
}
|
||||
|
||||
output;
|
||||
20
Test-Files/StaticsTest/Class1.vm
Normal file
20
Test-Files/StaticsTest/Class1.vm
Normal file
@@ -0,0 +1,20 @@
|
||||
// This file is part of www.nand2tetris.org
|
||||
// and the book "The Elements of Computing Systems"
|
||||
// by Nisan and Schocken, MIT Press.
|
||||
// File name: projects/08/FunctionCalls/StaticsTest/Class1.vm
|
||||
|
||||
// Stores two supplied arguments in static[0] and static[1].
|
||||
function Class1.set 0
|
||||
push argument 0
|
||||
pop static 0
|
||||
push argument 1
|
||||
pop static 1
|
||||
push constant 0
|
||||
return
|
||||
|
||||
// Returns static[0] - static[1].
|
||||
function Class1.get 0
|
||||
push static 0
|
||||
push static 1
|
||||
sub
|
||||
return
|
||||
20
Test-Files/StaticsTest/Class2.vm
Normal file
20
Test-Files/StaticsTest/Class2.vm
Normal file
@@ -0,0 +1,20 @@
|
||||
// This file is part of www.nand2tetris.org
|
||||
// and the book "The Elements of Computing Systems"
|
||||
// by Nisan and Schocken, MIT Press.
|
||||
// File name: projects/08/FunctionCalls/StaticsTest/Class2.vm
|
||||
|
||||
// Stores two supplied arguments in static[0] and static[1].
|
||||
function Class2.set 0
|
||||
push argument 0
|
||||
pop static 0
|
||||
push argument 1
|
||||
pop static 1
|
||||
push constant 0
|
||||
return
|
||||
|
||||
// Returns static[0] - static[1].
|
||||
function Class2.get 0
|
||||
push static 0
|
||||
push static 1
|
||||
sub
|
||||
return
|
||||
2
Test-Files/StaticsTest/StaticsTest.cmp
Normal file
2
Test-Files/StaticsTest/StaticsTest.cmp
Normal file
@@ -0,0 +1,2 @@
|
||||
| RAM[0] |RAM[261]|RAM[262]|
|
||||
| 263 | -2 | 8 |
|
||||
17
Test-Files/StaticsTest/StaticsTest.tst
Normal file
17
Test-Files/StaticsTest/StaticsTest.tst
Normal file
@@ -0,0 +1,17 @@
|
||||
// This file is part of www.nand2tetris.org
|
||||
// and the book "The Elements of Computing Systems"
|
||||
// by Nisan and Schocken, MIT Press.
|
||||
// File name: projects/08/FunctionCalls/StaticsTest/StaticsTest.tst
|
||||
|
||||
load StaticsTest.asm,
|
||||
output-file StaticsTest.out,
|
||||
compare-to StaticsTest.cmp,
|
||||
output-list RAM[0]%D1.6.1 RAM[261]%D1.6.1 RAM[262]%D1.6.1;
|
||||
|
||||
set RAM[0] 256,
|
||||
|
||||
repeat 2500 {
|
||||
ticktock;
|
||||
}
|
||||
|
||||
output;
|
||||
17
Test-Files/StaticsTest/StaticsTestVME.tst
Normal file
17
Test-Files/StaticsTest/StaticsTestVME.tst
Normal file
@@ -0,0 +1,17 @@
|
||||
// This file is part of www.nand2tetris.org
|
||||
// and the book "The Elements of Computing Systems"
|
||||
// by Nisan and Schocken, MIT Press.
|
||||
// File name: projects/08/FunctionCalls/StaticsTest/StaticsTestVME.tst
|
||||
|
||||
load, // loads all the VM files from the current directory.
|
||||
output-file StaticsTest.out,
|
||||
compare-to StaticsTest.cmp,
|
||||
output-list RAM[0]%D1.6.1 RAM[261]%D1.6.1 RAM[262]%D1.6.1;
|
||||
|
||||
set sp 261,
|
||||
|
||||
repeat 36 {
|
||||
vmstep;
|
||||
}
|
||||
|
||||
output;
|
||||
20
Test-Files/StaticsTest/Sys.vm
Normal file
20
Test-Files/StaticsTest/Sys.vm
Normal file
@@ -0,0 +1,20 @@
|
||||
// This file is part of www.nand2tetris.org
|
||||
// and the book "The Elements of Computing Systems"
|
||||
// by Nisan and Schocken, MIT Press.
|
||||
// File name: projects/08/FunctionCalls/StaticsTest/Sys.vm
|
||||
|
||||
// Tests that different functions, stored in two different
|
||||
// class files, manipulate the static segment correctly.
|
||||
function Sys.init 0
|
||||
push constant 6
|
||||
push constant 8
|
||||
call Class1.set 2
|
||||
pop temp 0 // Dumps the return value
|
||||
push constant 23
|
||||
push constant 15
|
||||
call Class2.set 2
|
||||
pop temp 0 // Dumps the return value
|
||||
call Class1.get 0
|
||||
call Class2.get 0
|
||||
label WHILE
|
||||
goto WHILE
|
||||
390
code_writer.cpp
390
code_writer.cpp
@@ -1,390 +0,0 @@
|
||||
#include "code_writer.hpp"
|
||||
|
||||
code_writer::code_writer(std::string name)
|
||||
{
|
||||
file_name = name;
|
||||
name.erase(name.find('.'));
|
||||
name.append(".asm");
|
||||
outfile.open(name);
|
||||
}
|
||||
|
||||
void code_writer::write_assembly()
|
||||
{
|
||||
parser toparse(file_name);
|
||||
toparse.parse();
|
||||
commands = toparse.get_commands();
|
||||
|
||||
std::unordered_map<c_type,
|
||||
std::function<void(std::string, int, bool)>>
|
||||
write_map({{C_ARITHMETIC,
|
||||
[this](std::string arg1, int arg2, bool arg3) {
|
||||
this->write_arithmetic(arg1, arg2, arg3);
|
||||
}},
|
||||
{C_PUSH,
|
||||
[this](std::string arg1, int arg2, bool arg3) {
|
||||
this->write_push(arg1, arg2, arg3);
|
||||
}},
|
||||
{C_POP,
|
||||
[this](std::string arg1, int arg2, bool arg3) {
|
||||
this->write_pop(arg1, arg2, arg3);
|
||||
}}});
|
||||
|
||||
for (auto curr : commands)
|
||||
{
|
||||
c_type write_type;
|
||||
std::string instruction;
|
||||
int index;
|
||||
std::tie(write_type, instruction, index) = curr;
|
||||
if (outfile.is_open())
|
||||
{
|
||||
write_map[write_type](instruction, index, this->dbg);
|
||||
}
|
||||
}
|
||||
outfile.close();
|
||||
}
|
||||
|
||||
void code_writer::write_push(std::string segment, int index, bool debug)
|
||||
{
|
||||
std::string toname = file_name;
|
||||
toname = toname.substr(toname.find_last_of('/') + 1);
|
||||
toname = toname.erase(toname.find('.'));
|
||||
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// push " << segment << " " << index << std::endl;
|
||||
}
|
||||
|
||||
if (segment == "constant")
|
||||
{
|
||||
outfile << "@" << index << std::endl
|
||||
<< "D=A" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "AM=M+1" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
else if (segment == "static")
|
||||
{
|
||||
outfile << "@" << toname << "." << index << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "AM=M+1" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
else if (segment == "pointer")
|
||||
{
|
||||
std::string which = (index == 0) ? "THIS" : "THAT";
|
||||
outfile << "@" << which << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "AM=M+1" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
else if (segment == "temp")
|
||||
{
|
||||
outfile << "@" << index + 5 << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "AM=M+1" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string which;
|
||||
if (segment == "local")
|
||||
{
|
||||
which = "LCL";
|
||||
}
|
||||
else if (segment == "argument")
|
||||
{
|
||||
which = "ARG";
|
||||
}
|
||||
else if (segment == "this")
|
||||
{
|
||||
which = "THIS";
|
||||
}
|
||||
else if (segment == "that")
|
||||
{
|
||||
which = "THAT";
|
||||
}
|
||||
outfile << "@" << which << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@" << index << std::endl
|
||||
<< "D=D+A" << std::endl
|
||||
<< "A=D" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "AM=M+1" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 10;
|
||||
}
|
||||
}
|
||||
|
||||
void code_writer::write_pop(std::string segment, int index, bool debug)
|
||||
{
|
||||
std::string toname = file_name;
|
||||
toname = toname.substr(toname.find_last_of('/')+1);
|
||||
toname = toname.erase(toname.find('.'));
|
||||
|
||||
if (debug && segment != "constant")
|
||||
{
|
||||
outfile << "// pop " << segment << " " << index << std::endl;
|
||||
}
|
||||
|
||||
if (segment == "static")
|
||||
{
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@" << toname << "." << index << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 5;
|
||||
}
|
||||
else if (segment == "pointer")
|
||||
{
|
||||
std::string which = (index == 0) ? "THIS" : "THAT";
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@" << which << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 4;
|
||||
}
|
||||
else if (segment == "temp")
|
||||
{
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@" << index + 5 << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string which;
|
||||
if (segment == "local")
|
||||
{
|
||||
which = "LCL";
|
||||
}
|
||||
else if (segment == "argument")
|
||||
{
|
||||
which = "ARG";
|
||||
}
|
||||
else if (segment == "this")
|
||||
{
|
||||
which = "THIS";
|
||||
}
|
||||
else if (segment == "that")
|
||||
{
|
||||
which = "THAT";
|
||||
}
|
||||
outfile << "@" << which << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@" << index << std::endl
|
||||
<< "D=D+A" << std::endl
|
||||
<< "@R13" << std::endl
|
||||
<< "M=D" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@R13" << std::endl
|
||||
<< "A=M" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 12;
|
||||
}
|
||||
}
|
||||
|
||||
void code_writer::write_arithmetic(std::string comm, int index, bool debug)
|
||||
{
|
||||
std::unordered_map<std::string,
|
||||
std::function<void(bool)>>
|
||||
arith_mapic({
|
||||
{"add",
|
||||
[this](bool arg1) { this->write_add(arg1); }},
|
||||
{"sub",
|
||||
[this](bool arg1) { this->write_sub(arg1); }},
|
||||
{"neg",
|
||||
[this](bool arg1) { this->write_neg(arg1); }},
|
||||
{"and",
|
||||
[this](bool arg1) { this->write_and(arg1); }},
|
||||
{"or",
|
||||
[this](bool arg1) { this->write_or(arg1); }},
|
||||
{"not",
|
||||
[this](bool arg1) { this->write_not(arg1); }},
|
||||
{"gt",
|
||||
[this](bool arg1) { this->write_gt(arg1); }},
|
||||
{"lt",
|
||||
[this](bool arg1) { this->write_lt(arg1); }},
|
||||
{"eq",
|
||||
[this](bool arg1) { this->write_eq(arg1); }},
|
||||
});
|
||||
|
||||
arith_mapic[comm](debug);
|
||||
}
|
||||
|
||||
void code_writer::write_add(bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// add" << std::endl;
|
||||
}
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "A=M-1" << std::endl
|
||||
<< "M=D+M" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
|
||||
void code_writer::write_sub(bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// sub" << std::endl;
|
||||
}
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "A=M-1" << std::endl
|
||||
<< "M=M-D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
|
||||
void code_writer::write_neg(bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// neg" << std::endl;
|
||||
}
|
||||
outfile << "@SP" << std::endl
|
||||
<< "A=M-1" << std::endl
|
||||
<< "M=-M" << std::endl;
|
||||
this->ins_no += 3;
|
||||
}
|
||||
|
||||
void code_writer::write_eq(bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// eq" << std::endl;
|
||||
}
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "D=M-D" << std::endl;
|
||||
this->ins_no += 5;
|
||||
outfile << "@" << this->ins_no + 6 << std::endl
|
||||
<< "D;JEQ" << std::endl
|
||||
<< "D=0" << std::endl;
|
||||
this->ins_no += 3;
|
||||
outfile << "@" << this->ins_no + 4 << std::endl
|
||||
<< "0;JMP" << std::endl
|
||||
<< "D=-1" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "A=M-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
|
||||
void code_writer::write_lt(bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// lt" << std::endl;
|
||||
}
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "D=M-D" << std::endl;
|
||||
this->ins_no += 5;
|
||||
outfile << "@" << this->ins_no + 6 << std::endl
|
||||
<< "D;JLT" << std::endl
|
||||
<< "D=0" << std::endl;
|
||||
this->ins_no += 3;
|
||||
outfile << "@" << this->ins_no + 4 << std::endl
|
||||
<< "0;JMP" << std::endl
|
||||
<< "D=-1" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "A=M-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
|
||||
void code_writer::write_gt(bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// gt" << std::endl;
|
||||
}
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "D=M-D" << std::endl;
|
||||
this->ins_no += 5;
|
||||
outfile << "@" << this->ins_no + 6 << std::endl
|
||||
<< "D;JGT" << std::endl
|
||||
<< "D=0" << std::endl;
|
||||
this->ins_no += 3;
|
||||
outfile << "@" << this->ins_no + 4 << std::endl
|
||||
<< "0;JMP" << std::endl
|
||||
<< "D=-1" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "A=M-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
|
||||
void code_writer::write_and(bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// and" << std::endl;
|
||||
}
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "A=M-1" << std::endl
|
||||
<< "M=D&M" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
|
||||
void code_writer::write_or(bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// or" << std::endl;
|
||||
}
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "A=M-1" << std::endl
|
||||
<< "M=D|M" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
|
||||
void code_writer::write_not(bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// not" << std::endl;
|
||||
}
|
||||
outfile << "@SP" << std::endl
|
||||
<< "A=M-1" << std::endl
|
||||
<< "M=!M" << std::endl;
|
||||
this->ins_no += 3;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
|
||||
#include "parser.hpp"
|
||||
#include <functional>
|
||||
class code_writer
|
||||
{
|
||||
private:
|
||||
std::string file_name;
|
||||
std::vector<vmcommand> commands;
|
||||
std::ofstream outfile;
|
||||
int ins_no = -1;
|
||||
bool dbg = false;
|
||||
|
||||
public:
|
||||
code_writer(std::string name);
|
||||
void write_assembly();
|
||||
void write_push(std::string segment, int index, bool debug);
|
||||
void write_pop(std::string segment, int index, bool debug);
|
||||
void write_arithmetic(std::string comm, int index, bool debug);
|
||||
void write_add(bool debug);
|
||||
void write_sub(bool debug);
|
||||
void write_neg(bool debug);
|
||||
void write_and(bool debug);
|
||||
void write_or(bool debug);
|
||||
void write_not(bool debug);
|
||||
void write_eq(bool debug);
|
||||
void write_lt(bool debug);
|
||||
void write_gt(bool debug);
|
||||
};
|
||||
53
include/code_writer.hpp
Normal file
53
include/code_writer.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "parser.hpp"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
typedef std::vector<std::string> lots_of_files;
|
||||
|
||||
const std::unordered_map<std::string, std::string> operation_map({{"add", "M+D"},
|
||||
{"sub", "M-D"},
|
||||
{"neg", "-M"},
|
||||
{"and", "D&M"},
|
||||
{"or", "D|M"},
|
||||
{"not", "!M"},
|
||||
{"eq", "JEQ"},
|
||||
{"lt", "JLT"},
|
||||
{"gt", "LGT"}});
|
||||
|
||||
class code_writer
|
||||
{
|
||||
private:
|
||||
lots_of_files files;
|
||||
std::vector<vmcommand> commands;
|
||||
std::ofstream outfile;
|
||||
std::string current_file = "";
|
||||
std::string current_function = "";
|
||||
int ins_no = -1;
|
||||
bool dbg = true;
|
||||
bool is_dir = false;
|
||||
|
||||
public:
|
||||
code_writer(lots_of_files name);
|
||||
void write_assembly();
|
||||
|
||||
void write_push(std::string segment, int index, bool debug);
|
||||
void write_push(std::string segment, bool debug);
|
||||
void write_pop(std::string segment, int index, bool debug);
|
||||
void write_pop(std::string segment, bool debug);
|
||||
|
||||
void write_arithmetic(std::string comm, int index, bool debug);
|
||||
void write_add_sub_and_or(std::string op, bool debug);
|
||||
void write_neg_not(std::string op, bool debug);
|
||||
void write_eq_lt_gt(std::string op, bool debug);
|
||||
|
||||
void write_init(bool debug);
|
||||
|
||||
void write_label(std::string label, int index, bool debug);
|
||||
void write_goto(std::string label, int index, bool debug);
|
||||
void write_if(std::string label, int index, bool debug);
|
||||
|
||||
void write_call(std::string fun_name, int args, bool debug);
|
||||
void write_return(bool debug);
|
||||
void write_function(std::string fun_name, int locals, bool debug);
|
||||
void write_frame_restore(std::string segment, int minus, bool debug);
|
||||
};
|
||||
@@ -27,7 +27,7 @@ private:
|
||||
std::vector<vmcommand> parsed;
|
||||
|
||||
public:
|
||||
parser(std::string file) : input_file(file) {}
|
||||
bool change_file(std::string name);
|
||||
std::vector<vmcommand> get_commands();
|
||||
c_type command_type(std::string com);
|
||||
void parse();
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include<sstream>
|
||||
#include <sstream>
|
||||
class vutility
|
||||
{
|
||||
private:
|
||||
37
src/Main.cpp
Normal file
37
src/Main.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "../include/code_writer.hpp"
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
lots_of_files files;
|
||||
std::string input(argv[1]);
|
||||
if (fs::is_directory(input))
|
||||
{
|
||||
files.push_back(input);
|
||||
for (auto &entry : fs::directory_iterator(input))
|
||||
{
|
||||
if (entry.path().extension() == ".vm")
|
||||
{
|
||||
std::string vmfile = entry.path().string();
|
||||
if (entry.path().filename() == "Sys.vm")
|
||||
{
|
||||
files.insert(files.begin() + 1, vmfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
files.push_back(vmfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
files.push_back(input);
|
||||
}
|
||||
|
||||
code_writer toassembly(files);
|
||||
toassembly.write_assembly();
|
||||
return 0;
|
||||
}
|
||||
518
src/code_writer.cpp
Normal file
518
src/code_writer.cpp
Normal file
@@ -0,0 +1,518 @@
|
||||
#include "../include/code_writer.hpp"
|
||||
|
||||
code_writer::code_writer(lots_of_files names)
|
||||
{
|
||||
this->files = names;
|
||||
std::string outfile_name;
|
||||
outfile_name = files.front();
|
||||
if (files.size() > 1)
|
||||
{
|
||||
files.erase(files.begin());
|
||||
std::string check = files.front();
|
||||
if (check.substr(check.length() - 6) == "Sys.vm")
|
||||
{
|
||||
this->is_dir = true;
|
||||
}
|
||||
outfile_name = outfile_name + "/" + outfile_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
outfile_name.erase(outfile_name.find('.'));
|
||||
}
|
||||
|
||||
outfile_name.append(".asm");
|
||||
outfile.open(outfile_name);
|
||||
}
|
||||
|
||||
void code_writer::write_assembly()
|
||||
{
|
||||
std::unordered_map<c_type,
|
||||
std::function<void(std::string, int, bool)>>
|
||||
write_map({{C_ARITHMETIC,
|
||||
[this](std::string arg1, int arg2, bool arg3) {
|
||||
this->write_arithmetic(arg1, arg2, arg3);
|
||||
}},
|
||||
{C_PUSH,
|
||||
[this](std::string arg1, int arg2, bool arg3) {
|
||||
this->write_push(arg1, arg2, arg3);
|
||||
}},
|
||||
{C_POP,
|
||||
[this](std::string arg1, int arg2, bool arg3) {
|
||||
this->write_pop(arg1, arg2, arg3);
|
||||
}},
|
||||
{C_LABEL,
|
||||
[this](std::string arg1, int arg2, bool arg3) {
|
||||
this->write_label(arg1, arg2, arg3);
|
||||
}},
|
||||
{C_FUNCTION,
|
||||
[this](std::string arg1, int arg2, bool arg3) {
|
||||
this->write_function(arg1, arg2, arg3);
|
||||
}},
|
||||
{C_RETURN,
|
||||
[this](std::string arg1, int arg2, bool arg3) {
|
||||
this->write_return(arg3);
|
||||
}},
|
||||
{C_GOTO,
|
||||
[this](std::string arg1, int arg2, bool arg3) {
|
||||
this->write_goto(arg1, arg2, arg3);
|
||||
}},
|
||||
{C_IF,
|
||||
[this](std::string arg1, int arg2, bool arg3) {
|
||||
this->write_if(arg1, arg2, arg3);
|
||||
}},
|
||||
{C_CALL,
|
||||
[this](std::string arg1, int arg2, bool arg3) {
|
||||
this->write_call(arg1, arg2, arg3);
|
||||
}}});
|
||||
|
||||
parser lets_parse;
|
||||
this->write_init(this->dbg);
|
||||
for (auto curr_file : this->files)
|
||||
{
|
||||
lets_parse.change_file(curr_file);
|
||||
lets_parse.parse();
|
||||
this->commands = lets_parse.get_commands();
|
||||
this->current_file = curr_file;
|
||||
for (auto curr : commands)
|
||||
{
|
||||
c_type write_type;
|
||||
std::string instruction;
|
||||
int index;
|
||||
std::tie(write_type, instruction, index) = curr;
|
||||
if (outfile.is_open())
|
||||
{
|
||||
write_map[write_type](instruction, index, this->dbg);
|
||||
}
|
||||
}
|
||||
this->commands.clear();
|
||||
}
|
||||
outfile.close();
|
||||
}
|
||||
|
||||
void code_writer::write_push(std::string segment, int index, bool debug)
|
||||
{
|
||||
std::string toname = this->current_file;
|
||||
toname = toname.substr(toname.find_last_of('/') + 1);
|
||||
toname = toname.erase(toname.find('.'));
|
||||
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// push " << segment << " " << index << " " << this->ins_no << std::endl;
|
||||
}
|
||||
|
||||
if (segment == "constant")
|
||||
{
|
||||
outfile << "@" << index << std::endl
|
||||
<< "D=A" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "AM=M+1" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
else if (segment == "static")
|
||||
{
|
||||
outfile << "@" << toname << "." << index << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "AM=M+1" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
else if (segment == "pointer")
|
||||
{
|
||||
std::string which = (index == 0) ? "THIS" : "THAT";
|
||||
outfile << "@" << which << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "AM=M+1" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
else if (segment == "temp")
|
||||
{
|
||||
outfile << "@" << index + 5 << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "AM=M+1" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string which;
|
||||
if (segment == "local")
|
||||
{
|
||||
which = "LCL";
|
||||
}
|
||||
else if (segment == "argument")
|
||||
{
|
||||
which = "ARG";
|
||||
}
|
||||
else if (segment == "this")
|
||||
{
|
||||
which = "THIS";
|
||||
}
|
||||
else if (segment == "that")
|
||||
{
|
||||
which = "THAT";
|
||||
}
|
||||
outfile << "@" << which << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@" << index << std::endl
|
||||
<< "D=D+A" << std::endl
|
||||
<< "A=D" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "AM=M+1" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 10;
|
||||
}
|
||||
}
|
||||
|
||||
void code_writer::write_push(std::string segment, bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// push " << segment << " " << this->ins_no << std::endl;
|
||||
}
|
||||
outfile << "@" << segment << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "AM=M+1" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
|
||||
void code_writer::write_pop(std::string segment, int index, bool debug)
|
||||
{
|
||||
std::string toname = this->current_file;
|
||||
toname = toname.substr(toname.find_last_of('/') + 1);
|
||||
toname = toname.erase(toname.find('.'));
|
||||
|
||||
if (debug && segment != "constant")
|
||||
{
|
||||
outfile << "// pop " << segment << " " << index << " " << this->ins_no << std::endl;
|
||||
}
|
||||
|
||||
if (segment == "static")
|
||||
{
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@" << toname << "." << index << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 5;
|
||||
}
|
||||
else if (segment == "pointer")
|
||||
{
|
||||
std::string which = (index == 0) ? "THIS" : "THAT";
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@" << which << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 5;
|
||||
}
|
||||
else if (segment == "temp")
|
||||
{
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@" << index + 5 << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string which;
|
||||
if (segment == "local")
|
||||
{
|
||||
which = "LCL";
|
||||
}
|
||||
else if (segment == "argument")
|
||||
{
|
||||
which = "ARG";
|
||||
}
|
||||
else if (segment == "this")
|
||||
{
|
||||
which = "THIS";
|
||||
}
|
||||
else if (segment == "that")
|
||||
{
|
||||
which = "THAT";
|
||||
}
|
||||
outfile << "@" << which << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@" << index << std::endl
|
||||
<< "D=D+A" << std::endl
|
||||
<< "@R13" << std::endl
|
||||
<< "M=D" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@R13" << std::endl
|
||||
<< "A=M" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 12;
|
||||
}
|
||||
}
|
||||
|
||||
void code_writer::write_pop(std::string segment, bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// pop " << segment << " " << this->ins_no << std::endl;
|
||||
}
|
||||
|
||||
outfile << "@SP" << std::endl
|
||||
<< "A=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@" << segment << std::endl
|
||||
<< "A=M" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
|
||||
void code_writer::write_arithmetic(std::string comm, int index, bool debug)
|
||||
{
|
||||
std::unordered_map<std::string,
|
||||
std::function<void(std::string, bool)>>
|
||||
arith_mapic({
|
||||
{"add",
|
||||
[this](std::string arg0, bool arg1) { this->write_add_sub_and_or(arg0, arg1); }},
|
||||
{"sub",
|
||||
[this](std::string arg0, bool arg1) { this->write_add_sub_and_or(arg0, arg1); }},
|
||||
{"and",
|
||||
[this](std::string arg0, bool arg1) { this->write_add_sub_and_or(arg0, arg1); }},
|
||||
{"or",
|
||||
[this](std::string arg0, bool arg1) { this->write_add_sub_and_or(arg0, arg1); }},
|
||||
{"neg",
|
||||
[this](std::string arg0, bool arg1) { this->write_neg_not(arg0, arg1); }},
|
||||
{"not",
|
||||
[this](std::string arg0, bool arg1) { this->write_neg_not(arg0, arg1); }},
|
||||
{"gt",
|
||||
[this](std::string arg0, bool arg1) { this->write_eq_lt_gt(arg0, arg1); }},
|
||||
{"lt",
|
||||
[this](std::string arg0, bool arg1) { this->write_eq_lt_gt(arg0, arg1); }},
|
||||
{"eq",
|
||||
[this](std::string arg0, bool arg1) { this->write_eq_lt_gt(arg0, arg1); }},
|
||||
});
|
||||
|
||||
arith_mapic[comm](comm, debug);
|
||||
}
|
||||
|
||||
void code_writer::write_add_sub_and_or(std::string op, bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// " << op << " " << this->ins_no << std::endl;
|
||||
}
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "A=M-1" << std::endl
|
||||
<< "M=" << operation_map.at(op) << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
|
||||
void code_writer::write_neg_not(std::string op, bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// " << op << " " << this->ins_no << std::endl;
|
||||
}
|
||||
outfile << "@SP" << std::endl
|
||||
<< "A=M-1" << std::endl
|
||||
<< "M=" << operation_map.at(op) << std::endl;
|
||||
this->ins_no += 3;
|
||||
}
|
||||
|
||||
void code_writer::write_eq_lt_gt(std::string op, bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// " << op << " " << this->ins_no << std::endl;
|
||||
}
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "D=M-D" << std::endl;
|
||||
this->ins_no += 5;
|
||||
outfile << "@" << this->ins_no + 6 << std::endl
|
||||
<< "D;" << operation_map.at(op) << std::endl
|
||||
<< "D=0" << std::endl;
|
||||
this->ins_no += 3;
|
||||
outfile << "@" << this->ins_no + 4 << std::endl
|
||||
<< "0;JMP" << std::endl
|
||||
<< "D=-1" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "A=M-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
}
|
||||
|
||||
void code_writer::write_init(bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// bootstrap code"
|
||||
<< " " << this->ins_no << std::endl;
|
||||
}
|
||||
outfile << "@256" << std::endl
|
||||
<< "D=A" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 4;
|
||||
|
||||
if (this->is_dir)
|
||||
{
|
||||
outfile << "// Sys.init defined adding function call..."
|
||||
<< " " << this->ins_no << std::endl;
|
||||
this->write_call("Sys.init", 0, debug);
|
||||
}
|
||||
}
|
||||
|
||||
void code_writer::write_call(std::string fun_name, int args, bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// call" << fun_name << " " << args << " " << this->ins_no << std::endl;
|
||||
}
|
||||
std::string curr_ins = std::to_string(this->ins_no);
|
||||
std::string ret_label("ret." + fun_name + "$" + curr_ins);
|
||||
|
||||
outfile << "@" << ret_label << std::endl
|
||||
<< "D=A" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "AM=M+1" << std::endl
|
||||
<< "A=A-1" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 6;
|
||||
|
||||
this->write_push("LCL", debug);
|
||||
this->write_push("ARG", debug);
|
||||
this->write_push("THIS", debug);
|
||||
this->write_push("THAT", debug);
|
||||
|
||||
outfile << "@SP" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@5" << std::endl
|
||||
<< "D=D-A" << std::endl
|
||||
<< "@" << args << std::endl
|
||||
<< "D=D-A" << std::endl
|
||||
<< "@ARG" << std::endl
|
||||
<< "M=D" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@LCL" << std::endl
|
||||
<< "M=D" << std::endl
|
||||
<< "@" << fun_name << std::endl
|
||||
<< "0;JMP" << std::endl
|
||||
<< "(" << ret_label << ")" << std::endl;
|
||||
this->ins_no += 14;
|
||||
}
|
||||
|
||||
void code_writer::write_label(std::string label, int index, bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// label " << label << " " << this->ins_no << std::endl;
|
||||
}
|
||||
outfile << "(" << current_function << "$" << label << ")" << std::endl;
|
||||
}
|
||||
|
||||
void code_writer::write_goto(std::string label, int index, bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// goto " << label << " " << this->ins_no << std::endl;
|
||||
}
|
||||
outfile << "@" << current_function << "$" << label << std::endl
|
||||
<< "0;JMP" << std::endl;
|
||||
this->ins_no += 2;
|
||||
}
|
||||
|
||||
void code_writer::write_function(std::string fun_name, int locals, bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// function " << fun_name << " " << locals << " " << this->ins_no << std::endl;
|
||||
}
|
||||
current_function = fun_name;
|
||||
outfile << "(" << fun_name << ")" << std::endl;
|
||||
for (int l = 0; l < locals; l++)
|
||||
{
|
||||
this->write_push("constant", 0, debug);
|
||||
}
|
||||
}
|
||||
|
||||
void code_writer::write_return(bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// return " << current_function << " " << this->ins_no << std::endl;
|
||||
}
|
||||
outfile << "@LCL" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@R7" << std::endl
|
||||
<< "M=D" << std::endl
|
||||
<< "@5" << std::endl
|
||||
<< "A=D-A" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@R14" << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 9;
|
||||
this->write_pop("ARG", debug);
|
||||
outfile << "@ARG" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@SP" << std::endl
|
||||
<< "M=D+1" << std::endl;
|
||||
this->ins_no += 4;
|
||||
this->write_frame_restore("THAT", 1, debug);
|
||||
this->write_frame_restore("THIS", 2, debug);
|
||||
this->write_frame_restore("ARG", 3, debug);
|
||||
this->write_frame_restore("LCL", 4, debug);
|
||||
outfile << "@R14" << std::endl
|
||||
<< "A=M" << std ::endl
|
||||
<< "0;JMP" << std::endl;
|
||||
this->ins_no += 3;
|
||||
}
|
||||
|
||||
void code_writer::write_frame_restore(std::string segment, int minus, bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// restoring " << segment << " " << this->ins_no << std::endl;
|
||||
}
|
||||
|
||||
outfile << "@R7" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@" << minus << std::endl
|
||||
<< "A=D-A" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@" << segment << std::endl
|
||||
<< "M=D" << std::endl;
|
||||
this->ins_no += 7;
|
||||
}
|
||||
|
||||
void code_writer::write_if(std::string label, int index, bool debug)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
outfile << "// if " << label << " " << this->ins_no << std::endl;
|
||||
}
|
||||
outfile << "@SP" << std::endl
|
||||
<< "AM=M-1" << std::endl
|
||||
<< "D=M" << std::endl
|
||||
<< "@" << current_function << "$" << label << std::endl
|
||||
<< "D;JNE" << std::endl;
|
||||
this->ins_no += 5;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "parser.hpp"
|
||||
#include "../include/parser.hpp"
|
||||
|
||||
c_type parser::command_type(std::string com)
|
||||
{
|
||||
@@ -75,4 +75,19 @@ void parser::parse()
|
||||
std::vector<vmcommand> parser::get_commands()
|
||||
{
|
||||
return this->parsed;
|
||||
}
|
||||
|
||||
bool parser::change_file(std::string name)
|
||||
{
|
||||
this->parsed.clear();
|
||||
this->input_file = name;
|
||||
|
||||
if (this->parsed.size() == 0 && input_file == name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "vutility.hpp"
|
||||
#include "../include/vutility.hpp"
|
||||
|
||||
std::vector<std::string> vutility::split(std::string str, char delimit)
|
||||
{
|
||||
@@ -14,14 +14,12 @@ std::vector<std::string> vutility::split(std::string str, char delimit)
|
||||
|
||||
std::string vutility::trim(std::string str)
|
||||
{
|
||||
if (str.substr(0, 2) == "//")
|
||||
if (str.find('/') != std::string::npos) // comments
|
||||
{
|
||||
str.erase();
|
||||
}
|
||||
else
|
||||
{
|
||||
str.erase(0, str.find_first_not_of(" \r\t\v\n")); //prefixing
|
||||
str.erase(str.find_last_not_of(" \r\t\v\n") + 1); //surfixing}
|
||||
str.erase(str.find('/'));
|
||||
}
|
||||
str.erase(0, str.find_first_not_of(" \r\t\v\n")); //prefixing
|
||||
str.erase(str.find_last_not_of(" \r\t\v\n") + 1); //surfixing}
|
||||
|
||||
return str;
|
||||
}
|
||||
Reference in New Issue
Block a user