Skip to content

Commit 8f75e6e

Browse files
committed
Import code from nand2tetris repository
`assembler` and `vmtranslator` build as individual projects.
1 parent 93d2d36 commit 8f75e6e

File tree

99 files changed

+3590
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+3590
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# CMake.gitignore
12
CMakeLists.txt.user
23
CMakeCache.txt
34
CMakeFiles
@@ -9,3 +10,6 @@ install_manifest.txt
910
compile_commands.json
1011
CTestTestfile.cmake
1112
_deps
13+
14+
build*/
15+
CPU Emulator.dat

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# NTU Introduction to Computer 2023 Fall Final Project

assembler/CMakeLists.txt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
cmake_minimum_required(VERSION 3.20)
2+
project(vmtranslator LANGUAGES CXX)
3+
4+
set(CMAKE_CXX_STANDARD 17)
5+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
6+
7+
if (POLICY CMP0135)
8+
cmake_policy(SET CMP0135 NEW)
9+
endif (POLICY CMP0135)
10+
include(FetchContent)
11+
FetchContent_Declare(
12+
absl
13+
URL https://github.com/abseil/abseil-cpp/archive/refs/heads/master.zip
14+
)
15+
set(ABSL_PROPAGATE_CXX_STD ON)
16+
FetchContent_MakeAvailable(absl)
17+
18+
add_executable(
19+
assembler
20+
src/main.cpp
21+
)
22+
target_link_libraries(
23+
assembler
24+
absl::check
25+
absl::log
26+
absl::strings
27+
)
28+
29+
install(
30+
TARGETS assembler
31+
DESTINATION ${CMAKE_SOURCE_DIR}
32+
)

assembler/src/main.cpp

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
#include <algorithm>
2+
#include <bitset>
3+
#include <cstdint>
4+
#include <filesystem>
5+
#include <fstream>
6+
#include <iostream>
7+
#include <memory>
8+
#include <string>
9+
#include <unordered_map>
10+
#include <utility>
11+
#include <vector>
12+
13+
#include "absl/log/check.h"
14+
#include "absl/log/log.h"
15+
#include "absl/strings/numbers.h"
16+
#include "absl/strings/str_cat.h"
17+
#include "absl/strings/str_format.h"
18+
19+
class Instruction {
20+
public:
21+
virtual std::string ToMachine() const = 0;
22+
virtual std::string ToAssembly() const = 0;
23+
};
24+
25+
class AInstruction : public Instruction {
26+
public:
27+
AInstruction(uint16_t value) : value_(value) {}
28+
29+
std::string ToMachine() const override {
30+
return absl::StrFormat("0%s", value_.to_string());
31+
}
32+
33+
std::string ToAssembly() const override {
34+
return absl::StrFormat("@%u", value_.to_ulong());
35+
}
36+
37+
private:
38+
std::bitset<15> value_;
39+
};
40+
41+
class CInstruction : public Instruction {
42+
public:
43+
std::string ToMachine() const override {
44+
return absl::StrFormat("111%s%s%s", computation_.to_string(),
45+
destination_.to_string(), jump_.to_string());
46+
}
47+
48+
std::string ToAssembly() const override {
49+
std::string assembly;
50+
if (destination_.any()) {
51+
if (destination_[2]) {
52+
assembly.push_back('A');
53+
}
54+
if (destination_[1]) {
55+
assembly.push_back('D');
56+
}
57+
if (destination_[0]) {
58+
assembly.push_back('M');
59+
}
60+
assembly.push_back('=');
61+
}
62+
63+
for (const auto &pair : kComputationCodes) {
64+
if (pair.second == computation_) {
65+
absl::StrAppend(&assembly, pair.first);
66+
break;
67+
}
68+
}
69+
70+
if (jump_.any()) {
71+
assembly.push_back(';');
72+
absl::StrAppend(&assembly, kJumpCodes[jump_.to_ulong()]);
73+
}
74+
return assembly;
75+
}
76+
77+
bool SetComputation(std::string_view computation_str) {
78+
for (const auto &pair : kComputationCodes) {
79+
if (pair.first == computation_str) {
80+
computation_ = pair.second;
81+
return true;
82+
}
83+
}
84+
return false;
85+
}
86+
87+
bool SetDestination(std::string_view destination_str) {
88+
for (char c : destination_str) {
89+
switch (c) {
90+
case 'A':
91+
destination_.set(2);
92+
break;
93+
case 'D':
94+
destination_.set(1);
95+
break;
96+
case 'M':
97+
destination_.set(0);
98+
break;
99+
default:
100+
LOG(ERROR) << "Unknown destination " << c;
101+
return false;
102+
}
103+
}
104+
return true;
105+
}
106+
107+
bool SetJump(std::string_view jump_str) {
108+
size_t found = std::find(kJumpCodes, kJumpCodes + 8, jump_str) - kJumpCodes;
109+
if (found == 8) {
110+
return false;
111+
}
112+
jump_ = found;
113+
return true;
114+
}
115+
116+
private:
117+
static constexpr std::pair<std::string_view, std::bitset<7>>
118+
kComputationCodes[] = {
119+
{"0", 0b0101010}, {"1", 0b0111111}, {"-1", 0b0111010},
120+
{"D", 0b0001100}, {"A", 0b0110000}, {"!D", 0b0001101},
121+
{"!A", 0b0110001}, {"-D", 0b0001111}, {"-A", 0b0110011},
122+
{"D+1", 0b0011111}, {"A+1", 0b0110111}, {"D-1", 0b0001110},
123+
{"A-1", 0b0110010}, {"D+A", 0b0000010}, {"D-A", 0b0010011},
124+
{"A-D", 0b0000111}, {"D&A", 0b0000000}, {"D|A", 0b0010101},
125+
{"M", 0b1110000}, {"!M", 0b1110001}, {"-M", 0b1110011},
126+
{"M+1", 0b1110111}, {"M-1", 0b1110010}, {"D+M", 0b1000010},
127+
{"D-M", 0b1010011}, {"M-D", 0b1000111}, {"D&M", 0b1000000},
128+
{"D|M", 0b1010101}};
129+
static constexpr std::string_view kJumpCodes[8] = {
130+
"", "JGT", "JEQ", "JGE", "JLT", "JNE", "JLE", "JMP"};
131+
132+
std::bitset<7> computation_;
133+
std::bitset<3> destination_;
134+
std::bitset<3> jump_;
135+
};
136+
137+
bool IsNumber(std::string_view str) {
138+
for (char c : str) {
139+
if (!isdigit(c)) {
140+
return false;
141+
}
142+
}
143+
return true;
144+
}
145+
146+
int main(int argc, char *argv[]) {
147+
CHECK_GE(argc, 2) << "No input file";
148+
std::ifstream asm_file(argv[1]);
149+
CHECK(asm_file.is_open()) << "Failed to open input file '" << argv[1] << "'";
150+
151+
std::string buffer;
152+
std::vector<std::string> trimmed_lines;
153+
while (std::getline(asm_file, buffer)) {
154+
std::string line;
155+
for (size_t i = 0; i < buffer.size(); ++i) {
156+
if (buffer[i] == ' ' || buffer[i] == '\t') {
157+
continue;
158+
}
159+
if (buffer.substr(i, 2) == "//") {
160+
break;
161+
}
162+
line.push_back(buffer[i]);
163+
}
164+
if (!line.empty()) {
165+
trimmed_lines.push_back(std::move(line));
166+
}
167+
}
168+
asm_file.close();
169+
170+
std::unordered_map<std::string, uint16_t> symbol_table = {
171+
{"R0", 0}, {"R1", 1}, {"R2", 2}, {"R3", 3}, {"R4", 4},
172+
{"R5", 5}, {"R6", 6}, {"R7", 7}, {"R8", 8}, {"R9", 9},
173+
{"R10", 10}, {"R11", 11}, {"R12", 12}, {"R13", 13}, {"R14", 14},
174+
{"R15", 15}, {"SCREEN", 16384}, {"KBD", 24576}, {"SP", 0}, {"LCL", 1},
175+
{"ARG", 2}, {"THIS", 3}, {"THAT", 4}};
176+
uint16_t instruction_counter = 0;
177+
for (const std::string &line : trimmed_lines) {
178+
if (line.front() == '(' && line.back() == ')') { // Is label.
179+
symbol_table[line.substr(1, line.size() - 2)] = instruction_counter;
180+
} else {
181+
++instruction_counter;
182+
}
183+
}
184+
185+
std::string hack_filename = std::filesystem::path(argv[1]).stem().string();
186+
absl::StrAppend(&hack_filename, ".hack");
187+
std::ofstream hack_file(hack_filename);
188+
CHECK(hack_file.is_open())
189+
<< "Failed to open output file '" << hack_filename << "'";
190+
191+
uint16_t variable_address = 16;
192+
for (const std::string &line : trimmed_lines) {
193+
if (line.front() == '(' && line.back() == ')') { // Label
194+
continue;
195+
}
196+
if (line.front() == '@') { // A-instruction
197+
std::string value_str = line.substr(1);
198+
if (IsNumber(value_str)) {
199+
uint32_t value;
200+
if (absl::SimpleAtoi(value_str, &value) && value <= UINT16_MAX) {
201+
hack_file << AInstruction(static_cast<uint16_t>(value)).ToMachine()
202+
<< std::endl;
203+
} else {
204+
LOG(ERROR) << "Cannot convert " << value_str << " to uint16_t";
205+
}
206+
} else {
207+
if (!symbol_table.count(value_str)) {
208+
symbol_table[value_str] = variable_address++;
209+
}
210+
hack_file << AInstruction(symbol_table[value_str]).ToMachine()
211+
<< std::endl;
212+
}
213+
} else { // C-instruction
214+
CInstruction instruction;
215+
std::string_view line_view(line);
216+
size_t found = line_view.find('=');
217+
if (found != std::string_view::npos) {
218+
instruction.SetDestination(line_view.substr(0, found));
219+
line_view.remove_prefix(found + 1);
220+
}
221+
222+
found = line_view.find(';');
223+
instruction.SetComputation(line_view.substr(0, found));
224+
if (found != std::string_view::npos) {
225+
line_view.remove_prefix(found + 1);
226+
instruction.SetJump(line_view);
227+
}
228+
hack_file << instruction.ToMachine() << std::endl;
229+
}
230+
}
231+
hack_file.close();
232+
return 0;
233+
}

0 commit comments

Comments
 (0)