1
0
mirror of https://github.com/lefticus/6502-cpp.git synced 2025-02-06 14:30:11 +00:00

Implement a bunch more instructions

This commit is contained in:
Jason Turner 2021-05-08 17:58:14 -06:00
parent f79a189a12
commit d2241528e5
3 changed files with 123 additions and 45 deletions

View File

@ -17,6 +17,15 @@ struct C64 : Personality
new_instructions.emplace_back(ASMLine::Type::Directive, ".byt $0B,$08,$0A,$00,$9E,$32,$30,$36,$31,$00,$00,$00"); new_instructions.emplace_back(ASMLine::Type::Directive, ".byt $0B,$08,$0A,$00,$9E,$32,$30,$36,$31,$00,$00,$00");
} }
[[nodiscard]] std::string_view stack_low_address() const override
{
return "$02";
}
[[nodiscard]] std::string_view stack_high_address() const override
{
return "$03";
}
[[nodiscard]] Operand get_register(const int reg_num) const override [[nodiscard]] Operand get_register(const int reg_num) const override
{ {
switch (reg_num) { switch (reg_num) {

View File

@ -9,6 +9,8 @@ class Personality
public: public:
virtual void insert_autostart_sequence(std::vector<mos6502> &new_instructions) const = 0; virtual void insert_autostart_sequence(std::vector<mos6502> &new_instructions) const = 0;
[[nodiscard]] virtual Operand get_register(const int reg_num) const = 0; [[nodiscard]] virtual Operand get_register(const int reg_num) const = 0;
[[nodiscard]] virtual std::string_view stack_low_address() const = 0;
[[nodiscard]] virtual std::string_view stack_high_address() const= 0;
virtual ~Personality() = default; virtual ~Personality() = default;
Personality(const Personality &) = delete; Personality(const Personality &) = delete;

View File

@ -9,17 +9,25 @@
#include <set> #include <set>
#include <string> #include <string>
#include <vector> #include <vector>
#include <spdlog/spdlog.h>
#include <charconv>
#include "../include/assembly.hpp" #include "../include/assembly.hpp"
#include "../include/6502.hpp" #include "../include/6502.hpp"
#include "../include/optimizer.hpp" #include "../include/optimizer.hpp"
#include "../include/personalities/c64.hpp" #include "../include/personalities/c64.hpp"
int parse_8bit_literal(const std::string &s)
{ int to_int(const std::string_view sv) {
return std::stoi(std::string(std::next(std::begin(s)), std::end(s))); int result{};
std::from_chars(sv.begin(), sv.end(), result);
return result;
} }
int parse_8bit_literal(const std::string_view s)
{
return to_int(s.substr(1));
}
std::string_view strip_lo_hi(std::string_view s) std::string_view strip_lo_hi(std::string_view s)
{ {
@ -66,6 +74,7 @@ struct AVR : ASMLine
unknown, unknown,
adc, adc,
adiw,
add, add,
andi, andi,
@ -76,6 +85,7 @@ struct AVR : ASMLine
clr, clr,
com, com,
cp,
cpc, cpc,
cpi, cpi,
cpse, cpse,
@ -83,7 +93,10 @@ struct AVR : ASMLine
eor, eor,
in,
ld, ld,
ldd,
ldi, ldi,
lds, lds,
lsl, lsl,
@ -99,12 +112,15 @@ struct AVR : ASMLine
rjmp, rjmp,
rol, rol,
sbc,
sbci, sbci,
sbiw, sbiw,
sbrc, sbrc,
sbrs, sbrs,
st, st,
std,
sts, sts,
sub,
subi, subi,
swap, swap,
}; };
@ -124,9 +140,13 @@ struct AVR : ASMLine
if (o == "rol") { return OpCode::rol; } if (o == "rol") { return OpCode::rol; }
if (o == "rcall") { return OpCode::rcall; } if (o == "rcall") { return OpCode::rcall; }
if (o == "ld") { return OpCode::ld; } if (o == "ld") { return OpCode::ld; }
if (o == "sub") { return OpCode::subi; }
if (o == "subi") { return OpCode::subi; } if (o == "subi") { return OpCode::subi; }
if (o == "sbc") { return OpCode::sbci; }
if (o == "sbci") { return OpCode::sbci; } if (o == "sbci") { return OpCode::sbci; }
if (o == "st") { return OpCode::st; } if (o == "st") { return OpCode::st; }
if (o == "std") { return OpCode::std; }
if (o == "ldd") { return OpCode::ldd; }
if (o == "lds") { return OpCode::lds; } if (o == "lds") { return OpCode::lds; }
if (o == "lsr") { return OpCode::lsr; } if (o == "lsr") { return OpCode::lsr; }
if (o == "andi") { return OpCode::andi; } if (o == "andi") { return OpCode::andi; }
@ -136,6 +156,7 @@ struct AVR : ASMLine
if (o == "sbrs") { return OpCode::sbrs; } if (o == "sbrs") { return OpCode::sbrs; }
if (o == "brne") { return OpCode::brne; } if (o == "brne") { return OpCode::brne; }
if (o == "dec") { return OpCode::dec; } if (o == "dec") { return OpCode::dec; }
if (o == "adiw") { return OpCode::adiw; }
if (o == "sbiw") { return OpCode::sbiw; } if (o == "sbiw") { return OpCode::sbiw; }
if (o == "push") { return OpCode::push; } if (o == "push") { return OpCode::push; }
if (o == "pop") { return OpCode::pop; } if (o == "pop") { return OpCode::pop; }
@ -148,11 +169,13 @@ struct AVR : ASMLine
if (o == "add") { return OpCode::add; } if (o == "add") { return OpCode::add; }
if (o == "adc") { return OpCode::adc; } if (o == "adc") { return OpCode::adc; }
if (o == "cpc") { return OpCode::cpc; } if (o == "cpc") { return OpCode::cpc; }
if (o == "cp") { return OpCode::cp; }
if (o == "brsh") { return OpCode::brsh; } if (o == "brsh") { return OpCode::brsh; }
if (o == "breq") { return OpCode::breq; } if (o == "breq") { return OpCode::breq; }
if (o == "in") { return OpCode::in; }
} }
} }
throw std::runtime_error(fmt::format("Unknown opcode: {}")); throw std::runtime_error(fmt::format("Unknown opcode: {}", o));
} }
static int get_register_number(const char reg_name) static int get_register_number(const char reg_name)
@ -177,7 +200,7 @@ struct AVR : ASMLine
} }
if (o[0] == 'r' && o.size() > 1) { if (o[0] == 'r' && o.size() > 1) {
return Operand(Operand::Type::reg, atoi(&o[1])); return Operand(Operand::Type::reg, to_int(o.substr(1)));
} else { } else {
return Operand(Operand::Type::literal, std::string{ o }); return Operand(Operand::Type::literal, std::string{ o });
} }
@ -196,17 +219,17 @@ struct AVR : ASMLine
Operand operand2; Operand operand2;
}; };
void indirect_load(std::vector<mos6502> &instructions, const std::string &from_address_low_byte, const std::string &to_address) void indirect_load(std::vector<mos6502> &instructions, const std::string &from_address_low_byte, const std::string &to_address, const int offset = 0)
{ {
instructions.emplace_back(mos6502::OpCode::ldy, Operand(Operand::Type::literal, "#0")); instructions.emplace_back(mos6502::OpCode::ldy, Operand(Operand::Type::literal, fmt::format("#{}", offset)));
instructions.emplace_back(mos6502::OpCode::lda, Operand(Operand::Type::literal, "(" + from_address_low_byte + "), Y")); instructions.emplace_back(mos6502::OpCode::lda, Operand(Operand::Type::literal, "(" + from_address_low_byte + "), Y"));
instructions.emplace_back(mos6502::OpCode::sta, Operand(Operand::Type::literal, to_address)); instructions.emplace_back(mos6502::OpCode::sta, Operand(Operand::Type::literal, to_address));
} }
void indirect_store(std::vector<mos6502> &instructions, const std::string &from_address, const std::string &to_address_low_byte) void indirect_store(std::vector<mos6502> &instructions, const std::string &from_address, const std::string &to_address_low_byte, const int offset = 0)
{ {
instructions.emplace_back(mos6502::OpCode::lda, Operand(Operand::Type::literal, from_address)); instructions.emplace_back(mos6502::OpCode::lda, Operand(Operand::Type::literal, from_address));
instructions.emplace_back(mos6502::OpCode::ldy, Operand(Operand::Type::literal, "#0")); instructions.emplace_back(mos6502::OpCode::ldy, Operand(Operand::Type::literal, fmt::format("#{}", offset)));
instructions.emplace_back(mos6502::OpCode::sta, Operand(Operand::Type::literal, "(" + to_address_low_byte + "), Y")); instructions.emplace_back(mos6502::OpCode::sta, Operand(Operand::Type::literal, "(" + to_address_low_byte + "), Y"));
} }
@ -232,6 +255,19 @@ void fixup_16_bit_N_Z_flags(std::vector<mos6502> &instructions)
instructions.emplace_back(ASMLine::Type::Directive, "; END remove if next is lda"); instructions.emplace_back(ASMLine::Type::Directive, "; END remove if next is lda");
} }
void add_16_bit(const Personality &personality, std::vector<mos6502> &instructions, int reg, const std::uint16_t value)
{
//instructions.emplace_back(mos6502::OpCode::sta, Operand(Operand::Type::literal, address_low_byte));
instructions.emplace_back(mos6502::OpCode::clc);
instructions.emplace_back(mos6502::OpCode::lda, personality.get_register(reg));
instructions.emplace_back(mos6502::OpCode::adc, Operand(Operand::Type::literal, "#" + std::to_string((value & 0xFFu))));
instructions.emplace_back(mos6502::OpCode::sta, personality.get_register(reg));
instructions.emplace_back(mos6502::OpCode::lda, personality.get_register(reg + 1));
instructions.emplace_back(mos6502::OpCode::adc, Operand(Operand::Type::literal, "#" + std::to_string((value >> 8u) & 0xFFu)));
instructions.emplace_back(mos6502::OpCode::sta, personality.get_register(reg + 1));
instructions.emplace_back(mos6502::OpCode::tax);
fixup_16_bit_N_Z_flags(instructions);
}
void subtract_16_bit(const Personality &personality, std::vector<mos6502> &instructions, int reg, const std::uint16_t value) void subtract_16_bit(const Personality &personality, std::vector<mos6502> &instructions, int reg, const std::uint16_t value)
{ {
@ -312,7 +348,34 @@ void translate_instruction(const Personality &personality, std::vector<mos6502>
increment_16_bit(personality, instructions, AVR::get_register_number(o2.value[0])); increment_16_bit(personality, instructions, AVR::get_register_number(o2.value[0]));
return; return;
} }
throw std::runtime_error("Unhandled ld to non-Z"); throw std::runtime_error("Unknown ld indexing");
}
case AVR::OpCode::ldd: {
indirect_load(instructions, personality.get_register(AVR::get_register_number(o2.value[0])).value, personality.get_register(o1_reg_num).value, to_int(o2.value.substr(1)));
return;
}
case AVR::OpCode::std: {
indirect_store(instructions, personality.get_register(o2_reg_num).value, personality.get_register(AVR::get_register_number(o1.value[0])).value, to_int(o2.value.substr(1)));
return;
}
case AVR::OpCode::sub: {
// we want to utilize the carry flag, however it was set previously
// (it's really a borrow flag on the 6502)
instructions.emplace_back(mos6502::OpCode::clc);
instructions.emplace_back(mos6502::OpCode::lda, personality.get_register(o1_reg_num));
instructions.emplace_back(mos6502::OpCode::sbc, personality.get_register(o1_reg_num));
instructions.emplace_back(mos6502::OpCode::sta, personality.get_register(o1_reg_num));
fixup_16_bit_N_Z_flags(instructions);
return;
}
case AVR::OpCode::sbc: {
// we want to utilize the carry flag, however it was set previously
// (it's really a borrow flag on the 6502)
instructions.emplace_back(mos6502::OpCode::lda, personality.get_register(o1_reg_num));
instructions.emplace_back(mos6502::OpCode::sbc, personality.get_register(o1_reg_num));
instructions.emplace_back(mos6502::OpCode::sta, personality.get_register(o1_reg_num));
fixup_16_bit_N_Z_flags(instructions);
return;
} }
case AVR::OpCode::sbci: { case AVR::OpCode::sbci: {
// we want to utilize the carry flag, however it was set previously // we want to utilize the carry flag, however it was set previously
@ -388,7 +451,7 @@ void translate_instruction(const Personality &personality, std::vector<mos6502>
return; return;
} }
case AVR::OpCode::sbrc: { case AVR::OpCode::sbrc: {
instructions.emplace_back(mos6502::OpCode::lda, Operand(o2.type, fixup_8bit_literal("$" + std::to_string(1 << (atoi(o2.value.c_str())))))); 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)); 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()); 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(mos6502::OpCode::beq, Operand(Operand::Type::literal, new_label_name));
@ -396,7 +459,7 @@ void translate_instruction(const Personality &personality, std::vector<mos6502>
return; return;
} }
case AVR::OpCode::sbrs: { case AVR::OpCode::sbrs: {
instructions.emplace_back(mos6502::OpCode::lda, Operand(o2.type, fixup_8bit_literal("$" + std::to_string(1 << (atoi(o2.value.c_str())))))); 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)); 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()); 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(mos6502::OpCode::bne, Operand(Operand::Type::literal, new_label_name));
@ -418,7 +481,12 @@ void translate_instruction(const Personality &personality, std::vector<mos6502>
} }
case AVR::OpCode::sbiw: { case AVR::OpCode::sbiw: {
subtract_16_bit(personality, instructions, o1_reg_num, static_cast<std::uint16_t>(std::stoi(o2.value))); subtract_16_bit(personality, instructions, o1_reg_num, static_cast<std::uint16_t>(to_int(o2.value)));
return;
}
case AVR::OpCode::adiw: {
add_16_bit(personality, instructions, o1_reg_num, static_cast<std::uint16_t>(to_int(o2.value)));
return; return;
} }
@ -497,7 +565,12 @@ void translate_instruction(const Personality &personality, std::vector<mos6502>
instructions.emplace_back(mos6502::OpCode::sta, personality.get_register(o1_reg_num)); instructions.emplace_back(mos6502::OpCode::sta, personality.get_register(o1_reg_num));
return; return;
} }
case AVR::OpCode::cp: {
// note that this will leave the C flag in the 6502 borrow state, not normal carry state
instructions.emplace_back(mos6502::OpCode::lda, personality.get_register(o1_reg_num));
instructions.emplace_back(mos6502::OpCode::cmp, personality.get_register(o2_reg_num));
return;
}
case AVR::OpCode::cpc: { case AVR::OpCode::cpc: {
// this instruction seems to need to be used in the case after a sbc operation, where the // this instruction seems to need to be used in the case after a sbc operation, where the
// carry flag is set to the appropriate borrow state, so I'm going to not invert // carry flag is set to the appropriate borrow state, so I'm going to not invert
@ -522,6 +595,22 @@ void translate_instruction(const Personality &personality, std::vector<mos6502>
} }
} }
case AVR::OpCode::in: {
if (o2.value == "__SP_L__") {
instructions.emplace_back(mos6502::OpCode::lda, Operand(Operand::Type::literal, std::string{personality.stack_low_address()}));
instructions.emplace_back(mos6502::OpCode::sta, o1);
return;
}
if (o2.value == "__SP_H__") {
instructions.emplace_back(mos6502::OpCode::lda, Operand(Operand::Type::literal, std::string{ personality.stack_high_address() }));
instructions.emplace_back(mos6502::OpCode::sta, o1);
return;
}
throw std::runtime_error("Could not translate unknown 'in' instruction");
}
case AVR::OpCode::breq: { case AVR::OpCode::breq: {
instructions.emplace_back(mos6502::OpCode::beq, o1); instructions.emplace_back(mos6502::OpCode::beq, o1);
return; return;
@ -534,33 +623,6 @@ void translate_instruction(const Personality &personality, std::vector<mos6502>
throw std::runtime_error("Could not translate unhandled instruction"); throw std::runtime_error("Could not translate unhandled instruction");
} }
enum class LogLevel {
Warning,
Error
};
std::string to_string(const LogLevel ll)
{
switch (ll) {
case LogLevel::Warning:
return "warning";
case LogLevel::Error:
return "error";
}
return "unknown";
}
void log(LogLevel ll, const AVR &i, const std::string &message)
{
std::cerr << to_string(ll) << ": " << i.line_num << ": " << message << ": `" << i.line_text << "`\n";
}
void log(LogLevel ll, const std::size_t line_no, const std::string &line, const std::string &message)
{
std::cerr << to_string(ll) << ": " << line_no << ": " << message << ": `" << line << "`\n";
}
void to_mos6502(const Personality &personality, const AVR &from_instruction, std::vector<mos6502> &instructions) void to_mos6502(const Personality &personality, const AVR &from_instruction, std::vector<mos6502> &instructions)
{ {
try { try {
@ -606,7 +668,7 @@ void to_mos6502(const Personality &personality, const AVR &from_instruction, std
translate_instruction(personality, instructions, from_instruction.opcode, from_instruction.operand1, from_instruction.operand2); translate_instruction(personality, instructions, from_instruction.opcode, from_instruction.operand1, from_instruction.operand2);
} catch (const std::exception &e) { } catch (const std::exception &e) {
instructions.emplace_back(ASMLine::Type::Directive, "; Unhandled opcode: '" + from_instruction.text + "' " + e.what()); instructions.emplace_back(ASMLine::Type::Directive, "; Unhandled opcode: '" + from_instruction.text + "' " + e.what());
log(LogLevel::Error, from_instruction, e.what()); spdlog::error("[{}]: Unhandled instruction: '{}': {}", from_instruction.line_num, from_instruction.line_text, e.what());
} }
auto text = from_instruction.line_text; auto text = from_instruction.line_text;
@ -619,7 +681,7 @@ void to_mos6502(const Personality &personality, const AVR &from_instruction, std
return; return;
} }
} catch (const std::exception &e) { } catch (const std::exception &e) {
log(LogLevel::Error, from_instruction, e.what()); spdlog::error("[{}]: Unhandled instruction: '{}': {}", from_instruction.line_num, from_instruction.line_text, e.what());
} }
} }
@ -737,7 +799,7 @@ void run(const Personality &personality, std::istream &input)
// skip empty lines // skip empty lines
} }
} catch (const std::exception &e) { } catch (const std::exception &e) {
log(LogLevel::Error, lineno, line, e.what()); spdlog::error("[{}]: parse exception with '{}': {}", lineno, line, e.what());
} }
++lineno; ++lineno;
@ -789,7 +851,12 @@ void run(const Personality &personality, std::istream &input)
if (i.text == "0") { if (i.text == "0") {
i.text = "-memcpy_0"; i.text = "-memcpy_0";
} else { } else {
i.text = new_labels.at(i.text); try {
i.text = new_labels.at(i.text);
} catch (...) {
spdlog::error("Error looking up label name: '{}'", i.text);
throw;
}
} }
} }