Compare commits
8 Commits
f0bb80ce45
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 4afdcd1660 | |||
| 214b3e4d9e | |||
| a53125fa6e | |||
| 3e3330b046 | |||
| 7eee6a08c8 | |||
| 51de990690 | |||
| ba4d9afda9 | |||
| 0ace0f424a |
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
out
|
||||||
|
hack-assembler
|
||||||
|
build
|
||||||
|
.cache
|
||||||
26
CMakeLists.txt
Normal file
26
CMakeLists.txt
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
set(NAME "hack-assembler")
|
||||||
|
project(${NAME} CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
# nlohmann::json
|
||||||
|
FetchContent_Declare(
|
||||||
|
json
|
||||||
|
GIT_REPOSITORY https://github.com/nlohmann/json.git
|
||||||
|
GIT_TAG v3.11.3
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(json)
|
||||||
|
|
||||||
|
|
||||||
|
set(SRC_CXX_FILES "./main.cpp")
|
||||||
|
|
||||||
|
add_executable(${NAME} ${SRC_CXX_FILES})
|
||||||
|
|
||||||
|
target_link_libraries(${NAME} PUBLIC nlohmann_json::nlohmann_json)
|
||||||
|
|
||||||
19
README.md
19
README.md
@@ -1,13 +1,26 @@
|
|||||||
# hack-assembler
|
# hack-assembler
|
||||||
|
|
||||||
An implementation of a hack assembler in C++.
|
An implementation of a hack assembler in C++, based on the project 6 of the [nand2tetris course](https://www.nand2tetris.org/https://b1391bd6-da3d-477d-8c01-38cdf774495a.filesusr.com/ugd/44046b_89a8e226476741a3b7c5204575b8a0b2.pdf).
|
||||||
|
|
||||||
|
## Compilation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake -S . -B build
|
||||||
|
cmake --build build
|
||||||
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
g++ -o hack-assembler main.cpp
|
cat <input_file.asm> | ./hack-assembler > ./out/<output_file.hack>
|
||||||
cat <input_file.asm> | ./hack-assembler > <output_file.hack>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
In order to check the assembler, you can generate the .hack files from the asm-samples directory. And test those within [nand2tetris CPU emulator](https://nand2tetris.github.io/web-ide/cpu/).
|
||||||
|
|
||||||
|
## References
|
||||||
|
- [nand2tetris course](https://www.nand2tetris.org/)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
This project is under the WTFPL License. See [LICENSE](LICENSE) for details.
|
This project is under the WTFPL License. See [LICENSE](LICENSE) for details.
|
||||||
|
|||||||
12
asm-samples/Add.asm
Normal file
12
asm-samples/Add.asm
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// This file is part of www.nand2tetris.org
|
||||||
|
// and the book "The Elements of Computing Systems"
|
||||||
|
// by Nisan and Schocken, MIT Press.
|
||||||
|
|
||||||
|
// Computes R0 = 2 + 3 (R0 refers to RAM[0])
|
||||||
|
|
||||||
|
@2
|
||||||
|
D=A
|
||||||
|
@3
|
||||||
|
D=D+A
|
||||||
|
@0
|
||||||
|
M=D
|
||||||
30
asm-samples/Max.asm
Normal file
30
asm-samples/Max.asm
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/6/max/Max.asm
|
||||||
|
|
||||||
|
// Computes R2 = max(R0, R1) (R0,R1,R2 refer to RAM[0],RAM[1],RAM[2])
|
||||||
|
// Usage: Before executing, put two values in R0 and R1.
|
||||||
|
|
||||||
|
// D = R0 - R1
|
||||||
|
@R0
|
||||||
|
D=M
|
||||||
|
@R1
|
||||||
|
D=D-M
|
||||||
|
// If (D > 0) goto ITSR0
|
||||||
|
@ITSR0
|
||||||
|
D;JGT
|
||||||
|
// Its R1
|
||||||
|
@R1
|
||||||
|
D=M
|
||||||
|
@OUTPUT_D
|
||||||
|
0;JMP
|
||||||
|
(ITSR0)
|
||||||
|
@R0
|
||||||
|
D=M
|
||||||
|
(OUTPUT_D)
|
||||||
|
@R2
|
||||||
|
M=D
|
||||||
|
(END)
|
||||||
|
@END
|
||||||
|
0;JMP
|
||||||
24
asm-samples/MaxL.asm
Normal file
24
asm-samples/MaxL.asm
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// 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/6/max/MaxL.asm
|
||||||
|
|
||||||
|
// Symbol-less version of the Max.asm program.
|
||||||
|
// Designed for testing the basic version of the assembler.
|
||||||
|
|
||||||
|
@0
|
||||||
|
D=M
|
||||||
|
@1
|
||||||
|
D=D-M
|
||||||
|
@10
|
||||||
|
D;JGT
|
||||||
|
@1
|
||||||
|
D=M
|
||||||
|
@12
|
||||||
|
0;JMP
|
||||||
|
@0
|
||||||
|
D=M
|
||||||
|
@2
|
||||||
|
M=D
|
||||||
|
@14
|
||||||
|
0;JMP
|
||||||
28375
asm-samples/Pong.asm
Normal file
28375
asm-samples/Pong.asm
Normal file
File diff suppressed because it is too large
Load Diff
27491
asm-samples/PongL.asm
Normal file
27491
asm-samples/PongL.asm
Normal file
File diff suppressed because it is too large
Load Diff
41
asm-samples/Rect.asm
Normal file
41
asm-samples/Rect.asm
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// 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/6/rect/Rect.asm
|
||||||
|
|
||||||
|
// Draws a rectangle at the top-left corner of the screen.
|
||||||
|
// The rectangle is 16 pixels wide and R0 pixels high.
|
||||||
|
// Usage: Before executing, put a value in R0.
|
||||||
|
|
||||||
|
// If (R0 <= 0) goto END else n = R0
|
||||||
|
@R0
|
||||||
|
D=M
|
||||||
|
@END
|
||||||
|
D;JLE
|
||||||
|
@n
|
||||||
|
M=D
|
||||||
|
// addr = base address of first screen row
|
||||||
|
@SCREEN
|
||||||
|
D=A
|
||||||
|
@addr
|
||||||
|
M=D
|
||||||
|
(LOOP)
|
||||||
|
// RAM[addr] = -1
|
||||||
|
@addr
|
||||||
|
A=M
|
||||||
|
M=-1
|
||||||
|
// addr = base address of next screen row
|
||||||
|
@addr
|
||||||
|
D=M
|
||||||
|
@32
|
||||||
|
D=D+A
|
||||||
|
@addr
|
||||||
|
M=D
|
||||||
|
// decrements n and loops
|
||||||
|
@n
|
||||||
|
MD=M-1
|
||||||
|
@LOOP
|
||||||
|
D;JGT
|
||||||
|
(END)
|
||||||
|
@END
|
||||||
|
0;JMP
|
||||||
33
asm-samples/RectL.asm
Normal file
33
asm-samples/RectL.asm
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// 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/6/rect/RectL.asm
|
||||||
|
|
||||||
|
// Symbol-less version of the Rect.asm program.
|
||||||
|
// Designed for testing the basic version of the assembler.
|
||||||
|
|
||||||
|
@0
|
||||||
|
D=M
|
||||||
|
@23
|
||||||
|
D;JLE
|
||||||
|
@16
|
||||||
|
M=D
|
||||||
|
@16384
|
||||||
|
D=A
|
||||||
|
@17
|
||||||
|
M=D
|
||||||
|
@17
|
||||||
|
A=M
|
||||||
|
M=-1
|
||||||
|
@17
|
||||||
|
D=M
|
||||||
|
@32
|
||||||
|
D=D+A
|
||||||
|
@17
|
||||||
|
M=D
|
||||||
|
@16
|
||||||
|
MD=M-1
|
||||||
|
@10
|
||||||
|
D;JGT
|
||||||
|
@23
|
||||||
|
0;JMP
|
||||||
94
main.cpp
94
main.cpp
@@ -3,6 +3,9 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
// Comp map
|
// Comp map
|
||||||
const std::map<std::string, std::string> compMap = {
|
const std::map<std::string, std::string> compMap = {
|
||||||
@@ -92,24 +95,96 @@ static inline std::string strip(const std::string &s) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using symbolsMap = std::map<std::string, int>;
|
||||||
|
using labelsMap = std::map<std::string, int>;
|
||||||
|
|
||||||
|
int find_available_address(const symbolsMap &symbols) {
|
||||||
|
int address = 16; // Start from 16 for variables
|
||||||
|
while (true) {
|
||||||
|
bool found = false;
|
||||||
|
for (const auto &pair : symbols) {
|
||||||
|
if (pair.second == address) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) return address;
|
||||||
|
++address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
// Load predefined symbols
|
||||||
|
std::ifstream file("./symbols.json");
|
||||||
|
nlohmann::json symbols_json = nlohmann::json::parse(file);
|
||||||
|
symbolsMap symbols_map = {};
|
||||||
|
labelsMap labels_map = symbols_json.get<symbolsMap>();
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(std::cin, line)) {
|
int line_number = 0;
|
||||||
|
std::vector<std::string> lines;
|
||||||
|
while (std::getline(std::cin, line)) { // Store lines for first pass
|
||||||
line = trim(line);
|
line = trim(line);
|
||||||
if (line.empty()) continue;
|
if (line.empty()) continue;
|
||||||
|
|
||||||
// skip comments
|
// skip comments
|
||||||
if (line[0] == '#' || (line.size() > 1 && line[0] == '/' && line[1] == '/')) continue;
|
if (line[0] == '#' || (line.size() > 1 && line[0] == '/' && line[1] == '/')) continue;
|
||||||
|
lines.push_back(line);
|
||||||
|
}
|
||||||
|
auto line_it = lines.begin();
|
||||||
|
while (line_it != lines.end()) { // First pass to handle labels
|
||||||
|
if ((*line_it)[0] == '(' && line_it->back() == ')') {
|
||||||
|
std::string label = line_it->substr(1, line_it->size() - 2);
|
||||||
|
labels_map[label] = line_number;
|
||||||
|
line_it = lines.erase(line_it); // Remove label lines
|
||||||
|
} else {
|
||||||
|
++line_it;
|
||||||
|
++line_number; // Only count non-label lines
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const std::string &line : lines) { // Second pass to handle variables
|
||||||
|
if (line[0] == '@') {
|
||||||
|
if (std::isalpha(line[1]) || line[1] == '_') {
|
||||||
|
std::string symbol = line.substr(1);
|
||||||
|
if (labels_map.find(symbol) != labels_map.end()) {
|
||||||
|
continue; // Skip labels, they are already handled
|
||||||
|
}
|
||||||
|
auto it = symbols_map.find(symbol);
|
||||||
|
if (it == symbols_map.end()) {
|
||||||
|
int address = find_available_address(symbols_map);
|
||||||
|
symbols_map[symbol] = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const std::string &line : lines) { // Second pass to generate
|
||||||
// A-instruction
|
// A-instruction
|
||||||
if (line[0] == '@') {
|
if (line[0] == '@') {
|
||||||
std::string value = line.substr(1);
|
// Check if it's a symbol or a number
|
||||||
if (value.empty()) continue;
|
if (std::isalpha(line[1]) || line[1] == '_') {
|
||||||
try {
|
std::string symbol = line.substr(1);
|
||||||
int intValue = std::stoi(value);
|
int address;
|
||||||
std::bitset<16> binaryValue(intValue);
|
// search labels first
|
||||||
|
auto labelIt = labels_map.find(symbol);
|
||||||
|
if (labelIt != labels_map.end()) {
|
||||||
|
address = labelIt->second;
|
||||||
|
} else {
|
||||||
|
// Search variables.
|
||||||
|
auto it = symbols_map.find(symbol);
|
||||||
|
if (it != symbols_map.end()) {
|
||||||
|
address = it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::bitset<16> binaryValue(address);
|
||||||
std::cout << binaryValue.to_string() << '\n';
|
std::cout << binaryValue.to_string() << '\n';
|
||||||
} catch (...) { continue; }
|
} else {
|
||||||
|
std::string value = line.substr(1);
|
||||||
|
if (value.empty()) continue;
|
||||||
|
try {
|
||||||
|
int intValue = std::stoi(value);
|
||||||
|
std::bitset<16> binaryValue(intValue);
|
||||||
|
std::cout << binaryValue.to_string() << '\n';
|
||||||
|
} catch (...) { continue; }
|
||||||
|
}
|
||||||
} else { // C-instruction
|
} else { // C-instruction
|
||||||
std::string instruction = "111";
|
std::string instruction = "111";
|
||||||
size_t eqPos = line.find('=');
|
size_t eqPos = line.find('=');
|
||||||
@@ -138,6 +213,7 @@ int main() {
|
|||||||
auto jumpIt = jumpMap.find(jump);
|
auto jumpIt = jumpMap.find(jump);
|
||||||
instruction += (jumpIt != jumpMap.end()) ? jumpIt->second : jumpMap.at("null");
|
instruction += (jumpIt != jumpMap.end()) ? jumpIt->second : jumpMap.at("null");
|
||||||
std::cout << instruction << '\n';
|
std::cout << instruction << '\n';
|
||||||
|
line_number++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
27
symbols.json
Normal file
27
symbols.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"SP": 0,
|
||||||
|
"LCL": 1,
|
||||||
|
"ARG": 2,
|
||||||
|
"THIS": 3,
|
||||||
|
"THAT": 4,
|
||||||
|
|
||||||
|
"R0": 0,
|
||||||
|
"R1": 1,
|
||||||
|
"R2": 2,
|
||||||
|
"R3": 3,
|
||||||
|
"R4": 4,
|
||||||
|
"R5": 5,
|
||||||
|
"R6": 6,
|
||||||
|
"R7": 7,
|
||||||
|
"R8": 8,
|
||||||
|
"R9": 9,
|
||||||
|
"R10": 10,
|
||||||
|
"R11": 11,
|
||||||
|
"R12": 12,
|
||||||
|
"R13": 13,
|
||||||
|
"R14": 14,
|
||||||
|
"R15": 15,
|
||||||
|
|
||||||
|
"SCREEN": 16384,
|
||||||
|
"KBD": 24576
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user