add support for linking multiple programs + binaries

This commit is contained in:
Zack Buhman 2022-04-05 03:10:13 -07:00
parent b60dbed70b
commit 210a8d1b06
10 changed files with 382 additions and 262 deletions

View File

@ -4,8 +4,8 @@ LDFLAGS =
TARGET = TARGET =
CXX = $(TARGET)g++ CXX = $(TARGET)g++
OBJS = main.o fromstring.o addressing_mode.o codec.o parser.o OBJS = main.o tostring.o fromstring.o addressing_mode.o codec.o parser.o symbol.o
HEADERS = $(wildcard *.h) HEADERS = $(wildcard *.hh)
all: main all: main

View File

@ -3,6 +3,8 @@
#include <optional> #include <optional>
#include <variant> #include <variant>
#include <vector> #include <vector>
#include <memory>
#include <unordered_map>
#include "isa.hh" #include "isa.hh"
@ -24,7 +26,27 @@ namespace assembler {
isa::mode mode; isa::mode mode;
std::variant<literal_t, reference_t, implied_t> value; std::variant<literal_t, reference_t, implied_t> value;
size_t location; size_t location;
ssize_t row; // debug
}; };
using program_t = std::vector<instruction_t>; struct blob_t {
struct {
size_t start;
size_t start_l;
size_t start_h;
size_t size_l;
size_t size_h;
} symbol;
size_t location;
std::shared_ptr<std::string> buf;
};
using program_t = std::vector<std::variant<instruction_t, blob_t>>;
struct symbol_strings_t {
size_t current_symbol;
std::unordered_map<std::string, size_t> map;
};
using symbol_table_t = std::unordered_map<size_t, size_t>;
} }

View File

@ -10,7 +10,7 @@ namespace codec {
const std::map<std::tuple<isa::op, isa::mode>, uint8_t>& encode() { const std::map<std::tuple<isa::op, isa::mode>, uint8_t>& encode() {
static const std::map<std::tuple<isa::op, isa::mode>, uint8_t> _ = { static const std::map<std::tuple<isa::op, isa::mode>, uint8_t> _ = {
{{isa::op::BRK, isa::mode::S}, 0}, {{isa::op::BRK, isa::mode::S}, 0},
{{isa::op::ORA, isa::mode::ZPX}, 1}, {{isa::op::ORA, isa::mode::ZPII}, 1},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 2}, //{{isa::op::O_INVALID, isa::mode::M_INVALID}, 2},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 3}, //{{isa::op::O_INVALID, isa::mode::M_INVALID}, 3},
{{isa::op::TSB, isa::mode::ZP}, 4}, {{isa::op::TSB, isa::mode::ZP}, 4},
@ -19,7 +19,7 @@ namespace codec {
{{isa::op::RMB0, isa::mode::ZP}, 7}, {{isa::op::RMB0, isa::mode::ZP}, 7},
{{isa::op::PHP, isa::mode::S}, 8}, {{isa::op::PHP, isa::mode::S}, 8},
{{isa::op::ORA, isa::mode::IMM}, 9}, {{isa::op::ORA, isa::mode::IMM}, 9},
{{isa::op::ASL, isa::mode::A}, 10}, {{isa::op::ASL, isa::mode::ACC}, 10},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 11}, //{{isa::op::O_INVALID, isa::mode::M_INVALID}, 11},
{{isa::op::TSB, isa::mode::A}, 12}, {{isa::op::TSB, isa::mode::A}, 12},
{{isa::op::ORA, isa::mode::A}, 13}, {{isa::op::ORA, isa::mode::A}, 13},
@ -36,7 +36,7 @@ namespace codec {
{{isa::op::RMB1, isa::mode::ZP}, 23}, {{isa::op::RMB1, isa::mode::ZP}, 23},
{{isa::op::CLC, isa::mode::I}, 24}, {{isa::op::CLC, isa::mode::I}, 24},
{{isa::op::ORA, isa::mode::AIY}, 25}, {{isa::op::ORA, isa::mode::AIY}, 25},
{{isa::op::INC, isa::mode::A}, 26}, {{isa::op::INC, isa::mode::ACC}, 26},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 27}, //{{isa::op::O_INVALID, isa::mode::M_INVALID}, 27},
{{isa::op::TRB, isa::mode::A}, 28}, {{isa::op::TRB, isa::mode::A}, 28},
{{isa::op::ORA, isa::mode::AIX}, 29}, {{isa::op::ORA, isa::mode::AIX}, 29},
@ -53,7 +53,7 @@ namespace codec {
{{isa::op::RMB2, isa::mode::ZP}, 39}, {{isa::op::RMB2, isa::mode::ZP}, 39},
{{isa::op::PLP, isa::mode::S}, 40}, {{isa::op::PLP, isa::mode::S}, 40},
{{isa::op::AND, isa::mode::IMM}, 41}, {{isa::op::AND, isa::mode::IMM}, 41},
{{isa::op::ROL, isa::mode::A}, 42}, {{isa::op::ROL, isa::mode::ACC}, 42},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 43}, //{{isa::op::O_INVALID, isa::mode::M_INVALID}, 43},
{{isa::op::BIT, isa::mode::A}, 44}, {{isa::op::BIT, isa::mode::A}, 44},
{{isa::op::AND, isa::mode::A}, 45}, {{isa::op::AND, isa::mode::A}, 45},
@ -70,7 +70,7 @@ namespace codec {
{{isa::op::RMB3, isa::mode::ZP}, 55}, {{isa::op::RMB3, isa::mode::ZP}, 55},
{{isa::op::SEC, isa::mode::I}, 56}, {{isa::op::SEC, isa::mode::I}, 56},
{{isa::op::AND, isa::mode::AIY}, 57}, {{isa::op::AND, isa::mode::AIY}, 57},
{{isa::op::DEC, isa::mode::A}, 58}, {{isa::op::DEC, isa::mode::ACC}, 58},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 59}, //{{isa::op::O_INVALID, isa::mode::M_INVALID}, 59},
{{isa::op::BIT, isa::mode::AIX}, 60}, {{isa::op::BIT, isa::mode::AIX}, 60},
{{isa::op::AND, isa::mode::AIX}, 61}, {{isa::op::AND, isa::mode::AIX}, 61},
@ -87,7 +87,7 @@ namespace codec {
{{isa::op::RMB4, isa::mode::ZP}, 71}, {{isa::op::RMB4, isa::mode::ZP}, 71},
{{isa::op::PHA, isa::mode::S}, 72}, {{isa::op::PHA, isa::mode::S}, 72},
{{isa::op::EOR, isa::mode::IMM}, 73}, {{isa::op::EOR, isa::mode::IMM}, 73},
{{isa::op::LSR, isa::mode::A}, 74}, {{isa::op::LSR, isa::mode::ACC}, 74},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 75}, //{{isa::op::O_INVALID, isa::mode::M_INVALID}, 75},
{{isa::op::JMP, isa::mode::A}, 76}, {{isa::op::JMP, isa::mode::A}, 76},
{{isa::op::EOR, isa::mode::A}, 77}, {{isa::op::EOR, isa::mode::A}, 77},
@ -121,7 +121,7 @@ namespace codec {
{{isa::op::RMB6, isa::mode::ZP}, 103}, {{isa::op::RMB6, isa::mode::ZP}, 103},
{{isa::op::PLA, isa::mode::S}, 104}, {{isa::op::PLA, isa::mode::S}, 104},
{{isa::op::ADC, isa::mode::IMM}, 105}, {{isa::op::ADC, isa::mode::IMM}, 105},
{{isa::op::ROR, isa::mode::A}, 106}, {{isa::op::ROR, isa::mode::ACC}, 106},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 107}, //{{isa::op::O_INVALID, isa::mode::M_INVALID}, 107},
{{isa::op::JMP, isa::mode::AI}, 108}, {{isa::op::JMP, isa::mode::AI}, 108},
{{isa::op::ADC, isa::mode::A}, 109}, {{isa::op::ADC, isa::mode::A}, 109},
@ -287,7 +287,7 @@ namespace codec {
const std::map<uint8_t, std::tuple<isa::op, isa::mode>>& decode() { const std::map<uint8_t, std::tuple<isa::op, isa::mode>>& decode() {
static const std::map<uint8_t, std::tuple<isa::op, isa::mode>> _ = { static const std::map<uint8_t, std::tuple<isa::op, isa::mode>> _ = {
{0, {isa::op::BRK, isa::mode::S}}, {0, {isa::op::BRK, isa::mode::S}},
{1, {isa::op::ORA, isa::mode::ZPX}}, {1, {isa::op::ORA, isa::mode::ZPII}},
//{2, {isa::op::O_INVALID, isa::mode::M_INVALID}}, //{2, {isa::op::O_INVALID, isa::mode::M_INVALID}},
//{3, {isa::op::O_INVALID, isa::mode::M_INVALID}}, //{3, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{4, {isa::op::TSB, isa::mode::ZP}}, {4, {isa::op::TSB, isa::mode::ZP}},
@ -296,7 +296,7 @@ namespace codec {
{7, {isa::op::RMB0, isa::mode::ZP}}, {7, {isa::op::RMB0, isa::mode::ZP}},
{8, {isa::op::PHP, isa::mode::S}}, {8, {isa::op::PHP, isa::mode::S}},
{9, {isa::op::ORA, isa::mode::IMM}}, {9, {isa::op::ORA, isa::mode::IMM}},
{10, {isa::op::ASL, isa::mode::A}}, {10, {isa::op::ASL, isa::mode::ACC}},
//{11, {isa::op::O_INVALID, isa::mode::M_INVALID}}, //{11, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{12, {isa::op::TSB, isa::mode::A}}, {12, {isa::op::TSB, isa::mode::A}},
{13, {isa::op::ORA, isa::mode::A}}, {13, {isa::op::ORA, isa::mode::A}},
@ -313,7 +313,7 @@ namespace codec {
{23, {isa::op::RMB1, isa::mode::ZP}}, {23, {isa::op::RMB1, isa::mode::ZP}},
{24, {isa::op::CLC, isa::mode::I}}, {24, {isa::op::CLC, isa::mode::I}},
{25, {isa::op::ORA, isa::mode::AIY}}, {25, {isa::op::ORA, isa::mode::AIY}},
{26, {isa::op::INC, isa::mode::A}}, {26, {isa::op::INC, isa::mode::ACC}},
//{27, {isa::op::O_INVALID, isa::mode::M_INVALID}}, //{27, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{28, {isa::op::TRB, isa::mode::A}}, {28, {isa::op::TRB, isa::mode::A}},
{29, {isa::op::ORA, isa::mode::AIX}}, {29, {isa::op::ORA, isa::mode::AIX}},
@ -330,7 +330,7 @@ namespace codec {
{39, {isa::op::RMB2, isa::mode::ZP}}, {39, {isa::op::RMB2, isa::mode::ZP}},
{40, {isa::op::PLP, isa::mode::S}}, {40, {isa::op::PLP, isa::mode::S}},
{41, {isa::op::AND, isa::mode::IMM}}, {41, {isa::op::AND, isa::mode::IMM}},
{42, {isa::op::ROL, isa::mode::A}}, {42, {isa::op::ROL, isa::mode::ACC}},
//{43, {isa::op::O_INVALID, isa::mode::M_INVALID}}, //{43, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{44, {isa::op::BIT, isa::mode::A}}, {44, {isa::op::BIT, isa::mode::A}},
{45, {isa::op::AND, isa::mode::A}}, {45, {isa::op::AND, isa::mode::A}},
@ -347,7 +347,7 @@ namespace codec {
{55, {isa::op::RMB3, isa::mode::ZP}}, {55, {isa::op::RMB3, isa::mode::ZP}},
{56, {isa::op::SEC, isa::mode::I}}, {56, {isa::op::SEC, isa::mode::I}},
{57, {isa::op::AND, isa::mode::AIY}}, {57, {isa::op::AND, isa::mode::AIY}},
{58, {isa::op::DEC, isa::mode::A}}, {58, {isa::op::DEC, isa::mode::ACC}},
//{59, {isa::op::O_INVALID, isa::mode::M_INVALID}}, //{59, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{60, {isa::op::BIT, isa::mode::AIX}}, {60, {isa::op::BIT, isa::mode::AIX}},
{61, {isa::op::AND, isa::mode::AIX}}, {61, {isa::op::AND, isa::mode::AIX}},
@ -364,7 +364,7 @@ namespace codec {
{71, {isa::op::RMB4, isa::mode::ZP}}, {71, {isa::op::RMB4, isa::mode::ZP}},
{72, {isa::op::PHA, isa::mode::S}}, {72, {isa::op::PHA, isa::mode::S}},
{73, {isa::op::EOR, isa::mode::IMM}}, {73, {isa::op::EOR, isa::mode::IMM}},
{74, {isa::op::LSR, isa::mode::A}}, {74, {isa::op::LSR, isa::mode::ACC}},
//{75, {isa::op::O_INVALID, isa::mode::M_INVALID}}, //{75, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{76, {isa::op::JMP, isa::mode::A}}, {76, {isa::op::JMP, isa::mode::A}},
{77, {isa::op::EOR, isa::mode::A}}, {77, {isa::op::EOR, isa::mode::A}},
@ -398,7 +398,7 @@ namespace codec {
{103, {isa::op::RMB6, isa::mode::ZP}}, {103, {isa::op::RMB6, isa::mode::ZP}},
{104, {isa::op::PLA, isa::mode::S}}, {104, {isa::op::PLA, isa::mode::S}},
{105, {isa::op::ADC, isa::mode::IMM}}, {105, {isa::op::ADC, isa::mode::IMM}},
{106, {isa::op::ROR, isa::mode::A}}, {106, {isa::op::ROR, isa::mode::ACC}},
//{107, {isa::op::O_INVALID, isa::mode::M_INVALID}}, //{107, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{108, {isa::op::JMP, isa::mode::AI}}, {108, {isa::op::JMP, isa::mode::AI}},
{109, {isa::op::ADC, isa::mode::A}}, {109, {isa::op::ADC, isa::mode::A}},

148
main.cc
View File

@ -2,83 +2,69 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <unordered_map> #include <unordered_map>
#include <memory>
#include "assembler.hh" #include "assembler.hh"
#include "addressing_mode.hh" #include "addressing_mode.hh"
#include "codec.hh" #include "codec.hh"
#include "parser.hh" #include "parser.hh"
#include "tostring.hh"
#include "symbol.hh"
using symbol_table_t = std::unordered_map<size_t, assembler::program_t::const_iterator>; using namespace std::literals;
bool resolve_symbols(assembler::program_t& program, symbol_table_t& symbol_table) void serialize_program(const assembler::program_t& program, const assembler::symbol_table_t &symbol_table, std::ostream& out)
{
size_t offset = 0;
for (auto ins = program.begin(); ins != program.end(); ins++) {
if (ins->symbol != std::nullopt) {
auto [_, ok] = symbol_table.insert({*(ins->symbol), ins});
if (!ok) {
std::cout << "duplicate symbol\n";
return false;
}
std::cout << *(ins->symbol) << ' ' << offset << '\n';
}
ins->location = offset;
auto am_it = addressing_mode().find(ins->mode);
assert (am_it != addressing_mode().end());
offset += 1 + am_it->second.len;
}
return true;
}
void serialize_program(const assembler::program_t& program, const symbol_table_t &symbol_table, size_t link_location, std::ostream& out)
{ {
auto serialize_instruction = [&](assembler::instruction_t& ins) -> void {
//std::cerr << "enc " << (int)ins.op << ' ' << (int)ins.mode << '\n';
char buf[3]; char buf[3];
for (auto ins = program.cbegin(); ins != program.cend(); ins++) { auto opcode_it = codec::encode().find({ins.op, ins.mode});
std::cerr << "enc " << (int)ins->op << ' ' << (int)ins->mode << '\n'; if (opcode_it == codec::encode().end()) {
auto opcode_it = codec::encode().find({ins->op, ins->mode}); std::cerr << ins.row << ": illegal op,mode: "
assert (opcode_it != codec::encode().end()); << tostring::op().find(ins.op)->second << ' '
<< tostring::mode().find(ins.mode)->second << '\n';
return;
}
buf[0] = opcode_it->second; buf[0] = opcode_it->second;
auto am_it = addressing_mode().find(ins->mode); auto am_it = addressing_mode().find(ins.mode);
assert (am_it != addressing_mode().end()); assert (am_it != addressing_mode().end());
auto get_value = [&]() -> ssize_t { auto get_value = [&]() -> ssize_t {
if (std::holds_alternative<assembler::reference_t>(ins->value)) { if (std::holds_alternative<assembler::reference_t>(ins.value)) {
assembler::reference_t ref = std::get<assembler::reference_t>(ins->value); assembler::reference_t ref = std::get<assembler::reference_t>(ins.value);
auto sym_it = symbol_table.find(ref.symbol); auto sym_it = symbol_table.find(ref.symbol);
assert (sym_it != symbol_table.end()); if (sym_it == symbol_table.end()) {
auto ref_ins = sym_it->second; std::cerr << "get_value: " << ref.symbol << '\n';
if (ins->mode == isa::mode::R) { assert(false);
std::cout << "relative\n"; }
ssize_t offset = static_cast<ssize_t>(ref_ins->location) - static_cast<ssize_t>(ins->location + 2); auto ref_location = sym_it->second;
if (ins.mode == isa::mode::R) {
ssize_t offset = static_cast<ssize_t>(ref_location) - static_cast<ssize_t>(ins.location + 2);
return offset; return offset;
} else { } else {
return ref_ins->location + link_location; return ref_location;
} }
} else if (std::holds_alternative<assembler::literal_t>(ins->value)) { } else if (std::holds_alternative<assembler::literal_t>(ins.value)) {
assembler::literal_t lit = std::get<assembler::literal_t>(ins->value); assembler::literal_t lit = std::get<assembler::literal_t>(ins.value);
return lit.num; return lit.num;
} else if (std::holds_alternative<assembler::implied_t>(ins->value)) { } else if (std::holds_alternative<assembler::implied_t>(ins.value)) {
assert (std::holds_alternative<assembler::implied_t>(ins->value)); //assert (std::holds_alternative<assembler::implied_t>(ins.value));
return 0;
} else { } else {
assert (false); assert (false);
} }
return 0;
}; };
size_t value = get_value(); size_t value = get_value();
if (!am_it->second.valid(value)) { if (!am_it->second.valid(value)) {
std::cout << "overflow at h" << std::hex << ins->location << '\n'; std::cerr << "overflow at " << ins.row << " value: " << value << '\n';
return;
} }
std::cout << "value " << std::hex << value << ' ' << am_it->second.len << '\n'; //std::cerr << "value " << std::hex << value << ' ' << am_it->second.len << '\n';
if (am_it->second.len >= 2) if (am_it->second.len >= 2)
buf[2] = (value >> 8) & 0xff; buf[2] = (value >> 8) & 0xff;
@ -86,6 +72,19 @@ void serialize_program(const assembler::program_t& program, const symbol_table_t
buf[1] = (value >> 0) & 0xff; buf[1] = (value >> 0) & 0xff;
out.write(buf, 1 + am_it->second.len); out.write(buf, 1 + am_it->second.len);
};
for (auto prog_it = program.cbegin(); prog_it != program.cend(); prog_it++) {
if (std::holds_alternative<assembler::instruction_t>(*prog_it)) {
auto ins = std::get<assembler::instruction_t>(*prog_it);
serialize_instruction(ins);
} else if (std::holds_alternative<assembler::blob_t>(*prog_it)) {
auto blob = std::get<assembler::blob_t>(*prog_it);
out.write(blob.buf->data(), blob.buf->size());
} else {
assert (false);
}
} }
} }
@ -96,7 +95,16 @@ int main(int argc, char * argv[])
return -1; return -1;
} }
std::string input_filename {argv[1]}; assembler::program_t program;
assembler::symbol_strings_t symbol_strings;
symbol_strings.current_symbol = 0;
assembler::symbol_table_t symbol_table;
bool ok;
for (int i = 1; i < (argc - 1); i++) {
std::cerr << "input[" << i << "]: " << argv[i] << '\n';
std::string input_filename {argv[i]};
std::ifstream is {input_filename, std::ios::binary | std::ios::ate}; std::ifstream is {input_filename, std::ios::binary | std::ios::ate};
if (!is.is_open()) { if (!is.is_open()) {
std::cerr << "failed to open " << input_filename << '\n'; std::cerr << "failed to open " << input_filename << '\n';
@ -104,33 +112,49 @@ int main(int argc, char * argv[])
} }
auto size = is.tellg(); auto size = is.tellg();
std::string buf(size, '\0'); // construct string to stream size auto buf = std::make_unique<std::string>(size, '\0'); // construct string to stream size
is.seekg(0); is.seekg(0);
is.read(buf.data(), size); is.read(buf->data(), size);
is.close(); is.close();
std::string_view ext = input_filename.substr(input_filename.size() - 4, 4);
if (ext == ".asm"s) {
ok = parser::parse(*buf, program, symbol_strings);
if (!ok)
return -1;
} else if (ext == ".bin"s) {
std::string name = input_filename.substr(0, input_filename.size() - 4);
assembler::blob_t blob {
.symbol = {
.start = symbol::get(symbol_strings, "_bin_"s + name + "_start"s),
.start_l = symbol::get(symbol_strings, "_bin_"s + name + "_start_l"s),
.start_h = symbol::get(symbol_strings, "_bin_"s + name + "_start_h"s),
.size_l = symbol::get(symbol_strings, "_bin_"s + name + "_size_l"s),
.size_h = symbol::get(symbol_strings, "_bin_"s + name + "_size_h"s),
},
.location = 0xeeee,
.buf = std::move(buf),
};
program.push_back(blob);
} else {
std::cerr << "unknown extension: " << ext << '\n';
return -1;
}
}
assembler::program_t program; ok = symbol::resolve(0xc000, program, symbol_table);
symbol_table_t symbol_table;
bool ok;
ok = parser::parse(buf, program);
if (!ok) if (!ok)
return -1; return -1;
ok = resolve_symbols(program, symbol_table); std::cerr << "output: " << argv[argc - 1] << '\n';
if (!ok) std::string output_filename {argv[argc - 1]};
return -1;
std::string output_filename {argv[2]};
std::ofstream out {output_filename, std::ios::binary}; std::ofstream out {output_filename, std::ios::binary};
if (!out.is_open()) { if (!out.is_open()) {
std::cerr << "failed to open " << output_filename << '\n'; std::cerr << "failed to open " << output_filename << '\n';
return -1; return -1;
} }
serialize_program(program, symbol_table, 0x8200, out); serialize_program(program, symbol_table, out);
out.close(); out.close();
return 0; return 0;

View File

@ -8,6 +8,7 @@
#include "addressing_mode.hh" #include "addressing_mode.hh"
#include "fromstring.hh" #include "fromstring.hh"
#include "isa.hh" #include "isa.hh"
#include "symbol.hh"
static bool tokenize(std::string_view buf, std::function<bool(std::string_view, ssize_t, ssize_t)> cb) static bool tokenize(std::string_view buf, std::function<bool(std::string_view, ssize_t, ssize_t)> cb)
{ {
@ -68,30 +69,14 @@ namespace parser {
}; };
} }
using symbol_strings_t = std::unordered_map<std::string_view, size_t>;
namespace parser { namespace parser {
bool parse(std::string_view buf, assembler::program_t& program) bool parse(std::string_view buf, assembler::program_t& program, assembler::symbol_strings_t& symbol_strings)
{ {
size_t current_symbol = 0;
symbol_strings_t ss;
parser::state state = parser::state::label_or_op; parser::state state = parser::state::label_or_op;
auto get_symbol = [&](std::string_view s) {
auto find = ss.find(s);
if (find == ss.end()) {
auto insert = ss.insert({s, current_symbol++});
assert(insert.second);
return (insert.first)->second;
} else {
return find->second;
}
};
assembler::instruction_t current_instruction; assembler::instruction_t current_instruction;
ssize_t current_row; current_instruction.location = 0xeeee;
bool inside_comment = false; bool inside_comment = false;
@ -100,11 +85,11 @@ bool parse(std::string_view buf, assembler::program_t& program)
switch (state) { switch (state) {
case parser::state::label_or_op: case parser::state::label_or_op:
{ {
current_row = row; current_instruction.row = row;
if (s.back() == ':') { if (s.back() == ':') {
std::string_view key = s.substr(0, s.length() - 1); std::string_view key = s.substr(0, s.length() - 1);
auto symbol = get_symbol(key); auto symbol = symbol::get(symbol_strings, key);
std::cerr << "label `" << symbol << "`\n"; //std::cerr << "label `" << symbol << "`\n";
current_instruction.symbol = symbol; current_instruction.symbol = symbol;
state = parser::state::op; state = parser::state::op;
return true; return true;
@ -121,28 +106,28 @@ bool parse(std::string_view buf, assembler::program_t& program)
} }
case parser::state::op: case parser::state::op:
{ {
assert(row == current_row); assert(row == current_instruction.row);
auto op_it = fromstring::op().find(s); auto op_it = fromstring::op().find(s);
if (op_it == fromstring::op().end()) { if (op_it == fromstring::op().end()) {
std::cerr << "invalid op `" << s << "`\n"; std::cerr << row << ',' << col << ": invalid op `" << s << "`\n";
return false; return false;
} else { } else {
current_instruction.op = op_it->second; current_instruction.op = op_it->second;
std::cerr << "ok op `" << static_cast<int>(op_it->second) << "`\n"; //std::cerr << row << ": ok op `" << static_cast<int>(op_it->second) << "`\n";
} }
state = parser::state::mode; state = parser::state::mode;
return true; return true;
} }
case parser::state::mode: case parser::state::mode:
{ {
assert(row == current_row); assert(row == current_instruction.row);
auto mode_it = fromstring::mode().find(s); auto mode_it = fromstring::mode().find(s);
if (mode_it == fromstring::mode().end()) { if (mode_it == fromstring::mode().end()) {
std::cerr << "invalid mode `" << s << "`\n"; std::cerr << row << ',' << col << ": invalid mode `" << s << "`\n";
return false; return false;
} else { } else {
current_instruction.mode = mode_it->second; current_instruction.mode = mode_it->second;
std::cerr << "ok mode `" << static_cast<int>(mode_it->second) << "`\n"; //std::cerr << "ok mode `" << static_cast<int>(mode_it->second) << "`\n";
} }
state = parser::state::value; state = parser::state::value;
return true; return true;
@ -152,14 +137,14 @@ bool parse(std::string_view buf, assembler::program_t& program)
auto am_it = addressing_mode().find(current_instruction.mode); auto am_it = addressing_mode().find(current_instruction.mode);
assert(am_it != addressing_mode().end()); assert(am_it != addressing_mode().end());
if (am_it->second.len == 0) { if (am_it->second.len == 0) {
std::cerr << "no value expected\n"; //std::cerr << row << ',' << col << ": no value expected\n";
assembler::implied_t i {}; assembler::implied_t i {};
current_instruction.value = i; current_instruction.value = i;
state = parser::state::comment; state = parser::state::comment;
continue; continue;
} }
assert(row == current_row); assert(row == current_instruction.row);
auto parse_integer = [](std::string_view s, int base) -> std::optional<ssize_t> { auto parse_integer = [](std::string_view s, int base) -> std::optional<ssize_t> {
std::string value_str {s.data(), s.length()}; std::string value_str {s.data(), s.length()};
@ -185,52 +170,62 @@ bool parse(std::string_view buf, assembler::program_t& program)
case '7': case '7':
case '8': case '8':
case '9': case '9':
case 'A': case 'a':
case 'B': case 'b':
case 'C': case 'c':
case 'D': case 'd':
case 'E': case 'e':
case 'F': case 'f':
{ {
literal = parse_integer(s, 16); literal = parse_integer(s, 16);
if (literal == std::nullopt) { if (literal == std::nullopt) {
std::cerr << row << ": invalid hex literal\n"; std::cerr << row << ',' << col << ": invalid hex literal\n";
} }
current_instruction.value = as_literal(*literal); current_instruction.value = as_literal(*literal);
std::cerr << "value hex literal `" << *literal << "`\n"; //std::cerr << "value hex literal `" << *literal << "`\n";
break; break;
} }
case 'h': case '%':
{
literal = parse_integer(s.substr(1, s.length() - 1), 2);
if (literal == std::nullopt) {
std::cerr << row << ',' << col << ": invalid bin literal\n";
}
current_instruction.value = as_literal(*literal);
//std::cerr << "value bin literal `" << *literal << "`\n";
break;
}
case '$':
{ {
literal = parse_integer(s.substr(1, s.length() - 1), 16); literal = parse_integer(s.substr(1, s.length() - 1), 16);
if (literal == std::nullopt) { if (literal == std::nullopt) {
std::cerr << row << ": invalid hex literal\n"; std::cerr << row << ',' << col << ": invalid hex literal\n";
} }
current_instruction.value = as_literal(*literal); current_instruction.value = as_literal(*literal);
std::cerr << "value hex literal `" << *literal << "`\n"; //std::cerr << "value hex literal `" << *literal << "`\n";
break; break;
} }
case 'd': case 'i':
{ {
literal = parse_integer(s.substr(1, s.length() - 1), 10); literal = parse_integer(s.substr(1, s.length() - 1), 10);
if (literal == std::nullopt) { if (literal == std::nullopt) {
std::cerr << row << ": invalid dec literal\n"; std::cerr << row << ',' << col << ": invalid dec literal\n";
return false; return false;
} }
current_instruction.value = as_literal(*literal); current_instruction.value = as_literal(*literal);
std::cerr << "value dec literal `" << *literal << "`\n"; //std::cerr << "value dec literal `" << *literal << "`\n";
break; break;
} }
case ':': case ':':
{ {
std::string_view key = s.substr(1, s.length() - 1); std::string_view key = s.substr(1, s.length() - 1);
assembler::reference_t reference = { get_symbol(key) }; assembler::reference_t reference = { symbol::get(symbol_strings, key) };
current_instruction.value = reference; current_instruction.value = reference;
std::cerr << "value reference `" << reference.symbol << "`\n"; //std::cerr << "value reference `" << reference.symbol << "`\n";
break; break;
} }
default: default:
std::cerr << row << ": invalid base\n"; std::cerr << row << ',' << col << ": invalid base\n";
return false; return false;
} }
@ -239,8 +234,8 @@ bool parse(std::string_view buf, assembler::program_t& program)
} }
case parser::state::comment: case parser::state::comment:
{ {
if (row != current_row) { if (row != current_instruction.row) {
std::cerr << "push " << (int)current_instruction.op << '\n'; //std::cerr << "push " << (int)current_instruction.op << '\n';
inside_comment = false; inside_comment = false;
program.push_back(current_instruction); program.push_back(current_instruction);
state = parser::state::label_or_op; state = parser::state::label_or_op;
@ -253,13 +248,13 @@ bool parse(std::string_view buf, assembler::program_t& program)
} else if (inside_comment) { } else if (inside_comment) {
return true; return true;
} else { } else {
std::cerr << row << ": expected comment\n"; std::cerr << row << ',' << col << ": expected comment\n";
return false; return false;
} }
} }
case parser::state::just_comment: case parser::state::just_comment:
{ {
if (row != current_row) { if (row != current_instruction.row) {
inside_comment = false; inside_comment = false;
state = parser::state::label_or_op; state = parser::state::label_or_op;
continue; continue;
@ -271,7 +266,7 @@ bool parse(std::string_view buf, assembler::program_t& program)
} else if (inside_comment) { } else if (inside_comment) {
return true; return true;
} else { } else {
std::cerr << "expected comment\n"; std::cerr << row << ',' << col << ": expected comment\n";
return false; return false;
} }
} }

View File

@ -5,5 +5,5 @@
#include "assembler.hh" #include "assembler.hh"
namespace parser { namespace parser {
bool parse(std::string_view buf, assembler::program_t& program); bool parse(std::string_view buf, assembler::program_t& program, assembler::symbol_strings_t& symbol_strings);
} }

63
symbol.cc Normal file
View File

@ -0,0 +1,63 @@
#include <string_view>
#include <cassert>
#include <iostream>
#include "assembler.hh"
#include "addressing_mode.hh"
namespace symbol {
size_t get(assembler::symbol_strings_t& symbol_strings, std::string_view s) {
std::string key {s};
auto find = symbol_strings.map.find(key);
if (find == symbol_strings.map.end()) {
std::cerr << "get new: " << s << ' ' << symbol_strings.current_symbol << '\n';
auto insert = symbol_strings.map.insert({key, symbol_strings.current_symbol++});
assert(insert.second);
return (insert.first)->second;
} else {
return find->second;
}
}
bool resolve(size_t link_location, assembler::program_t& program, assembler::symbol_table_t& symbol_table)
{
size_t offset = link_location;
for (auto prog_it = program.begin(); prog_it != program.end(); prog_it++) {
if (std::holds_alternative<assembler::instruction_t>(*prog_it)) {
auto& ins = std::get<assembler::instruction_t>(*prog_it);
ins.location = offset;
if (ins.symbol != std::nullopt) {
auto [_, ok] = symbol_table.insert({*(ins.symbol), offset});
if (!ok) {
std::cerr << ins.row << ": duplicate symbol\n";
return false;
}
}
auto am_it = addressing_mode().find(ins.mode);
assert (am_it != addressing_mode().end());
offset += 1 + am_it->second.len;
} else if (std::holds_alternative<assembler::blob_t>(*prog_it)) {
auto& blob = std::get<assembler::blob_t>(*prog_it);
blob.location = offset + 4;
auto [_1, ok1] = symbol_table.insert({blob.symbol.start, offset});
auto [_2, ok2] = symbol_table.insert({blob.symbol.start_l, (offset >> 0) & 0xff});
auto [_3, ok3] = symbol_table.insert({blob.symbol.start_h, (offset >> 8) & 0xff});
auto [_4, ok4] = symbol_table.insert({blob.symbol.size_l, (blob.buf->size() >> 0) & 0xff});
auto [_5, ok5] = symbol_table.insert({blob.symbol.size_h, (blob.buf->size() >> 8) & 0xff});
if (!ok1 || !ok2 || !ok3 || !ok4 || !ok5) {
std::cerr << "duplicate blob symbol\n";
return false;
}
offset += 4 + blob.buf->size();
} else {
assert(false);
}
}
return true;
}
}

11
symbol.hh Normal file
View File

@ -0,0 +1,11 @@
#include <string_view>
#include "assembler.hh"
namespace symbol {
size_t get(assembler::symbol_strings_t& symbol_strings, std::string_view s);
bool resolve(size_t link_location, assembler::program_t& program, assembler::symbol_table_t& symbol_table);
}

View File

@ -1,127 +1,133 @@
#include <map> #include <unordered_map>
#include <string> #include <string>
#include "instruction.hh" #include "isa.hh"
#include "mneumonic.hh" #include "mneumonic.hh"
namespace tostring { namespace tostring {
const std::unordered_map<op_t, std::string_view> op { const std::unordered_map<isa::op, std::string_view>& op() {
{op::ADC, mneumonic::op::ADC}, static const std::unordered_map<isa::op, std::string_view> _ = {
{op::AND, mneumonic::op::AND}, {isa::op::ADC, mneumonic::op::ADC},
{op::ASL, mneumonic::op::ASL}, {isa::op::AND, mneumonic::op::AND},
{op::BBR0, mneumonic::op::BBR0}, {isa::op::ASL, mneumonic::op::ASL},
{op::BBR1, mneumonic::op::BBR1}, {isa::op::BBR0, mneumonic::op::BBR0},
{op::BBR2, mneumonic::op::BBR2}, {isa::op::BBR1, mneumonic::op::BBR1},
{op::BBR3, mneumonic::op::BBR3}, {isa::op::BBR2, mneumonic::op::BBR2},
{op::BBR4, mneumonic::op::BBR4}, {isa::op::BBR3, mneumonic::op::BBR3},
{op::BBR5, mneumonic::op::BBR5}, {isa::op::BBR4, mneumonic::op::BBR4},
{op::BBR6, mneumonic::op::BBR6}, {isa::op::BBR5, mneumonic::op::BBR5},
{op::BBR7, mneumonic::op::BBR7}, {isa::op::BBR6, mneumonic::op::BBR6},
{op::BBS0, mneumonic::op::BBS0}, {isa::op::BBR7, mneumonic::op::BBR7},
{op::BBS1, mneumonic::op::BBS1}, {isa::op::BBS0, mneumonic::op::BBS0},
{op::BBS2, mneumonic::op::BBS2}, {isa::op::BBS1, mneumonic::op::BBS1},
{op::BBS3, mneumonic::op::BBS3}, {isa::op::BBS2, mneumonic::op::BBS2},
{op::BBS4, mneumonic::op::BBS4}, {isa::op::BBS3, mneumonic::op::BBS3},
{op::BBS5, mneumonic::op::BBS5}, {isa::op::BBS4, mneumonic::op::BBS4},
{op::BBS6, mneumonic::op::BBS6}, {isa::op::BBS5, mneumonic::op::BBS5},
{op::BBS7, mneumonic::op::BBS7}, {isa::op::BBS6, mneumonic::op::BBS6},
{op::BCC, mneumonic::op::BCC}, {isa::op::BBS7, mneumonic::op::BBS7},
{op::BCS, mneumonic::op::BCS}, {isa::op::BCC, mneumonic::op::BCC},
{op::BEQ, mneumonic::op::BEQ}, {isa::op::BCS, mneumonic::op::BCS},
{op::BIT, mneumonic::op::BIT}, {isa::op::BEQ, mneumonic::op::BEQ},
{op::BMI, mneumonic::op::BMI}, {isa::op::BIT, mneumonic::op::BIT},
{op::BNE, mneumonic::op::BNE}, {isa::op::BMI, mneumonic::op::BMI},
{op::BPL, mneumonic::op::BPL}, {isa::op::BNE, mneumonic::op::BNE},
{op::BRA, mneumonic::op::BRA}, {isa::op::BPL, mneumonic::op::BPL},
{op::BRK, mneumonic::op::BRK}, {isa::op::BRA, mneumonic::op::BRA},
{op::BVC, mneumonic::op::BVC}, {isa::op::BRK, mneumonic::op::BRK},
{op::BVS, mneumonic::op::BVS}, {isa::op::BVC, mneumonic::op::BVC},
{op::CLC, mneumonic::op::CLC}, {isa::op::BVS, mneumonic::op::BVS},
{op::CLD, mneumonic::op::CLD}, {isa::op::CLC, mneumonic::op::CLC},
{op::CLI, mneumonic::op::CLI}, {isa::op::CLD, mneumonic::op::CLD},
{op::CLV, mneumonic::op::CLV}, {isa::op::CLI, mneumonic::op::CLI},
{op::CMP, mneumonic::op::CMP}, {isa::op::CLV, mneumonic::op::CLV},
{op::CPX, mneumonic::op::CPX}, {isa::op::CMP, mneumonic::op::CMP},
{op::CPY, mneumonic::op::CPY}, {isa::op::CPX, mneumonic::op::CPX},
{op::DEC, mneumonic::op::DEC}, {isa::op::CPY, mneumonic::op::CPY},
{op::DEX, mneumonic::op::DEX}, {isa::op::DEC, mneumonic::op::DEC},
{op::DEY, mneumonic::op::DEY}, {isa::op::DEX, mneumonic::op::DEX},
{op::EOR, mneumonic::op::EOR}, {isa::op::DEY, mneumonic::op::DEY},
{op::INC, mneumonic::op::INC}, {isa::op::EOR, mneumonic::op::EOR},
{op::INX, mneumonic::op::INX}, {isa::op::INC, mneumonic::op::INC},
{op::INY, mneumonic::op::INY}, {isa::op::INX, mneumonic::op::INX},
{op::JMP, mneumonic::op::JMP}, {isa::op::INY, mneumonic::op::INY},
{op::JSR, mneumonic::op::JSR}, {isa::op::JMP, mneumonic::op::JMP},
{op::LDA, mneumonic::op::LDA}, {isa::op::JSR, mneumonic::op::JSR},
{op::LDX, mneumonic::op::LDX}, {isa::op::LDA, mneumonic::op::LDA},
{op::LDY, mneumonic::op::LDY}, {isa::op::LDX, mneumonic::op::LDX},
{op::LSR, mneumonic::op::LSR}, {isa::op::LDY, mneumonic::op::LDY},
{op::NOP, mneumonic::op::NOP}, {isa::op::LSR, mneumonic::op::LSR},
{op::ORA, mneumonic::op::ORA}, {isa::op::NOP, mneumonic::op::NOP},
{op::PHA, mneumonic::op::PHA}, {isa::op::ORA, mneumonic::op::ORA},
{op::PHP, mneumonic::op::PHP}, {isa::op::PHA, mneumonic::op::PHA},
{op::PHX, mneumonic::op::PHX}, {isa::op::PHP, mneumonic::op::PHP},
{op::PHY, mneumonic::op::PHY}, {isa::op::PHX, mneumonic::op::PHX},
{op::PLA, mneumonic::op::PLA}, {isa::op::PHY, mneumonic::op::PHY},
{op::PLP, mneumonic::op::PLP}, {isa::op::PLA, mneumonic::op::PLA},
{op::PLX, mneumonic::op::PLX}, {isa::op::PLP, mneumonic::op::PLP},
{op::PLY, mneumonic::op::PLY}, {isa::op::PLX, mneumonic::op::PLX},
{op::RMB0, mneumonic::op::RMB0}, {isa::op::PLY, mneumonic::op::PLY},
{op::RMB1, mneumonic::op::RMB1}, {isa::op::RMB0, mneumonic::op::RMB0},
{op::RMB2, mneumonic::op::RMB2}, {isa::op::RMB1, mneumonic::op::RMB1},
{op::RMB3, mneumonic::op::RMB3}, {isa::op::RMB2, mneumonic::op::RMB2},
{op::RMB4, mneumonic::op::RMB4}, {isa::op::RMB3, mneumonic::op::RMB3},
{op::RMB5, mneumonic::op::RMB5}, {isa::op::RMB4, mneumonic::op::RMB4},
{op::RMB6, mneumonic::op::RMB6}, {isa::op::RMB5, mneumonic::op::RMB5},
{op::RMB7, mneumonic::op::RMB7}, {isa::op::RMB6, mneumonic::op::RMB6},
{op::ROL, mneumonic::op::ROL}, {isa::op::RMB7, mneumonic::op::RMB7},
{op::ROR, mneumonic::op::ROR}, {isa::op::ROL, mneumonic::op::ROL},
{op::RTI, mneumonic::op::RTI}, {isa::op::ROR, mneumonic::op::ROR},
{op::RTS, mneumonic::op::RTS}, {isa::op::RTI, mneumonic::op::RTI},
{op::SBC, mneumonic::op::SBC}, {isa::op::RTS, mneumonic::op::RTS},
{op::SEC, mneumonic::op::SEC}, {isa::op::SBC, mneumonic::op::SBC},
{op::SED, mneumonic::op::SED}, {isa::op::SEC, mneumonic::op::SEC},
{op::SEI, mneumonic::op::SEI}, {isa::op::SED, mneumonic::op::SED},
{op::SMB0, mneumonic::op::SMB0}, {isa::op::SEI, mneumonic::op::SEI},
{op::SMB1, mneumonic::op::SMB1}, {isa::op::SMB0, mneumonic::op::SMB0},
{op::SMB2, mneumonic::op::SMB2}, {isa::op::SMB1, mneumonic::op::SMB1},
{op::SMB3, mneumonic::op::SMB3}, {isa::op::SMB2, mneumonic::op::SMB2},
{op::SMB4, mneumonic::op::SMB4}, {isa::op::SMB3, mneumonic::op::SMB3},
{op::SMB5, mneumonic::op::SMB5}, {isa::op::SMB4, mneumonic::op::SMB4},
{op::SMB6, mneumonic::op::SMB6}, {isa::op::SMB5, mneumonic::op::SMB5},
{op::SMB7, mneumonic::op::SMB7}, {isa::op::SMB6, mneumonic::op::SMB6},
{op::STA, mneumonic::op::STA}, {isa::op::SMB7, mneumonic::op::SMB7},
{op::STP, mneumonic::op::STP}, {isa::op::STA, mneumonic::op::STA},
{op::STX, mneumonic::op::STX}, {isa::op::STP, mneumonic::op::STP},
{op::STY, mneumonic::op::STY}, {isa::op::STX, mneumonic::op::STX},
{op::STZ, mneumonic::op::STZ}, {isa::op::STY, mneumonic::op::STY},
{op::TAX, mneumonic::op::TAX}, {isa::op::STZ, mneumonic::op::STZ},
{op::TAY, mneumonic::op::TAY}, {isa::op::TAX, mneumonic::op::TAX},
{op::TRB, mneumonic::op::TRB}, {isa::op::TAY, mneumonic::op::TAY},
{op::TSB, mneumonic::op::TSB}, {isa::op::TRB, mneumonic::op::TRB},
{op::TSX, mneumonic::op::TSX}, {isa::op::TSB, mneumonic::op::TSB},
{op::TXA, mneumonic::op::TXA}, {isa::op::TSX, mneumonic::op::TSX},
{op::TXS, mneumonic::op::TXS}, {isa::op::TXA, mneumonic::op::TXA},
{op::TYA, mneumonic::op::TYA}, {isa::op::TXS, mneumonic::op::TXS},
{op::WAI, mneumonic::op::WAI}, {isa::op::TYA, mneumonic::op::TYA},
}; {isa::op::WAI, mneumonic::op::WAI},
const std::unordered_map<amode_t, std::string_view> mode {
{mode::A, mneumonic::mode::A},
{mode::AII, mneumonic::mode::AII},
{mode::AIX, mneumonic::mode::AIX},
{mode::AIY, mneumonic::mode::AIY},
{mode::AI, mneumonic::mode::AI},
{mode::ACC, mneumonic::mode::ACC},
{mode::IMM, mneumonic::mode::IMM},
{mode::I, mneumonic::mode::I},
{mode::R, mneumonic::mode::R},
{mode::S, mneumonic::mode::S},
{mode::ZP, mneumonic::mode::ZP},
{mode::ZPII, mneumonic::mode::ZPII},
{mode::ZPX, mneumonic::mode::ZPX},
{mode::ZPY, mneumonic::mode::ZPY},
{mode::ZPI, mneumonic::mode::ZPI},
{mode::ZPIY, mneumonic::mode::ZPIY},
}; };
return _;
}
const std::unordered_map<isa::mode, std::string_view>& mode() {
static const std::unordered_map<isa::mode, std::string_view> _ = {
{isa::mode::A, mneumonic::mode::A},
{isa::mode::AII, mneumonic::mode::AII},
{isa::mode::AIX, mneumonic::mode::AIX},
{isa::mode::AIY, mneumonic::mode::AIY},
{isa::mode::AI, mneumonic::mode::AI},
{isa::mode::ACC, mneumonic::mode::ACC},
{isa::mode::IMM, mneumonic::mode::IMM},
{isa::mode::I, mneumonic::mode::I},
{isa::mode::R, mneumonic::mode::R},
{isa::mode::S, mneumonic::mode::S},
{isa::mode::ZP, mneumonic::mode::ZP},
{isa::mode::ZPII, mneumonic::mode::ZPII},
{isa::mode::ZPX, mneumonic::mode::ZPX},
{isa::mode::ZPY, mneumonic::mode::ZPY},
{isa::mode::ZPI, mneumonic::mode::ZPI},
{isa::mode::ZPIY, mneumonic::mode::ZPIY},
};
return _;
}
} }

View File

@ -5,7 +5,6 @@
#include "mneumonic.hh" #include "mneumonic.hh"
namespace tostring { namespace tostring {
extern const std::unordered_map<op_t, std::string_view> op; const std::unordered_map<isa::op, std::string_view>& op();
const std::unordered_map<isa::mode, std::string_view>& mode();
extern const std::unordered_map<amode_t, std::string_view> mode;
} }