Files
hack-assembler/main.cpp
2026-06-02 15:59:28 +02:00

222 lines
6.8 KiB
C++

#include <iostream>
#include <bitset>
#include <string>
#include <cctype>
#include <map>
#include <fstream>
#include <nlohmann/json.hpp>
// Comp map
const std::map<std::string, std::string> compMap = {
{"0", "0101010"},
{"1", "0111111"},
{"-1", "0111010"},
{"D", "0001100"},
{"A", "0110000"},
{"!D", "0001101"},
{"!A", "0110001"},
{"-D", "0001111"},
{"-A", "0110011"},
{"D+1", "0011111"},
{"A+1", "0110111"},
{"D-1", "0001110"},
{"A-1", "0110010"},
{"D+A", "0000010"},
{"A+D", "0000010"},
{"D-A", "0010011"},
{"A-D", "0000111"},
{"D&A", "0000000"},
{"A&D", "0000000"},
{"D|A", "0010101"},
{"A|D", "0010101"},
{"M", "1110000"},
{"!M", "1110001"},
{"-M", "1110011"},
{"M+1", "1110111"},
{"M-1", "1110010"},
{"D+M", "1000010"},
{"D+M", "1000010"},
{"D-M", "1010011"},
{"M-D", "1000111"},
{"D&M", "1000000"},
{"M&D", "1000000"},
{"D|M", "1010101"},
{"M|D", "1010101"}
};
// dest Map
const std::map<std::string, std::string> destMap = {
{"null", "000"},
{"M", "001"},
{"D", "010"},
{"MD", "011"},
{"DM", "011"},
{"A", "100"},
{"AM", "101"},
{"MA", "101"},
{"AD", "110"},
{"DA", "110"},
{"ADM", "111"},
{"AMD", "111"},
{"DAM", "111"},
{"DMA", "111"},
{"MDA", "111"},
{"MAD", "111"}
};
// jump Map
const std::map<std::string, std::string> jumpMap = {
{"null", "000"},
{"JGT", "001"},
{"JEQ", "010"},
{"JGE", "011"},
{"JLT", "100"},
{"JNE", "101"},
{"JLE", "110"},
{"JMP", "111"}
};
static inline std::string trim(const std::string &s) {
size_t a = s.find_first_not_of(" \t\n\r");
if (a == std::string::npos) return "";
size_t b = s.find_last_not_of(" \t\n\r");
return s.substr(a, b - a + 1);
}
static inline std::string strip(const std::string &s) {
std::string result;
for (char c : s) {
if (!std::isspace(static_cast<unsigned char>(c))) {
result += c;
}
}
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() {
// 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;
int line_number = 0;
std::vector<std::string> lines;
while (std::getline(std::cin, line)) { // Store lines for first pass
line = trim(line);
if (line.empty()) continue;
// skip comments
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
if (line[0] == '@') {
// Check if it's a symbol or a number
if (std::isalpha(line[1]) || line[1] == '_') {
std::string symbol = line.substr(1);
int address;
// 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';
} 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
std::string instruction = "111";
size_t eqPos = line.find('=');
size_t scPos = line.find(';');
// comp
std::string comp;
if (eqPos != std::string::npos) {
comp = line.substr(eqPos + 1, scPos - eqPos - 1);
} else if (scPos != std::string::npos) {
comp = line.substr(0, scPos);
} else {
comp = line;
}
comp = strip(comp);
auto compIt = compMap.find(comp);
if (compIt == compMap.end()) continue;
instruction += compIt->second;
// dest use use destMap "null" key if not found
std::string dest = (eqPos != std::string::npos) ? line.substr(0, eqPos) : "null";
dest = strip(dest);
auto destIt = destMap.find(dest);
instruction += (destIt != destMap.end()) ? destIt->second : destMap.at("null");
// jump
std::string jump = (scPos != std::string::npos) ? line.substr(scPos + 1) : "null";
jump = strip(jump);
auto jumpIt = jumpMap.find(jump);
instruction += (jumpIt != jumpMap.end()) ? jumpIt->second : jumpMap.at("null");
std::cout << instruction << '\n';
line_number++;
}
}
return 0;
}