mirror of
https://github.com/lefticus/6502-cpp.git
synced 2024-11-01 05:05:23 +00:00
Reorg of register handling
This commit is contained in:
parent
622354e6b2
commit
d59fbdaf4c
340
src/main.cpp
340
src/main.cpp
@ -52,6 +52,10 @@ struct Operand
|
||||
|
||||
Operand() = default;
|
||||
|
||||
bool operator==(const Operand &other) const {
|
||||
return type == other.type && reg_num == other.reg_num && value == other.value;
|
||||
}
|
||||
|
||||
Operand(const Type t, std::string v)
|
||||
: type(t), value(std::move(v))
|
||||
{
|
||||
@ -65,13 +69,26 @@ struct Operand
|
||||
}
|
||||
};
|
||||
|
||||
Operand get_register(int reg_num) {
|
||||
Operand get_register(const int reg_num, const int offset = 0) {
|
||||
switch (reg_num) {
|
||||
case 1: return Operand(Operand::Type::literal, "$fb");
|
||||
case 2: return Operand(Operand::Type::literal, "$fc");
|
||||
case 3: return Operand(Operand::Type::literal, "$fd");
|
||||
// http://sta.c64.org/cbm64mem.html
|
||||
case 0x00: return Operand(Operand::Type::literal, "$03"); // unused, fp->int routine pointer
|
||||
case 0x01: return Operand(Operand::Type::literal, "$04");
|
||||
case 0x02: return Operand(Operand::Type::literal, "$05"); // unused, int->fp routine pointer
|
||||
case 0x03: return Operand(Operand::Type::literal, "$06");
|
||||
case 0x04: return Operand(Operand::Type::literal, "$fb"); // unused
|
||||
case 0x05: return Operand(Operand::Type::literal, "$fc"); // unused
|
||||
case 0x06: return Operand(Operand::Type::literal, "$fd"); // unused
|
||||
case 0x07: return Operand(Operand::Type::literal, "$fe"); // unused
|
||||
case 0x08: return Operand(Operand::Type::literal, "$22"); // unused
|
||||
case 0x09: return Operand(Operand::Type::literal, "$23"); // unused
|
||||
case 0x10: return get_register(0x00 + offset);
|
||||
case 0x11: return get_register(0x02 + offset);
|
||||
case 0x12: return get_register(0x04 + offset);
|
||||
case 0x13: return get_register(0x06 + offset);
|
||||
case 0x14: return get_register(0x08 + offset);
|
||||
};
|
||||
throw std::runtime_error("Cannot translate instruction");
|
||||
throw std::runtime_error("Unhandled register number: " + std::to_string(reg_num));
|
||||
}
|
||||
|
||||
struct mos6502 : ASMLine
|
||||
@ -103,7 +120,8 @@ struct mos6502 : ASMLine
|
||||
jmp,
|
||||
adc,
|
||||
sbc,
|
||||
rts
|
||||
rts,
|
||||
clc
|
||||
};
|
||||
|
||||
static bool get_is_branch(const OpCode o) {
|
||||
@ -134,6 +152,7 @@ struct mos6502 : ASMLine
|
||||
case OpCode::adc:
|
||||
case OpCode::sbc:
|
||||
case OpCode::rts:
|
||||
case OpCode::clc:
|
||||
case OpCode::unknown:
|
||||
break;
|
||||
}
|
||||
@ -168,6 +187,7 @@ struct mos6502 : ASMLine
|
||||
case OpCode::adc:
|
||||
case OpCode::sbc:
|
||||
case OpCode::rts:
|
||||
case OpCode::clc:
|
||||
case OpCode::unknown:
|
||||
break;
|
||||
}
|
||||
@ -243,6 +263,8 @@ struct mos6502 : ASMLine
|
||||
return "sbc";
|
||||
case OpCode::rts:
|
||||
return "rts";
|
||||
case OpCode::clc:
|
||||
return "clc";
|
||||
case OpCode::unknown:
|
||||
return "";
|
||||
};
|
||||
@ -282,6 +304,7 @@ struct i386 : ASMLine
|
||||
xorl,
|
||||
andl,
|
||||
andb,
|
||||
addb,
|
||||
ret,
|
||||
movb,
|
||||
cmpb,
|
||||
@ -298,11 +321,15 @@ struct i386 : ASMLine
|
||||
sarl,
|
||||
addl,
|
||||
subl,
|
||||
subb,
|
||||
sall,
|
||||
orl,
|
||||
orb,
|
||||
rep,
|
||||
pushl
|
||||
pushl,
|
||||
sbbb,
|
||||
negb,
|
||||
notb
|
||||
};
|
||||
|
||||
static OpCode parse_opcode(Type t, const std::string &o)
|
||||
@ -333,7 +360,9 @@ struct i386 : ASMLine
|
||||
if (o == "je") return OpCode::je;
|
||||
if (o == "js") return OpCode::js;
|
||||
if (o == "subl") return OpCode::subl;
|
||||
if (o == "subb") return OpCode::subb;
|
||||
if (o == "addl") return OpCode::addl;
|
||||
if (o == "addb") return OpCode::addb;
|
||||
if (o == "sall") return OpCode::sall;
|
||||
if (o == "orl") return OpCode::orl;
|
||||
if (o == "andb") return OpCode::andb;
|
||||
@ -341,6 +370,10 @@ struct i386 : ASMLine
|
||||
if (o == "decb") return OpCode::decb;
|
||||
if (o == "incb") return OpCode::incb;
|
||||
if (o == "rep") return OpCode::rep;
|
||||
if (o == "notb") return OpCode::notb;
|
||||
if (o == "negb") return OpCode::negb;
|
||||
if (o == "sbbb") return OpCode::sbbb;
|
||||
if (o == "pushl") return OpCode::pushl;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Unknown opcode: " + o);
|
||||
@ -353,16 +386,34 @@ struct i386 : ASMLine
|
||||
}
|
||||
|
||||
if (o[0] == '%') {
|
||||
if (o == "%al" || o == "%eax") {
|
||||
return Operand(Operand::Type::reg, 0);
|
||||
} else if (o == "%bl" || o == "%ebx") {
|
||||
return Operand(Operand::Type::reg, 1);
|
||||
} else if (o == "%cl" || o == "%ecx") {
|
||||
return Operand(Operand::Type::reg, 2);
|
||||
} else if (o == "%dl" || o == "%edx") {
|
||||
return Operand(Operand::Type::reg, 3);
|
||||
} else if (o == "%di") {
|
||||
return Operand(Operand::Type::reg, 6);
|
||||
if (o == "%al") {
|
||||
return Operand(Operand::Type::reg, 0x00);
|
||||
} else if (o == "%ah") {
|
||||
return Operand(Operand::Type::reg, 0x01);
|
||||
} else if (o == "%bl") {
|
||||
return Operand(Operand::Type::reg, 0x02);
|
||||
} else if (o == "%bh") {
|
||||
return Operand(Operand::Type::reg, 0x03);
|
||||
} else if (o == "%cl") {
|
||||
return Operand(Operand::Type::reg, 0x04);
|
||||
} else if (o == "%ch") {
|
||||
return Operand(Operand::Type::reg, 0x05);
|
||||
} else if (o == "%dl") {
|
||||
return Operand(Operand::Type::reg, 0x06);
|
||||
} else if (o == "%dh") {
|
||||
return Operand(Operand::Type::reg, 0x07);
|
||||
} else if (o == "%sil") {
|
||||
return Operand(Operand::Type::reg, 0x08);
|
||||
} else if (o == "%ax" || o == "%eax") {
|
||||
return Operand(Operand::Type::reg, 0x10);
|
||||
} else if (o == "%bx" || o == "%ebx") {
|
||||
return Operand(Operand::Type::reg, 0x11);
|
||||
} else if (o == "%cx" || o == "%ecx") {
|
||||
return Operand(Operand::Type::reg, 0x12);
|
||||
} else if (o == "%dx" || o == "%edx") {
|
||||
return Operand(Operand::Type::reg, 0x13);
|
||||
} else if (o == "%si" || o == "%esi") {
|
||||
return Operand(Operand::Type::reg, 0x14);
|
||||
} else {
|
||||
throw std::runtime_error("Unknown register operand: '" + o + "'");
|
||||
}
|
||||
@ -391,67 +442,69 @@ void translate_instruction(std::vector<mos6502> &instructions, const i386::OpCod
|
||||
case i386::OpCode::ret:
|
||||
instructions.emplace_back(mos6502::OpCode::rts);
|
||||
break;
|
||||
case i386::OpCode::movb:
|
||||
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::literal) {
|
||||
instructions.emplace_back(mos6502::OpCode::ldy, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
instructions.emplace_back(mos6502::OpCode::sty, o2);
|
||||
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg && o2.reg_num == 0) {
|
||||
instructions.emplace_back(mos6502::OpCode::lda, o1);
|
||||
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
||||
instructions.emplace_back(mos6502::OpCode::ldy, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
instructions.emplace_back(mos6502::OpCode::sty, get_register(o2.reg_num));
|
||||
} else if (o1.type == Operand::Type::reg && o1.reg_num == 0 && o2.type == Operand::Type::reg) {
|
||||
case i386::OpCode::movl:
|
||||
if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg) {
|
||||
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
|
||||
} else if (o1.type == Operand::Type::reg && o1.reg_num == 0 && o2.type == Operand::Type::literal) {
|
||||
instructions.emplace_back(mos6502::OpCode::sta, o2);
|
||||
} else if (o1.type == Operand::Type::reg && o2.type == Operand::Type::literal) {
|
||||
instructions.emplace_back(mos6502::OpCode::ldy, get_register(o1.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::sty, o2);
|
||||
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num, 1));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num, 1));
|
||||
} else {
|
||||
throw std::runtime_error("Cannot translate instruction");
|
||||
throw std::runtime_error("Cannot translate movl instruction");
|
||||
}
|
||||
break;
|
||||
case i386::OpCode::movl:
|
||||
if (o1.type == Operand::Type::reg && o1.reg_num == 0 && o2.type == Operand::Type::reg) {
|
||||
case i386::OpCode::xorl:
|
||||
if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg
|
||||
&& o1.reg_num == o2.reg_num) {
|
||||
instructions.emplace_back(mos6502::OpCode::lda, Operand(Operand::Type::literal, "#$00"));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num, 1));
|
||||
} else {
|
||||
throw std::runtime_error("Cannot translate movl instruction");
|
||||
}
|
||||
break;
|
||||
case i386::OpCode::movb:
|
||||
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::literal) {
|
||||
instructions.emplace_back(mos6502::OpCode::lda, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, o2);
|
||||
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
||||
instructions.emplace_back(mos6502::OpCode::lda, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
|
||||
} else if (o1.type == Operand::Type::reg && o2.type == Operand::Type::literal) {
|
||||
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, o2);
|
||||
} else if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg) {
|
||||
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
|
||||
} else {
|
||||
throw std::runtime_error("Cannot translate instruction");
|
||||
throw std::runtime_error("Cannot translate movb instruction");
|
||||
}
|
||||
break;
|
||||
case i386::OpCode::orb:
|
||||
case i386::OpCode::orl:
|
||||
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::literal) {
|
||||
instructions.emplace_back(mos6502::OpCode::tay); // transfer memory through A register, pushing and popping around it
|
||||
instructions.emplace_back(mos6502::OpCode::lda, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
instructions.emplace_back(mos6502::OpCode::ORA, o2);
|
||||
instructions.emplace_back(mos6502::OpCode::sta, o2);
|
||||
instructions.emplace_back(mos6502::OpCode::tya);
|
||||
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg && o2.reg_num == 0) {
|
||||
instructions.emplace_back(mos6502::OpCode::ORA, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
} else if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg) {
|
||||
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::ORA, get_register(o2.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
|
||||
} else {
|
||||
throw std::runtime_error("Cannot translate instruction");
|
||||
throw std::runtime_error("Cannot translate orb instruction");
|
||||
}
|
||||
break;
|
||||
|
||||
case i386::OpCode::movzbl:
|
||||
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg && o2.reg_num == 0) {
|
||||
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
||||
instructions.emplace_back(mos6502::OpCode::lda, o1);
|
||||
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
||||
instructions.emplace_back(mos6502::OpCode::ldy, o1);
|
||||
instructions.emplace_back(mos6502::OpCode::sty, get_register(o2.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
|
||||
} else {
|
||||
throw std::runtime_error("Cannot translate instruction");
|
||||
throw std::runtime_error("Cannot translate movzbl instruction");
|
||||
}
|
||||
break;
|
||||
case i386::OpCode::sarl:
|
||||
case i386::OpCode::shrb:
|
||||
if (o1.type == Operand::Type::reg || o2.type == Operand::Type::reg) {
|
||||
const auto do_shift = [&instructions](const int reg_num) {
|
||||
if (reg_num == 0) {
|
||||
instructions.emplace_back(mos6502::OpCode::lsr, Operand(Operand::Type::literal, "a"));
|
||||
} else {
|
||||
instructions.emplace_back(mos6502::OpCode::lsr, get_register(reg_num));
|
||||
}
|
||||
instructions.emplace_back(mos6502::OpCode::lsr, get_register(reg_num));
|
||||
};
|
||||
|
||||
if (o1.type == Operand::Type::literal) {
|
||||
@ -463,43 +516,34 @@ void translate_instruction(std::vector<mos6502> &instructions, const i386::OpCod
|
||||
do_shift(o1.reg_num);
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Cannot translate instruction");
|
||||
throw std::runtime_error("Cannot translate shrb instruction");
|
||||
}
|
||||
break;
|
||||
case i386::OpCode::testb:
|
||||
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg && o2.reg_num == 0) {
|
||||
instructions.emplace_back(mos6502::OpCode::pha);
|
||||
instructions.emplace_back(mos6502::OpCode::AND, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
instructions.emplace_back(mos6502::OpCode::tay);
|
||||
instructions.emplace_back(mos6502::OpCode::pla);
|
||||
instructions.emplace_back(mos6502::OpCode::cpy, Operand(Operand::Type::literal, "#0"));
|
||||
|
||||
|
||||
// instructions.emplace_back(mos6502::OpCode::cmp, Operand(Operand::Type::literal, "#$0"));
|
||||
} else if (o1.type == Operand::Type::reg && o1.reg_num == 0 && o2.type == Operand::Type::reg && o2.reg_num == 0) {
|
||||
//transfer A to Y, to set the status flags appropriately
|
||||
instructions.emplace_back(mos6502::OpCode::tay);
|
||||
} else if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg && o1.reg_num == o2.reg_num) {
|
||||
instructions.emplace_back(mos6502::OpCode::ldy, get_register(o1.reg_num));
|
||||
if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg && o1.reg_num == o2.reg_num) {
|
||||
// this just tests the register for 0
|
||||
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
||||
} else if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg) {
|
||||
// ands the values
|
||||
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::AND, get_register(o2.reg_num));
|
||||
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
||||
// ands the values
|
||||
instructions.emplace_back(mos6502::OpCode::lda, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
instructions.emplace_back(mos6502::OpCode::AND, get_register(o2.reg_num));
|
||||
} else {
|
||||
throw std::runtime_error("Cannot translate instruction");
|
||||
throw std::runtime_error("Cannot translate testb instruction");
|
||||
}
|
||||
break;
|
||||
case i386::OpCode::decb:
|
||||
case i386::OpCode::decl:
|
||||
if (o1.type == Operand::Type::reg && o1.reg_num == 0) {
|
||||
instructions.emplace_back(mos6502::OpCode::sbc, Operand(Operand::Type::literal, "#1"));
|
||||
} else if (o1.type == Operand::Type::reg) {
|
||||
if (o1.type == Operand::Type::reg) {
|
||||
instructions.emplace_back(mos6502::OpCode::dec, get_register(o1.reg_num));
|
||||
} else {
|
||||
instructions.emplace_back(mos6502::OpCode::dec, o1);
|
||||
}
|
||||
break;
|
||||
case i386::OpCode::incb:
|
||||
case i386::OpCode::incl:
|
||||
if (o1.type == Operand::Type::reg && o1.reg_num == 0) {
|
||||
instructions.emplace_back(mos6502::OpCode::adc, Operand(Operand::Type::literal, "#1"));
|
||||
} else if (o1.type == Operand::Type::reg) {
|
||||
if (o1.type == Operand::Type::reg) {
|
||||
instructions.emplace_back(mos6502::OpCode::inc, get_register(o1.reg_num));
|
||||
} else {
|
||||
instructions.emplace_back(mos6502::OpCode::inc, o1);
|
||||
@ -517,77 +561,92 @@ void translate_instruction(std::vector<mos6502> &instructions, const i386::OpCod
|
||||
case i386::OpCode::jmp:
|
||||
instructions.emplace_back(mos6502::OpCode::jmp, o1);
|
||||
break;
|
||||
case i386::OpCode::xorl:
|
||||
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg && o2.reg_num == 0) {
|
||||
instructions.emplace_back(mos6502::OpCode::eor, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
} else if (o1.type == Operand::Type::reg && o2.reg_num == 0 && o2.type == Operand::Type::reg && o2.reg_num == 0) {
|
||||
// cheater shortcut on x86 to 0 out a register
|
||||
instructions.emplace_back(mos6502::OpCode::lda, Operand(Operand::Type::literal, "#0"));
|
||||
} else if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg && o1.reg_num == o2.reg_num) {
|
||||
// cheater shortcut on x86 to 0 out a register
|
||||
instructions.emplace_back(mos6502::OpCode::ldy, Operand(Operand::Type::literal, "#0"));
|
||||
instructions.emplace_back(mos6502::OpCode::sty, get_register(o1.reg_num));
|
||||
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
||||
instructions.emplace_back(mos6502::OpCode::tay); // transfer memory through A register, pushing and popping around it
|
||||
instructions.emplace_back(mos6502::OpCode::lda, get_register(o2.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::eor, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::tya);
|
||||
} else {
|
||||
throw std::runtime_error("Cannot translate instruction");
|
||||
}
|
||||
break;
|
||||
case i386::OpCode::addl:
|
||||
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg && o2.reg_num == 0) {
|
||||
instructions.emplace_back(mos6502::OpCode::adc, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
||||
instructions.emplace_back(mos6502::OpCode::tay); // transfer memory through A register, pushing and popping around it
|
||||
case i386::OpCode::addb:
|
||||
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
||||
instructions.emplace_back(mos6502::OpCode::lda, get_register(o2.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::clc);
|
||||
instructions.emplace_back(mos6502::OpCode::adc, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::tya);
|
||||
} else if (o1.type == Operand::Type::reg && o2.type == Operand::Type::literal) {
|
||||
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::clc);
|
||||
instructions.emplace_back(mos6502::OpCode::adc, o2);
|
||||
instructions.emplace_back(mos6502::OpCode::sta, o2);
|
||||
} else {
|
||||
throw std::runtime_error("Cannot translate instruction");
|
||||
throw std::runtime_error("Cannot translate addb instruction");
|
||||
}
|
||||
break;
|
||||
case i386::OpCode::cmpb:
|
||||
if (o1.type == Operand::Type::reg && o1.reg_num == 0 && o2.type == Operand::Type::reg) {
|
||||
instructions.emplace_back(mos6502::OpCode::cmp, get_register(o2.reg_num));
|
||||
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg && o2.reg_num == 0) {
|
||||
instructions.emplace_back(mos6502::OpCode::cmp, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::literal) {
|
||||
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::literal) {
|
||||
instructions.emplace_back(mos6502::OpCode::ldy, o2);
|
||||
// if (o1.value != "$0") {
|
||||
instructions.emplace_back(mos6502::OpCode::cpy, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
// }
|
||||
instructions.emplace_back(mos6502::OpCode::cpy, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
||||
instructions.emplace_back(mos6502::OpCode::ldy, get_register(o2.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::cpy, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
} else {
|
||||
throw std::runtime_error("Cannot translate instruction");
|
||||
throw std::runtime_error("Cannot translate cmb instruction");
|
||||
}
|
||||
break;
|
||||
case i386::OpCode::andb:
|
||||
case i386::OpCode::andl:
|
||||
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg && o2.reg_num == 0) {
|
||||
instructions.emplace_back(mos6502::OpCode::AND, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
||||
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
|
||||
const auto reg = get_register(o2.reg_num);
|
||||
instructions.emplace_back(mos6502::OpCode::tay); // transfer memory through A register, pushing and popping around it
|
||||
instructions.emplace_back(mos6502::OpCode::lda, reg);
|
||||
instructions.emplace_back(mos6502::OpCode::AND, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, reg);
|
||||
instructions.emplace_back(mos6502::OpCode::tya); // transfer memory through A register, pushing and popping around it
|
||||
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::literal) {
|
||||
const auto reg = get_register(o2.reg_num);
|
||||
instructions.emplace_back(mos6502::OpCode::lda, o2);
|
||||
instructions.emplace_back(mos6502::OpCode::AND, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, o2);
|
||||
} else {
|
||||
throw std::runtime_error("Cannot translate instruction");
|
||||
throw std::runtime_error("Cannot translate andb instruction");
|
||||
}
|
||||
break;
|
||||
case i386::OpCode::subl:
|
||||
if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg && o2.reg_num == 0) {
|
||||
instructions.emplace_back(mos6502::OpCode::sbc, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
case i386::OpCode::negb:
|
||||
if (o1.type == Operand::Type::reg) {
|
||||
// perform a two's complement of the register location
|
||||
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::eor, Operand(Operand::Type::literal, "#$ff"));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, get_register(o1.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::inc, get_register(o1.reg_num));
|
||||
} else {
|
||||
throw std::runtime_error("Cannot translate instruction");
|
||||
throw std::runtime_error("Cannot translate negb instruction");
|
||||
}
|
||||
break;
|
||||
case i386::OpCode::notb:
|
||||
if (o1.type == Operand::Type::reg) {
|
||||
// exclusive or against 0xff to perform a logical not
|
||||
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
|
||||
instructions.emplace_back(mos6502::OpCode::eor, Operand(Operand::Type::literal, "#$ff"));
|
||||
instructions.emplace_back(mos6502::OpCode::sta, get_register(o1.reg_num));
|
||||
} else {
|
||||
throw std::runtime_error("Cannot translate notb instruction");
|
||||
}
|
||||
break;
|
||||
case i386::OpCode::subb:
|
||||
// instructions.emplace_back(mos6502::OpCode::sbc, Operand(o1.type, fixup_8bit_literal(o1.value)));
|
||||
// Ensure that we set the carry flag before performing the subtraction
|
||||
throw std::runtime_error("Cannot translate subb instruction");
|
||||
break;
|
||||
case i386::OpCode::sbbb:
|
||||
// DEST <- (DEST – (SRC + CF))
|
||||
// o2 <- (o2 - (o1 + cf))
|
||||
// if o1 and o2 are the same we get
|
||||
// o2 <- (o2 - (o2 + cf))
|
||||
// o2 <- -cf
|
||||
if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg
|
||||
&& o1.reg_num == o2.reg_num) {
|
||||
instructions.emplace_back(mos6502::OpCode::lda, Operand(Operand::Type::literal, "#$00")); // reset a
|
||||
instructions.emplace_back(mos6502::OpCode::sbc, Operand(Operand::Type::literal, "#$00")); // subtract out the carry flag
|
||||
instructions.emplace_back(mos6502::OpCode::eor, Operand(Operand::Type::literal, "#$ff")); // invert the bits
|
||||
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num)); // place the value
|
||||
} else {
|
||||
throw std::runtime_error("Cannot translate sbb instruction");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Cannot translate instruction");
|
||||
throw std::runtime_error("Cannot translate unhandled instruction");
|
||||
|
||||
};
|
||||
|
||||
@ -630,9 +689,10 @@ void to_mos6502(const i386 &i, std::vector<mos6502> &instructions)
|
||||
instructions.emplace_back(i.type, i.text);
|
||||
return;
|
||||
case ASMLine::Type::Directive:
|
||||
instructions.emplace_back(i.type, i.text);
|
||||
// instructions.emplace_back(i.type, i.text);
|
||||
return;
|
||||
case ASMLine::Type::Instruction:
|
||||
// instructions.emplace_back(ASMLine::Type::Directive, "; " + i.line_text);
|
||||
translate_instruction(instructions, i.opcode, i.operand1, i.operand2);
|
||||
return;
|
||||
}
|
||||
@ -655,8 +715,36 @@ bool optimize(std::vector<mos6502> &instructions)
|
||||
instructions.erase(std::next(std::begin(instructions), op + 1), std::next(std::begin(instructions), op+2));
|
||||
return true;
|
||||
}
|
||||
if (instructions[op].opcode == mos6502::OpCode::sta
|
||||
&& instructions[op+1].opcode == mos6502::OpCode::lda
|
||||
&& instructions[op].op == instructions[op+1].op)
|
||||
{
|
||||
instructions.erase(std::next(std::begin(instructions), op + 1), std::next(std::begin(instructions), op+2));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t op = 0; op < instructions.size() - 1; ++op)
|
||||
{
|
||||
if (instructions[op].opcode == mos6502::OpCode::lda
|
||||
&& instructions[op].op.type == Operand::Type::literal)
|
||||
{
|
||||
const auto operand = instructions[op].op;
|
||||
auto op2 = op+1;
|
||||
while (op2 < instructions.size() && instructions[op2].opcode == mos6502::OpCode::sta) {
|
||||
++op2;
|
||||
}
|
||||
if (instructions[op2].opcode == mos6502::OpCode::lda
|
||||
&& operand == instructions[op2].op)
|
||||
{
|
||||
instructions.erase(std::next(std::begin(instructions), op2), std::next(std::begin(instructions), op2+1));
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
339
src/pong.cpp
Normal file
339
src/pong.cpp
Normal file
@ -0,0 +1,339 @@
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
constexpr uint16_t SPRITE_DATA_POINTERS = 2040;
|
||||
constexpr uint16_t VIDEO_REGISTERS = 53248;
|
||||
constexpr uint16_t SPRITE_ENABLE_BITS = VIDEO_REGISTERS + 21;
|
||||
constexpr uint16_t SPRITE_EXPAND_HORIZONTAL = VIDEO_REGISTERS + 29;
|
||||
constexpr uint16_t SPRITE_EXPAND_VERTICAL = VIDEO_REGISTERS + 23;
|
||||
constexpr uint16_t SPRITE_POSITION_REGISTERS = VIDEO_REGISTERS;
|
||||
constexpr uint16_t SPRITE_COLLISIONS = VIDEO_REGISTERS + 30;
|
||||
constexpr uint16_t SPRITE_MULTICOLOR = VIDEO_REGISTERS + 28;
|
||||
constexpr uint16_t VIDEO_MEMORY = 1024;
|
||||
constexpr auto starting_bank = 192;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
volatile uint8_t &memory(const uint16_t loc)
|
||||
{
|
||||
return *reinterpret_cast<uint8_t *>(loc);
|
||||
}
|
||||
|
||||
struct Color
|
||||
{
|
||||
enum class Name : uint8_t {
|
||||
Black = 0,
|
||||
White = 1,
|
||||
Red = 2,
|
||||
Cyan = 3,
|
||||
Purple = 4,
|
||||
Green = 5,
|
||||
Blue = 6,
|
||||
Yellow = 7,
|
||||
Orange = 8,
|
||||
Brown = 9,
|
||||
LightRed = 10,
|
||||
DarkGrey = 11,
|
||||
Grey = 12,
|
||||
LightGreen = 13,
|
||||
LightBlue = 14,
|
||||
LightGrey = 15
|
||||
};
|
||||
|
||||
constexpr Color(const Name t_name, const uint8_t t_r, const uint8_t t_g, const uint8_t t_b)
|
||||
: name(t_name), r(t_r), g(t_g), b(t_b)
|
||||
{
|
||||
}
|
||||
|
||||
Name name;
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr auto square(T t)
|
||||
{
|
||||
return t*t;
|
||||
}
|
||||
|
||||
constexpr auto distance(const Color &lhs, const uint8_t t_r, const uint8_t t_g, const uint8_t t_b)
|
||||
{
|
||||
// http://stackoverflow.com/questions/4754506/color-similarity-distance-in-rgba-color-space
|
||||
return square(lhs.r - t_r) + square(lhs.g - t_g) + square(lhs.b - t_b);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr auto distance_table(T colors, const uint8_t t_r, const uint8_t t_g, const uint8_t t_b)
|
||||
{
|
||||
std::array<int, colors.size()> distances{};
|
||||
auto pos = std::begin(distances);
|
||||
|
||||
|
||||
for (const auto &color : colors) {
|
||||
*pos = distance(color, t_r, t_g, t_b);
|
||||
++pos;
|
||||
}
|
||||
|
||||
return distances;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Color nearest_color(T colors,
|
||||
const uint8_t t_r, const uint8_t t_g, const uint8_t t_b)
|
||||
{
|
||||
const auto ds = distance_table(colors, t_r, t_g, t_b);
|
||||
const auto distance = std::min_element(std::begin(ds), std::end(ds)) - std::begin(ds);
|
||||
return colors[distance];
|
||||
|
||||
/*
|
||||
return *std::min_element(std::begin(colors), std::end(colors),
|
||||
[&](const auto &lhs, const auto &rhs)
|
||||
{
|
||||
return distance(lhs, t_r, t_g, t_b) < distance(rhs, t_r, t_g, t_b);
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
void clear_bit(const uint16_t loc, const uint8_t bitnum)
|
||||
{
|
||||
memory(loc) &= (0xFF ^ (1 << bitnum));
|
||||
}
|
||||
|
||||
void set_bit(const uint16_t loc, const uint8_t bitnum)
|
||||
{
|
||||
memory(loc) |= (1 << bitnum);
|
||||
}
|
||||
|
||||
void write_multi_color_pixel(uint16_t)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename D1, typename D2, typename D3, typename D4, typename ... D >
|
||||
void write_multi_color_pixel(uint16_t loc, D1 d1, D2 d2, D3 d3, D4 d4, D ... d)
|
||||
{
|
||||
memory(loc) = (d1 << 6) | (d2 << 4) | (d3 << 2) | d4;
|
||||
write_multi_color_pixel(loc + 1, d...);
|
||||
}
|
||||
|
||||
constexpr bool test_bit(const uint8_t data, const uint8_t bit)
|
||||
{
|
||||
return (data & (1 << bit)) != 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename ... D>
|
||||
void make_sprite(uint8_t memory_loc, D ... d)
|
||||
{
|
||||
write_multi_color_pixel((starting_bank + memory_loc) * 64, d...);
|
||||
}
|
||||
|
||||
|
||||
void enable_sprite(const uint8_t sprite_number, const uint8_t memory_loc,
|
||||
const bool multicolor,
|
||||
const bool double_width, const bool double_height)
|
||||
{
|
||||
set_bit(SPRITE_ENABLE_BITS, sprite_number);
|
||||
memory(SPRITE_DATA_POINTERS + sprite_number) = starting_bank + memory_loc;
|
||||
if (double_width) {
|
||||
set_bit(SPRITE_EXPAND_HORIZONTAL, sprite_number);
|
||||
} else {
|
||||
clear_bit(SPRITE_EXPAND_HORIZONTAL, sprite_number);
|
||||
}
|
||||
|
||||
if (double_height) {
|
||||
set_bit(SPRITE_EXPAND_VERTICAL, sprite_number);
|
||||
} else {
|
||||
clear_bit(SPRITE_EXPAND_VERTICAL, sprite_number);
|
||||
}
|
||||
|
||||
if (multicolor) {
|
||||
set_bit(SPRITE_MULTICOLOR, sprite_number);
|
||||
} else {
|
||||
clear_bit(SPRITE_MULTICOLOR, sprite_number);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void display_int(uint8_t x, uint8_t y, uint8_t val)
|
||||
{
|
||||
memory(VIDEO_MEMORY + y * 40 + x) = val;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
constexpr std::array<Color, 16> colors {Color{Color::Name::Black, 0,0,0},
|
||||
Color{Color::Name::White, 255,255,255},
|
||||
Color{Color::Name::Red, 104, 55, 43},
|
||||
Color{Color::Name::Cyan, 112, 164, 178},
|
||||
Color{Color::Name::Purple, 111, 61, 134},
|
||||
Color{Color::Name::Green, 88, 141, 67},
|
||||
Color{Color::Name::Blue, 53, 40, 121},
|
||||
Color{Color::Name::Yellow, 184, 199, 111},
|
||||
Color{Color::Name::Orange, 111, 79, 37},
|
||||
Color{Color::Name::Brown, 67, 57, 0},
|
||||
Color{Color::Name::LightRed, 154, 103, 89},
|
||||
Color{Color::Name::DarkGrey, 68, 68, 68},
|
||||
Color{Color::Name::Grey, 108, 108, 108},
|
||||
Color{Color::Name::LightGreen, 154, 210, 132},
|
||||
Color{Color::Name::LightBlue, 108, 94, 181},
|
||||
Color{Color::Name::LightGrey, 149, 149, 149}};
|
||||
|
||||
//constexpr auto nearest_black = nearest_color(colors, 0, 0, 0);
|
||||
//constexpr auto nearest_red = nearest_color(colors, 255, 255, 0);
|
||||
|
||||
|
||||
make_sprite(0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,2,2,2,2,0,0,0,0,
|
||||
0,0,0,2,2,2,2,2,2,0,0,0,
|
||||
0,0,2,2,2,1,1,2,2,2,0,0,
|
||||
0,0,2,2,1,1,1,1,2,2,0,0,
|
||||
0,0,2,2,1,1,1,1,2,2,0,0,
|
||||
0,0,2,2,1,1,1,1,2,2,0,0,
|
||||
0,0,2,2,1,1,1,1,2,2,0,0,
|
||||
0,0,2,2,1,1,1,1,2,2,0,0,
|
||||
0,0,2,2,1,1,1,1,2,2,0,0,
|
||||
0,0,2,2,1,1,1,1,2,2,0,0,
|
||||
0,0,2,2,2,1,1,2,2,2,0,0,
|
||||
0,0,0,2,2,2,2,2,2,0,0,0,
|
||||
0,0,0,2,2,2,2,2,2,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0
|
||||
);
|
||||
|
||||
enable_sprite(0, 0, true, false, false);
|
||||
|
||||
// start timer
|
||||
memory(56590) = 1;
|
||||
|
||||
|
||||
const auto joy = [](const uint8_t d){
|
||||
struct State{
|
||||
State(const uint8_t portdata)
|
||||
: up(!test_bit(portdata,0)),
|
||||
down(!test_bit(portdata,1)),
|
||||
left(!test_bit(portdata,2)),
|
||||
right(!test_bit(portdata,3)),
|
||||
fire(!test_bit(portdata,4))
|
||||
{
|
||||
}
|
||||
|
||||
auto direction_vector() const
|
||||
{
|
||||
const auto dir = [](const bool neg, const bool pos) {
|
||||
if (neg) return -1;
|
||||
if (pos) return 1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
return std::make_pair(dir(left, right), dir(up, down));
|
||||
}
|
||||
|
||||
bool up;
|
||||
bool down;
|
||||
bool left;
|
||||
bool right;
|
||||
bool fire;
|
||||
};
|
||||
return State(d);
|
||||
};
|
||||
|
||||
const auto joy_port2 = [joy](){
|
||||
return joy(memory(56320));
|
||||
};
|
||||
|
||||
const auto joy_port1 = [joy](){
|
||||
return joy(memory(56321));
|
||||
};
|
||||
|
||||
|
||||
const auto sprite_x = [](const uint8_t sprite_num) -> decltype(auto)
|
||||
{
|
||||
return (memory(SPRITE_POSITION_REGISTERS + sprite_num * 2));
|
||||
};
|
||||
|
||||
const auto sprite_y = [](const uint8_t sprite_num) -> decltype(auto)
|
||||
{
|
||||
return (memory(SPRITE_POSITION_REGISTERS + sprite_num * 2 + 1));
|
||||
};
|
||||
|
||||
const auto sprite_collisions = []() {
|
||||
const auto collisions = memory(SPRITE_COLLISIONS);
|
||||
memory(SPRITE_COLLISIONS) = 0;
|
||||
struct Col_Data {
|
||||
bool sprite0;
|
||||
bool sprite1;
|
||||
bool sprite2;
|
||||
bool sprite3;
|
||||
bool sprite4;
|
||||
bool sprite5;
|
||||
bool sprite6;
|
||||
bool sprite7;
|
||||
};
|
||||
|
||||
return Col_Data{test_bit(collisions, 0),test_bit(collisions, 1),test_bit(collisions, 2),test_bit(collisions, 3),
|
||||
test_bit(collisions, 4),test_bit(collisions, 5),test_bit(collisions, 6),test_bit(collisions, 7)};
|
||||
};
|
||||
|
||||
|
||||
std::pair<int8_t, int8_t> ball_vec{1,1};
|
||||
uint8_t player1 = 0;
|
||||
uint8_t player2 = 0;
|
||||
|
||||
sprite_x(0) = 50;
|
||||
sprite_y(0) = 50;
|
||||
|
||||
while (true) {
|
||||
|
||||
if (memory(56325) == 0) {
|
||||
// Move ball
|
||||
const auto ball_x = sprite_x(0) += std::get<0>(ball_vec);
|
||||
const auto ball_y = sprite_y(0) += std::get<1>(ball_vec);
|
||||
|
||||
// Update paddle positions
|
||||
const auto vec = joy_port2().direction_vector();
|
||||
sprite_y(1) += std::get<1>(vec);
|
||||
const auto vec2 = joy_port1().direction_vector();
|
||||
sprite_y(2) += std::get<1>(vec2);
|
||||
|
||||
|
||||
if (const auto col_data = sprite_collisions();
|
||||
(col_data.sprite1 || col_data.sprite2) && col_data.sprite0) {
|
||||
// ball hit a paddle
|
||||
std::get<0>(ball_vec) *= -1;
|
||||
}
|
||||
|
||||
// ball hit the top or bottom wall
|
||||
if (ball_y == 30 || ball_y == 240) {
|
||||
std::get<1>(ball_vec) *= -1;
|
||||
}
|
||||
|
||||
if (ball_x == 1) {
|
||||
// ball hit left wall, player 2 scored
|
||||
++player2;
|
||||
} else if (ball_x == 254) {
|
||||
// ball hit right wall, player 1 scored
|
||||
++player1;
|
||||
}
|
||||
|
||||
display_int(10, 3, player1);
|
||||
display_int(30, 3, player2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user