mirror of
https://github.com/lefticus/6502-cpp.git
synced 2024-12-21 10:30:35 +00:00
Almost working programs with AVR conversion
* need to add a decent header address * probably need to fix sbrc and sbrs
This commit is contained in:
parent
13af1e8cca
commit
5591249f2a
@ -1,13 +1,11 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
set (CMAKE_CXX_STANDARD 14)
|
|
||||||
|
|
||||||
project(x86-to-6502)
|
project(x86-to-6502)
|
||||||
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCC)
|
|
||||||
add_definitions(-Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -pedantic -std=c++14)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_executable(x86-to-6502 src/main.cpp)
|
add_executable(x86-to-6502 src/main.cpp)
|
||||||
|
|
||||||
|
target_compile_features(x86-to-6502 PRIVATE cxx_std_20)
|
||||||
|
|
||||||
|
if (CMAKE_COMPILER_IS_GNUCC)
|
||||||
|
target_compile_options(x86-to-6502 PRIVATE -Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -Wpedantic)
|
||||||
|
endif ()
|
||||||
|
@ -6,22 +6,22 @@ enum class Colors : uint8_t
|
|||||||
BLACK=0x00
|
BLACK=0x00
|
||||||
};
|
};
|
||||||
|
|
||||||
volatile uint8_t &memory_loc(const uint16_t loc)
|
inline volatile uint8_t &memory_loc(const uint16_t loc)
|
||||||
{
|
{
|
||||||
return *reinterpret_cast<volatile uint8_t *>(loc);
|
return *reinterpret_cast<volatile uint8_t *>(loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void decrement_border_color()
|
inline void decrement_border_color()
|
||||||
{
|
{
|
||||||
--memory_loc(0xd020);
|
--memory_loc(0xd020);
|
||||||
}
|
}
|
||||||
|
|
||||||
void increment_border_color()
|
inline void increment_border_color()
|
||||||
{
|
{
|
||||||
++memory_loc(0xd020);
|
++memory_loc(0xd020);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool joystick_down()
|
inline bool joystick_down()
|
||||||
{
|
{
|
||||||
uint8_t joystick_state = memory_loc(0xDC00);
|
uint8_t joystick_state = memory_loc(0xDC00);
|
||||||
return (joystick_state & 0x2) == 0;
|
return (joystick_state & 0x2) == 0;
|
||||||
|
173
src/main.cpp
173
src/main.cpp
@ -34,6 +34,10 @@ std::string fixup_8bit_literal(const std::string &s)
|
|||||||
{
|
{
|
||||||
return "#" + std::to_string(static_cast<uint8_t>(parse_8bit_literal(s)));
|
return "#" + std::to_string(static_cast<uint8_t>(parse_8bit_literal(s)));
|
||||||
} else {
|
} else {
|
||||||
|
if (s.starts_with("lo8(") && s.ends_with(")")) {
|
||||||
|
return "#<" + s.substr(4, s.size() - 5);
|
||||||
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,6 +64,7 @@ struct Operand
|
|||||||
Operand(const Type t, std::string v)
|
Operand(const Type t, std::string v)
|
||||||
: type(t), value(std::move(v))
|
: type(t), value(std::move(v))
|
||||||
{
|
{
|
||||||
|
|
||||||
assert(type == Type::literal);
|
assert(type == Type::literal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,6 +81,8 @@ struct mos6502 : ASMLine
|
|||||||
{
|
{
|
||||||
unknown,
|
unknown,
|
||||||
lda,
|
lda,
|
||||||
|
asl,
|
||||||
|
rol,
|
||||||
ldy,
|
ldy,
|
||||||
tay,
|
tay,
|
||||||
tya,
|
tya,
|
||||||
@ -140,6 +147,8 @@ struct mos6502 : ASMLine
|
|||||||
case OpCode::clc:
|
case OpCode::clc:
|
||||||
case OpCode::sec:
|
case OpCode::sec:
|
||||||
case OpCode::bit:
|
case OpCode::bit:
|
||||||
|
case OpCode::asl:
|
||||||
|
case OpCode::rol:
|
||||||
case OpCode::unknown:
|
case OpCode::unknown:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -179,6 +188,8 @@ struct mos6502 : ASMLine
|
|||||||
case OpCode::rts:
|
case OpCode::rts:
|
||||||
case OpCode::clc:
|
case OpCode::clc:
|
||||||
case OpCode::sec:
|
case OpCode::sec:
|
||||||
|
case OpCode::rol:
|
||||||
|
case OpCode::asl:
|
||||||
case OpCode::unknown:
|
case OpCode::unknown:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -206,6 +217,10 @@ struct mos6502 : ASMLine
|
|||||||
switch (o) {
|
switch (o) {
|
||||||
case OpCode::lda:
|
case OpCode::lda:
|
||||||
return "lda";
|
return "lda";
|
||||||
|
case OpCode::asl:
|
||||||
|
return "asl";
|
||||||
|
case OpCode::rol:
|
||||||
|
return "rol";
|
||||||
case OpCode::ldy:
|
case OpCode::ldy:
|
||||||
return "ldy";
|
return "ldy";
|
||||||
case OpCode::tay:
|
case OpCode::tay:
|
||||||
@ -482,7 +497,21 @@ struct AVR : ASMLine
|
|||||||
unknown,
|
unknown,
|
||||||
ldi,
|
ldi,
|
||||||
sts,
|
sts,
|
||||||
ret
|
ret,
|
||||||
|
mov,
|
||||||
|
lsl,
|
||||||
|
rol,
|
||||||
|
rcall,
|
||||||
|
ld,
|
||||||
|
subi,
|
||||||
|
st,
|
||||||
|
lds,
|
||||||
|
lsr,
|
||||||
|
andi,
|
||||||
|
eor,
|
||||||
|
sbrc,
|
||||||
|
sbrs,
|
||||||
|
rjmp
|
||||||
};
|
};
|
||||||
|
|
||||||
static OpCode parse_opcode(Type t, const std::string &o)
|
static OpCode parse_opcode(Type t, const std::string &o)
|
||||||
@ -495,9 +524,23 @@ struct AVR : ASMLine
|
|||||||
return OpCode::unknown;
|
return OpCode::unknown;
|
||||||
case Type::Instruction:
|
case Type::Instruction:
|
||||||
{
|
{
|
||||||
if (o == "ldi") { std::cout << "; parsed ldi\n"; return OpCode::ldi; }
|
if (o == "ldi") return OpCode::ldi;
|
||||||
if (o == "sts") return OpCode::sts;
|
if (o == "sts") return OpCode::sts;
|
||||||
if (o == "ret") return OpCode::ret;
|
if (o == "ret") return OpCode::ret;
|
||||||
|
if (o == "mov") return OpCode::mov;
|
||||||
|
if (o == "lsl") return OpCode::lsl;
|
||||||
|
if (o == "rol") return OpCode::rol;
|
||||||
|
if (o == "rcall") return OpCode::rcall;
|
||||||
|
if (o == "ld") return OpCode::ld;
|
||||||
|
if (o == "subi") return OpCode::subi;
|
||||||
|
if (o == "st") return OpCode::st;
|
||||||
|
if (o == "lds") return OpCode::lds;
|
||||||
|
if (o == "lsr") return OpCode::lsr;
|
||||||
|
if (o == "andi") return OpCode::andi;
|
||||||
|
if (o == "eor") return OpCode::eor;
|
||||||
|
if (o == "sbrc") return OpCode::sbrc;
|
||||||
|
if (o == "rjmp") return OpCode::rjmp;
|
||||||
|
if (o == "sbrs") return OpCode::sbrs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw std::runtime_error("Unknown opcode: " + o);
|
throw std::runtime_error("Unknown opcode: " + o);
|
||||||
@ -538,7 +581,7 @@ static Operand get_register(const int reg_num, [[maybe_unused]] const int offset
|
|||||||
case 29: return Operand(Operand::Type::literal, "$5e");
|
case 29: return Operand(Operand::Type::literal, "$5e");
|
||||||
case 30: return Operand(Operand::Type::literal, "$5f");
|
case 30: return Operand(Operand::Type::literal, "$5f");
|
||||||
case 31: return Operand(Operand::Type::literal, "$60");
|
case 31: return Operand(Operand::Type::literal, "$60");
|
||||||
};
|
}
|
||||||
throw std::runtime_error("Unhandled register number: " + std::to_string(reg_num));
|
throw std::runtime_error("Unhandled register number: " + std::to_string(reg_num));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,20 +614,111 @@ static Operand get_register(const int reg_num, [[maybe_unused]] const int offset
|
|||||||
|
|
||||||
void translate_instruction(std::vector<mos6502> &instructions, const AVR::OpCode op, const Operand &o1, const Operand &o2)
|
void translate_instruction(std::vector<mos6502> &instructions, const AVR::OpCode op, const Operand &o1, const Operand &o2)
|
||||||
{
|
{
|
||||||
std::cout << "; translating avr instruction : " << static_cast<int>(op) << '\n';
|
|
||||||
switch(op)
|
switch(op)
|
||||||
{
|
{
|
||||||
case AVR::OpCode::ldi:
|
case AVR::OpCode::ldi:
|
||||||
instructions.emplace_back(mos6502::OpCode::lda, Operand(o2.type, o2.value));
|
instructions.emplace_back(mos6502::OpCode::lda, Operand(o2.type, fixup_8bit_literal(o2.value)));
|
||||||
instructions.emplace_back(mos6502::OpCode::sta, AVR::get_register(o1.reg_num));
|
instructions.emplace_back(mos6502::OpCode::sta, AVR::get_register(o1.reg_num));
|
||||||
return;
|
return;
|
||||||
case AVR::OpCode::sts:
|
case AVR::OpCode::sts:
|
||||||
instructions.emplace_back(mos6502::OpCode::lda, AVR::get_register(o2.reg_num));
|
instructions.emplace_back(mos6502::OpCode::lda, AVR::get_register(o2.reg_num));
|
||||||
instructions.emplace_back(mos6502::OpCode::sta, Operand(o2.type, o2.value));
|
instructions.emplace_back(mos6502::OpCode::sta, Operand(o1.type, o1.value));
|
||||||
return;
|
return;
|
||||||
case AVR::OpCode::ret:
|
case AVR::OpCode::ret:
|
||||||
instructions.emplace_back(mos6502::OpCode::rts);
|
instructions.emplace_back(mos6502::OpCode::rts);
|
||||||
return;
|
return;
|
||||||
|
case AVR::OpCode::mov:
|
||||||
|
instructions.emplace_back(mos6502::OpCode::lda, AVR::get_register(o2.reg_num));
|
||||||
|
instructions.emplace_back(mos6502::OpCode::sta, AVR::get_register(o1.reg_num));
|
||||||
|
return;
|
||||||
|
case AVR::OpCode::lsl:
|
||||||
|
instructions.emplace_back(mos6502::OpCode::asl, AVR::get_register(o1.reg_num));
|
||||||
|
return;
|
||||||
|
case AVR::OpCode::rol:
|
||||||
|
instructions.emplace_back(mos6502::OpCode::rol, AVR::get_register(o1.reg_num));
|
||||||
|
return;
|
||||||
|
case AVR::OpCode::rcall:
|
||||||
|
instructions.emplace_back(mos6502::OpCode::jsr, Operand(o1.type, o1.value));
|
||||||
|
return;
|
||||||
|
case AVR::OpCode::ld:
|
||||||
|
{
|
||||||
|
if (o2.value == "Z") {
|
||||||
|
instructions.emplace_back(mos6502::OpCode::ldy, Operand(Operand::Type::literal, "#0"));
|
||||||
|
instructions.emplace_back(mos6502::OpCode::lda, Operand(Operand::Type::literal,
|
||||||
|
"(" + AVR::get_register(30).value + "), Y"));
|
||||||
|
instructions.emplace_back(mos6502::OpCode::sta, AVR::get_register(o1.reg_num));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Unhandled ld to non-Z");
|
||||||
|
}
|
||||||
|
case AVR::OpCode::subi:
|
||||||
|
{
|
||||||
|
// to do: deal with Carry bit (and other flags) nonsense from AVR
|
||||||
|
// if |x| < |y| -> x-y +carry
|
||||||
|
// for these special cases with -(1) and -(-(1))
|
||||||
|
if (o2.value == "lo8(-(-1))") {
|
||||||
|
instructions.emplace_back(mos6502::OpCode::dec, AVR::get_register(o1.reg_num));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (o2.value == "lo8(-(1))") {
|
||||||
|
instructions.emplace_back(mos6502::OpCode::inc, AVR::get_register(o1.reg_num));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Unhandled subi sub case");
|
||||||
|
}
|
||||||
|
case AVR::OpCode::st: {
|
||||||
|
if (o1.value == "Z") {
|
||||||
|
instructions.emplace_back(mos6502::OpCode::lda, AVR::get_register(o2.reg_num));
|
||||||
|
instructions.emplace_back(mos6502::OpCode::ldy, Operand(Operand::Type::literal, "#0"));
|
||||||
|
instructions.emplace_back(mos6502::OpCode::sta, Operand(Operand::Type::literal,
|
||||||
|
"(" + AVR::get_register(30).value + "), Y"));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Unhandled st with non-Z parameter");
|
||||||
|
}
|
||||||
|
case AVR::OpCode::lds: {
|
||||||
|
instructions.emplace_back(mos6502::OpCode::lda, o2);
|
||||||
|
instructions.emplace_back(mos6502::OpCode::sta, AVR::get_register(o1.reg_num));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case AVR::OpCode::lsr: {
|
||||||
|
instructions.emplace_back(mos6502::OpCode::lsr, AVR::get_register(o1.reg_num));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case AVR::OpCode::andi: {
|
||||||
|
instructions.emplace_back(mos6502::OpCode::lda, AVR::get_register(o1.reg_num));
|
||||||
|
instructions.emplace_back(mos6502::OpCode::AND, Operand(o2.type, fixup_8bit_literal(o2.value)));
|
||||||
|
instructions.emplace_back(mos6502::OpCode::sta, AVR::get_register(o1.reg_num));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case AVR::OpCode::eor: {
|
||||||
|
instructions.emplace_back(mos6502::OpCode::lda, AVR::get_register(o1.reg_num));
|
||||||
|
instructions.emplace_back(mos6502::OpCode::eor, AVR::get_register(o2.reg_num));
|
||||||
|
instructions.emplace_back(mos6502::OpCode::sta, AVR::get_register(o1.reg_num));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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())-1)))));
|
||||||
|
instructions.emplace_back(mos6502::OpCode::bit, AVR::get_register(o1.reg_num));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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())-1)))));
|
||||||
|
instructions.emplace_back(mos6502::OpCode::bit, AVR::get_register(o1.reg_num));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
case AVR::OpCode::rjmp: {
|
||||||
|
instructions.emplace_back(mos6502::OpCode::jmp, o1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("Could not translate unhandled instruction");
|
throw std::runtime_error("Could not translate unhandled instruction");
|
||||||
@ -905,7 +1039,6 @@ void log(LogLevel ll, const int line_no, const std::string &line, const std::str
|
|||||||
template<typename FromArch>
|
template<typename FromArch>
|
||||||
void to_mos6502(const FromArch &i, std::vector<mos6502> &instructions)
|
void to_mos6502(const FromArch &i, std::vector<mos6502> &instructions)
|
||||||
{
|
{
|
||||||
std::cout << "; arch " << typeid(FromArch).name() << '\n';
|
|
||||||
try {
|
try {
|
||||||
switch(i.type)
|
switch(i.type)
|
||||||
{
|
{
|
||||||
@ -916,8 +1049,6 @@ void to_mos6502(const FromArch &i, std::vector<mos6502> &instructions)
|
|||||||
instructions.emplace_back(i.type, i.text);
|
instructions.emplace_back(i.type, i.text);
|
||||||
return;
|
return;
|
||||||
case ASMLine::Type::Instruction:
|
case ASMLine::Type::Instruction:
|
||||||
instructions.emplace_back(ASMLine::Type::Directive, "; " + i.line_text);
|
|
||||||
|
|
||||||
const auto head = instructions.size();
|
const auto head = instructions.size();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1104,7 +1235,6 @@ void run(std::istream &input) {
|
|||||||
{
|
{
|
||||||
std::string line;
|
std::string line;
|
||||||
getline(input, line);
|
getline(input, line);
|
||||||
std::cout << "; read line: '" << line << "'\n";
|
|
||||||
try {
|
try {
|
||||||
std::smatch match;
|
std::smatch match;
|
||||||
if (std::regex_match(line, match, Label))
|
if (std::regex_match(line, match, Label))
|
||||||
@ -1132,12 +1262,8 @@ void run(std::istream &input) {
|
|||||||
++lineno;
|
++lineno;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "; done reading file" << std::endl;
|
|
||||||
|
|
||||||
std::set<std::string> labels;
|
std::set<std::string> labels;
|
||||||
|
|
||||||
std::cout << "; " << instructions.size() << " instructions parsed" << std::endl;
|
|
||||||
|
|
||||||
for (const auto &i : instructions)
|
for (const auto &i : instructions)
|
||||||
{
|
{
|
||||||
if (i.type == ASMLine::Type::Label) {
|
if (i.type == ASMLine::Type::Label) {
|
||||||
@ -1212,10 +1338,27 @@ void run(std::istream &input) {
|
|||||||
|
|
||||||
std::vector<mos6502> new_instructions;
|
std::vector<mos6502> new_instructions;
|
||||||
|
|
||||||
|
bool skip_next_instruction = false;
|
||||||
|
std::string next_label_name;
|
||||||
for (const auto &i : instructions)
|
for (const auto &i : instructions)
|
||||||
{
|
{
|
||||||
std::cout << "; translating instruction: '" << i.text << '\'' << std::endl;
|
|
||||||
to_mos6502(i, new_instructions);
|
to_mos6502(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 (skip_next_instruction) {
|
||||||
|
new_instructions.emplace_back(ASMLine::Type::Label, next_label_name);
|
||||||
|
skip_next_instruction = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_instruction.type == ASMLine::Type::Directive && last_instruction.text.starts_with("skip_next_instruction"))
|
||||||
|
{
|
||||||
|
skip_next_instruction = true;
|
||||||
|
next_label_name = last_instruction.text;
|
||||||
|
new_instructions.erase(std::next(new_instructions.begin(), static_cast<std::ptrdiff_t>(last_instruction_loc)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (fix_overwritten_flags(new_instructions))
|
while (fix_overwritten_flags(new_instructions))
|
||||||
|
Loading…
Reference in New Issue
Block a user