This commit is contained in:
Zack Buhman 2022-03-20 15:50:14 -07:00
commit 8054ac43ac
17 changed files with 1623 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.o
*.asm
main

23
Makefile Normal file
View File

@ -0,0 +1,23 @@
CXXFLAGS = -O0 -g -Wall -Wextra -Werror -Wpedantic -std=c++20 -Wno-error=unused-parameter -Wno-error=unused-but-set-variable -fPIE
LDFLAGS =
TARGET =
CXX = $(TARGET)g++
OBJS = main.o fromstring.o addressing_mode.o codec.o parser.o
HEADERS = $(wildcard *.h)
all: main
%.o: %.cc $(HEADERS) Makefile
$(CXX) $(CXXFLAGS) -c $< -o $@
main: $(OBJS)
$(CXX) $(LDFLAGS) $^ -o $@
clean:
rm -f *.o *.elf *.bin *.out *.imem *.hex
.SUFFIXES:
.INTERMEDIATE:
.PHONY: all clean %.dump

35
addressing_mode.cc Normal file
View File

@ -0,0 +1,35 @@
#include <functional>
#include <unordered_map>
#include "addressing_mode.hh"
#include "isa.hh"
static bool is_uint16(ssize_t n) { return n >= 0 && n <= 65535; }
static bool is_uint8(ssize_t n) { return n >= 0 && n <= 255; }
static bool is_int8(ssize_t n) { return n >= -128 && n <= 127; }
static bool always_valid(ssize_t _) { return true; }
std::unordered_map<isa::mode, addressing_mode_t>& addressing_mode() {
static std::unordered_map<isa::mode, addressing_mode_t> _ = {
{isa::mode::A, { .len = 2, .valid = is_uint16 }},
{isa::mode::AII, { .len = 2, .valid = is_uint16 }},
{isa::mode::AIX, { .len = 2, .valid = is_uint16 }},
{isa::mode::AIY, { .len = 2, .valid = is_uint16 }},
{isa::mode::AI, { .len = 2, .valid = is_uint16 }},
{isa::mode::ACC, { .len = 0, .valid = always_valid }},
{isa::mode::IMM, { .len = 1, .valid = is_uint8 }},
{isa::mode::I, { .len = 0, .valid = always_valid }},
{isa::mode::R, { .len = 1, .valid = is_int8 }},
{isa::mode::S, { .len = 0, .valid = always_valid }},
{isa::mode::ZP, { .len = 1, .valid = is_uint8 }},
{isa::mode::ZPII, { .len = 1, .valid = is_uint8 }},
{isa::mode::ZPX, { .len = 1, .valid = is_uint8 }},
{isa::mode::ZPY, { .len = 1, .valid = is_uint8 }},
{isa::mode::ZPI, { .len = 1, .valid = is_uint8 }},
{isa::mode::ZPIY, { .len = 1, .valid = is_uint8 }},
};
return _;
}

13
addressing_mode.hh Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include <functional>
#include <unordered_map>
#include "isa.hh"
struct addressing_mode_t {
size_t len;
std::function<bool(ssize_t)> valid;
};
std::unordered_map<isa::mode, addressing_mode_t>& addressing_mode();

30
assembler.hh Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#include <optional>
#include <variant>
#include <vector>
#include "isa.hh"
namespace assembler {
struct literal_t {
ssize_t num;
};
struct reference_t {
size_t symbol;
};
struct implied_t {
};
struct instruction_t {
std::optional<size_t> symbol;
isa::op op;
isa::mode mode;
std::variant<literal_t, reference_t, implied_t> value;
size_t location;
};
using program_t = std::vector<instruction_t>;
}

563
codec.cc Normal file
View File

@ -0,0 +1,563 @@
#include <array>
#include <cstdint>
#include <map>
#include <tuple>
#include "codec.hh"
#include "isa.hh"
namespace codec {
const std::map<std::tuple<isa::op, isa::mode>, uint8_t>& encode() {
static const std::map<std::tuple<isa::op, isa::mode>, uint8_t> _ = {
{{isa::op::BRK, isa::mode::S}, 0},
{{isa::op::ORA, isa::mode::ZPX}, 1},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 2},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 3},
{{isa::op::TSB, isa::mode::ZP}, 4},
{{isa::op::ORA, isa::mode::ZP}, 5},
{{isa::op::ASL, isa::mode::ZP}, 6},
{{isa::op::RMB0, isa::mode::ZP}, 7},
{{isa::op::PHP, isa::mode::S}, 8},
{{isa::op::ORA, isa::mode::IMM}, 9},
{{isa::op::ASL, isa::mode::A}, 10},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 11},
{{isa::op::TSB, isa::mode::A}, 12},
{{isa::op::ORA, isa::mode::A}, 13},
{{isa::op::ASL, isa::mode::A}, 14},
{{isa::op::BBR0, isa::mode::R}, 15},
{{isa::op::BPL, isa::mode::R}, 16},
{{isa::op::ORA, isa::mode::ZPIY}, 17},
{{isa::op::ORA, isa::mode::ZPI}, 18},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 19},
{{isa::op::TRB, isa::mode::ZP}, 20},
{{isa::op::ORA, isa::mode::ZPX}, 21},
{{isa::op::ASL, isa::mode::ZPX}, 22},
{{isa::op::RMB1, isa::mode::ZP}, 23},
{{isa::op::CLC, isa::mode::I}, 24},
{{isa::op::ORA, isa::mode::AIY}, 25},
{{isa::op::INC, isa::mode::A}, 26},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 27},
{{isa::op::TRB, isa::mode::A}, 28},
{{isa::op::ORA, isa::mode::AIX}, 29},
{{isa::op::ASL, isa::mode::AIX}, 30},
{{isa::op::BBR1, isa::mode::R}, 31},
{{isa::op::JSR, isa::mode::A}, 32},
{{isa::op::AND, isa::mode::ZPII}, 33},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 34},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 35},
{{isa::op::BIT, isa::mode::ZP}, 36},
{{isa::op::AND, isa::mode::ZP}, 37},
{{isa::op::ROL, isa::mode::ZP}, 38},
{{isa::op::RMB2, isa::mode::ZP}, 39},
{{isa::op::PLP, isa::mode::S}, 40},
{{isa::op::AND, isa::mode::IMM}, 41},
{{isa::op::ROL, isa::mode::A}, 42},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 43},
{{isa::op::BIT, isa::mode::A}, 44},
{{isa::op::AND, isa::mode::A}, 45},
{{isa::op::ROL, isa::mode::A}, 46},
{{isa::op::BBR2, isa::mode::R}, 47},
{{isa::op::BMI, isa::mode::R}, 48},
{{isa::op::AND, isa::mode::ZPIY}, 49},
{{isa::op::AND, isa::mode::ZPI}, 50},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 51},
{{isa::op::BIT, isa::mode::ZPX}, 52},
{{isa::op::AND, isa::mode::ZPX}, 53},
{{isa::op::ROL, isa::mode::ZPX}, 54},
{{isa::op::RMB3, isa::mode::ZP}, 55},
{{isa::op::SEC, isa::mode::I}, 56},
{{isa::op::AND, isa::mode::AIY}, 57},
{{isa::op::DEC, isa::mode::A}, 58},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 59},
{{isa::op::BIT, isa::mode::AIX}, 60},
{{isa::op::AND, isa::mode::AIX}, 61},
{{isa::op::ROL, isa::mode::AIX}, 62},
{{isa::op::BBR3, isa::mode::R}, 63},
{{isa::op::RTI, isa::mode::S}, 64},
{{isa::op::EOR, isa::mode::ZPII}, 65},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 66},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 67},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 68},
{{isa::op::EOR, isa::mode::ZP}, 69},
{{isa::op::LSR, isa::mode::ZP}, 70},
{{isa::op::RMB4, isa::mode::ZP}, 71},
{{isa::op::PHA, isa::mode::S}, 72},
{{isa::op::EOR, isa::mode::IMM}, 73},
{{isa::op::LSR, isa::mode::A}, 74},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 75},
{{isa::op::JMP, isa::mode::A}, 76},
{{isa::op::EOR, isa::mode::A}, 77},
{{isa::op::LSR, isa::mode::A}, 78},
{{isa::op::BBR4, isa::mode::R}, 79},
{{isa::op::BVC, isa::mode::R}, 80},
{{isa::op::EOR, isa::mode::ZPIY}, 81},
{{isa::op::EOR, isa::mode::ZPI}, 82},
{{isa::op::EOR, isa::mode::ZPX}, 83},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 84},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 85},
{{isa::op::LSR, isa::mode::ZPX}, 86},
{{isa::op::RMB5, isa::mode::ZP}, 87},
{{isa::op::CLI, isa::mode::I}, 88},
{{isa::op::EOR, isa::mode::AIY}, 89},
{{isa::op::PHY, isa::mode::S}, 90},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 91},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 92},
{{isa::op::EOR, isa::mode::AIX}, 93},
{{isa::op::LSR, isa::mode::AIX}, 94},
{{isa::op::BBR5, isa::mode::R}, 95},
{{isa::op::RTS, isa::mode::S}, 96},
{{isa::op::ADC, isa::mode::ZPII}, 97},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 98},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 99},
{{isa::op::STZ, isa::mode::ZP}, 100},
{{isa::op::ADC, isa::mode::ZP}, 101},
{{isa::op::ROR, isa::mode::ZP}, 102},
{{isa::op::RMB6, isa::mode::ZP}, 103},
{{isa::op::PLA, isa::mode::S}, 104},
{{isa::op::ADC, isa::mode::IMM}, 105},
{{isa::op::ROR, isa::mode::A}, 106},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 107},
{{isa::op::JMP, isa::mode::AI}, 108},
{{isa::op::ADC, isa::mode::A}, 109},
{{isa::op::ROR, isa::mode::A}, 110},
{{isa::op::BBR6, isa::mode::R}, 111},
{{isa::op::BVS, isa::mode::R}, 112},
{{isa::op::ADC, isa::mode::ZPIY}, 113},
{{isa::op::ADC, isa::mode::ZPI}, 114},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 115},
{{isa::op::STZ, isa::mode::ZPX}, 116},
{{isa::op::ADC, isa::mode::ZPX}, 117},
{{isa::op::ROR, isa::mode::ZPX}, 118},
{{isa::op::RMB7, isa::mode::ZP}, 119},
{{isa::op::SEI, isa::mode::I}, 120},
{{isa::op::ADC, isa::mode::AIY}, 121},
{{isa::op::PLY, isa::mode::S}, 122},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 123},
{{isa::op::JMP, isa::mode::AII}, 124},
{{isa::op::ADC, isa::mode::AIX}, 125},
{{isa::op::ROR, isa::mode::AIX}, 126},
{{isa::op::BBR7, isa::mode::R}, 127},
{{isa::op::BRA, isa::mode::R}, 128},
{{isa::op::STA, isa::mode::ZPII}, 129},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 130},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 131},
{{isa::op::STY, isa::mode::ZP}, 132},
{{isa::op::STA, isa::mode::ZP}, 133},
{{isa::op::STX, isa::mode::ZP}, 134},
{{isa::op::SMB0, isa::mode::ZP}, 135},
{{isa::op::DEY, isa::mode::I}, 136},
{{isa::op::BIT, isa::mode::IMM}, 137},
{{isa::op::TXA, isa::mode::I}, 138},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 139},
{{isa::op::STY, isa::mode::A}, 140},
{{isa::op::STA, isa::mode::A}, 141},
{{isa::op::STX, isa::mode::A}, 142},
{{isa::op::BBS0, isa::mode::R}, 143},
{{isa::op::BCC, isa::mode::R}, 144},
{{isa::op::STA, isa::mode::ZPIY}, 145},
{{isa::op::STA, isa::mode::ZPI}, 146},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 147},
{{isa::op::STY, isa::mode::ZPX}, 148},
{{isa::op::STA, isa::mode::ZPX}, 149},
{{isa::op::STX, isa::mode::ZPY}, 150},
{{isa::op::SMB1, isa::mode::ZP}, 151},
{{isa::op::TYA, isa::mode::I}, 152},
{{isa::op::STA, isa::mode::AIY}, 153},
{{isa::op::TXS, isa::mode::I}, 154},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 155},
{{isa::op::STZ, isa::mode::A}, 156},
{{isa::op::STA, isa::mode::AIX}, 157},
{{isa::op::STZ, isa::mode::AIX}, 158},
{{isa::op::BBS1, isa::mode::R}, 159},
{{isa::op::LDY, isa::mode::IMM}, 160},
{{isa::op::LDA, isa::mode::ZPII}, 161},
{{isa::op::LDX, isa::mode::IMM}, 162},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 163},
{{isa::op::LDY, isa::mode::ZP}, 164},
{{isa::op::LDA, isa::mode::ZP}, 165},
{{isa::op::LDX, isa::mode::ZP}, 166},
{{isa::op::SMB2, isa::mode::ZP}, 167},
{{isa::op::TAY, isa::mode::I}, 168},
{{isa::op::LDA, isa::mode::IMM}, 169},
{{isa::op::TAX, isa::mode::I}, 170},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 171},
{{isa::op::LDY, isa::mode::A}, 172},
{{isa::op::LDA, isa::mode::A}, 173},
{{isa::op::LDX, isa::mode::A}, 174},
{{isa::op::BBS2, isa::mode::R}, 175},
{{isa::op::BCS, isa::mode::R}, 176},
{{isa::op::LDA, isa::mode::ZPIY}, 177},
{{isa::op::LDA, isa::mode::ZPI}, 178},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 179},
{{isa::op::LDY, isa::mode::ZPX}, 180},
{{isa::op::LDA, isa::mode::ZPX}, 181},
{{isa::op::LDX, isa::mode::ZPY}, 182},
{{isa::op::SMB3, isa::mode::ZP}, 183},
{{isa::op::CLV, isa::mode::I}, 184},
{{isa::op::LDA, isa::mode::AIY}, 185},
{{isa::op::TSX, isa::mode::I}, 186},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 187},
{{isa::op::LDY, isa::mode::AIX}, 188},
{{isa::op::LDA, isa::mode::AIX}, 189},
{{isa::op::LDX, isa::mode::AIY}, 190},
{{isa::op::BBS3, isa::mode::R}, 191},
{{isa::op::CPY, isa::mode::IMM}, 192},
{{isa::op::CMP, isa::mode::ZPII}, 193},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 194},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 195},
{{isa::op::CPY, isa::mode::ZP}, 196},
{{isa::op::CMP, isa::mode::ZP}, 197},
{{isa::op::DEC, isa::mode::ZP}, 198},
{{isa::op::SMB4, isa::mode::ZP}, 199},
{{isa::op::INY, isa::mode::I}, 200},
{{isa::op::CMP, isa::mode::IMM}, 201},
{{isa::op::DEX, isa::mode::I}, 202},
{{isa::op::WAI, isa::mode::I}, 203},
{{isa::op::CPY, isa::mode::A}, 204},
{{isa::op::CMP, isa::mode::A}, 205},
{{isa::op::DEC, isa::mode::A}, 206},
{{isa::op::BBS4, isa::mode::R}, 207},
{{isa::op::BNE, isa::mode::R}, 208},
{{isa::op::CMP, isa::mode::ZPIY}, 209},
{{isa::op::CMP, isa::mode::ZPI}, 210},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 211},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 212},
{{isa::op::CMP, isa::mode::ZPX}, 213},
{{isa::op::DEC, isa::mode::ZPX}, 214},
{{isa::op::SMB5, isa::mode::ZP}, 215},
{{isa::op::CLD, isa::mode::I}, 216},
{{isa::op::CMP, isa::mode::AIY}, 217},
{{isa::op::PHX, isa::mode::S}, 218},
{{isa::op::STP, isa::mode::I}, 219},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 220},
{{isa::op::CMP, isa::mode::AIX}, 221},
{{isa::op::DEC, isa::mode::AIX}, 222},
{{isa::op::BBS5, isa::mode::R}, 223},
{{isa::op::CPX, isa::mode::IMM}, 224},
{{isa::op::SBC, isa::mode::ZPII}, 225},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 226},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 227},
{{isa::op::CPX, isa::mode::ZP}, 228},
{{isa::op::SBC, isa::mode::ZP}, 229},
{{isa::op::INC, isa::mode::ZP}, 230},
{{isa::op::SMB6, isa::mode::ZP}, 231},
{{isa::op::INX, isa::mode::I}, 232},
{{isa::op::SBC, isa::mode::IMM}, 233},
{{isa::op::NOP, isa::mode::I}, 234},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 235},
{{isa::op::CPX, isa::mode::A}, 236},
{{isa::op::SBC, isa::mode::A}, 237},
{{isa::op::INC, isa::mode::A}, 238},
{{isa::op::BBS6, isa::mode::R}, 239},
{{isa::op::BEQ, isa::mode::R}, 240},
{{isa::op::SBC, isa::mode::ZPIY}, 241},
{{isa::op::SBC, isa::mode::ZPI}, 242},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 243},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 244},
{{isa::op::SBC, isa::mode::ZPX}, 245},
{{isa::op::INC, isa::mode::ZPX}, 246},
{{isa::op::SMB7, isa::mode::ZP}, 247},
{{isa::op::SED, isa::mode::I}, 248},
{{isa::op::SBC, isa::mode::AIY}, 249},
{{isa::op::PLX, isa::mode::S}, 250},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 251},
//{{isa::op::O_INVALID, isa::mode::M_INVALID}, 252},
{{isa::op::SBC, isa::mode::AIX}, 253},
{{isa::op::INC, isa::mode::AIX}, 254},
{{isa::op::BBS7, isa::mode::R}, 255},
};
return _;
}
const std::map<uint8_t, std::tuple<isa::op, isa::mode>>& decode() {
static const std::map<uint8_t, std::tuple<isa::op, isa::mode>> _ = {
{0, {isa::op::BRK, isa::mode::S}},
{1, {isa::op::ORA, isa::mode::ZPX}},
//{2, {isa::op::O_INVALID, isa::mode::M_INVALID}},
//{3, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{4, {isa::op::TSB, isa::mode::ZP}},
{5, {isa::op::ORA, isa::mode::ZP}},
{6, {isa::op::ASL, isa::mode::ZP}},
{7, {isa::op::RMB0, isa::mode::ZP}},
{8, {isa::op::PHP, isa::mode::S}},
{9, {isa::op::ORA, isa::mode::IMM}},
{10, {isa::op::ASL, isa::mode::A}},
//{11, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{12, {isa::op::TSB, isa::mode::A}},
{13, {isa::op::ORA, isa::mode::A}},
{14, {isa::op::ASL, isa::mode::A}},
{15, {isa::op::BBR0, isa::mode::R}},
{16, {isa::op::BPL, isa::mode::R}},
{17, {isa::op::ORA, isa::mode::ZPIY}},
{18, {isa::op::ORA, isa::mode::ZPI}},
//{19, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{20, {isa::op::TRB, isa::mode::ZP}},
{21, {isa::op::ORA, isa::mode::ZPX}},
{22, {isa::op::ASL, isa::mode::ZPX}},
{23, {isa::op::RMB1, isa::mode::ZP}},
{24, {isa::op::CLC, isa::mode::I}},
{25, {isa::op::ORA, isa::mode::AIY}},
{26, {isa::op::INC, isa::mode::A}},
//{27, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{28, {isa::op::TRB, isa::mode::A}},
{29, {isa::op::ORA, isa::mode::AIX}},
{30, {isa::op::ASL, isa::mode::AIX}},
{31, {isa::op::BBR1, isa::mode::R}},
{32, {isa::op::JSR, isa::mode::A}},
{33, {isa::op::AND, isa::mode::ZPII}},
//{34, {isa::op::O_INVALID, isa::mode::M_INVALID}},
//{35, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{36, {isa::op::BIT, isa::mode::ZP}},
{37, {isa::op::AND, isa::mode::ZP}},
{38, {isa::op::ROL, isa::mode::ZP}},
{39, {isa::op::RMB2, isa::mode::ZP}},
{40, {isa::op::PLP, isa::mode::S}},
{41, {isa::op::AND, isa::mode::IMM}},
{42, {isa::op::ROL, isa::mode::A}},
//{43, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{44, {isa::op::BIT, isa::mode::A}},
{45, {isa::op::AND, isa::mode::A}},
{46, {isa::op::ROL, isa::mode::A}},
{47, {isa::op::BBR2, isa::mode::R}},
{48, {isa::op::BMI, isa::mode::R}},
{49, {isa::op::AND, isa::mode::ZPIY}},
{50, {isa::op::AND, isa::mode::ZPI}},
//{51, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{52, {isa::op::BIT, isa::mode::ZPX}},
{53, {isa::op::AND, isa::mode::ZPX}},
{54, {isa::op::ROL, isa::mode::ZPX}},
{55, {isa::op::RMB3, isa::mode::ZP}},
{56, {isa::op::SEC, isa::mode::I}},
{57, {isa::op::AND, isa::mode::AIY}},
{58, {isa::op::DEC, isa::mode::A}},
//{59, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{60, {isa::op::BIT, isa::mode::AIX}},
{61, {isa::op::AND, isa::mode::AIX}},
{62, {isa::op::ROL, isa::mode::AIX}},
{63, {isa::op::BBR3, isa::mode::R}},
{64, {isa::op::RTI, isa::mode::S}},
{65, {isa::op::EOR, isa::mode::ZPII}},
//{66, {isa::op::O_INVALID, isa::mode::M_INVALID}},
//{67, {isa::op::O_INVALID, isa::mode::M_INVALID}},
//{68, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{69, {isa::op::EOR, isa::mode::ZP}},
{70, {isa::op::LSR, isa::mode::ZP}},
{71, {isa::op::RMB4, isa::mode::ZP}},
{72, {isa::op::PHA, isa::mode::S}},
{73, {isa::op::EOR, isa::mode::IMM}},
{74, {isa::op::LSR, isa::mode::A}},
//{75, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{76, {isa::op::JMP, isa::mode::A}},
{77, {isa::op::EOR, isa::mode::A}},
{78, {isa::op::LSR, isa::mode::A}},
{79, {isa::op::BBR4, isa::mode::R}},
{80, {isa::op::BVC, isa::mode::R}},
{81, {isa::op::EOR, isa::mode::ZPIY}},
{82, {isa::op::EOR, isa::mode::ZPI}},
{83, {isa::op::EOR, isa::mode::ZPX}},
//{84, {isa::op::O_INVALID, isa::mode::M_INVALID}},
//{85, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{86, {isa::op::LSR, isa::mode::ZPX}},
{87, {isa::op::RMB5, isa::mode::ZP}},
{88, {isa::op::CLI, isa::mode::I}},
{89, {isa::op::EOR, isa::mode::AIY}},
{90, {isa::op::PHY, isa::mode::S}},
//{91, {isa::op::O_INVALID, isa::mode::M_INVALID}},
//{92, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{93, {isa::op::EOR, isa::mode::AIX}},
{94, {isa::op::LSR, isa::mode::AIX}},
{95, {isa::op::BBR5, isa::mode::R}},
{96, {isa::op::RTS, isa::mode::S}},
{97, {isa::op::ADC, isa::mode::ZPII}},
//{98, {isa::op::O_INVALID, isa::mode::M_INVALID}},
//{99, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{100, {isa::op::STZ, isa::mode::ZP}},
{101, {isa::op::ADC, isa::mode::ZP}},
{102, {isa::op::ROR, isa::mode::ZP}},
{103, {isa::op::RMB6, isa::mode::ZP}},
{104, {isa::op::PLA, isa::mode::S}},
{105, {isa::op::ADC, isa::mode::IMM}},
{106, {isa::op::ROR, isa::mode::A}},
//{107, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{108, {isa::op::JMP, isa::mode::AI}},
{109, {isa::op::ADC, isa::mode::A}},
{110, {isa::op::ROR, isa::mode::A}},
{111, {isa::op::BBR6, isa::mode::R}},
{112, {isa::op::BVS, isa::mode::R}},
{113, {isa::op::ADC, isa::mode::ZPIY}},
{114, {isa::op::ADC, isa::mode::ZPI}},
//{115, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{116, {isa::op::STZ, isa::mode::ZPX}},
{117, {isa::op::ADC, isa::mode::ZPX}},
{118, {isa::op::ROR, isa::mode::ZPX}},
{119, {isa::op::RMB7, isa::mode::ZP}},
{120, {isa::op::SEI, isa::mode::I}},
{121, {isa::op::ADC, isa::mode::AIY}},
{122, {isa::op::PLY, isa::mode::S}},
//{123, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{124, {isa::op::JMP, isa::mode::AII}},
{125, {isa::op::ADC, isa::mode::AIX}},
{126, {isa::op::ROR, isa::mode::AIX}},
{127, {isa::op::BBR7, isa::mode::R}},
{128, {isa::op::BRA, isa::mode::R}},
{129, {isa::op::STA, isa::mode::ZPII}},
//{130, {isa::op::O_INVALID, isa::mode::M_INVALID}},
//{131, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{132, {isa::op::STY, isa::mode::ZP}},
{133, {isa::op::STA, isa::mode::ZP}},
{134, {isa::op::STX, isa::mode::ZP}},
{135, {isa::op::SMB0, isa::mode::ZP}},
{136, {isa::op::DEY, isa::mode::I}},
{137, {isa::op::BIT, isa::mode::IMM}},
{138, {isa::op::TXA, isa::mode::I}},
//{139, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{140, {isa::op::STY, isa::mode::A}},
{141, {isa::op::STA, isa::mode::A}},
{142, {isa::op::STX, isa::mode::A}},
{143, {isa::op::BBS0, isa::mode::R}},
{144, {isa::op::BCC, isa::mode::R}},
{145, {isa::op::STA, isa::mode::ZPIY}},
{146, {isa::op::STA, isa::mode::ZPI}},
//{147, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{148, {isa::op::STY, isa::mode::ZPX}},
{149, {isa::op::STA, isa::mode::ZPX}},
{150, {isa::op::STX, isa::mode::ZPY}},
{151, {isa::op::SMB1, isa::mode::ZP}},
{152, {isa::op::TYA, isa::mode::I}},
{153, {isa::op::STA, isa::mode::AIY}},
{154, {isa::op::TXS, isa::mode::I}},
//{155, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{156, {isa::op::STZ, isa::mode::A}},
{157, {isa::op::STA, isa::mode::AIX}},
{158, {isa::op::STZ, isa::mode::AIX}},
{159, {isa::op::BBS1, isa::mode::R}},
{160, {isa::op::LDY, isa::mode::IMM}},
{161, {isa::op::LDA, isa::mode::ZPII}},
{162, {isa::op::LDX, isa::mode::IMM}},
//{163, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{164, {isa::op::LDY, isa::mode::ZP}},
{165, {isa::op::LDA, isa::mode::ZP}},
{166, {isa::op::LDX, isa::mode::ZP}},
{167, {isa::op::SMB2, isa::mode::ZP}},
{168, {isa::op::TAY, isa::mode::I}},
{169, {isa::op::LDA, isa::mode::IMM}},
{170, {isa::op::TAX, isa::mode::I}},
//{171, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{172, {isa::op::LDY, isa::mode::A}},
{173, {isa::op::LDA, isa::mode::A}},
{174, {isa::op::LDX, isa::mode::A}},
{175, {isa::op::BBS2, isa::mode::R}},
{176, {isa::op::BCS, isa::mode::R}},
{177, {isa::op::LDA, isa::mode::ZPIY}},
{178, {isa::op::LDA, isa::mode::ZPI}},
//{179, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{180, {isa::op::LDY, isa::mode::ZPX}},
{181, {isa::op::LDA, isa::mode::ZPX}},
{182, {isa::op::LDX, isa::mode::ZPY}},
{183, {isa::op::SMB3, isa::mode::ZP}},
{184, {isa::op::CLV, isa::mode::I}},
{185, {isa::op::LDA, isa::mode::AIY}},
{186, {isa::op::TSX, isa::mode::I}},
//{187, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{188, {isa::op::LDY, isa::mode::AIX}},
{189, {isa::op::LDA, isa::mode::AIX}},
{190, {isa::op::LDX, isa::mode::AIY}},
{191, {isa::op::BBS3, isa::mode::R}},
{192, {isa::op::CPY, isa::mode::IMM}},
{193, {isa::op::CMP, isa::mode::ZPII}},
//{194, {isa::op::O_INVALID, isa::mode::M_INVALID}},
//{195, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{196, {isa::op::CPY, isa::mode::ZP}},
{197, {isa::op::CMP, isa::mode::ZP}},
{198, {isa::op::DEC, isa::mode::ZP}},
{199, {isa::op::SMB4, isa::mode::ZP}},
{200, {isa::op::INY, isa::mode::I}},
{201, {isa::op::CMP, isa::mode::IMM}},
{202, {isa::op::DEX, isa::mode::I}},
{203, {isa::op::WAI, isa::mode::I}},
{204, {isa::op::CPY, isa::mode::A}},
{205, {isa::op::CMP, isa::mode::A}},
{206, {isa::op::DEC, isa::mode::A}},
{207, {isa::op::BBS4, isa::mode::R}},
{208, {isa::op::BNE, isa::mode::R}},
{209, {isa::op::CMP, isa::mode::ZPIY}},
{210, {isa::op::CMP, isa::mode::ZPI}},
//{211, {isa::op::O_INVALID, isa::mode::M_INVALID}},
//{212, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{213, {isa::op::CMP, isa::mode::ZPX}},
{214, {isa::op::DEC, isa::mode::ZPX}},
{215, {isa::op::SMB5, isa::mode::ZP}},
{216, {isa::op::CLD, isa::mode::I}},
{217, {isa::op::CMP, isa::mode::AIY}},
{218, {isa::op::PHX, isa::mode::S}},
{219, {isa::op::STP, isa::mode::I}},
//{220, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{221, {isa::op::CMP, isa::mode::AIX}},
{222, {isa::op::DEC, isa::mode::AIX}},
{223, {isa::op::BBS5, isa::mode::R}},
{224, {isa::op::CPX, isa::mode::IMM}},
{225, {isa::op::SBC, isa::mode::ZPII}},
//{226, {isa::op::O_INVALID, isa::mode::M_INVALID}},
//{227, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{228, {isa::op::CPX, isa::mode::ZP}},
{229, {isa::op::SBC, isa::mode::ZP}},
{230, {isa::op::INC, isa::mode::ZP}},
{231, {isa::op::SMB6, isa::mode::ZP}},
{232, {isa::op::INX, isa::mode::I}},
{233, {isa::op::SBC, isa::mode::IMM}},
{234, {isa::op::NOP, isa::mode::I}},
//{235, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{236, {isa::op::CPX, isa::mode::A}},
{237, {isa::op::SBC, isa::mode::A}},
{238, {isa::op::INC, isa::mode::A}},
{239, {isa::op::BBS6, isa::mode::R}},
{240, {isa::op::BEQ, isa::mode::R}},
{241, {isa::op::SBC, isa::mode::ZPIY}},
{242, {isa::op::SBC, isa::mode::ZPI}},
//{243, {isa::op::O_INVALID, isa::mode::M_INVALID}},
//{244, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{245, {isa::op::SBC, isa::mode::ZPX}},
{246, {isa::op::INC, isa::mode::ZPX}},
{247, {isa::op::SMB7, isa::mode::ZP}},
{248, {isa::op::SED, isa::mode::I}},
{249, {isa::op::SBC, isa::mode::AIY}},
{250, {isa::op::PLX, isa::mode::S}},
//{251, {isa::op::O_INVALID, isa::mode::M_INVALID}},
//{252, {isa::op::O_INVALID, isa::mode::M_INVALID}},
{253, {isa::op::SBC, isa::mode::AIX}},
{254, {isa::op::INC, isa::mode::AIX}},
{255, {isa::op::BBS7, isa::mode::R}},
};
return _;
}
}

12
codec.hh Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <cstdint>
#include <map>
#include <tuple>
#include "isa.hh"
namespace codec {
extern const std::map<std::tuple<isa::op, isa::mode>, uint8_t>& encode();
const std::map<uint8_t, std::tuple<isa::op, isa::mode>>& decode();
}

0
field.hh Normal file
View File

133
fromstring.cc Normal file
View File

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

14
fromstring.hh Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <map>
#include <string>
#include "isa.hh"
#include "codec.hh"
#include "mneumonic.hh"
namespace fromstring {
const std::unordered_map<std::string_view, isa::op>& op();
const std::unordered_map<std::string_view, isa::mode>& mode();
}

125
isa.hh Normal file
View File

@ -0,0 +1,125 @@
#pragma once
namespace isa {
enum class op {
ADC,
AND,
ASL,
BBR0,
BBR1,
BBR2,
BBR3,
BBR4,
BBR5,
BBR6,
BBR7,
BBS0,
BBS1,
BBS2,
BBS3,
BBS4,
BBS5,
BBS6,
BBS7,
BCC,
BCS,
BEQ,
BIT,
BMI,
BNE,
BPL,
BRA,
BRK,
BVC,
BVS,
CLC,
CLD,
CLI,
CLV,
CMP,
CPX,
CPY,
DEC,
DEX,
DEY,
EOR,
INC,
INX,
INY,
JMP,
JSR,
LDA,
LDX,
LDY,
LSR,
NOP,
ORA,
PHA,
PHP,
PHX,
PHY,
PLA,
PLP,
PLX,
PLY,
RMB0,
RMB1,
RMB2,
RMB3,
RMB4,
RMB5,
RMB6,
RMB7,
ROL,
ROR,
RTI,
RTS,
SBC,
SEC,
SED,
SEI,
SMB0,
SMB1,
SMB2,
SMB3,
SMB4,
SMB5,
SMB6,
SMB7,
STA,
STP,
STX,
STY,
STZ,
TAX,
TAY,
TRB,
TSB,
TSX,
TXA,
TXS,
TYA,
WAI,
};
enum class mode {
A, // Absolute a
AII, // Absolute Indexed Indirect (a,x)
AIX, // Absolute Indexed with X a,x
AIY, // Absolute Indexed with Y a,y
AI, // Absolute Indrect (a)
ACC, // Accumulator A
IMM, // Immediate #
I, // Implied i
R, // Program Counter Relative r
S, // Stack s
ZP, // Zero Page zp
ZPII, // Zero Page Indexed Indirect (zp,x)
ZPX, // Zero Page Indexed with X zp,x
ZPY, // Zero Page Indexed with Y zp,y
ZPI, // Zero Page Indirect (zp)
ZPIY, // Zero Page Indirect Indexed with Y (zp),y
};
}

137
main.cc Normal file
View File

@ -0,0 +1,137 @@
#include <cassert>
#include <fstream>
#include <iostream>
#include <unordered_map>
#include "assembler.hh"
#include "addressing_mode.hh"
#include "codec.hh"
#include "parser.hh"
using symbol_table_t = std::unordered_map<size_t, assembler::program_t::const_iterator>;
bool resolve_symbols(assembler::program_t& program, symbol_table_t& symbol_table)
{
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)
{
char buf[3];
for (auto ins = program.cbegin(); ins != program.cend(); ins++) {
std::cerr << "enc " << (int)ins->op << ' ' << (int)ins->mode << '\n';
auto opcode_it = codec::encode().find({ins->op, ins->mode});
assert (opcode_it != codec::encode().end());
buf[0] = opcode_it->second;
auto am_it = addressing_mode().find(ins->mode);
assert (am_it != addressing_mode().end());
auto get_value = [&]() -> ssize_t {
if (std::holds_alternative<assembler::reference_t>(ins->value)) {
assembler::reference_t ref = std::get<assembler::reference_t>(ins->value);
auto sym_it = symbol_table.find(ref.symbol);
assert (sym_it != symbol_table.end());
auto ref_ins = sym_it->second;
if (ins->mode == isa::mode::R) {
std::cout << "relative\n";
ssize_t offset = static_cast<ssize_t>(ref_ins->location) - static_cast<ssize_t>(ins->location + 2);
return offset;
} else {
return ref_ins->location + link_location;
}
} else if (std::holds_alternative<assembler::literal_t>(ins->value)) {
assembler::literal_t lit = std::get<assembler::literal_t>(ins->value);
return lit.num;
} else if (std::holds_alternative<assembler::implied_t>(ins->value)) {
assert (std::holds_alternative<assembler::implied_t>(ins->value));
} else {
assert (false);
}
return 0;
};
size_t value = get_value();
if (!am_it->second.valid(value)) {
std::cout << "overflow at h" << std::hex << ins->location << '\n';
}
std::cout << "value " << std::hex << value << ' ' << am_it->second.len << '\n';
if (am_it->second.len >= 2)
buf[2] = (value >> 8) & 0xff;
if (am_it->second.len >= 1)
buf[1] = (value >> 0) & 0xff;
out.write(buf, 1 + am_it->second.len);
}
}
int main(int argc, char * argv[])
{
if (argc < 3) {
std::cerr << "argc < 3\n";
return -1;
}
std::string input_filename {argv[1]};
std::ifstream is {input_filename, std::ios::binary | std::ios::ate};
if (!is.is_open()) {
std::cerr << "failed to open " << input_filename << '\n';
return -1;
}
auto size = is.tellg();
std::string buf(size, '\0'); // construct string to stream size
is.seekg(0);
is.read(buf.data(), size);
is.close();
assembler::program_t program;
symbol_table_t symbol_table;
bool ok;
ok = parser::parse(buf, program);
if (!ok)
return -1;
ok = resolve_symbols(program, symbol_table);
if (!ok)
return -1;
std::string output_filename {argv[2]};
std::ofstream out {output_filename, std::ios::binary};
if (!out.is_open()) {
std::cerr << "failed to open " << output_filename << '\n';
return -1;
}
serialize_program(program, symbol_table, 0x8000, out);
out.close();
return 0;
}

127
mneumonic.hh Normal file
View File

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

261
parser.cc Normal file
View File

@ -0,0 +1,261 @@
#include <cassert>
#include <string_view>
#include <functional>
#include <iostream>
#include <variant>
#include "assembler.hh"
#include "addressing_mode.hh"
#include "fromstring.hh"
#include "isa.hh"
static bool tokenize(std::string_view buf, std::function<bool(std::string_view, ssize_t, ssize_t)> cb)
{
auto it = buf.cbegin();
std::optional<decltype(it)> token_begin = std::nullopt;
ssize_t row = 0;
ssize_t col = 0;
while (it != buf.cend()) {
auto c = *it;
switch (c) {
case '\n':
case ' ':
if (token_begin != std::nullopt) {
bool ret = cb(buf.substr(*token_begin - buf.cbegin(), it - *token_begin), row, col);
if (!ret) {
// cb failed
return false;
}
token_begin = std::nullopt;
}
break;
default:
if (token_begin == std::nullopt) {
token_begin = it;
}
break;
}
if (c == '\n') {
col = 0;
row++;
} else {
col++;
};
it++;
}
bool ret = cb("", row + 1, col);
if (!ret) {
// cb failed
return false;
}
return true;
}
namespace parser {
enum class state {
label_or_op,
op,
mode,
value,
comment,
just_comment,
};
}
using symbol_strings_t = std::unordered_map<std::string_view, size_t>;
namespace parser {
bool parse(std::string_view buf, assembler::program_t& program)
{
size_t current_symbol = 0;
symbol_strings_t ss;
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;
ssize_t current_row;
bool inside_comment = false;
bool parsed = tokenize(buf, [&](std::string_view s, ssize_t row, ssize_t col) -> bool {
while (true) {
switch (state) {
case parser::state::label_or_op:
{
current_row = row;
if (s.back() == ':') {
std::string_view key = s.substr(0, s.length() - 1);
auto symbol = get_symbol(key);
std::cout << "label `" << symbol << "`\n";
current_instruction.symbol = symbol;
state = parser::state::op;
return true;
} else if (s.front() == ';') {
state = parser::state::just_comment;
continue;
} else if (s.empty()) {
// I hate myself for this dirty hack
return true;
} else {
current_instruction.symbol = std::nullopt;
[[fallthrough]];
}
}
case parser::state::op:
{
assert(row == current_row);
auto op_it = fromstring::op().find(s);
if (op_it == fromstring::op().end()) {
std::cout << "invalid op `" << s << "`\n";
return false;
} else {
current_instruction.op = op_it->second;
std::cout << "ok op `" << static_cast<int>(op_it->second) << "`\n";
}
state = parser::state::mode;
return true;
}
case parser::state::mode:
{
assert(row == current_row);
auto mode_it = fromstring::mode().find(s);
if (mode_it == fromstring::mode().end()) {
std::cout << "invalid mode `" << s << "`\n";
return false;
} else {
current_instruction.mode = mode_it->second;
std::cout << "ok mode `" << static_cast<int>(mode_it->second) << "`\n";
}
state = parser::state::value;
return true;
}
case parser::state::value:
{
auto am_it = addressing_mode().find(current_instruction.mode);
assert(am_it != addressing_mode().end());
if (am_it->second.len == 0) {
std::cout << "no value expected\n";
assembler::implied_t i {};
current_instruction.value = i;
state = parser::state::comment;
continue;
}
assert(row == current_row);
auto parse_integer = [](std::string_view s, int base) -> std::optional<ssize_t> {
std::string value_str {s.data() + 1, s.size() - 1};
size_t pos;
ssize_t value;
value = std::stoll(value_str, &pos, base);
if (pos != value_str.length())
return std::nullopt;
else
return value;
};
std::optional<ssize_t> literal;
auto as_literal = [](ssize_t n) -> assembler::literal_t { return {n}; };
switch (*s.cbegin()) {
case 'h':
{
literal = parse_integer(s, 16);
if (literal == std::nullopt) {
std::cout << "invalid hex literal\n";
}
current_instruction.value = as_literal(*literal);
std::cout << "value hex literal `" << *literal << "`\n";
break;
}
case 'd':
{
literal = parse_integer(s, 10);
if (literal == std::nullopt) {
std::cout << "invalid dec literal\n";
return false;
}
current_instruction.value = as_literal(*literal);
std::cout << "value dec literal `" << *literal << "`\n";
break;
}
case ':':
{
std::string_view key = s.substr(1, s.length() - 1);
assembler::reference_t reference = { get_symbol(key) };
current_instruction.value = reference;
std::cout << "value reference `" << reference.symbol << "`\n";
break;
}
default:
std::cout << "invalid base\n";
return false;
}
state = parser::state::comment;
return true;
}
case parser::state::comment:
{
if (row != current_row) {
std::cerr << "push " << (int)current_instruction.op << '\n';
inside_comment = false;
program.push_back(current_instruction);
state = parser::state::label_or_op;
continue;
}
if (*s.cbegin() == ';') {
inside_comment = true;
return true;
} else if (inside_comment) {
return true;
} else {
std::cout << "expected comment\n";
return false;
}
}
case parser::state::just_comment:
{
if (row != current_row) {
inside_comment = false;
state = parser::state::label_or_op;
continue;
}
if (*s.cbegin() == ';') {
inside_comment = true;
return true;
} else if (inside_comment) {
return true;
} else {
std::cout << "expected comment\n";
return false;
}
}
}
}
return true;
});
return parsed;
}
}

9
parser.hh Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <string_view>
#include "assembler.hh"
namespace parser {
bool parse(std::string_view buf, assembler::program_t& program);
}

127
tostring.cc Normal file
View File

@ -0,0 +1,127 @@
#include <map>
#include <string>
#include "instruction.hh"
#include "mneumonic.hh"
namespace tostring {
const std::unordered_map<op_t, std::string_view> op {
{op::ADC, mneumonic::op::ADC},
{op::AND, mneumonic::op::AND},
{op::ASL, mneumonic::op::ASL},
{op::BBR0, mneumonic::op::BBR0},
{op::BBR1, mneumonic::op::BBR1},
{op::BBR2, mneumonic::op::BBR2},
{op::BBR3, mneumonic::op::BBR3},
{op::BBR4, mneumonic::op::BBR4},
{op::BBR5, mneumonic::op::BBR5},
{op::BBR6, mneumonic::op::BBR6},
{op::BBR7, mneumonic::op::BBR7},
{op::BBS0, mneumonic::op::BBS0},
{op::BBS1, mneumonic::op::BBS1},
{op::BBS2, mneumonic::op::BBS2},
{op::BBS3, mneumonic::op::BBS3},
{op::BBS4, mneumonic::op::BBS4},
{op::BBS5, mneumonic::op::BBS5},
{op::BBS6, mneumonic::op::BBS6},
{op::BBS7, mneumonic::op::BBS7},
{op::BCC, mneumonic::op::BCC},
{op::BCS, mneumonic::op::BCS},
{op::BEQ, mneumonic::op::BEQ},
{op::BIT, mneumonic::op::BIT},
{op::BMI, mneumonic::op::BMI},
{op::BNE, mneumonic::op::BNE},
{op::BPL, mneumonic::op::BPL},
{op::BRA, mneumonic::op::BRA},
{op::BRK, mneumonic::op::BRK},
{op::BVC, mneumonic::op::BVC},
{op::BVS, mneumonic::op::BVS},
{op::CLC, mneumonic::op::CLC},
{op::CLD, mneumonic::op::CLD},
{op::CLI, mneumonic::op::CLI},
{op::CLV, mneumonic::op::CLV},
{op::CMP, mneumonic::op::CMP},
{op::CPX, mneumonic::op::CPX},
{op::CPY, mneumonic::op::CPY},
{op::DEC, mneumonic::op::DEC},
{op::DEX, mneumonic::op::DEX},
{op::DEY, mneumonic::op::DEY},
{op::EOR, mneumonic::op::EOR},
{op::INC, mneumonic::op::INC},
{op::INX, mneumonic::op::INX},
{op::INY, mneumonic::op::INY},
{op::JMP, mneumonic::op::JMP},
{op::JSR, mneumonic::op::JSR},
{op::LDA, mneumonic::op::LDA},
{op::LDX, mneumonic::op::LDX},
{op::LDY, mneumonic::op::LDY},
{op::LSR, mneumonic::op::LSR},
{op::NOP, mneumonic::op::NOP},
{op::ORA, mneumonic::op::ORA},
{op::PHA, mneumonic::op::PHA},
{op::PHP, mneumonic::op::PHP},
{op::PHX, mneumonic::op::PHX},
{op::PHY, mneumonic::op::PHY},
{op::PLA, mneumonic::op::PLA},
{op::PLP, mneumonic::op::PLP},
{op::PLX, mneumonic::op::PLX},
{op::PLY, mneumonic::op::PLY},
{op::RMB0, mneumonic::op::RMB0},
{op::RMB1, mneumonic::op::RMB1},
{op::RMB2, mneumonic::op::RMB2},
{op::RMB3, mneumonic::op::RMB3},
{op::RMB4, mneumonic::op::RMB4},
{op::RMB5, mneumonic::op::RMB5},
{op::RMB6, mneumonic::op::RMB6},
{op::RMB7, mneumonic::op::RMB7},
{op::ROL, mneumonic::op::ROL},
{op::ROR, mneumonic::op::ROR},
{op::RTI, mneumonic::op::RTI},
{op::RTS, mneumonic::op::RTS},
{op::SBC, mneumonic::op::SBC},
{op::SEC, mneumonic::op::SEC},
{op::SED, mneumonic::op::SED},
{op::SEI, mneumonic::op::SEI},
{op::SMB0, mneumonic::op::SMB0},
{op::SMB1, mneumonic::op::SMB1},
{op::SMB2, mneumonic::op::SMB2},
{op::SMB3, mneumonic::op::SMB3},
{op::SMB4, mneumonic::op::SMB4},
{op::SMB5, mneumonic::op::SMB5},
{op::SMB6, mneumonic::op::SMB6},
{op::SMB7, mneumonic::op::SMB7},
{op::STA, mneumonic::op::STA},
{op::STP, mneumonic::op::STP},
{op::STX, mneumonic::op::STX},
{op::STY, mneumonic::op::STY},
{op::STZ, mneumonic::op::STZ},
{op::TAX, mneumonic::op::TAX},
{op::TAY, mneumonic::op::TAY},
{op::TRB, mneumonic::op::TRB},
{op::TSB, mneumonic::op::TSB},
{op::TSX, mneumonic::op::TSX},
{op::TXA, mneumonic::op::TXA},
{op::TXS, mneumonic::op::TXS},
{op::TYA, mneumonic::op::TYA},
{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},
};
}

11
tostring.hh Normal file
View File

@ -0,0 +1,11 @@
#include <map>
#include <string>
#include "codec.hh"
#include "mneumonic.hh"
namespace tostring {
extern const std::unordered_map<op_t, std::string_view> op;
extern const std::unordered_map<amode_t, std::string_view> mode;
}