Compare commits

...

3 Commits

Author SHA1 Message Date
4afdcd1660 Cleanup the project
* Add .cache to gitignore (for the nlohmann::json library)
* Fix the project name
* Update the readme
2026-06-03 10:17:03 +02:00
214b3e4d9e Add symbol support 2026-06-02 15:59:28 +02:00
a53125fa6e asm-samples: remove heavy .hack files
This was tested for comparison and should be later generated on the fly for unit testing
2026-06-02 11:21:46 +02:00
8 changed files with 86 additions and 27543 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
out out
hack-assembler hack-assembler
build build
.cache

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.18) cmake_minimum_required(VERSION 3.18)
set(NAME "hack-assembly") set(NAME "hack-assembler")
project(${NAME} CXX) project(${NAME} CXX)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)

View File

@@ -1,6 +1,6 @@
# 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 ## Compilation
@@ -12,9 +12,15 @@ 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 > ./out/<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.

View File

@@ -1,6 +0,0 @@
0000000000000010
1110110000010000
0000000000000011
1110000010010000
0000000000000000
1110001100001000

View File

@@ -1,16 +0,0 @@
0000000000000000
1111110000010000
0000000000000001
1111010011010000
0000000000001010
1110001100000001
0000000000000001
1111110000010000
0000000000001100
1110101010000111
0000000000000000
1111110000010000
0000000000000010
1110001100001000
0000000000001110
1110101010000111

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +0,0 @@
0000000000000000
1111110000010000
0000000000010111
1110001100000110
0000000000010000
1110001100001000
0100000000000000
1110110000010000
0000000000010001
1110001100001000
0000000000010001
1111110000100000
1110111010001000
0000000000010001
1111110000010000
0000000000100000
1110000010010000
0000000000010001
1110001100001000
0000000000010000
1111110010011000
0000000000001010
1110001100000001
0000000000010111
1110101010000111

View File

@@ -96,30 +96,95 @@ static inline std::string strip(const std::string &s) {
} }
using symbolsMap = std::map<std::string, int>; 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 // Load predefined symbols
std::ifstream file("./symbols.json"); std::ifstream file("./symbols.json");
nlohmann::json symbols_json = nlohmann::json::parse(file); nlohmann::json symbols_json = nlohmann::json::parse(file);
symbolsMap symbols_map = symbols_json.get<symbolsMap>(); 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('=');
@@ -148,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;