Minor optimizer fixes

This commit is contained in:
Jason Turner 2021-06-02 21:12:28 -06:00
parent f52296a00f
commit 2536c4e2c7
3 changed files with 295 additions and 96 deletions

View File

@ -1,3 +1,4 @@
#include <algorithm>
#include <array>
#include <chrono>
#include <cstdint>
@ -5,41 +6,38 @@
#include <span>
#include <string_view>
#include <variant>
#include <algorithm>
#include <functional>
#include "chargen.hpp"
enum struct Colors : std::uint8_t
{
enum struct Colors : std::uint8_t {
black = 0,
white = 1,
red = 2,
cyan=3,
violet=4,
green=5,
blue=6,
yellow=7,
orange=8,
brown=9,
light_red=10,
dark_grey=11,
grey=12,
light_green=13,
light_blue=14,
light_grey=15
cyan = 3,
violet = 4,
green = 5,
blue = 6,
yellow = 7,
orange = 8,
brown = 9,
light_red = 10,
dark_grey = 11,
grey = 12,
light_green = 13,
light_blue = 14,
light_grey = 15
};
constexpr char charToPETSCII(char c) noexcept {
if (c >= 'A' && c <= 'Z') {
return c - 'A' + 1;
}
constexpr char charToPETSCII(char c) noexcept
{
if (c >= 'A' && c <= 'Z') { return c - 'A' + 1; }
return c;
}
template<std::size_t Size>
constexpr auto PETSCII(const char (&value)[Size]) noexcept
template<std::size_t Size> constexpr auto PETSCII(const char (&value)[Size]) noexcept
{
std::array<char, Size-1> result{};
std::array<char, Size - 1> result{};
std::transform(std::begin(value), std::prev(std::end(value)), std::begin(result), charToPETSCII);
return result;
}
@ -75,7 +73,7 @@ static bool joystick_down()
void use_data(std::array<char, 1024> &data);
static void puts(uint8_t x, uint8_t y, const auto &range, const Colors color=Colors::white)
static void puts(uint8_t x, uint8_t y, const auto &range, const Colors color = Colors::white)
{
const auto offset = y * 40 + x;
@ -126,6 +124,12 @@ template<std::uint8_t Width, std::uint8_t Height> struct Graphic
}
};
template<std::uint8_t Width, std::uint8_t Height> struct ColoredGraphic
{
Graphic<Width, Height> data;
Graphic<Width, Height> colors;
};
static constexpr auto load_charset(const std::span<const std::uint8_t, 256 * 8> &bits)
{
@ -236,6 +240,12 @@ static std::uint8_t loadc(uint8_t x, uint8_t y)
return peek(start);
}
static void invertc(uint8_t x, uint8_t y)
{
const auto start = 0x400 + (y * 40 + x);
memory_loc(start) += 128;
}
static void put_hex(uint8_t x, uint8_t y, uint8_t value, Colors color)
{
@ -260,10 +270,22 @@ static void put_hex(uint8_t x, uint8_t y, uint16_t value, Colors color)
static void put_graphic(uint8_t x, uint8_t y, const auto &graphic)
{
for (uint8_t cur_y = 0; cur_y < graphic.height(); ++cur_y) {
for (uint8_t cur_x = 0; cur_x < graphic.width(); ++cur_x) { putc(cur_x + x, cur_y + y, graphic(cur_x, cur_y), Colors::white); }
for (uint8_t cur_x = 0; cur_x < graphic.width(); ++cur_x) {
putc(cur_x + x, cur_y + y, graphic(cur_x, cur_y), Colors::white);
}
}
}
static void put_graphic(uint8_t x, uint8_t y, const auto &graphic) requires requires { graphic.colors(0, 0); }
{
for (uint8_t cur_y = 0; cur_y < graphic.data.height(); ++cur_y) {
for (uint8_t cur_x = 0; cur_x < graphic.data.width(); ++cur_x) {
putc(cur_x + x, cur_y + y, graphic.data(cur_x, cur_y), static_cast<Colors>(graphic.colors(cur_x, cur_y)));
}
}
}
struct Clock
{
using milliseconds = std::chrono::duration<std::uint16_t, std::milli>;
@ -324,7 +346,8 @@ struct Screen
}
}
void hide(auto &s) {
void hide(auto &s)
{
if (s.is_shown) {
put_graphic(s.x, s.y, s.saved_background);
s.is_shown = false;
@ -345,8 +368,7 @@ struct Screen
}
};
template<std::uint8_t Width, std::uint8_t Height>
struct Map;
template<std::uint8_t Width, std::uint8_t Height> struct Map;
struct GameState
{
@ -363,16 +385,24 @@ struct GameState
Map<10, 5> const *current_map = nullptr;
constexpr void set_current_map(const Map<10, 5> &new_map) {
constexpr void set_current_map(const Map<10, 5> &new_map)
{
current_map = &new_map;
redraw = true;
}
constexpr void execute_actions(const auto &character) noexcept
constexpr void execute_actions(std::uint8_t new_x, std::uint8_t new_y, const auto &character) noexcept
{
if (new_x + character.width() > 40) { new_x = x; }
if (new_y + character.height() > 20) { new_y = y; }
x = new_x;
y = new_y;
if (current_map) {
for (auto &action : current_map->actions) {
action.execute_if_collision(x,y,character.width(), character.height(), *this);
action.execute_if_collision(x, y, character.width(), character.height(), *this);
}
}
}
@ -423,10 +453,13 @@ struct Map_Action
using Action_Func = void (*)(GameState &);
Action_Func action = nullptr;
constexpr void execute_if_collision(std::uint8_t obj_x, std::uint8_t obj_y, std::uint8_t obj_width, std::uint8_t obj_height, GameState &game) const {
if (action == nullptr) {
return;
}
constexpr void execute_if_collision(std::uint8_t obj_x,
std::uint8_t obj_y,
std::uint8_t obj_width,
std::uint8_t obj_height,
GameState &game) const
{
if (action == nullptr) { return; }
const std::uint8_t rect1_x1 = x;
const std::uint8_t rect1_x2 = x + width;
@ -438,26 +471,43 @@ struct Map_Action
const std::uint8_t rect2_y1 = obj_y;
const std::uint8_t rect2_y2 = obj_y + obj_height;
if (rect1_x1 < rect2_x2 && rect1_x2 > rect2_x1 &&
rect1_y1 < rect2_y2 && rect1_y2 > rect2_y1) {
action(game);
}
if (rect1_x1 < rect2_x2 && rect1_x2 > rect2_x1 && rect1_y1 < rect2_y2 && rect1_y2 > rect2_y1) { action(game); }
}
};
template<std::uint8_t Width, std::uint8_t Height>
struct Map {
template<std::uint8_t Width, std::uint8_t Height> struct Map
{
std::string_view name;
Graphic<Width, Height> layout;
std::span<const Map_Action> actions;
};
void draw_box(uint8_t x, uint8_t y, uint8_t width, uint8_t height, Colors color) {
putc(x, y, 85, color);
putc(x + width - 1, y, 73, color);
putc(x + width - 1, y + height - 1, 75, color);
putc(x, y + height - 1, 74, color);
for (uint8_t cur_x = x + 1; cur_x < x + width - 1; ++cur_x) {
putc(cur_x, y, 67, color);
putc(cur_x, y + height - 1, 67, color);
}
for (uint8_t cur_y = y + 1; cur_y < y + height - 1; ++cur_y) {
putc(x, cur_y, 93, color);
putc(x + width - 1, cur_y, 93, color);
}
}
int main()
{
// static constexpr auto charset = load_charset(uppercase);
// clang-format off
static constexpr auto inn = Graphic<6,5> {
32,233,160,160,223,32,
233,160,160,160,160,223,
@ -482,12 +532,29 @@ int main()
160,160,160,160,76,160,
};
/*
static constexpr auto town = Graphic<4, 4>{
85, 67, 67, 73,
93, 233, 223, 93,
93, 160, 160, 93,
74, 67, 67, 75 };
*/
static constexpr auto town = ColoredGraphic<4, 4>{
{
32, 32,32, 32,
233,223,233,223,
224,224,224,224,
104,104,104,104
},
{
2,2,10,10,
4,4,7,7,
4,4,7,7,
11,11,11,11
}
};
static constexpr auto mountain = Graphic<4, 4>{
32, 78, 77, 32,
@ -495,6 +562,21 @@ int main()
78, 77, 32, 32,
32, 78, 77, 32 };
static constexpr auto colored_mountain = ColoredGraphic<4, 4>{
{
32, 78, 77, 32,
32, 32, 233, 223,
233, 223, 32, 32,
32, 78, 77, 32 },
{
1, 9, 9, 1,
1, 1, 8, 8,
9, 9, 1, 1,
1, 8, 8, 1
}
};
auto character = SimpleSprite<2, 3>{
32, 87,
@ -529,14 +611,14 @@ int main()
},
std::span<const Map_Action>(begin(overview_actions), end(overview_actions))
};
// clang-format on
static constexpr std::array<void (*)(std::uint8_t, std::uint8_t), 7> tile_types{
[](std::uint8_t x, std::uint8_t y) {
/* do nothing for 0th */
},
[](std::uint8_t x, std::uint8_t y) { put_graphic(x, y, mountain); },
[](std::uint8_t x, std::uint8_t y) { put_graphic(x, y, colored_mountain); },
[](std::uint8_t x, std::uint8_t y) {
/* do nothing for 2 */
},
@ -557,24 +639,6 @@ int main()
}
};
constexpr auto draw_box = [](uint8_t x, uint8_t y, uint8_t width, uint8_t height, Colors color) {
putc(x, y, 85, color);
putc(x + width - 1, y, 73, color);
putc(x + width - 1, y + height - 1, 75, color);
putc(x, y + height - 1, 74, color);
for (uint8_t cur_x = x + 1; cur_x < x + width - 1; ++cur_x) {
putc(cur_x, y, 67, color);
putc(cur_x, y + height - 1, 67, color);
}
for (uint8_t cur_y = y + 1; cur_y < y + height - 1; ++cur_y) {
putc(x, cur_y, 93, color);
putc(x + width - 1, cur_y, 93, color);
}
};
GameState game;
game.current_map = &overview_map;
@ -591,13 +655,132 @@ int main()
Screen screen;
auto eventHandler = overloaded{ [&](const GameState::JoyStick2StateChanged &e) {
if (e.state.up()) { --game.y; }
if (e.state.down()) { ++game.y; }
if (e.state.left()) { --game.x; }
if (e.state.right()) { ++game.x; }
static constexpr auto menu_options =
std::array {
std::string_view{"info"},
std::string_view{"test2"},
std::string_view{"test3"},
std::string_view{"an even longer thing"}
};
struct Menu {
consteval Menu(std::span<const std::string_view> t_options)
: options{t_options}, width{std::max_element(begin(options), end(options),
[](std::string_view lhs, std::string_view rhs) {
return lhs.size() < rhs.size();
}
)->size() + 2},
height{options.size() + 2},
x{(40 - width) / 2},
y{(20 - height) / 2}
{
game.execute_actions(character.graphic);
}
void highlight(std::uint8_t selection) {
const std::uint8_t cur_y = selection + 1 + y;
for (auto cur_x = 1; cur_x < width - 1; ++cur_x) {
invertc(x + cur_x, cur_y);
}
}
void unhighlight(std::uint8_t selection)
{
highlight(selection);
}
void hide(GameState &game) {
displayed = false;
game.redraw = true;
}
bool show(GameState &game, std::uint8_t &selection) {
if (!displayed) {
displayed = true;
draw_box(x, y, width, height, Colors::white);
for (auto cur_y = y + 1; const auto &str : options) {
puts(x+1, cur_y++, str, Colors::grey);
}
highlight(current_selection);
}
if (current_selection != next_selection) {
unhighlight(current_selection);
highlight(next_selection);
current_selection = next_selection;
}
if (selected) {
selected = false;
selection = current_selection;
return true;
} else {
return false;
}
}
bool process_event(const GameState::Event &e) {
if (not displayed) {
return false;
}
if (const auto *ptr = std::get_if<GameState::JoyStick2StateChanged>(&e); ptr) {
if (ptr->state.up()) {
next_selection = current_selection - 1;
}
if (ptr->state.down()) {
next_selection = current_selection + 1;
}
if (next_selection > options.size() - 1) {
next_selection = options.size() - 1;
}
if (ptr->state.fire()) {
selected = true;
}
return true;
}
return false;
}
std::span<const std::string_view> options;
std::uint8_t width;
std::uint8_t height;
std::uint8_t x;
std::uint8_t y;
std::uint8_t current_selection{0};
std::uint8_t next_selection{0};
bool selected{false};
bool displayed{false};
};
Menu m(menu_options);
bool show_game_menu = false;
auto eventHandler = overloaded{ [&](const GameState::JoyStick2StateChanged &e) {
auto new_x = game.x;
auto new_y = game.y;
if (e.state.fire()) {
show_game_menu = true;
return;
}
if (e.state.up()) { --new_y; }
if (e.state.down()) { ++new_y; }
if (e.state.left()) { --new_x; }
if (e.state.right()) { ++new_x; }
game.execute_actions(new_x, new_y, character.graphic);
screen.show(game.x, game.y, character);
@ -606,7 +789,12 @@ int main()
[](const GameState::TimeElapsed &e) { put_hex(36, 0, e.us.count(), Colors::dark_grey); } };
while (true) {
std::visit(eventHandler, game.next_event());
const auto next_event = game.next_event();
if (not m.process_event(next_event)) {
// if no gui elements needed the event, then we handle it
std::visit(eventHandler, next_event);
}
if (game.redraw) {
screen.hide(character);
@ -624,6 +812,14 @@ int main()
screen.show(game.x, game.y, character);
}
if (std::uint8_t result = 0; show_game_menu && m.show(game, result)) {
// we had a menu item selected
m.hide(game);
show_game_menu = false;
}
increment_border_color();
}

View File

@ -121,7 +121,7 @@ bool optimize_dead_sta(std::span<mos6502> &block, const Personality &personality
for (auto itr = block.begin(); itr != block.end(); ++itr) {
if (itr->opcode == mos6502::OpCode::sta && is_virtual_register_op(*itr, personality)) {
for (auto inner = std::next(itr); inner != block.end(); ++inner) {
if (inner->op.value.find('(') != std::string::npos) {
if (not inner->op.value.starts_with("#<(") && inner->op.value.find('(') != std::string::npos) {
// this is an indexed operation, which is risky to optimize a sta around on the virtual registers,
// so we'll skip this block
break;
@ -260,9 +260,11 @@ bool optimize(std::vector<mos6502> &instructions, [[maybe_unused]] const Persona
// it might make sense in the future to only insert these if determined they are needed?
for (size_t op = 10; op < instructions.size(); ++op) {
if (instructions[op].opcode == mos6502::OpCode::lda || instructions[op].opcode == mos6502::OpCode::bcc
|| instructions[op].opcode == mos6502::OpCode::bcs || instructions[op].opcode == mos6502::OpCode::ldy) {
if (instructions[op - 1].text == "; END remove if next is lda, bcc, bcs, ldy"
|| (instructions[op - 2].text == "; END remove if next is lda, bcc, bcs, ldy"
|| instructions[op].opcode == mos6502::OpCode::bcs || instructions[op].opcode == mos6502::OpCode::ldy
|| instructions[op].opcode == mos6502::OpCode::inc || instructions[op].opcode == mos6502::OpCode::clc
|| instructions[op].opcode == mos6502::OpCode::sec) {
if (instructions[op - 1].text == "; END remove if next is lda, bcc, bcs, ldy, inc, clc, sec"
|| (instructions[op - 2].text == "; END remove if next is lda, bcc, bcs, ldy, inc, clc, sec"
&& instructions[op - 1].type == ASMLine::Type::Directive)) {
for (size_t inner_op = op - 1; inner_op > 1; --inner_op) {
instructions[inner_op] =

View File

@ -210,7 +210,7 @@ struct AVR : ASMLine
if (o == "nop") { return OpCode::nop; }
if (o == "jmp") { return OpCode::jmp; }
if (o == "tst") { return OpCode::tst; }
if (o == "brge"){ return OpCode::brge; }
if (o == "brge") { return OpCode::brge; }
if (o == "brlt") { return OpCode::brlt; }
if (o == "or") { return OpCode::OR; }
}
@ -291,7 +291,7 @@ void indirect_store(std::vector<mos6502> &instructions,
instructions.emplace_back(ASMLine::Type::Label, n_set);
instructions.emplace_back(mos6502::OpCode::bvs, Operand(Operand::Type::literal, s_clear));
instructions.emplace_back(mos6502::OpCode::jmp, Operand(Operand::Type::literal, s_set));
return {s_set, s_clear};
return { s_set, s_clear };
}
void fixup_16_bit_N_Z_flags(std::vector<mos6502> &instructions)
@ -313,7 +313,7 @@ void fixup_16_bit_N_Z_flags(std::vector<mos6502> &instructions)
// if low order byte is negative, just load 1, this will properly set the Z flag and leave C correct
instructions.emplace_back(mos6502::OpCode::lda, Operand(Operand::Type::literal, "#1"));
instructions.emplace_back(ASMLine::Type::Label, set_flag_label);
instructions.emplace_back(ASMLine::Type::Directive, "; END remove if next is lda, bcc, bcs, ldy");
instructions.emplace_back(ASMLine::Type::Directive, "; END remove if next is lda, bcc, bcs, ldy, inc, clc, sec");
}
void add_16_bit(const Personality &personality,
@ -617,7 +617,7 @@ void translate_instruction(const Personality &personality,
case AVR::OpCode::cpse: {
instructions.emplace_back(mos6502::OpCode::lda, personality.get_register(o1_reg_num));
instructions.emplace_back(mos6502::OpCode::cmp, personality.get_register(o2_reg_num));
std::string new_label_name = "skip_next_instruction_" + std::to_string(instructions.size()) + "__optimizable";
std::string new_label_name = "skip_next_instruction_" + std::to_string(instructions.size());
instructions.emplace_back(mos6502::OpCode::beq, Operand(Operand::Type::literal, new_label_name));
instructions.emplace_back(ASMLine::Type::Directive, new_label_name);
return;
@ -626,7 +626,7 @@ void translate_instruction(const Personality &personality,
instructions.emplace_back(
mos6502::OpCode::lda, Operand(o2.type, fixup_8bit_literal("$" + std::to_string(1 << (to_int(o2.value))))));
instructions.emplace_back(mos6502::OpCode::bit, personality.get_register(o1_reg_num));
std::string new_label_name = "skip_next_instruction_" + std::to_string(instructions.size()) + "__optimizable";
std::string new_label_name = "skip_next_instruction_" + std::to_string(instructions.size());
instructions.emplace_back(mos6502::OpCode::beq, Operand(Operand::Type::literal, new_label_name));
instructions.emplace_back(ASMLine::Type::Directive, new_label_name);
return;
@ -635,7 +635,7 @@ void translate_instruction(const Personality &personality,
instructions.emplace_back(mos6502::OpCode::lda,
Operand(o2.type, fixup_8bit_literal("$" + std::to_string(1 << (to_int(o2.value.c_str()))))));
instructions.emplace_back(mos6502::OpCode::bit, personality.get_register(o1_reg_num));
std::string new_label_name = "skip_next_instruction_" + std::to_string(instructions.size()) + "__optimizable";
std::string new_label_name = "skip_next_instruction_" + std::to_string(instructions.size());
instructions.emplace_back(mos6502::OpCode::bne, Operand(Operand::Type::literal, new_label_name));
instructions.emplace_back(ASMLine::Type::Directive, new_label_name);
return;
@ -646,7 +646,7 @@ void translate_instruction(const Personality &personality,
return;
} else if (o1.value == ".+2") {
// assumes 6502 'borrow' for Carry flag instead of carry, so bcc instead of bcs
std::string new_label_name = "skip_next_instruction_" + std::to_string(instructions.size()) + "__optimizable";
std::string new_label_name = "skip_next_instruction_" + std::to_string(instructions.size());
instructions.emplace_back(mos6502::OpCode::bne, Operand(Operand::Type::literal, new_label_name));
instructions.emplace_back(ASMLine::Type::Directive, new_label_name);
return;
@ -688,7 +688,6 @@ void translate_instruction(const Personality &personality,
instructions.emplace_back(mos6502::OpCode::lda, Operand(Operand::Type::literal, "#$FF"));
instructions.emplace_back(mos6502::OpCode::eor, personality.get_register(o1_reg_num));
instructions.emplace_back(mos6502::OpCode::sta, personality.get_register(o1_reg_num));
instructions.emplace_back(mos6502::OpCode::sec);
return;
}
case AVR::OpCode::clr: {
@ -708,7 +707,7 @@ void translate_instruction(const Personality &personality,
if (o1.value == ".+2") {
// assumes 6502 'borrow' for Carry flag instead of carry, so bcc instead of bcs
std::string new_label_name = "skip_next_instruction_" + std::to_string(instructions.size()) + "__optimizable";
std::string new_label_name = "skip_next_instruction_" + std::to_string(instructions.size());
instructions.emplace_back(mos6502::OpCode::bcc, Operand(Operand::Type::literal, new_label_name));
instructions.emplace_back(ASMLine::Type::Directive, new_label_name);
return;
@ -773,7 +772,7 @@ void translate_instruction(const Personality &personality,
case AVR::OpCode::brsh: {
if (o1.value == ".+2") {
// assumes 6502 'borrow' for Carry flag instead of carry, so bcs instead of bcc
std::string new_label_name = "skip_next_instruction_" + std::to_string(instructions.size()) + "__optimizable";
std::string new_label_name = "skip_next_instruction_" + std::to_string(instructions.size());
instructions.emplace_back(mos6502::OpCode::bcs, Operand(Operand::Type::literal, new_label_name));
instructions.emplace_back(ASMLine::Type::Directive, new_label_name);
return;
@ -816,7 +815,7 @@ void translate_instruction(const Personality &personality,
case AVR::OpCode::breq: {
if (o1.value == ".+2") {
// assumes 6502 'borrow' for Carry flag instead of carry, so bcc instead of bcs
std::string new_label_name = "skip_next_instruction_" + std::to_string(instructions.size()) + "__optimizable";
std::string new_label_name = "skip_next_instruction_" + std::to_string(instructions.size());
instructions.emplace_back(mos6502::OpCode::beq, Operand(Operand::Type::literal, new_label_name));
instructions.emplace_back(ASMLine::Type::Directive, new_label_name);
return;
@ -857,7 +856,7 @@ void to_mos6502(const Personality &personality, const AVR &from_instruction, std
if (const auto results = matcher(from_instruction.text); results) {
const auto matched_gs = results.get<1>().to_string();
instructions.emplace_back(ASMLine::Type::Directive, ".word " + std::string{strip_gs(matched_gs)});
instructions.emplace_back(ASMLine::Type::Directive, ".word " + std::string{ strip_gs(matched_gs) });
} else {
instructions.emplace_back(ASMLine::Type::Directive, ".word " + from_instruction.text.substr(6));
// spdlog::warn("Unknown .word directive '{}'", from_instruction.text);
@ -925,7 +924,7 @@ bool fix_long_branches(std::vector<mos6502> &instructions, int &branch_patch_cou
for (size_t op = 0; op < instructions.size(); ++op) {
if (instructions[op].is_branch
&& std::abs(static_cast<int>(labels[instructions[op].op.value]) - static_cast<int>(op)) * 3 > 255) {
&& std::abs(static_cast<int>(labels[instructions[op].op.value]) - static_cast<int>(op)) * 4 > 255) {
++branch_patch_count;
const auto going_to = instructions[op].op.value;
const auto new_pos = "patch_" + std::to_string(branch_patch_count);
@ -1051,7 +1050,7 @@ std::vector<mos6502> run(const Personality &personality, std::istream &input, co
if (const auto results = matcher(i.text); results) {
const auto matched_gs = results.get<1>().to_string();
spdlog::trace("matched .word: '{}' from '{}'", matched_gs, i.text);
check_label(std::string{strip_gs(matched_gs)});
check_label(std::string{ strip_gs(matched_gs) });
}
}
}
@ -1086,6 +1085,7 @@ std::vector<mos6502> run(const Personality &personality, std::istream &input, co
i.text = new_labels.at(i.text);
} catch (...) {
spdlog::warn("Unused label: '{}', consider making function static until we remove unused functions", i.text);
i.text = "; Label is unused: " + i.text;
}
}
}
@ -1097,9 +1097,7 @@ std::vector<mos6502> run(const Personality &personality, std::istream &input, co
const auto matched_gs = results.get<1>().to_string();
const auto possible_label = std::string{ strip_gs(matched_gs) };
const auto matched_label = new_labels.find(possible_label);
if (matched_label != new_labels.end()) {
i.text = ".word " + matched_label->second;
}
if (matched_label != new_labels.end()) { i.text = ".word " + matched_label->second; }
}
}
@ -1131,30 +1129,33 @@ std::vector<mos6502> run(const Personality &personality, std::istream &input, co
new_instructions.emplace_back(mos6502::OpCode::jmp, Operand(Operand::Type::literal, "main"));
int instructions_to_skip = 0;
int instructions_to_skip = -1;
std::string next_label_name;
for (const auto &i : instructions) {
to_mos6502(personality, i, new_instructions);
// intentionally copy so we don't invalidate the reference
const auto last_instruction = new_instructions.back();
const auto last_instruction_loc = new_instructions.size() - 1;
if (instructions_to_skip == 1) { new_instructions.emplace_back(ASMLine::Type::Label, next_label_name); }
if (i.type == ASMLine::Type::Instruction) { --instructions_to_skip; }
if (instructions_to_skip == 0) {
new_instructions.emplace_back(ASMLine::Type::Label, next_label_name);
// todo: I kind of hate this -1 as a marker
instructions_to_skip = -1;
}
--instructions_to_skip;
if (last_instruction.type == ASMLine::Type::Directive
&& last_instruction.text.starts_with("skip_next_instruction")) {
instructions_to_skip = 1;
next_label_name = last_instruction.text;
new_instructions.erase(std::next(new_instructions.begin(), static_cast<std::ptrdiff_t>(last_instruction_loc)));
new_instructions.pop_back();
}
if (last_instruction.type == ASMLine::Type::Directive
&& last_instruction.text.starts_with("skip_next_2_instructions")) {
instructions_to_skip = 2;
next_label_name = last_instruction.text;
new_instructions.erase(std::next(new_instructions.begin(), static_cast<std::ptrdiff_t>(last_instruction_loc)));
new_instructions.pop_back();
}
}