mirror of https://github.com/bsnes-emu/bsnes.git
Update to v100r10 release.
byuu says: Redesigned the handling of reading/writing registers to be about eight times faster than the old system. More work may be needed ... it seems data registers tend to preserve their upper bits upon assignment; whereas address registers tend to sign-extend values into them. It may make sense to have DataRegister and AddressRegister classes with separate read/write handlers. I'd have to hold two Register objects inside the EffectiveAddress (EA) class if we do that. Implemented 19 opcodes now (out of somewhere between 60 and 90.) That gets the first ~530,000 instructions in Sonic the Hedgehog running (though probably wrong. But we can run a lot thanks to large initialization loops.) If I force the core to loop back to the reset vector on an invalid opcode, I'm getting about 1500fps with a dumb 320x240 blit 60 times a second and just the 68K running alone (no Z80, PSG, VDP, YM2612.) I don't know if that's good or not. I guess we'll find out. I had to stop tonight because the final opcode I execute is an RTS (return from subroutine) that's branching back to address 0; which is invalid ... meaning something went terribly wrong and the system crashed.
This commit is contained in:
parent
be3f6ac0d5
commit
4b897ba791
|
@ -11,7 +11,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "100.09";
|
static const string Version = "100.10";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "http://byuu.org/";
|
static const string Website = "http://byuu.org/";
|
||||||
|
|
|
@ -10,8 +10,8 @@ auto CPU::Enter() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::boot() -> void {
|
auto CPU::boot() -> void {
|
||||||
r.ssp = read(1, 0) << 16 | read(1, 2) << 0;
|
r.da[A7] = read(1, 0) << 16 | read(1, 2) << 0;
|
||||||
r.pc = read(1, 4) << 16 | read(1, 6) << 0;
|
r.pc = read(1, 4) << 16 | read(1, 6) << 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::main() -> void {
|
auto CPU::main() -> void {
|
||||||
|
|
|
@ -63,7 +63,7 @@ template<uint Size> auto M68K::_suffix() -> string {
|
||||||
|
|
||||||
auto M68K::_condition(uint4 condition) -> string {
|
auto M68K::_condition(uint4 condition) -> string {
|
||||||
static const string conditions[16] = {
|
static const string conditions[16] = {
|
||||||
"ra", "sr", "hi", "ls", "cc", "cs", "ne", "eq",
|
"t ", "f ", "hi", "ls", "cc", "cs", "ne", "eq",
|
||||||
"vc", "vs", "pl", "mi", "ge", "lt", "gt", "le",
|
"vc", "vs", "pl", "mi", "ge", "lt", "gt", "le",
|
||||||
};
|
};
|
||||||
return conditions[condition];
|
return conditions[condition];
|
||||||
|
@ -76,8 +76,8 @@ auto M68K::disassemble(uint32 pc) -> string {
|
||||||
|
|
||||||
auto M68K::disassembleRegisters() -> string {
|
auto M68K::disassembleRegisters() -> string {
|
||||||
return {
|
return {
|
||||||
hex(r.d0, 8L), " ", hex(r.d1, 8L), " ", hex(r.d2, 8L), " ", hex(r.d3, 8L), " ",
|
hex(r.da[D0], 8L), " ", hex(r.da[D1], 8L), " ", hex(r.da[D2], 8L), " ", hex(r.da[D3], 8L), " ",
|
||||||
hex(r.d4, 8L), " ", hex(r.d5, 8L), " ", hex(r.d6, 8L), " ", hex(r.d7, 8L), " ",
|
hex(r.da[D4], 8L), " ", hex(r.da[D5], 8L), " ", hex(r.da[D6], 8L), " ", hex(r.da[D7], 8L), " ",
|
||||||
r.t ? "T" : "t",
|
r.t ? "T" : "t",
|
||||||
r.s ? "S" : "s",
|
r.s ? "S" : "s",
|
||||||
(uint)r.i,
|
(uint)r.i,
|
||||||
|
@ -86,8 +86,8 @@ auto M68K::disassembleRegisters() -> string {
|
||||||
r.z ? "Z" : "z",
|
r.z ? "Z" : "z",
|
||||||
r.n ? "N" : "n",
|
r.n ? "N" : "n",
|
||||||
r.x ? "X" : "x", "\n",
|
r.x ? "X" : "x", "\n",
|
||||||
hex(r.a0, 8L), " ", hex(r.a1, 8L), " ", hex(r.a2, 8L), " ", hex(r.a3, 8L), " ",
|
hex(r.da[A0], 8L), " ", hex(r.da[A1], 8L), " ", hex(r.da[A2], 8L), " ", hex(r.da[A3], 8L), " ",
|
||||||
hex(r.a4, 8L), " ", hex(r.a5, 8L), " ", hex(r.a6, 8L), " ", hex(r.ssp, 8L), " ", hex(r.usp, 8L)
|
hex(r.da[A4], 8L), " ", hex(r.da[A5], 8L), " ", hex(r.da[A6], 8L), " ", hex(r.da[A7], 8L), " ", hex(r.sp, 8L)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,32 @@ template<uint Size> auto M68K::disassembleANDI(EA ea) -> string {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto M68K::disassembleBCC(uint4 condition, uint8 displacement) -> string {
|
auto M68K::disassembleBCC(uint4 condition, uint8 displacement) -> string {
|
||||||
return {"b", _condition(condition), " ", _branch(displacement)};
|
auto cc = _condition(condition);
|
||||||
|
if(condition == 0) cc = "ra";
|
||||||
|
if(condition == 1) cc = "sr";
|
||||||
|
return {"b", cc, " ", _branch(displacement)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<uint Size> auto M68K::disassembleBTST(Register rd, EA ea) -> string {
|
||||||
|
return {"btst ", _register(rd), ",", _read<Size>(ea)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<uint Size> auto M68K::disassembleBTST(EA ea) -> string {
|
||||||
|
return {"btst ", _immediate<Byte>(), ",", _read<Size>(ea)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<uint Size> auto M68K::disassembleCLR(EA ea) -> string {
|
||||||
|
return {"clr", _suffix<Size>(), " ", _read<Size>(ea)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<uint Size> auto M68K::disassembleCMP(Register rd, EA ea) -> string {
|
||||||
|
return {"cmp", _suffix<Size>(), " ", _read<Word>(ea), ",", _register(rd)};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto M68K::disassembleDBCC(uint4 condition, Register rd) -> string {
|
||||||
|
auto base = _pc;
|
||||||
|
auto displacement = (int16)_readPC();
|
||||||
|
return {"db", _condition(condition), " ", _register(rd), ",$", hex(base + displacement, 6L)};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto M68K::disassembleLEA(Register ra, EA ea) -> string {
|
auto M68K::disassembleLEA(Register ra, EA ea) -> string {
|
||||||
|
@ -142,6 +167,14 @@ auto M68K::disassembleMOVEQ(Register rd, uint8 immediate) -> string {
|
||||||
return {"moveq #$", hex(immediate, 2L), ",", _register(rd)};
|
return {"moveq #$", hex(immediate, 2L), ",", _register(rd)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto M68K::disassembleMOVE_FROM_SR(EA ea) -> string {
|
||||||
|
return {"move sr,", _read<Word>(ea)};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto M68K::disassembleMOVE_TO_SR(EA ea) -> string {
|
||||||
|
return {"move ", _read<Word>(ea), ",sr"};
|
||||||
|
}
|
||||||
|
|
||||||
auto M68K::disassembleMOVE_USP(uint1 direction, Register ra) -> string {
|
auto M68K::disassembleMOVE_USP(uint1 direction, Register ra) -> string {
|
||||||
if(direction == 0) {
|
if(direction == 0) {
|
||||||
return {"move ", _register(ra), ",usp"};
|
return {"move ", _register(ra), ",usp"};
|
||||||
|
@ -154,6 +187,10 @@ auto M68K::disassembleNOP() -> string {
|
||||||
return {"nop "};
|
return {"nop "};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto M68K::disassembleRTS() -> string {
|
||||||
|
return {"rts "};
|
||||||
|
}
|
||||||
|
|
||||||
template<uint Size> auto M68K::disassembleTST(EA ea) -> string {
|
template<uint Size> auto M68K::disassembleTST(EA ea) -> string {
|
||||||
return {"tst", _suffix<Size>(), " ", _read<Size>(ea)};
|
return {"tst", _suffix<Size>(), " ", _read<Size>(ea)};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +1,53 @@
|
||||||
template<uint Size> auto M68K::fetch(EA& ea) -> uint32 {
|
template<uint Size> auto M68K::fetch(EA& ea) -> uint32 {
|
||||||
ea.valid = true;
|
if(!ea.valid.raise()) return ea.address;
|
||||||
|
|
||||||
switch(ea.mode) {
|
switch(ea.mode) {
|
||||||
|
|
||||||
case 0: { //data register direct
|
case DataRegisterDirect: {
|
||||||
return read(ea.reg);
|
return read(ea.reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 1: { //data register indirect
|
case AddressRegisterDirect: {
|
||||||
return read(ea.reg);
|
return read(ea.reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 2: { //address register indirect
|
case AddressRegisterIndirect: {
|
||||||
return read(ea.reg);
|
return read(ea.reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 3: { //address register indirect with post-increment
|
case AddressRegisterIndirectWithPostIncrement: {
|
||||||
return read(ea.reg);
|
return read(ea.reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 4: { //address register indirect with pre-decrement
|
case AddressRegisterIndirectWithPreDecrement: {
|
||||||
return read(ea.reg);
|
return read(ea.reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 5: { //address register indirect with displacement
|
case AddressRegisterIndirectWithDisplacement: {
|
||||||
return read(ea.reg) + (int16)readPC();
|
return read(ea.reg) + (int16)readPC();
|
||||||
}
|
}
|
||||||
|
|
||||||
case 6: { //address register indirect with index
|
case AddressRegisterIndirectWithIndex: {
|
||||||
auto extension = readPC();
|
auto extension = readPC();
|
||||||
auto index = read(Register{extension >> 12});
|
auto index = read(Register{extension >> 12});
|
||||||
if(extension & 0x800) index = (int16)index;
|
if(extension & 0x800) index = (int16)index;
|
||||||
return read(ea.reg) + index + (int8)extension;
|
return read(ea.reg) + index + (int8)extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 7: { //absolute short indirect
|
case AbsoluteShortIndirect: {
|
||||||
return (int16)readPC();
|
return (int16)readPC();
|
||||||
}
|
}
|
||||||
|
|
||||||
case 8: { //absolute long indirect
|
case AbsoluteLongIndirect: {
|
||||||
return readPC<Long>();
|
return readPC<Long>();
|
||||||
}
|
}
|
||||||
|
|
||||||
case 9: { //program counter indirect with displacement
|
case ProgramCounterIndirectWithDisplacement: {
|
||||||
auto base = r.pc;
|
auto base = r.pc;
|
||||||
return base + (int16)readPC();
|
return base + (int16)readPC();
|
||||||
}
|
}
|
||||||
|
|
||||||
case 10: { //program counter indirect with index
|
case ProgramCounterIndirectWithIndex: {
|
||||||
auto base = r.pc;
|
auto base = r.pc;
|
||||||
auto extension = readPC();
|
auto extension = readPC();
|
||||||
auto index = read(Register{extension >> 12});
|
auto index = read(Register{extension >> 12});
|
||||||
|
@ -55,7 +55,7 @@ template<uint Size> auto M68K::fetch(EA& ea) -> uint32 {
|
||||||
return base + index + (int8)extension;
|
return base + index + (int8)extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 11: { //immediate
|
case Immediate: {
|
||||||
return readPC<Size>();
|
return readPC<Size>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,59 +65,59 @@ template<uint Size> auto M68K::fetch(EA& ea) -> uint32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<uint Size, bool Update> auto M68K::read(EA& ea) -> uint32 {
|
template<uint Size, bool Update> auto M68K::read(EA& ea) -> uint32 {
|
||||||
if(!ea.valid) ea.address = fetch<Size>(ea);
|
ea.address = fetch<Size>(ea);
|
||||||
|
|
||||||
switch(ea.mode) {
|
switch(ea.mode) {
|
||||||
|
|
||||||
case 0: { //data register direct
|
case DataRegisterDirect: {
|
||||||
return clip<Size>(ea.address);
|
return clip<Size>(ea.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 1: { //address register direct
|
case AddressRegisterDirect: {
|
||||||
return clip<Size>(ea.address);
|
return clip<Size>(ea.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 2: { //address register indirect
|
case AddressRegisterIndirect: {
|
||||||
return read<Size>(ea.address);
|
return read<Size>(ea.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 3: { //address register indirect with post-increment
|
case AddressRegisterIndirectWithPostIncrement: {
|
||||||
auto data = read<Size>(ea.address);
|
auto data = read<Size>(ea.address);
|
||||||
if(Update) write(ea.reg, ea.address += (Size == Long ? 4 : 2));
|
if(Update) write(ea.reg, ea.address += (Size == Long ? 4 : 2));
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 4: { //address register indirect with pre-decrement
|
case AddressRegisterIndirectWithPreDecrement: {
|
||||||
auto data = read<Size>((uint32)(ea.address - (Size == Long ? 4 : 2)));
|
auto data = read<Size>(ea.address - (Size == Long ? 4 : 2));
|
||||||
if(Update) write(ea.reg, ea.address -= (Size == Long ? 4 : 2));
|
if(Update) write(ea.reg, ea.address -= (Size == Long ? 4 : 2));
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 5: { //address register indirect with displacement
|
case AddressRegisterIndirectWithDisplacement: {
|
||||||
return read<Size>(ea.address);
|
return read<Size>(ea.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 6: { //address register indirect with index
|
case AddressRegisterIndirectWithIndex: {
|
||||||
return read<Size>(ea.address);
|
return read<Size>(ea.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 7: { //absolute short indirect
|
case AbsoluteShortIndirect: {
|
||||||
return read<Size>(ea.address);
|
return read<Size>(ea.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 8: { //absolute long indirect
|
case AbsoluteLongIndirect: {
|
||||||
return read<Size>(ea.address);
|
return read<Size>(ea.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 9: { //program counter indirect with displacement
|
case ProgramCounterIndirectWithDisplacement: {
|
||||||
return read<Size>(ea.address);
|
return read<Size>(ea.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 10: { //program counter indirect with index
|
case ProgramCounterIndirectWithIndex: {
|
||||||
return read<Size>(ea.address);
|
return read<Size>(ea.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 11: { //immediate
|
case Immediate: {
|
||||||
return clip<Size>(ea.address);
|
return clip<Size>(ea.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,59 +127,59 @@ template<uint Size, bool Update> auto M68K::read(EA& ea) -> uint32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<uint Size, bool Update> auto M68K::write(EA& ea, uint32 data) -> void {
|
template<uint Size, bool Update> auto M68K::write(EA& ea, uint32 data) -> void {
|
||||||
if(!ea.valid) ea.address = fetch<Size>(ea);
|
ea.address = fetch<Size>(ea);
|
||||||
|
|
||||||
switch(ea.mode) {
|
switch(ea.mode) {
|
||||||
|
|
||||||
case 0: { //data register direct
|
case DataRegisterDirect: {
|
||||||
return write<Size>(ea.reg, data);
|
return write<Size>(ea.reg, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 1: { //address register direct
|
case AddressRegisterDirect: {
|
||||||
return write<Size>(ea.reg, data);
|
return write<Size>(ea.reg, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 2: { //address register indirect
|
case AddressRegisterIndirect: {
|
||||||
return write<Size>(ea.address, data);
|
return write<Size>(ea.address, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 3: { //address register indirect with post-increment
|
case AddressRegisterIndirectWithPostIncrement: {
|
||||||
write<Size>(ea.address, data);
|
write<Size>(ea.address, data);
|
||||||
if(Update) write(ea.reg, ea.address += (Size == Long ? 4 : 2));
|
if(Update) write(ea.reg, ea.address += (Size == Long ? 4 : 2));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 4: { //address register indirect with pre-decrement
|
case AddressRegisterIndirectWithPreDecrement: {
|
||||||
write<Size>((uint32)(ea.address - (Size == Long ? 4 : 2)), data);
|
write<Size, Reverse>(ea.address - (Size == Long ? 4 : 2), data);
|
||||||
if(Update) write(ea.reg, ea.address -= (Size == Long ? 4 : 2));
|
if(Update) write(ea.reg, ea.address -= (Size == Long ? 4 : 2));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 5: { //address register indirect with displacement
|
case AddressRegisterIndirectWithDisplacement: {
|
||||||
return write<Size>(ea.address, data);
|
return write<Size>(ea.address, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 6: { //address register indirect with index
|
case AddressRegisterIndirectWithIndex: {
|
||||||
return write<Size>(ea.address, data);
|
return write<Size>(ea.address, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 7: { //absolute short indirect
|
case AbsoluteShortIndirect: {
|
||||||
return write<Size>(ea.address, data);
|
return write<Size>(ea.address, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 8: { //absolute long indirect
|
case AbsoluteLongIndirect: {
|
||||||
return write<Size>(ea.address, data);
|
return write<Size>(ea.address, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 9: { //program counter indirect with displacement
|
case ProgramCounterIndirectWithDisplacement: {
|
||||||
return write<Size>(ea.address, data);
|
return write<Size>(ea.address, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 10: { //program counter indirect with index
|
case ProgramCounterIndirectWithIndex: {
|
||||||
return write<Size>(ea.address, data);
|
return write<Size>(ea.address, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 11: { //immediate
|
case Immediate: {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,12 +189,12 @@ template<uint Size, bool Update> auto M68K::write(EA& ea, uint32 data) -> void {
|
||||||
template<uint Size> auto M68K::flush(EA& ea, uint32 data) -> void {
|
template<uint Size> auto M68K::flush(EA& ea, uint32 data) -> void {
|
||||||
switch(ea.mode) {
|
switch(ea.mode) {
|
||||||
|
|
||||||
case 3: { //address register indirect with post-increment
|
case AddressRegisterIndirectWithPostIncrement: {
|
||||||
write<Size>(ea.reg, data);
|
write<Size>(ea.reg, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 4: { //address register indirect with pre-decrement
|
case AddressRegisterIndirectWithPreDecrement: {
|
||||||
write<Size>(ea.reg, data);
|
write<Size>(ea.reg, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,19 +8,25 @@ auto M68K::trap() -> void {
|
||||||
|
|
||||||
auto M68K::instruction() -> void {
|
auto M68K::instruction() -> void {
|
||||||
instructionsExecuted++;
|
instructionsExecuted++;
|
||||||
print(disassembleRegisters(), "\n");
|
//print(disassembleRegisters(), "\n");
|
||||||
print(disassemble(r.pc), "\n");
|
//print(disassemble(r.pc), "\n");
|
||||||
print("\n");
|
//print("\n");
|
||||||
|
|
||||||
opcode = readPC();
|
opcode = readPC();
|
||||||
return instructionTable[opcode]();
|
return instructionTable[opcode]();
|
||||||
}
|
}
|
||||||
|
|
||||||
M68K::M68K() {
|
M68K::M68K() {
|
||||||
#define bind(id, name, ...) \
|
#define bind(id, name, ...) { \
|
||||||
assert(!instructionTable[id]); \
|
assert(!instructionTable[id]); \
|
||||||
instructionTable[id] = [=] { return instruction##name(__VA_ARGS__); }; \
|
instructionTable[id] = [=] { return instruction##name(__VA_ARGS__); }; \
|
||||||
disassembleTable[id] = [=] { return disassemble##name(__VA_ARGS__); };
|
disassembleTable[id] = [=] { return disassemble##name(__VA_ARGS__); }; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define unbind(id) { \
|
||||||
|
instructionTable[id].reset(); \
|
||||||
|
disassembleTable[id].reset(); \
|
||||||
|
}
|
||||||
|
|
||||||
#define pattern(s) \
|
#define pattern(s) \
|
||||||
std::integral_constant<uint16_t, bit::test(s)>::value
|
std::integral_constant<uint16_t, bit::test(s)>::value
|
||||||
|
@ -33,18 +39,20 @@ M68K::M68K() {
|
||||||
auto opcode = pattern("1101 ---- ++-- ----") | dreg << 9 | direction << 8 | mode << 3 | reg << 0;
|
auto opcode = pattern("1101 ---- ++-- ----") | dreg << 9 | direction << 8 | mode << 3 | reg << 0;
|
||||||
if(direction == 1 && (mode == 0 || mode == 1 || (mode == 7 && reg >= 2))) continue;
|
if(direction == 1 && (mode == 0 || mode == 1 || (mode == 7 && reg >= 2))) continue;
|
||||||
|
|
||||||
Register rd{0u + dreg};
|
Register rd{D0 + dreg};
|
||||||
EA ea{mode, reg};
|
EA ea{mode, reg};
|
||||||
bind(opcode | 0 << 6, ADD<Byte>, rd, direction, ea);
|
bind(opcode | 0 << 6, ADD<Byte>, rd, direction, ea);
|
||||||
bind(opcode | 1 << 6, ADD<Word>, rd, direction, ea);
|
bind(opcode | 1 << 6, ADD<Word>, rd, direction, ea);
|
||||||
bind(opcode | 2 << 6, ADD<Long>, rd, direction, ea);
|
bind(opcode | 2 << 6, ADD<Long>, rd, direction, ea);
|
||||||
|
|
||||||
|
if(direction == 0 && mode == 1) unbind(opcode | 0 << 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
//ANDI
|
//ANDI
|
||||||
for(uint3 mode : range(8))
|
for(uint3 mode : range(8))
|
||||||
for(uint3 reg : range(8)) {
|
for(uint3 reg : range(8)) {
|
||||||
auto opcode = pattern("0000 0010 ++-- ----") | mode << 3 | reg << 0;
|
auto opcode = pattern("0000 0010 ++-- ----") | mode << 3 | reg << 0;
|
||||||
if(mode == 1 || (mode == 7 && reg >= 4)) continue;
|
if(mode == 1 || (mode == 7 && reg >= 2)) continue;
|
||||||
|
|
||||||
EA ea{mode, reg};
|
EA ea{mode, reg};
|
||||||
bind(opcode | 0 << 6, ANDI<Byte>, ea);
|
bind(opcode | 0 << 6, ANDI<Byte>, ea);
|
||||||
|
@ -60,14 +68,74 @@ M68K::M68K() {
|
||||||
bind(opcode, BCC, condition, displacement);
|
bind(opcode, BCC, condition, displacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//BTST (register)
|
||||||
|
for(uint3 dreg : range(8))
|
||||||
|
for(uint3 mode : range(8))
|
||||||
|
for(uint3 reg : range(8)) {
|
||||||
|
auto opcode = pattern("0000 ---1 00-- ----") | dreg << 9 | mode << 3 | reg << 0;
|
||||||
|
if(mode == 1) continue;
|
||||||
|
|
||||||
|
Register rd{D0 + dreg};
|
||||||
|
EA ea{mode, reg};
|
||||||
|
if(mode == 0) bind(opcode, BTST<Long>, rd, ea);
|
||||||
|
if(mode != 0) bind(opcode, BTST<Byte>, rd, ea);
|
||||||
|
}
|
||||||
|
|
||||||
|
//BTST (immediate)
|
||||||
|
for(uint3 mode : range(8))
|
||||||
|
for(uint3 reg : range(8)) {
|
||||||
|
auto opcode = pattern("0000 1000 00-- ----") | mode << 3 | reg << 0;
|
||||||
|
if(mode == 1 || (mode == 7 && reg == 2)) continue;
|
||||||
|
|
||||||
|
EA ea{mode, reg};
|
||||||
|
if(mode == 0) bind(opcode, BTST<Long>, ea);
|
||||||
|
if(mode != 0) bind(opcode, BTST<Byte>, ea);
|
||||||
|
}
|
||||||
|
|
||||||
|
//CLR
|
||||||
|
for(uint3 mode : range(8))
|
||||||
|
for(uint3 reg : range(8)) {
|
||||||
|
auto opcode = pattern("0100 0010 ++-- ----") | mode << 3 | reg << 0;
|
||||||
|
if(mode == 1 || (mode == 7 && reg >= 2)) continue;
|
||||||
|
|
||||||
|
EA ea{mode, reg};
|
||||||
|
bind(opcode | 0 << 6, CLR<Byte>, ea);
|
||||||
|
bind(opcode | 1 << 6, CLR<Word>, ea);
|
||||||
|
bind(opcode | 2 << 6, CLR<Long>, ea);
|
||||||
|
}
|
||||||
|
|
||||||
|
//CMP
|
||||||
|
for(uint3 dreg : range(8))
|
||||||
|
for(uint3 mode : range(8))
|
||||||
|
for(uint3 reg : range(8)) {
|
||||||
|
auto opcode = pattern("1011 ---0 ++-- ----") | dreg << 9 | mode << 3 | reg << 0;
|
||||||
|
|
||||||
|
Register rd{D0 + dreg};
|
||||||
|
EA ea{mode, reg};
|
||||||
|
bind(opcode | 0 << 6, CMP<Byte>, rd, ea);
|
||||||
|
bind(opcode | 1 << 6, CMP<Word>, rd, ea);
|
||||||
|
bind(opcode | 2 << 6, CMP<Long>, rd, ea);
|
||||||
|
|
||||||
|
if(mode == 1) unbind(opcode | 0 << 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
//DBCC
|
||||||
|
for(uint4 condition : range(16))
|
||||||
|
for(uint3 dreg : range( 8)) {
|
||||||
|
auto opcode = pattern("0101 ---- 1100 1---") | condition << 8 | dreg << 0;
|
||||||
|
|
||||||
|
Register rd{D0 + dreg};
|
||||||
|
bind(opcode, DBCC, condition, rd);
|
||||||
|
}
|
||||||
|
|
||||||
//LEA
|
//LEA
|
||||||
for(uint3 areg : range(8))
|
for(uint3 areg : range(8))
|
||||||
for(uint3 mode : range(8))
|
for(uint3 mode : range(8))
|
||||||
for(uint3 reg : range(8)) {
|
for(uint3 reg : range(8)) {
|
||||||
auto opcode = pattern("0100 ---1 11-- ----") | areg << 9 | mode << 3 | reg << 0;
|
auto opcode = pattern("0100 ---1 11-- ----") | areg << 9 | mode << 3 | reg << 0;
|
||||||
if(mode <= 1 || mode == 3 || mode == 4 || (mode == 7 && reg >= 4)) continue;
|
if(mode <= 1 || mode == 3 || mode == 4 || (mode == 7 && reg == 4)) continue;
|
||||||
|
|
||||||
Register ra{8u + areg};
|
Register ra{A0 + areg};
|
||||||
EA ea{mode, reg};
|
EA ea{mode, reg};
|
||||||
bind(opcode, LEA, ra, ea);
|
bind(opcode, LEA, ra, ea);
|
||||||
}
|
}
|
||||||
|
@ -78,13 +146,15 @@ M68K::M68K() {
|
||||||
for(uint3 fromMode : range(8))
|
for(uint3 fromMode : range(8))
|
||||||
for(uint3 fromReg : range(8)) {
|
for(uint3 fromReg : range(8)) {
|
||||||
auto opcode = pattern("00++ ---- ---- ----") | toReg << 9 | toMode << 6 | fromMode << 3 | fromReg << 0;
|
auto opcode = pattern("00++ ---- ---- ----") | toReg << 9 | toMode << 6 | fromMode << 3 | fromReg << 0;
|
||||||
if(toMode == 1 || (toMode == 7 && toReg >= 4)) continue;
|
if(toMode == 1 || (toMode == 7 && toReg >= 2)) continue;
|
||||||
|
|
||||||
EA to{toMode, toReg};
|
EA to{toMode, toReg};
|
||||||
EA from{fromMode, fromReg};
|
EA from{fromMode, fromReg};
|
||||||
bind(opcode | 1 << 12, MOVE<Byte>, to, from);
|
bind(opcode | 1 << 12, MOVE<Byte>, to, from);
|
||||||
bind(opcode | 3 << 12, MOVE<Word>, to, from);
|
bind(opcode | 3 << 12, MOVE<Word>, to, from);
|
||||||
bind(opcode | 2 << 12, MOVE<Long>, to, from);
|
bind(opcode | 2 << 12, MOVE<Long>, to, from);
|
||||||
|
|
||||||
|
if(fromMode == 1) unbind(opcode | 1 << 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
//MOVEA
|
//MOVEA
|
||||||
|
@ -93,7 +163,7 @@ M68K::M68K() {
|
||||||
for(uint3 reg : range(8)) {
|
for(uint3 reg : range(8)) {
|
||||||
auto opcode = pattern("00++ ---0 01-- ----") | areg << 9 | mode << 3 | reg << 0;
|
auto opcode = pattern("00++ ---0 01-- ----") | areg << 9 | mode << 3 | reg << 0;
|
||||||
|
|
||||||
Register ra{8u + areg};
|
Register ra{A0 + areg};
|
||||||
EA ea{mode, reg};
|
EA ea{mode, reg};
|
||||||
bind(opcode | 3 << 12, MOVEA<Word>, ra, ea);
|
bind(opcode | 3 << 12, MOVEA<Word>, ra, ea);
|
||||||
bind(opcode | 2 << 12, MOVEA<Long>, ra, ea);
|
bind(opcode | 2 << 12, MOVEA<Long>, ra, ea);
|
||||||
|
@ -104,7 +174,8 @@ M68K::M68K() {
|
||||||
for(uint3 mode : range(8))
|
for(uint3 mode : range(8))
|
||||||
for(uint3 reg : range(8)) {
|
for(uint3 reg : range(8)) {
|
||||||
auto opcode = pattern("0100 1-00 1+-- ----") | direction << 10 | mode << 3 | reg << 0;
|
auto opcode = pattern("0100 1-00 1+-- ----") | direction << 10 | mode << 3 | reg << 0;
|
||||||
if(mode <= 1 || mode == 3 || (mode == 7 && reg >= 4));
|
if(direction == 0 && (mode <= 1 || mode == 3 || (mode == 7 && reg >= 2)));
|
||||||
|
if(direction == 1 && (mode <= 1 || mode == 4 || (mode == 7 && reg == 4)));
|
||||||
|
|
||||||
EA ea{mode, reg};
|
EA ea{mode, reg};
|
||||||
bind(opcode | 0 << 6, MOVEM<Word>, direction, ea);
|
bind(opcode | 0 << 6, MOVEM<Word>, direction, ea);
|
||||||
|
@ -116,16 +187,36 @@ M68K::M68K() {
|
||||||
for(uint8 immediate : range(256)) {
|
for(uint8 immediate : range(256)) {
|
||||||
auto opcode = pattern("0111 ---0 ---- ----") | dreg << 9 | immediate << 0;
|
auto opcode = pattern("0111 ---0 ---- ----") | dreg << 9 | immediate << 0;
|
||||||
|
|
||||||
Register rd{0u + dreg};
|
Register rd{D0 + dreg};
|
||||||
bind(opcode, MOVEQ, rd, immediate);
|
bind(opcode, MOVEQ, rd, immediate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//MOVE_FROM_SR
|
||||||
|
for(uint3 mode : range(8))
|
||||||
|
for(uint3 reg : range(8)) {
|
||||||
|
auto opcode = pattern("0100 0000 11-- ----") | mode << 3 | reg << 0;
|
||||||
|
if(mode == 1 || (mode == 7 && reg >= 2)) continue;
|
||||||
|
|
||||||
|
EA ea{mode, reg};
|
||||||
|
bind(opcode, MOVE_FROM_SR, ea);
|
||||||
|
}
|
||||||
|
|
||||||
|
//MOVE_TO_SR
|
||||||
|
for(uint3 mode : range(8))
|
||||||
|
for(uint3 reg : range(8)) {
|
||||||
|
auto opcode = pattern("0100 0110 11-- ----") | mode << 3 | reg << 0;
|
||||||
|
if(mode == 1) continue;
|
||||||
|
|
||||||
|
EA ea{mode, reg};
|
||||||
|
bind(opcode, MOVE_TO_SR, ea);
|
||||||
|
}
|
||||||
|
|
||||||
//MOVE_USP
|
//MOVE_USP
|
||||||
for(uint1 direction : range(2))
|
for(uint1 direction : range(2))
|
||||||
for(uint3 areg : range(8)) {
|
for(uint3 areg : range(8)) {
|
||||||
auto opcode = pattern("0100 1110 0110 ----") | direction << 3 | areg << 0;
|
auto opcode = pattern("0100 1110 0110 ----") | direction << 3 | areg << 0;
|
||||||
|
|
||||||
Register ra{8u + areg};
|
Register ra{A0 + areg};
|
||||||
bind(opcode, MOVE_USP, direction, ra);
|
bind(opcode, MOVE_USP, direction, ra);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,18 +226,28 @@ M68K::M68K() {
|
||||||
bind(opcode, NOP);
|
bind(opcode, NOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//RTS
|
||||||
|
{ auto opcode = pattern("0100 1110 0111 0101");
|
||||||
|
|
||||||
|
bind(opcode, RTS);
|
||||||
|
}
|
||||||
|
|
||||||
//TST
|
//TST
|
||||||
for(uint3 mode : range(8))
|
for(uint3 mode : range(8))
|
||||||
for(uint3 reg : range(8)) {
|
for(uint3 reg : range(8)) {
|
||||||
auto opcode = pattern("0100 1010 ++-- ----") | mode << 3 | reg << 0;
|
auto opcode = pattern("0100 1010 ++-- ----") | mode << 3 | reg << 0;
|
||||||
|
if(mode == 7 && reg >= 2) continue;
|
||||||
|
|
||||||
EA ea{mode, reg};
|
EA ea{mode, reg};
|
||||||
bind(opcode | 0 << 6, TST<Byte>, ea);
|
bind(opcode | 0 << 6, TST<Byte>, ea);
|
||||||
bind(opcode | 1 << 6, TST<Word>, ea);
|
bind(opcode | 1 << 6, TST<Word>, ea);
|
||||||
bind(opcode | 2 << 6, TST<Long>, ea);
|
bind(opcode | 2 << 6, TST<Long>, ea);
|
||||||
|
|
||||||
|
if(mode == 1) unbind(opcode | 0 << 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef bind
|
#undef bind
|
||||||
|
#undef unbind
|
||||||
#undef pattern
|
#undef pattern
|
||||||
|
|
||||||
uint unimplemented = 0;
|
uint unimplemented = 0;
|
||||||
|
@ -156,5 +257,5 @@ M68K::M68K() {
|
||||||
disassembleTable[opcode] = [=] { return string{"???"}; };
|
disassembleTable[opcode] = [=] { return string{"???"}; };
|
||||||
unimplemented++;
|
unimplemented++;
|
||||||
}
|
}
|
||||||
//print("[M68K] unimplemented opcodes: ", unimplemented, "\n");
|
print("[M68K] unimplemented opcodes: ", unimplemented, "\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
auto M68K::testCondition(uint4 condition) -> bool {
|
auto M68K::testCondition(uint4 condition) -> bool {
|
||||||
switch(condition) {
|
switch(condition) {
|
||||||
case 0: return true; //RA
|
case 0: return true; //T
|
||||||
case 1: return false; //NV,SR
|
case 1: return false; //F
|
||||||
case 2: return !r.c && !r.z; //HI
|
case 2: return !r.c && !r.z; //HI
|
||||||
case 3: return r.c || r.z; //LS
|
case 3: return r.c || r.z; //LS
|
||||||
case 4: return !r.c; //CC,HS
|
case 4: return !r.c; //CC,HS
|
||||||
|
@ -22,6 +22,14 @@ auto M68K::testCondition(uint4 condition) -> bool {
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
template<> auto M68K::bits<Byte>() -> uint { return 8; }
|
||||||
|
template<> auto M68K::bits<Word>() -> uint { return 16; }
|
||||||
|
template<> auto M68K::bits<Long>() -> uint { return 32; }
|
||||||
|
|
||||||
|
template<> auto M68K::mask<Byte>() -> uint32 { return 0xff; }
|
||||||
|
template<> auto M68K::mask<Word>() -> uint32 { return 0xffff; }
|
||||||
|
template<> auto M68K::mask<Long>() -> uint32 { return 0xffffffff; }
|
||||||
|
|
||||||
template<> auto M68K::clip<Byte>(uint32 data) -> uint32 { return data & 0xff; }
|
template<> auto M68K::clip<Byte>(uint32 data) -> uint32 { return data & 0xff; }
|
||||||
template<> auto M68K::clip<Word>(uint32 data) -> uint32 { return data & 0xffff; }
|
template<> auto M68K::clip<Word>(uint32 data) -> uint32 { return data & 0xffff; }
|
||||||
template<> auto M68K::clip<Long>(uint32 data) -> uint32 { return data & 0xffffffff; }
|
template<> auto M68K::clip<Long>(uint32 data) -> uint32 { return data & 0xffffffff; }
|
||||||
|
@ -86,12 +94,58 @@ template<uint Size> auto M68K::instructionANDI(EA ea) -> void {
|
||||||
|
|
||||||
auto M68K::instructionBCC(uint4 condition, uint8 displacement) -> void {
|
auto M68K::instructionBCC(uint4 condition, uint8 displacement) -> void {
|
||||||
auto extension = readPC();
|
auto extension = readPC();
|
||||||
if(condition == 1); //push<Long>(r.pc);
|
if(condition == 1) push<Long>(r.pc);
|
||||||
r.pc -= 2;
|
r.pc -= 2;
|
||||||
if(!testCondition(condition == 1 ? (uint4)0 : condition)) return;
|
if(condition >= 2 && !testCondition(condition)) return; //0 = BRA; 1 = BSR
|
||||||
r.pc += displacement ? sign<Byte>(displacement) : sign<Word>(extension);
|
r.pc += displacement ? sign<Byte>(displacement) : sign<Word>(extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<uint Size> auto M68K::instructionBTST(Register rd, EA ea) -> void {
|
||||||
|
auto bit = read<Size>(rd);
|
||||||
|
auto test = read<Size>(ea);
|
||||||
|
bit &= bits<Size>() - 1;
|
||||||
|
|
||||||
|
r.z = test.bit(bit) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<uint Size> auto M68K::instructionBTST(EA ea) -> void {
|
||||||
|
auto bit = (uint8)readPC<Word>();
|
||||||
|
auto test = read<Size>(ea);
|
||||||
|
bit &= bits<Size>() - 1;
|
||||||
|
|
||||||
|
r.z = test.bit(bit) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<uint Size> auto M68K::instructionCLR(EA ea) -> void {
|
||||||
|
read<Size>(ea);
|
||||||
|
write<Size>(ea, 0);
|
||||||
|
|
||||||
|
r.c = 0;
|
||||||
|
r.v = 0;
|
||||||
|
r.z = 1;
|
||||||
|
r.n = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<uint Size> auto M68K::instructionCMP(Register rd, EA ea) -> void {
|
||||||
|
auto source = read<Size>(ea);
|
||||||
|
auto target = read<Size>(rd);
|
||||||
|
auto result = target - source;
|
||||||
|
|
||||||
|
r.c = carry<Size>(result, source);
|
||||||
|
r.v = overflow<Size>(result, source, target);
|
||||||
|
r.z = zero<Size>(result);
|
||||||
|
r.n = negative<Size>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto M68K::instructionDBCC(uint4 condition, Register rd) -> void {
|
||||||
|
auto displacement = (int16)readPC();
|
||||||
|
if(!testCondition(condition)) {
|
||||||
|
uint16 result = read<Word>(rd);
|
||||||
|
write<Word>(rd, result - 1);
|
||||||
|
if(result) r.pc -= 2, r.pc += displacement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto M68K::instructionLEA(Register ra, EA ea) -> void {
|
auto M68K::instructionLEA(Register ra, EA ea) -> void {
|
||||||
write<Long>(ra, fetch<Long>(ea));
|
write<Long>(ra, fetch<Long>(ea));
|
||||||
}
|
}
|
||||||
|
@ -109,7 +163,7 @@ template<uint Size> auto M68K::instructionMOVE(EA to, EA from) -> void {
|
||||||
template<uint Size> auto M68K::instructionMOVEA(Register ra, EA ea) -> void {
|
template<uint Size> auto M68K::instructionMOVEA(Register ra, EA ea) -> void {
|
||||||
auto data = read<Size>(ea);
|
auto data = read<Size>(ea);
|
||||||
if(Size == Word) data = (int16)data;
|
if(Size == Word) data = (int16)data;
|
||||||
write<Size>(ra, data);
|
write<Long>(ra, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<uint Size> auto M68K::instructionMOVEM(uint1 direction, EA ea) -> void {
|
template<uint Size> auto M68K::instructionMOVEM(uint1 direction, EA ea) -> void {
|
||||||
|
@ -135,18 +189,33 @@ auto M68K::instructionMOVEQ(Register rd, uint8 immediate) -> void {
|
||||||
r.n = negative<Byte>(immediate);
|
r.n = negative<Byte>(immediate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto M68K::instructionMOVE_FROM_SR(EA ea) -> void {
|
||||||
|
write<Word>(ea, r.sr);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto M68K::instructionMOVE_TO_SR(EA ea) -> void {
|
||||||
|
if(!supervisor()) return;
|
||||||
|
|
||||||
|
setSR(read<Word>(ea));
|
||||||
|
}
|
||||||
|
|
||||||
auto M68K::instructionMOVE_USP(uint1 direction, Register ra) -> void {
|
auto M68K::instructionMOVE_USP(uint1 direction, Register ra) -> void {
|
||||||
if(!r.s) trap(); //todo: proper trap
|
if(!supervisor()) return;
|
||||||
|
|
||||||
if(direction == 0) {
|
if(direction == 0) {
|
||||||
r.usp = read<Long>(ra);
|
r.sp = read<Long>(ra);
|
||||||
} else {
|
} else {
|
||||||
write<Long>(ra, r.usp);
|
write<Long>(ra, r.sp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto M68K::instructionNOP() -> void {
|
auto M68K::instructionNOP() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto M68K::instructionRTS() -> void {
|
||||||
|
r.pc = pop<Long>();
|
||||||
|
}
|
||||||
|
|
||||||
template<uint Size> auto M68K::instructionTST(EA ea) -> void {
|
template<uint Size> auto M68K::instructionTST(EA ea) -> void {
|
||||||
auto data = read<Size>(ea);
|
auto data = read<Size>(ea);
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
namespace Processor {
|
namespace Processor {
|
||||||
|
|
||||||
enum : uint { Byte, Word, Long };
|
enum : uint { Byte, Word, Long };
|
||||||
|
enum : bool { Reverse = 1 };
|
||||||
|
|
||||||
#include "registers.cpp"
|
#include "registers.cpp"
|
||||||
#include "memory.cpp"
|
#include "memory.cpp"
|
||||||
|
@ -18,11 +19,16 @@ auto M68K::power() -> void {
|
||||||
auto M68K::reset() -> void {
|
auto M68K::reset() -> void {
|
||||||
instructionsExecuted = 0;
|
instructionsExecuted = 0;
|
||||||
|
|
||||||
for(uint rn : range(15)) write<Long>(Register{rn}, 0);
|
for(auto& da : r.da) da = 0;
|
||||||
r.ssp = 0;
|
r.sp = 0;
|
||||||
r.usp = 0;
|
|
||||||
r.pc = 0;
|
r.pc = 0;
|
||||||
r.sr = 0x2000;
|
r.sr = 0x2000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto M68K::supervisor() -> bool {
|
||||||
|
if(r.s) return true;
|
||||||
|
//todo: raise TRAP exception
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,25 @@
|
||||||
namespace Processor {
|
namespace Processor {
|
||||||
|
|
||||||
struct M68K {
|
struct M68K {
|
||||||
|
enum : bool { User, Supervisor };
|
||||||
enum : uint { Byte, Word, Long };
|
enum : uint { Byte, Word, Long };
|
||||||
enum : bool { NoUpdate = 0 };
|
enum : bool { NoUpdate = 0, Reverse = 1 };
|
||||||
|
enum : uint { D0, D1, D2, D3, D4, D5, D6, D7, A0, A1, A2, A3, A4, A5, A6, A7 };
|
||||||
|
|
||||||
|
enum : uint {
|
||||||
|
DataRegisterDirect,
|
||||||
|
AddressRegisterDirect,
|
||||||
|
AddressRegisterIndirect,
|
||||||
|
AddressRegisterIndirectWithPostIncrement,
|
||||||
|
AddressRegisterIndirectWithPreDecrement,
|
||||||
|
AddressRegisterIndirectWithDisplacement,
|
||||||
|
AddressRegisterIndirectWithIndex,
|
||||||
|
AbsoluteShortIndirect,
|
||||||
|
AbsoluteLongIndirect,
|
||||||
|
ProgramCounterIndirectWithDisplacement,
|
||||||
|
ProgramCounterIndirectWithIndex,
|
||||||
|
Immediate,
|
||||||
|
};
|
||||||
|
|
||||||
M68K();
|
M68K();
|
||||||
|
|
||||||
|
@ -16,25 +33,29 @@ struct M68K {
|
||||||
|
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
auto reset() -> void;
|
auto reset() -> void;
|
||||||
|
auto supervisor() -> bool;
|
||||||
|
|
||||||
//registers.cpp
|
//registers.cpp
|
||||||
struct Register {
|
struct Register {
|
||||||
Register(uint number) : number(number) {}
|
explicit Register(uint number_) : number(number_) {}
|
||||||
|
|
||||||
uint4 number;
|
uint4 number;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<uint Size = Long> auto read(Register reg) -> uint32;
|
template<uint Size = Long> auto read(Register reg) -> uint32;
|
||||||
template<uint Size = Long> auto write(Register reg, uint32 value) -> void;
|
template<uint Size = Long> auto write(Register reg, uint32 value) -> void;
|
||||||
|
auto setSR(uint16 sr) -> void;
|
||||||
|
|
||||||
//memory.cpp
|
//memory.cpp
|
||||||
template<uint Size> auto read(uint32 addr) -> uint32;
|
template<uint Size> auto read(uint32 addr) -> uint32;
|
||||||
template<uint Size> auto write(uint32 addr, uint32 data) -> void;
|
template<uint Size, bool Order = 0> auto write(uint32 addr, uint32 data) -> void;
|
||||||
template<uint Size = Word> auto readPC() -> uint32;
|
template<uint Size = Word> auto readPC() -> uint32;
|
||||||
|
template<uint Size> auto pop() -> uint32;
|
||||||
|
template<uint Size> auto push(uint32 data) -> void;
|
||||||
|
|
||||||
//ea.cpp
|
//ea.cpp
|
||||||
struct EA {
|
struct EA {
|
||||||
EA(uint mode_, uint reg_) : mode(mode_), reg(reg_) {
|
explicit EA(uint mode_, uint reg_) : mode(mode_), reg(reg_) {
|
||||||
if(mode == 7) mode += reg.number; //optimization: convert modes {7; 0-4} to {8-11}
|
if(mode == 7) mode += reg.number; //optimization: convert modes {7; 0-4} to {8-11}
|
||||||
if(mode != 0) reg.number += 8; //optimization: linear index to all registers: d0-d7; a0-a7
|
if(mode != 0) reg.number += 8; //optimization: linear index to all registers: d0-d7; a0-a7
|
||||||
}
|
}
|
||||||
|
@ -58,6 +79,8 @@ struct M68K {
|
||||||
//instructions.cpp
|
//instructions.cpp
|
||||||
auto testCondition(uint4 condition) -> bool;
|
auto testCondition(uint4 condition) -> bool;
|
||||||
|
|
||||||
|
template<uint Size> auto bits() -> uint;
|
||||||
|
template<uint Size> auto mask() -> uint32;
|
||||||
template<uint Size> auto clip(uint32 data) -> uint32;
|
template<uint Size> auto clip(uint32 data) -> uint32;
|
||||||
template<uint Size> auto sign(uint32 data) -> int32;
|
template<uint Size> auto sign(uint32 data) -> int32;
|
||||||
|
|
||||||
|
@ -69,13 +92,21 @@ struct M68K {
|
||||||
template<uint Size> auto instructionADD(Register rd, uint1 direction, EA ea) -> void;
|
template<uint Size> auto instructionADD(Register rd, uint1 direction, EA ea) -> void;
|
||||||
template<uint Size> auto instructionANDI(EA ea) -> void;
|
template<uint Size> auto instructionANDI(EA ea) -> void;
|
||||||
auto instructionBCC(uint4 condition, uint8 displacement) -> void;
|
auto instructionBCC(uint4 condition, uint8 displacement) -> void;
|
||||||
|
template<uint Size> auto instructionBTST(Register rd, EA ea) -> void;
|
||||||
|
template<uint Size> auto instructionBTST(EA ea) -> void;
|
||||||
|
template<uint Size> auto instructionCLR(EA ea) -> void;
|
||||||
|
template<uint Size> auto instructionCMP(Register rd, EA ea) -> void;
|
||||||
|
auto instructionDBCC(uint4 condition, Register rd) -> void;
|
||||||
auto instructionLEA(Register ra, EA ea) -> void;
|
auto instructionLEA(Register ra, EA ea) -> void;
|
||||||
template<uint Size> auto instructionMOVE(EA to, EA from) -> void;
|
template<uint Size> auto instructionMOVE(EA to, EA from) -> void;
|
||||||
template<uint Size> auto instructionMOVEA(Register ra, EA ea) -> void;
|
template<uint Size> auto instructionMOVEA(Register ra, EA ea) -> void;
|
||||||
template<uint Size> auto instructionMOVEM(uint1 direction, EA ea) -> void;
|
template<uint Size> auto instructionMOVEM(uint1 direction, EA ea) -> void;
|
||||||
auto instructionMOVEQ(Register rd, uint8 immediate) -> void;
|
auto instructionMOVEQ(Register rd, uint8 immediate) -> void;
|
||||||
|
auto instructionMOVE_FROM_SR(EA ea) -> void;
|
||||||
|
auto instructionMOVE_TO_SR(EA ea) -> void;
|
||||||
auto instructionMOVE_USP(uint1 direction, Register ra) -> void;
|
auto instructionMOVE_USP(uint1 direction, Register ra) -> void;
|
||||||
auto instructionNOP() -> void;
|
auto instructionNOP() -> void;
|
||||||
|
auto instructionRTS() -> void;
|
||||||
template<uint Size> auto instructionTST(EA ea) -> void;
|
template<uint Size> auto instructionTST(EA ea) -> void;
|
||||||
|
|
||||||
//disassembler.cpp
|
//disassembler.cpp
|
||||||
|
@ -83,8 +114,8 @@ struct M68K {
|
||||||
auto disassembleRegisters() -> string;
|
auto disassembleRegisters() -> string;
|
||||||
|
|
||||||
struct Registers {
|
struct Registers {
|
||||||
uint32 d0, d1, d2, d3, d4, d5, d6, d7;
|
uint32 da[16]; //a7 = primary stack pointer
|
||||||
uint32 a0, a1, a2, a3, a4, a5, a6, ssp, usp;
|
uint32 sp; //sp = secondary stack pointer
|
||||||
uint32 pc;
|
uint32 pc;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
|
@ -112,13 +143,21 @@ private:
|
||||||
template<uint Size> auto disassembleADD(Register rd, uint1 direction, EA ea) -> string;
|
template<uint Size> auto disassembleADD(Register rd, uint1 direction, EA ea) -> string;
|
||||||
template<uint Size> auto disassembleANDI(EA ea) -> string;
|
template<uint Size> auto disassembleANDI(EA ea) -> string;
|
||||||
auto disassembleBCC(uint4 condition, uint8 displacement) -> string;
|
auto disassembleBCC(uint4 condition, uint8 displacement) -> string;
|
||||||
|
template<uint Size> auto disassembleBTST(Register rd, EA ea) -> string;
|
||||||
|
template<uint Size> auto disassembleBTST(EA ea) -> string;
|
||||||
|
template<uint Size> auto disassembleCLR(EA ea) -> string;
|
||||||
|
template<uint Size> auto disassembleCMP(Register rd, EA ea) -> string;
|
||||||
|
auto disassembleDBCC(uint4 condition, Register rd) -> string;
|
||||||
auto disassembleLEA(Register ra, EA ea) -> string;
|
auto disassembleLEA(Register ra, EA ea) -> string;
|
||||||
template<uint Size> auto disassembleMOVE(EA to, EA from) -> string;
|
template<uint Size> auto disassembleMOVE(EA to, EA from) -> string;
|
||||||
template<uint Size> auto disassembleMOVEA(Register ra, EA ea) -> string;
|
template<uint Size> auto disassembleMOVEA(Register ra, EA ea) -> string;
|
||||||
template<uint Size> auto disassembleMOVEM(uint1 direction, EA ea) -> string;
|
template<uint Size> auto disassembleMOVEM(uint1 direction, EA ea) -> string;
|
||||||
auto disassembleMOVEQ(Register rd, uint8 immediate) -> string;
|
auto disassembleMOVEQ(Register rd, uint8 immediate) -> string;
|
||||||
|
auto disassembleMOVE_FROM_SR(EA ea) -> string;
|
||||||
|
auto disassembleMOVE_TO_SR(EA ea) -> string;
|
||||||
auto disassembleMOVE_USP(uint1 direction, Register ra) -> string;
|
auto disassembleMOVE_USP(uint1 direction, Register ra) -> string;
|
||||||
auto disassembleNOP() -> string;
|
auto disassembleNOP() -> string;
|
||||||
|
auto disassembleRTS() -> string;
|
||||||
template<uint Size> auto disassembleTST(EA ea) -> string;
|
template<uint Size> auto disassembleTST(EA ea) -> string;
|
||||||
|
|
||||||
template<uint Size> auto _read(uint32 addr) -> uint32;
|
template<uint Size> auto _read(uint32 addr) -> uint32;
|
||||||
|
|
|
@ -1,17 +1,3 @@
|
||||||
/*
|
|
||||||
auto M68K::readPC(uint2 size) -> uint32 {
|
|
||||||
step(4);
|
|
||||||
uint32 data = read(size != Byte, r.pc);
|
|
||||||
r.pc += 2;
|
|
||||||
if(size != Long) return data;
|
|
||||||
|
|
||||||
step(4);
|
|
||||||
data = data << 16 | read(1, r.pc);
|
|
||||||
r.pc += 2;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
template<> auto M68K::read<Byte>(uint32 addr) -> uint32 {
|
template<> auto M68K::read<Byte>(uint32 addr) -> uint32 {
|
||||||
step(4);
|
step(4);
|
||||||
return read(0, addr);
|
return read(0, addr);
|
||||||
|
@ -32,36 +18,74 @@ template<> auto M68K::read<Long>(uint32 addr) -> uint32 {
|
||||||
//
|
//
|
||||||
|
|
||||||
template<> auto M68K::write<Byte>(uint32 addr, uint32 data) -> void {
|
template<> auto M68K::write<Byte>(uint32 addr, uint32 data) -> void {
|
||||||
|
step(4);
|
||||||
|
return write(0, addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> auto M68K::write<Word>(uint32 addr, uint32 data) -> void {
|
template<> auto M68K::write<Word>(uint32 addr, uint32 data) -> void {
|
||||||
|
step(4);
|
||||||
|
return write(1, addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> auto M68K::write<Long>(uint32 addr, uint32 data) -> void {
|
template<> auto M68K::write<Long>(uint32 addr, uint32 data) -> void {
|
||||||
|
step(4);
|
||||||
|
write(1, addr + 0, data >> 16);
|
||||||
|
step(4);
|
||||||
|
write(1, addr + 2, data >> 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> auto M68K::write<Byte, Reverse>(uint32 addr, uint32 data) -> void {
|
||||||
|
step(4);
|
||||||
|
return write(0, addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> auto M68K::write<Word, Reverse>(uint32 addr, uint32 data) -> void {
|
||||||
|
step(4);
|
||||||
|
return write(1, addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> auto M68K::write<Long, Reverse>(uint32 addr, uint32 data) -> void {
|
||||||
|
step(4);
|
||||||
|
write(1, addr + 2, data >> 0);
|
||||||
|
step(4);
|
||||||
|
write(1, addr + 0, data >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
template<> auto M68K::readPC<Byte>() -> uint32 {
|
template<> auto M68K::readPC<Byte>() -> uint32 {
|
||||||
step(4);
|
step(4);
|
||||||
uint32 data = read(1, r.pc);
|
auto data = read(1, r.pc);
|
||||||
r.pc += 2;
|
r.pc += 2;
|
||||||
return (uint8)data;
|
return (uint8)data;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> auto M68K::readPC<Word>() -> uint32 {
|
template<> auto M68K::readPC<Word>() -> uint32 {
|
||||||
step(4);
|
step(4);
|
||||||
uint32 data = read(1, r.pc);
|
auto data = read(1, r.pc);
|
||||||
r.pc += 2;
|
r.pc += 2;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> auto M68K::readPC<Long>() -> uint32 {
|
template<> auto M68K::readPC<Long>() -> uint32 {
|
||||||
step(4);
|
step(4);
|
||||||
uint32 data = read(1, r.pc) << 16;
|
auto hi = read(1, r.pc);
|
||||||
r.pc += 2;
|
r.pc += 2;
|
||||||
step(4);
|
step(4);
|
||||||
data |= read(1, r.pc);
|
auto lo = read(1, r.pc);
|
||||||
r.pc += 2;
|
r.pc += 2;
|
||||||
|
return hi << 16 | lo << 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
template<uint Size> auto M68K::pop() -> uint32 {
|
||||||
|
auto data = read<Size>((uint32)r.da[A7]);
|
||||||
|
r.da[A7] += Size == Long ? 4 : 2;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<uint Size> auto M68K::push(uint32 data) -> void {
|
||||||
|
r.da[A7] -= Size == Long ? 4 : 2;
|
||||||
|
return write<Size, Reverse>((uint32)r.da[A7], data);
|
||||||
|
}
|
||||||
|
|
|
@ -1,42 +1,13 @@
|
||||||
template<uint Size> auto M68K::read(Register reg) -> uint32 {
|
template<uint Size> auto M68K::read(Register reg) -> uint32 {
|
||||||
switch(reg.number) {
|
return clip<Size>(r.da[reg.number]);
|
||||||
case 0: return clip<Size>(r.d0);
|
|
||||||
case 1: return clip<Size>(r.d1);
|
|
||||||
case 2: return clip<Size>(r.d2);
|
|
||||||
case 3: return clip<Size>(r.d3);
|
|
||||||
case 4: return clip<Size>(r.d4);
|
|
||||||
case 5: return clip<Size>(r.d5);
|
|
||||||
case 6: return clip<Size>(r.d6);
|
|
||||||
case 7: return clip<Size>(r.d7);
|
|
||||||
case 8: return clip<Size>(r.a0);
|
|
||||||
case 9: return clip<Size>(r.a1);
|
|
||||||
case 10: return clip<Size>(r.a2);
|
|
||||||
case 11: return clip<Size>(r.a3);
|
|
||||||
case 12: return clip<Size>(r.a4);
|
|
||||||
case 13: return clip<Size>(r.a5);
|
|
||||||
case 14: return clip<Size>(r.a6);
|
|
||||||
case 15: return r.s ? clip<Size>(r.ssp) : clip<Size>(r.usp);
|
|
||||||
}
|
|
||||||
unreachable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<uint Size> auto M68K::write(Register reg, uint32 data) -> void {
|
template<uint Size> auto M68K::write(Register reg, uint32 data) -> void {
|
||||||
switch(reg.number) {
|
r.da[reg.number] = (r.da[reg.number] & ~mask<Size>()) | (data & mask<Size>());
|
||||||
case 0: r.d0 = clip<Size>(data); return;
|
}
|
||||||
case 1: r.d1 = clip<Size>(data); return;
|
|
||||||
case 2: r.d2 = clip<Size>(data); return;
|
auto M68K::setSR(uint16 sr) -> void {
|
||||||
case 3: r.d3 = clip<Size>(data); return;
|
//when entering or exiting supervisor mode; swap SSP and USP into A7
|
||||||
case 4: r.d4 = clip<Size>(data); return;
|
if(r.sr.bit(13) != sr.bit(13)) swap(r.da[A7], r.sp);
|
||||||
case 5: r.d5 = clip<Size>(data); return;
|
r.sr = sr;
|
||||||
case 6: r.d6 = clip<Size>(data); return;
|
|
||||||
case 7: r.d7 = clip<Size>(data); return;
|
|
||||||
case 8: r.a0 = clip<Size>(data); return;
|
|
||||||
case 9: r.a1 = clip<Size>(data); return;
|
|
||||||
case 10: r.a2 = clip<Size>(data); return;
|
|
||||||
case 11: r.a3 = clip<Size>(data); return;
|
|
||||||
case 12: r.a4 = clip<Size>(data); return;
|
|
||||||
case 13: r.a5 = clip<Size>(data); return;
|
|
||||||
case 14: r.a6 = clip<Size>(data); return;
|
|
||||||
case 15: r.s ? r.ssp = clip<Size>(data) : r.usp = clip<Size>(data); return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue