diff --git a/higan/md/GNUmakefile b/higan/md/GNUmakefile index 9457886..4c39bf9 100644 --- a/higan/md/GNUmakefile +++ b/higan/md/GNUmakefile @@ -1,8 +1,8 @@ -components += m68k z80 sn76489 +components += sn76489 objects += md-interface objects += md-cpu md-apu md-vdp md-psg md-ym2612 -objects += md-mcd +#objects += md-mcd objects += md-system md-cartridge md-expansion objects += md-controller @@ -12,7 +12,7 @@ obj/md-apu.o: md/apu/apu.cpp obj/md-vdp.o: md/vdp/vdp.cpp obj/md-psg.o: md/psg/psg.cpp obj/md-ym2612.o: md/ym2612/ym2612.cpp -obj/md-mcd.o: md/mcd/mcd.cpp +#obj/md-mcd.o: md/mcd/mcd.cpp obj/md-system.o: md/system/system.cpp obj/md-cartridge.o: md/cartridge/cartridge.cpp obj/md-expansion.o: md/expansion/expansion.cpp diff --git a/higan/md/apu/algorithms.cpp b/higan/md/apu/algorithms.cpp new file mode 100644 index 0000000..e04dcd8 --- /dev/null +++ b/higan/md/apu/algorithms.cpp @@ -0,0 +1,277 @@ +auto APU::ADD(uint8 x, uint8 y, bool c) -> uint8 { + uint9 z = x + y + c; + + CF = z.bit(8); + NF = 0; + VF = uint8(~(x ^ y) & (x ^ z)).bit(7); + XF = z.bit(3); + HF = uint8(x ^ y ^ z).bit(4); + YF = z.bit(5); + ZF = uint8(z) == 0; + SF = z.bit(7); + + return z; +} + +auto APU::AND(uint8 x, uint8 y) -> uint8 { + uint8 z = x & y; + + CF = 0; + NF = 0; + PF = parity(z); + XF = z.bit(3); + HF = 1; + YF = z.bit(5); + ZF = z == 0; + SF = z.bit(7); + + return z; +} + +auto APU::BIT(uint3 bit, uint8 x) -> uint8 { + uint8 z = x & 1 << bit; + + NF = 0; + PF = parity(z); + XF = x.bit(3); + HF = 1; + YF = x.bit(5); + ZF = z == 0; + SF = z.bit(7); + + return x; +} + +auto APU::CP(uint8 x, uint8 y) -> void { + uint9 z = x - y; + + CF = z.bit(8); + NF = 1; + VF = uint8((x ^ y) & (x ^ z)).bit(7); + XF = y.bit(3); + HF = uint8(x ^ y ^ z).bit(4); + YF = y.bit(5); + ZF = uint8(z) == 0; + SF = z.bit(7); +} + +auto APU::DEC(uint8 x) -> uint8 { + uint8 z = x - 1; + + NF = 1; + VF = z == 0x7f; + XF = z.bit(3); + HF = z.bit(0,3) == 0x0f; + YF = z.bit(5); + ZF = z == 0; + SF = z.bit(7); + + return z; +} + +auto APU::IN(uint8 x) -> uint8 { + NF = 0; + PF = parity(x); + XF = x.bit(3); + HF = 0; + YF = x.bit(5); + ZF = x == 0; + SF = x.bit(7); + + return x; +} + +auto APU::INC(uint8 x) -> uint8 { + uint8 z = x + 1; + + NF = 0; + VF = z == 0x80; + XF = z.bit(3); + HF = z.bit(0,3) == 0x00; + YF = z.bit(5); + ZF = z == 0; + SF = z.bit(7); + + return z; +} + +auto APU::OR(uint8 x, uint8 y) -> uint8 { + uint8 z = x | y; + + CF = 0; + NF = 0; + PF = parity(z); + XF = z.bit(3); + HF = 0; + YF = z.bit(5); + ZF = z == 0; + SF = z.bit(7); + + return z; +} + +auto APU::RES(uint3 bit, uint8 x) -> uint8 { + x &= ~(1 << bit); + return x; +} + +auto APU::RL(uint8 x) -> uint8 { + bool c = x.bit(7); + x = x << 1 | CF; + + CF = c; + NF = 0; + PF = parity(x); + XF = x.bit(3); + HF = 0; + YF = x.bit(5); + ZF = x == 0; + SF = x.bit(7); + + return x; +} + +auto APU::RLC(uint8 x) -> uint8 { + x = x << 1 | x >> 7; + + CF = x.bit(0); + NF = 0; + PF = parity(x); + XF = x.bit(3); + HF = 0; + YF = x.bit(5); + ZF = x == 0; + SF = x.bit(7); + + return x; +} + +auto APU::RR(uint8 x) -> uint8 { + bool c = x.bit(0); + x = x >> 1 | CF << 7; + + CF = c; + NF = 0; + PF = parity(x); + XF = x.bit(3); + HF = 0; + YF = x.bit(5); + ZF = x == 0; + SF = x.bit(7); + + return x; +} + +auto APU::RRC(uint8 x) -> uint8 { + x = x >> 1 | x << 7; + + CF = x.bit(7); + NF = 0; + PF = parity(x); + XF = x.bit(3); + HF = 0; + YF = x.bit(5); + ZF = x == 0; + SF = x.bit(7); + + return x; +} + +auto APU::SET(uint3 bit, uint8 x) -> uint8 { + x |= (1 << bit); + return x; +} + +auto APU::SLA(uint8 x) -> uint8 { + bool c = x.bit(7); + x = x << 1; + + CF = c; + NF = 0; + PF = parity(x); + XF = x.bit(3); + HF = 0; + YF = x.bit(5); + ZF = x == 0; + SF = x.bit(7); + + return x; +} + +auto APU::SLL(uint8 x) -> uint8 { + bool c = x.bit(7); + x = x << 1 | 1; + + CF = c; + NF = 0; + PF = parity(x); + XF = x.bit(3); + HF = 0; + YF = x.bit(5); + ZF = x == 0; + SF = x.bit(7); + + return x; +} + +auto APU::SRA(uint8 x) -> uint8 { + bool c = x.bit(0); + x = (int8)x >> 1; + + CF = c; + NF = 0; + PF = parity(x); + XF = x.bit(3); + HF = 0; + YF = x.bit(5); + ZF = x == 0; + SF = x.bit(7); + + return x; +} + +auto APU::SRL(uint8 x) -> uint8 { + bool c = x.bit(0); + x = x >> 1; + + CF = c; + NF = 0; + PF = parity(x); + XF = x.bit(3); + HF = 0; + YF = x.bit(5); + ZF = x == 0; + SF = x.bit(7); + + return x; +} + +auto APU::SUB(uint8 x, uint8 y, bool c) -> uint8 { + uint9 z = x - y - c; + + CF = z.bit(8); + NF = 1; + VF = uint8((x ^ y) & (x ^ z)).bit(7); + XF = z.bit(3); + HF = uint8(x ^ y ^ z).bit(4); + YF = z.bit(5); + ZF = uint8(z) == 0; + SF = z.bit(7); + + return z; +} + +auto APU::XOR(uint8 x, uint8 y) -> uint8 { + uint8 z = x ^ y; + + CF = 0; + NF = 0; + PF = parity(z); + XF = z.bit(3); + HF = 0; + YF = z.bit(5); + ZF = z == 0; + SF = z.bit(7); + + return z; +} diff --git a/higan/md/apu/apu.cpp b/higan/md/apu/apu.cpp index c108653..a24b7b8 100644 --- a/higan/md/apu/apu.cpp +++ b/higan/md/apu/apu.cpp @@ -6,6 +6,48 @@ APU apu; #include "bus.cpp" #include "serialization.cpp" +#if !defined(NO_EVENTINSTRUCTION_NOTIFY) +#include "disassembler.cpp" +#endif + +#include "registers.cpp" +#include "instruction.cpp" +#include "algorithms.cpp" +#include "instructions.cpp" + +// Simplified: MegaDrive only ever uses IM 0 or IM1 +// MegaDrive hard-codes reset address to 0x38 +// MegaDrive does not use NMI +auto APU::irq() -> bool { + if((!IFF1) || EI) return false; + uint cycles; + R.bit(0,6)++; + + push(PC); + + // Assumes IM 2 will never be used + if (IM) { + //constant address + WZ = 0x38; + cycles = 7; + } else { + //external data bus ($ff = RST $38) + WZ = 0xff; + cycles = 6; + } + + PC = WZ; + IFF1 = 0; + IFF2 = 0; + HALT = 0; + if(P) PF = 0; + P = 0; + Q = 0; + + wait(cycles); + return true; +} + auto APU::load(Node::Object parent, Node::Object from) -> void { node = Node::append(parent, from, "APU"); from = Node::scan(parent = node, from); @@ -25,12 +67,12 @@ auto APU::unload() -> void { auto APU::main() -> void { // See: higan/component/processor/z80/memory.cpp #if defined(SCHEDULER_SYNCHRO) - if(bus->requested() && !synchronizing()) { - bus->grant(true); + if(requested() && !synchronizing()) { + grant(true); return step(1); } - bus->grant(false); + grant(false); #endif updateBus(); @@ -38,18 +80,13 @@ auto APU::main() -> void { if(!running()) { return step(1); } - + + /* MD doesn't use NMI, so we can skip the check! if(state.nmiLine) { state.nmiLine = 0; //edge-sensitive if(eventInterrupt->enabled()) eventInterrupt->notify("NMI"); irq(0, 0x0066, 0xff); - } - - if(state.intLine) { - //level-sensitive - if(eventInterrupt->enabled()) eventInterrupt->notify("IRQ"); - irq(1, 0x0038, 0xff); - } + }*/ #if !defined(NO_EVENTINSTRUCTION_NOTIFY) if(eventInstruction->enabled() && eventInstruction->address(r.pc)) { @@ -69,25 +106,6 @@ auto APU::step(uint clocks) -> void { #endif } -auto APU::setNMI(uint1 value) -> void { - state.nmiLine = value; -} - -auto APU::setINT(uint1 value) -> void { - state.intLine = value; -} - -auto APU::setRES(uint1 value) -> void { - if(!value && arbstate.resetLine) { - power(true); - } - arbstate.resetLine = value; -} - -auto APU::setBREQ(uint1 value) -> void { - arbstate.busreqLine = value; -} - auto APU::updateBus() -> void { if(!arbstate.resetLine) return; // Z80 bus switch may be blocked by reset if(arbstate.busreqLine && !arbstate.busreqLatch) { @@ -97,8 +115,16 @@ auto APU::updateBus() -> void { } auto APU::power(bool reset) -> void { - Z80::bus = this; - Z80::power(); + mosfet = MOSFET::NMOS; + + prefix = Prefix::hl; + r = {}; + AF = 0xffff; + SP = 0xffff; + IFF1 = 0; + IFF2 = 0; + IM = 1; + ym2612.power(reset); Thread::create(system.frequency() / 15.0, {&APU::main, this}); if(!reset) { diff --git a/higan/md/apu/apu.hpp b/higan/md/apu/apu.hpp index c7b5594..9d836a1 100644 --- a/higan/md/apu/apu.hpp +++ b/higan/md/apu/apu.hpp @@ -1,44 +1,341 @@ //Zilog Z80 -struct APU : Z80, Z80::Bus, Thread { +struct APU : Thread { Node::Component node; Node::Instruction eventInstruction; Node::Notification eventInterrupt; - inline auto synchronizing() const -> bool override { return scheduler.synchronizing(); } + constexpr inline auto synchronizing() const -> bool { return scheduler.synchronizing(); } + constexpr inline auto requested() -> bool { return _requested; } + constexpr inline auto granted() -> bool { return _granted; } + + constexpr inline auto request(bool value) -> void { _requested = value; } + constexpr inline auto grant(bool value) -> void { _granted = value; } //z80.cpp auto load(Node::Object, Node::Object) -> void; auto unload() -> void; auto main() -> void; - auto step(uint clocks) -> void override; + inline auto step(uint clocks) -> void; auto power(bool reset) -> void; - auto running() -> bool { return arbstate.resetLine && busStatus(); } + constexpr inline auto running() -> bool { return arbstate.resetLine && busStatus(); } + + alwaysinline auto setNMI(uint1 value) -> void { + state.nmiLine = value; + } + + alwaysinline auto setINT(uint1 value) -> void { + state.intLine = value; + if (state.intLine) { + state.interruptPending = true; + } + } + + alwaysinline auto setRES(uint1 value) -> void { + if(!value && arbstate.resetLine) { + power(true); + } + arbstate.resetLine = value; + } - auto setNMI(uint1 value) -> void; - auto setINT(uint1 value) -> void; - auto setRES(uint1 value) -> void; - auto setBREQ(uint1 value) -> void; + alwaysinline auto setBREQ(uint1 value) -> void { + arbstate.busreqLine = value; + } auto updateBus() -> void; - auto busStatus() -> uint1 { + + inline auto busStatus() -> uint1 { // 0->68K, 1->Z80 return (arbstate.resetLine & arbstate.busreqLatch) ^ 1; } //bus.cpp - auto read(uint16 address) -> uint8 override; - auto write(uint16 address, uint8 data) -> void override; + inline auto read(const uint16 address) -> uint8; + inline auto write(const uint16 address, const uint8 data) -> void; - auto in(uint16 address) -> uint8 override; - auto out(uint16 address, uint8 data) -> void override; + inline auto in(const uint16 address) -> uint8; + inline auto out(const uint16 address, const uint8 data) -> void; //serialization.cpp auto serialize(serializer&) -> void; + + //CMOS: out (c) writes 0x00 + //NMOS: out (c) writes 0xff; + // if an interrupt fires during "ld a,i" or "ld a,r", PF is cleared + enum class MOSFET : uint { CMOS, NMOS }; + + alwaysinline auto irq() -> bool; + + constexpr auto parity(uint8_t value) const -> bool { + value ^= value >> 4; + value ^= value >> 2; + value ^= value >> 1; + return !(value & 1); + } + + //memory.cpp + alwaysinline auto yield() -> void { + // freeze Z80, allow external access until relinquished + // when using synchro, move this logic to the component's main loop + // (generally found in the inheriting component, like the Mega Drive APU) + #if !defined(SCHEDULER_SYNCHRO) + if(requested()) { + grant(true); + while(requested() && !synchronizing()) step(1); + grant(false); + } + #endif + } + + alwaysinline auto wait(uint clocks) -> void { + yield(); + step(clocks); + } + + alwaysinline auto opcode() -> uint8 { + yield(); + step(1); + return read(r.pc++); + } + + alwaysinline auto operand() -> uint8 { + return read(r.pc++); + } + + alwaysinline auto operands() -> uint16 { + uint16 data = operand() << 0; + return data | operand() << 8; + } + + alwaysinline auto push(uint16 x) -> void { + write(--r.sp, x >> 8); + write(--r.sp, x >> 0); + } + + alwaysinline auto pop() -> uint16 { + uint16 data = read(r.sp++) << 0; + return data | read(r.sp++) << 8; + } + + alwaysinline auto displace(uint16& x) -> uint16 { + if(&x != &r.ix.word && &x != &r.iy.word) return x; + auto d = operand(); + wait(5); + r.wz.word = x + (int8)d; + return r.wz.word; + } + + //instruction.cpp + alwaysinline auto instruction() -> void; + alwaysinline auto instruction(uint8 code) -> void; + alwaysinline auto instructionCB(uint8 code) -> void; + alwaysinline auto instructionCBd(uint16 addr, uint8 code) -> void; + alwaysinline auto instructionED(uint8 code) -> void; + + //algorithms.cpp + alwaysinline auto ADD(uint8, uint8, bool = false) -> uint8; + alwaysinline auto AND(uint8, uint8) -> uint8; + alwaysinline auto BIT(uint3, uint8) -> uint8; + alwaysinline auto CP (uint8, uint8) -> void; + alwaysinline auto DEC(uint8) -> uint8; + alwaysinline auto IN (uint8) -> uint8; + alwaysinline auto INC(uint8) -> uint8; + alwaysinline auto OR (uint8, uint8) -> uint8; + alwaysinline auto RES(uint3, uint8) -> uint8; + alwaysinline auto RL (uint8) -> uint8; + alwaysinline auto RLC(uint8) -> uint8; + alwaysinline auto RR (uint8) -> uint8; + alwaysinline auto RRC(uint8) -> uint8; + alwaysinline auto SET(uint3, uint8) -> uint8; + alwaysinline auto SLA(uint8) -> uint8; + alwaysinline auto SLL(uint8) -> uint8; + alwaysinline auto SRA(uint8) -> uint8; + alwaysinline auto SRL(uint8) -> uint8; + alwaysinline auto SUB(uint8, uint8, bool = false) -> uint8; + alwaysinline auto XOR(uint8, uint8) -> uint8; + + //instructions.cpp + alwaysinline auto instructionADC_a_irr(uint16&) -> void; + alwaysinline auto instructionADC_a_n() -> void; + alwaysinline auto instructionADC_a_r(uint8&) -> void; + alwaysinline auto instructionADC_hl_rr(uint16&) -> void; + alwaysinline auto instructionADD_a_irr(uint16&) -> void; + alwaysinline auto instructionADD_a_n() -> void; + alwaysinline auto instructionADD_a_r(uint8&) -> void; + alwaysinline auto instructionADD_hl_rr(uint16&) -> void; + alwaysinline auto instructionAND_a_irr(uint16&) -> void; + alwaysinline auto instructionAND_a_n() -> void; + alwaysinline auto instructionAND_a_r(uint8&) -> void; + alwaysinline auto instructionBIT_o_irr(uint3, uint16&) -> void; + alwaysinline auto instructionBIT_o_irr_r(uint3, uint16&, uint8&) -> void; + alwaysinline auto instructionBIT_o_r(uint3, uint8&) -> void; + alwaysinline auto instructionCALL_c_nn(bool c) -> void; + alwaysinline auto instructionCALL_nn() -> void; + alwaysinline auto instructionCCF() -> void; + alwaysinline auto instructionCP_a_irr(uint16& x) -> void; + alwaysinline auto instructionCP_a_n() -> void; + alwaysinline auto instructionCP_a_r(uint8& x) -> void; + alwaysinline auto instructionCPD() -> void; + alwaysinline auto instructionCPDR() -> void; + alwaysinline auto instructionCPI() -> void; + alwaysinline auto instructionCPIR() -> void; + alwaysinline auto instructionCPL() -> void; + alwaysinline auto instructionDAA() -> void; + alwaysinline auto instructionDEC_irr(uint16&) -> void; + alwaysinline auto instructionDEC_r(uint8&) -> void; + alwaysinline auto instructionDEC_rr(uint16&) -> void; + alwaysinline auto instructionDI() -> void; + alwaysinline auto instructionDJNZ_e() -> void; + alwaysinline auto instructionEI() -> void; + alwaysinline auto instructionEX_irr_rr(uint16&, uint16&) -> void; + alwaysinline auto instructionEX_rr_rr(uint16&, uint16&) -> void; + alwaysinline auto instructionEXX() -> void; + alwaysinline auto instructionHALT() -> void; + alwaysinline auto instructionIM_o(uint2) -> void; + alwaysinline auto instructionIN_a_in() -> void; + alwaysinline auto instructionIN_r_ic(uint8&) -> void; + alwaysinline auto instructionIN_ic() -> void; + alwaysinline auto instructionINC_irr(uint16&) -> void; + alwaysinline auto instructionINC_r(uint8&) -> void; + alwaysinline auto instructionINC_rr(uint16&) -> void; + alwaysinline auto instructionIND() -> void; + alwaysinline auto instructionINDR() -> void; + alwaysinline auto instructionINI() -> void; + alwaysinline auto instructionINIR() -> void; + alwaysinline auto instructionJP_c_nn(bool) -> void; + alwaysinline auto instructionJP_rr(uint16&) -> void; + alwaysinline auto instructionJR_c_e(bool) -> void; + alwaysinline auto instructionLD_a_inn() -> void; + alwaysinline auto instructionLD_a_irr(uint16& x) -> void; + alwaysinline auto instructionLD_inn_a() -> void; + alwaysinline auto instructionLD_inn_rr(uint16&) -> void; + alwaysinline auto instructionLD_irr_a(uint16&) -> void; + alwaysinline auto instructionLD_irr_n(uint16&) -> void; + alwaysinline auto instructionLD_irr_r(uint16&, uint8&) -> void; + alwaysinline auto instructionLD_r_n(uint8&) -> void; + alwaysinline auto instructionLD_r_irr(uint8&, uint16&) -> void; + alwaysinline auto instructionLD_r_r(uint8&, uint8&) -> void; + alwaysinline auto instructionLD_r_r1(uint8&, uint8&) -> void; + alwaysinline auto instructionLD_r_r2(uint8&, uint8&) -> void; + alwaysinline auto instructionLD_rr_inn(uint16&) -> void; + alwaysinline auto instructionLD_rr_nn(uint16&) -> void; + alwaysinline auto instructionLD_sp_rr(uint16&) -> void; + alwaysinline auto instructionLDD() -> void; + alwaysinline auto instructionLDDR() -> void; + alwaysinline auto instructionLDI() -> void; + alwaysinline auto instructionLDIR() -> void; + alwaysinline auto instructionNEG() -> void; + alwaysinline auto instructionNOP() -> void; + alwaysinline auto instructionOR_a_irr(uint16&) -> void; + alwaysinline auto instructionOR_a_n() -> void; + alwaysinline auto instructionOR_a_r(uint8&) -> void; + alwaysinline auto instructionOTDR() -> void; + alwaysinline auto instructionOTIR() -> void; + alwaysinline auto instructionOUT_ic_r(uint8&) -> void; + alwaysinline auto instructionOUT_ic() -> void; + alwaysinline auto instructionOUT_in_a() -> void; + alwaysinline auto instructionOUTD() -> void; + alwaysinline auto instructionOUTI() -> void; + alwaysinline auto instructionPOP_rr(uint16&) -> void; + alwaysinline auto instructionPUSH_rr(uint16&) -> void; + alwaysinline auto instructionRES_o_irr(uint3, uint16&) -> void; + alwaysinline auto instructionRES_o_irr_r(uint3, uint16&, uint8&) -> void; + alwaysinline auto instructionRES_o_r(uint3, uint8&) -> void; + alwaysinline auto instructionRET() -> void; + alwaysinline auto instructionRET_c(bool c) -> void; + alwaysinline auto instructionRETI() -> void; + alwaysinline auto instructionRETN() -> void; + alwaysinline auto instructionRL_irr(uint16&) -> void; + alwaysinline auto instructionRL_irr_r(uint16&, uint8&) -> void; + alwaysinline auto instructionRL_r(uint8&) -> void; + alwaysinline auto instructionRLA() -> void; + alwaysinline auto instructionRLC_irr(uint16&) -> void; + alwaysinline auto instructionRLC_irr_r(uint16&, uint8&) -> void; + alwaysinline auto instructionRLC_r(uint8&) -> void; + alwaysinline auto instructionRLCA() -> void; + alwaysinline auto instructionRLD() -> void; + alwaysinline auto instructionRR_irr(uint16&) -> void; + alwaysinline auto instructionRR_irr_r(uint16&, uint8&) -> void; + alwaysinline auto instructionRR_r(uint8&) -> void; + alwaysinline auto instructionRRA() -> void; + alwaysinline auto instructionRRC_irr(uint16&) -> void; + alwaysinline auto instructionRRC_irr_r(uint16&, uint8&) -> void; + alwaysinline auto instructionRRC_r(uint8&) -> void; + alwaysinline auto instructionRRCA() -> void; + alwaysinline auto instructionRRD() -> void; + alwaysinline auto instructionRST_o(uint3) -> void; + alwaysinline auto instructionSBC_a_irr(uint16&) -> void; + alwaysinline auto instructionSBC_a_n() -> void; + alwaysinline auto instructionSBC_a_r(uint8&) -> void; + alwaysinline auto instructionSBC_hl_rr(uint16&) -> void; + alwaysinline auto instructionSCF() -> void; + alwaysinline auto instructionSET_o_irr(uint3, uint16&) -> void; + alwaysinline auto instructionSET_o_irr_r(uint3, uint16&, uint8&) -> void; + alwaysinline auto instructionSET_o_r(uint3, uint8&) -> void; + alwaysinline auto instructionSLA_irr(uint16&) -> void; + alwaysinline auto instructionSLA_irr_r(uint16&, uint8&) -> void; + alwaysinline auto instructionSLA_r(uint8&) -> void; + alwaysinline auto instructionSLL_irr(uint16&) -> void; + alwaysinline auto instructionSLL_irr_r(uint16&, uint8&) -> void; + alwaysinline auto instructionSLL_r(uint8&) -> void; + alwaysinline auto instructionSRA_irr(uint16&) -> void; + alwaysinline auto instructionSRA_irr_r(uint16&, uint8&) -> void; + alwaysinline auto instructionSRA_r(uint8&) -> void; + alwaysinline auto instructionSRL_irr(uint16&) -> void; + alwaysinline auto instructionSRL_irr_r(uint16&, uint8&) -> void; + alwaysinline auto instructionSRL_r(uint8&) -> void; + alwaysinline auto instructionSUB_a_irr(uint16&) -> void; + alwaysinline auto instructionSUB_a_n() -> void; + alwaysinline auto instructionSUB_a_r(uint8&) -> void; + alwaysinline auto instructionXOR_a_irr(uint16&) -> void; + alwaysinline auto instructionXOR_a_n() -> void; + alwaysinline auto instructionXOR_a_r(uint8&) -> void; + + //disassembler.cpp +#if !defined(NO_EVENTINSTRUCTION_NOTIFY) + auto disassembleInstruction(maybe pc = {}) -> string; + auto disassembleContext() -> string; + + auto disassemble(uint16 pc, uint8 prefix, uint8 code) -> string; + auto disassembleCB(uint16 pc, uint8 prefix, uint8 code) -> string; + auto disassembleCBd(uint16 pc, uint8 prefix, int8 d, uint8 code) -> string; + auto disassembleED(uint16 pc, uint8 prefix, uint8 code) -> string; +#endif + + MOSFET mosfet = MOSFET::NMOS; + enum class Prefix : uint { hl, ix, iy } prefix = Prefix::hl; + + struct Registers { + union Pair { + Pair() : word(0) {} + uint16 word; + struct Byte { uint8 order_msb2(hi, lo); } byte; + }; + + Pair af, af_; + Pair bc, bc_; + Pair de, de_; + Pair hl, hl_; + Pair ix; + Pair iy; + Pair ir; + Pair wz; + uint16 sp; + uint16 pc; + + boolean ei; //"ei" executed last + boolean p; //"ld a,i" or "ld a,r" executed last + boolean q; //opcode that updated flag registers executed last + boolean halt; //HALT instruction executed + boolean iff1; //interrupt flip-flop 1 + boolean iff2; //interrupt flip-flop 2 + uint2 im; //interrupt mode (0-2) + } r; + private: Memory::Writable ram; @@ -55,7 +352,11 @@ struct APU : Z80, Z80::Bus, Thread { struct State { uint1 nmiLine; uint1 intLine; + uint1 interruptPending; } state; + + bool _requested; + bool _granted; }; extern APU apu; diff --git a/higan/md/apu/bus.cpp b/higan/md/apu/bus.cpp index ee4de6b..e8c1ed4 100644 --- a/higan/md/apu/bus.cpp +++ b/higan/md/apu/bus.cpp @@ -5,10 +5,13 @@ * for now, assume that only the cartridge and expansion buses are also accessible. */ -auto APU::read(uint16 address) -> uint8 { +auto APU::read(const uint16 address) -> uint8 { + yield(); + step(3); + //$2000-3fff mirrors $0000-1fff - if(address >= 0x0000 && address <= 0x3fff) return ram.read(address); - if(address >= 0x4000 && address <= 0x4003) return ym2612.readStatus(); + if(address <= 0x3fff) return ram.read(address); + if(address <= 0x4003) return ym2612.readStatus(); if(address >= 0x8000 && address <= 0xffff) { #if !defined(SCHEDULER_SYNCHRO) while(vdp.dma.active) { @@ -22,7 +25,7 @@ auto APU::read(uint16 address) -> uint8 { step(3); uint24 location = io.bank << 15 | (uint15)address & ~1; - if(location >= 0xa00000 && location <= 0xffffff) { + if(location >= 0xa00000) { //todo: apparently *some* I/O addresses can be read or written from the Z80. //it is not currently known which addresses are accepted. if(location != 0xa10000) return 0xff; //version register can be read @@ -36,18 +39,25 @@ auto APU::read(uint16 address) -> uint8 { return 0x00; } -auto APU::write(uint16 address, uint8 data) -> void { +auto APU::write(const uint16 address, const uint8 data) -> void { + yield(); + step(3); + //$2000-3fff mirrors $0000-1fff - if(address >= 0x0000 && address <= 0x3fff) return ram.write(address, data); - if(address == 0x4000) return ym2612.writeAddress(0 << 8 | data); - if(address == 0x4001) return ym2612.writeData(data); - if(address == 0x4002) return ym2612.writeAddress(1 << 8 | data); - if(address == 0x4003) return ym2612.writeData(data); - if(address == 0x6000) return (void)(io.bank = data.bit(0) << 8 | io.bank >> 1); - if(address == 0x7f11) return psg.write(data); - if(address == 0x7f13) return psg.write(data); - if(address == 0x7f15) return psg.write(data); - if(address == 0x7f17) return psg.write(data); + if(address <= 0x3fff) return ram.write(address, data); + + switch(address) { + case 0x4000: return ym2612.writeAddress(0 << 8 | data); + case 0x4001: return ym2612.writeData(data); + case 0x4002: return ym2612.writeAddress(1 << 8 | data); + case 0x4003: return ym2612.writeData(data); + case 0x6000: return (void)(io.bank = data.bit(0) << 8 | io.bank >> 1); + case 0x7f11: return psg.write(data); + case 0x7f13: return psg.write(data); + case 0x7f15: return psg.write(data); + case 0x7f17: return psg.write(data); + } + if(address >= 0x8000 && address <= 0xffff) { #if !defined(SCHEDULER_SYNCHRO) while(vdp.dma.active) { @@ -71,10 +81,14 @@ auto APU::write(uint16 address, uint8 data) -> void { } //unused on Mega Drive -auto APU::in(uint16 address) -> uint8 { +auto APU::in(const uint16 address) -> uint8 { + yield(); + step(4); return 0x00; } //unused on Mega Drive -auto APU::out(uint16 address, uint8 data) -> void { +auto APU::out(const uint16 address, const uint8 data) -> void { + yield(); + step(4); } diff --git a/higan/md/apu/disassembler.cpp b/higan/md/apu/disassembler.cpp new file mode 100644 index 0000000..a378a91 --- /dev/null +++ b/higan/md/apu/disassembler.cpp @@ -0,0 +1,1085 @@ +auto APU::disassembleInstruction(maybe _pc) -> string { + auto pc = _pc ? *_pc : r.pc; + string s, output; + + uint8 prefix = 0x00; + auto code = read(pc++); + if(code == 0xdd || code == 0xfd) { + prefix = code; + + code = ead(pc++); + if(code == 0xdd || code == 0xfd) { + if(prefix == 0xdd) { + s.append("ix:"); + goto finish; + } + if(prefix == 0xfd) { + s.append("iy:"); + goto finish; + } + } + } + + if(code == 0xcb && prefix) { + auto d = (int8)read(pc++); + code = read(pc++); + output = disassembleCBd(pc, prefix, d, code); + } else if(code == 0xcb) { + code = read(pc++); + output = disassembleCB(pc, prefix, code); + } else if(code == 0xed) { + code = read(pc++); + output = disassembleED(pc, prefix, code); + } else { + output = disassemble(pc, prefix, code); + } + +finish: + return pad(output, -18L); +} + +auto APU::disassembleContext() -> string { + string s; + + s.append( "AF:", hex(r.af.word, 4L)); + s.append(" BC:", hex(r.bc.word, 4L)); + s.append(" DE:", hex(r.de.word, 4L)); + s.append(" HL:", hex(r.hl.word, 4L)); + s.append(" IX:", hex(r.ix.word, 4L)); + s.append(" IY:", hex(r.iy.word, 4L)); + s.append(" SP:", hex(r.sp, 4L)); + s.append(" IFF:", (uint1)r.iff1, (uint1)r.iff2); + s.append(" IM:", r.im); + + return s; +} + +#define op(id, name, ...) case id: return {name, " ", vector{__VA_ARGS__}.merge(",")}; + +#define N string{"$", hex(byte(), 2L)} +#define IN string{"(", N, ")"} +#define NN string{"$", hex(word(), 4L)} +#define INN string{"(", NN, ")"} +#define REL string{"$", hex(branch(), 4L)} + +#define A "a" +#define F "f" +#define B "b" +#define C "c" +#define D "d" +#define E "e" +#define H prefix == 0xdd ? "ixh" : prefix == 0xfd ? "iyh" : "h" +#define L prefix == 0xdd ? "ixl" : prefix == 0xfd ? "iyl" : "l" +#define _H "h" +#define _L "l" +#define _HL "hl" + +#define AF "af" +#define BC "bc" +#define DE "de" +#define HL prefix == 0xdd ? "ix" : prefix == 0xfd ? "iy" : "hl" + +#define AF_ "af'" +#define BC_ "bc'" +#define DE_ "de'" +#define HL_ "hl'" + +#define SP "sp" +#define PC "pc" + +#define I "i" +#define R "r" + +#define IC "(c)" +#define IBC "(bc)" +#define IDE "(de)" +#define IHL string{"(", HL, displace(), ")"} +#define ISP "(sp)" + +auto APU::disassemble(uint16 pc, uint8 prefix, uint8 code) -> string { + auto byte = [&] { + return read(pc++); + }; + + auto word = [&] { + uint16 data = byte() << 0; + return data | byte() << 8; + }; + + auto branch = [&] { + auto d = byte(); + return pc + (int8)d; + }; + + auto displace = [&] { + if(!prefix) return string{}; + auto d = (int8)byte(); + return d >= 0 ? string{"+$", hex(d, 2L)} : string{"-$", hex(-d, 2L)}; + }; + + switch(code) { + op(0x00, "nop ") + op(0x01, "ld ", BC, NN) + op(0x02, "ld ", IBC, A) + op(0x03, "inc ", BC) + op(0x04, "inc ", B) + op(0x05, "dec ", B) + op(0x06, "ld ", B, N) + op(0x07, "rlca") + op(0x08, "ex ", AF, AF_) + op(0x09, "add ", HL, BC) + op(0x0a, "ld ", A, IBC) + op(0x0b, "dec ", BC) + op(0x0c, "inc ", C) + op(0x0d, "dec ", C) + op(0x0e, "ld ", C, N) + op(0x0f, "rrca") + op(0x10, "djnz", REL) + op(0x11, "ld ", DE, NN) + op(0x12, "ld ", IDE, A) + op(0x13, "inc ", DE) + op(0x14, "inc ", D) + op(0x15, "dec ", D) + op(0x16, "ld ", E, N) + op(0x17, "rla ") + op(0x18, "jr ", REL) + op(0x19, "add ", HL, DE) + op(0x1a, "ld ", A, IDE) + op(0x1b, "dec ", DE) + op(0x1c, "inc ", E) + op(0x1d, "dec ", E) + op(0x1e, "ld ", E, N) + op(0x1f, "rra ") + op(0x20, "jr ", "nz", REL) + op(0x21, "ld ", HL, NN) + op(0x22, "ld ", INN, HL) + op(0x23, "inc ", HL) + op(0x24, "inc ", H) + op(0x25, "dec ", H) + op(0x26, "ld ", H, N) + op(0x27, "daa ") + op(0x28, "jr ", "z", REL) + op(0x29, "add ", HL, HL) + op(0x2a, "ld ", HL, INN) + op(0x2b, "dec ", HL) + op(0x2c, "inc ", L) + op(0x2d, "dec ", L) + op(0x2e, "ld ", L, N) + op(0x2f, "cpl ") + op(0x30, "jr ", "nc", REL) + op(0x31, "ld ", SP, NN) + op(0x32, "ld ", INN, A) + op(0x33, "inc ", SP) + op(0x34, "inc ", IHL) + op(0x35, "dec ", IHL) + op(0x36, "ld ", IHL, N) + op(0x37, "scf ") + op(0x38, "jr ", "c", REL) + op(0x39, "add ", HL, SP) + op(0x3a, "ld ", A, INN) + op(0x3b, "dec ", SP) + op(0x3c, "inc ", A) + op(0x3d, "dec ", A) + op(0x3e, "ld ", A, N) + op(0x3f, "ccf ") + op(0x40, "ld ", B, B) + op(0x41, "ld ", B, C) + op(0x42, "ld ", B, D) + op(0x43, "ld ", B, E) + op(0x44, "ld ", B, H) + op(0x45, "ld ", B, L) + op(0x46, "ld ", B, IHL) + op(0x47, "ld ", B, A) + op(0x48, "ld ", C, B) + op(0x49, "ld ", C, C) + op(0x4a, "ld ", C, D) + op(0x4b, "ld ", C, E) + op(0x4c, "ld ", C, H) + op(0x4d, "ld ", C, L) + op(0x4e, "ld ", C, IHL) + op(0x4f, "ld ", C, A) + op(0x50, "ld ", D, B) + op(0x51, "ld ", D, C) + op(0x52, "ld ", D, D) + op(0x53, "ld ", D, E) + op(0x54, "ld ", D, H) + op(0x55, "ld ", D, L) + op(0x56, "ld ", D, IHL) + op(0x57, "ld ", D, A) + op(0x58, "ld ", E, B) + op(0x59, "ld ", E, C) + op(0x5a, "ld ", E, D) + op(0x5b, "ld ", E, E) + op(0x5c, "ld ", E, H) + op(0x5d, "ld ", E, L) + op(0x5e, "ld ", E, IHL) + op(0x5f, "ld ", E, A) + op(0x60, "ld ", H, B) + op(0x61, "ld ", H, C) + op(0x62, "ld ", H, D) + op(0x63, "ld ", H, E) + op(0x64, "ld ", H, H) + op(0x65, "ld ", H, L) + op(0x66, "ld ", _H, IHL) + op(0x67, "ld ", H, A) + op(0x68, "ld ", L, B) + op(0x69, "ld ", L, C) + op(0x6a, "ld ", L, D) + op(0x6b, "ld ", L, E) + op(0x6c, "ld ", L, H) + op(0x6d, "ld ", L, L) + op(0x6e, "ld ", _L, IHL) + op(0x6f, "ld ", L, A) + op(0x70, "ld ", IHL, B) + op(0x71, "ld ", IHL, C) + op(0x72, "ld ", IHL, D) + op(0x73, "ld ", IHL, E) + op(0x74, "ld ", IHL, _H) + op(0x75, "ld ", IHL, _L) + op(0x76, "halt") + op(0x77, "ld ", IHL, A) + op(0x78, "ld ", A, B) + op(0x79, "ld ", A, C) + op(0x7a, "ld ", A, D) + op(0x7b, "ld ", A, E) + op(0x7c, "ld ", A, H) + op(0x7d, "ld ", A, L) + op(0x7e, "ld ", A, IHL) + op(0x7f, "ld ", A, A) + op(0x80, "add ", A, B) + op(0x81, "add ", A, C) + op(0x82, "add ", A, D) + op(0x83, "add ", A, E) + op(0x84, "add ", A, H) + op(0x85, "add ", A, L) + op(0x86, "add ", A, IHL) + op(0x87, "add ", A, A) + op(0x88, "adc ", A, B) + op(0x89, "adc ", A, C) + op(0x8a, "adc ", A, D) + op(0x8b, "adc ", A, E) + op(0x8c, "adc ", A, H) + op(0x8d, "adc ", A, L) + op(0x8e, "adc ", A, IHL) + op(0x8f, "adc ", A, A) + op(0x90, "sub ", A, B) + op(0x91, "sub ", A, C) + op(0x92, "sub ", A, D) + op(0x93, "sub ", A, E) + op(0x94, "sub ", A, H) + op(0x95, "sub ", A, L) + op(0x96, "sub ", A, IHL) + op(0x97, "sub ", A, A) + op(0x98, "sbc ", A, B) + op(0x99, "sbc ", A, C) + op(0x9a, "sbc ", A, D) + op(0x9b, "sbc ", A, E) + op(0x9c, "sbc ", A, H) + op(0x9d, "sbc ", A, L) + op(0x9e, "sbc ", A, IHL) + op(0x9f, "sbc ", A, A) + op(0xa0, "and ", A, B) + op(0xa1, "and ", A, C) + op(0xa2, "and ", A, D) + op(0xa3, "and ", A, E) + op(0xa4, "and ", A, H) + op(0xa5, "and ", A, L) + op(0xa6, "and ", A, IHL) + op(0xa7, "and ", A, A) + op(0xa8, "xor ", A, B) + op(0xa9, "xor ", A, C) + op(0xaa, "xor ", A, D) + op(0xab, "xor ", A, E) + op(0xac, "xor ", A, H) + op(0xad, "xor ", A, L) + op(0xae, "xor ", A, HL) + op(0xaf, "xor ", A, A) + op(0xb0, "or ", A, B) + op(0xb1, "or ", A, C) + op(0xb2, "or ", A, D) + op(0xb3, "or ", A, E) + op(0xb4, "or ", A, H) + op(0xb5, "or ", A, L) + op(0xb6, "or ", A, IHL) + op(0xb7, "or ", A, A) + op(0xb8, "cp ", A, B) + op(0xb9, "cp ", A, C) + op(0xba, "cp ", A, D) + op(0xbb, "cp ", A, E) + op(0xbc, "cp ", A, H) + op(0xbd, "cp ", A, L) + op(0xbe, "cp ", A, IHL) + op(0xbf, "cp ", A, A) + op(0xc0, "ret ", "nz") + op(0xc1, "pop ", BC) + op(0xc2, "jp ", "nz", NN) + op(0xc3, "jp ", NN) + op(0xc4, "call", "nz", NN) + op(0xc5, "push", BC) + op(0xc6, "add ", A, N) + op(0xc7, "rst ", "0") + op(0xc8, "ret ", "z") + op(0xc9, "ret ") + op(0xca, "jp ", "z", NN) + op(0xcb, "cb: ") + op(0xcc, "call", "z", NN) + op(0xcd, "call", NN) + op(0xce, "adc ", A, N) + op(0xcf, "rst ", "1") + op(0xd0, "ret ", "nc") + op(0xd1, "pop ", DE) + op(0xd2, "jp ", "nc", NN) + op(0xd3, "out ", IN, A) + op(0xd4, "call", "nc", NN) + op(0xd5, "push", DE) + op(0xd6, "sub ", A, N) + op(0xd7, "rst ", "2") + op(0xd8, "ret ", "c") + op(0xd9, "exx ") + op(0xda, "jp ", "c", NN) + op(0xdb, "in ", A, IN) + op(0xdc, "call", "c", NN) + op(0xdd, "ix: ") + op(0xde, "sbc ", A, N) + op(0xdf, "rst ", "3") + op(0xe0, "ret ", "po") + op(0xe1, "pop ", HL) + op(0xe2, "jp ", "po", NN) + op(0xe3, "ex ", ISP, HL) + op(0xe4, "call", "po", NN) + op(0xe5, "push", HL) + op(0xe6, "and ", A, N) + op(0xe7, "rst ", "4") + op(0xe8, "ret ", "pe") + op(0xe9, "jp ", HL) //officially jp (hl); but as read is not indirect, use jp hl + op(0xea, "jp ", "pe", NN) + op(0xeb, "ex ", DE, _HL) + op(0xec, "call", "pe", NN) + op(0xed, "ed: ") + op(0xee, "xor ", A, N) + op(0xef, "rst ", "5") + op(0xf0, "ret ", "p") + op(0xf1, "pop ", AF) + op(0xf2, "jp ", "p", NN) + op(0xf3, "di ") + op(0xf4, "call", "p", NN) + op(0xf5, "push", AF) + op(0xf6, "or ", A, N) + op(0xf7, "rst ", "6") + op(0xf8, "ret ", "m") + op(0xf9, "ld ", SP, HL) + op(0xfa, "jp ", "m", NN) + op(0xfb, "ei ") + op(0xfc, "call", "m", NN) + op(0xfd, "iy: ") + op(0xfe, "cp ", A, N) + op(0xff, "rst ", "7") + } + + unreachable; +} + +auto APU::disassembleCB(uint16 pc, uint8 prefix, uint8 code) -> string { + auto byte = [&] { + return read(pc++); + }; + + auto word = [&] { + uint16 data = byte() << 0; + return data | byte() << 8; + }; + + auto branch = [&] { + auto d = byte(); + return pc + (int8)d; + }; + + auto displace = [&] { + if(!prefix) return string{}; + auto d = (int8)byte(); + return d >= 0 ? string{"+$", hex(d, 2L)} : string{"-$", hex(-d, 2L)}; + }; + + if(prefix) { + auto d = (int8)code; + string ds = d >= 0 ? string{"+$", hex(d, 2L)} : string{"-$", hex(-d, 2L)}; + return {"rlc (", HL, ds, ")"}; + } + + switch(code) { + op(0x00, "rlc ", B) + op(0x01, "rlc ", C) + op(0x02, "rlc ", D) + op(0x03, "rlc ", E) + op(0x04, "rlc ", H) + op(0x05, "rlc ", L) + op(0x06, "rlc ", IHL) + op(0x07, "rlc ", A) + op(0x08, "rrc ", B) + op(0x09, "rrc ", C) + op(0x0a, "rrc ", D) + op(0x0b, "rrc ", E) + op(0x0c, "rrc ", H) + op(0x0d, "rrc ", L) + op(0x0e, "rrc ", IHL) + op(0x0f, "rrc ", A) + op(0x10, "rl ", B) + op(0x11, "rl ", C) + op(0x12, "rl ", D) + op(0x13, "rl ", E) + op(0x14, "rl ", H) + op(0x15, "rl ", L) + op(0x16, "rl ", IHL) + op(0x17, "rl ", A) + op(0x18, "rr ", B) + op(0x19, "rr ", C) + op(0x1a, "rr ", D) + op(0x1b, "rr ", E) + op(0x1c, "rr ", H) + op(0x1d, "rr ", L) + op(0x1e, "rr ", IHL) + op(0x1f, "rr ", A) + op(0x20, "sla ", B) + op(0x21, "sla ", C) + op(0x22, "sla ", D) + op(0x23, "sla ", E) + op(0x24, "sla ", H) + op(0x25, "sla ", L) + op(0x26, "sla ", IHL) + op(0x27, "sla ", A) + op(0x28, "sra ", B) + op(0x29, "sra ", C) + op(0x2a, "sra ", D) + op(0x2b, "sra ", E) + op(0x2c, "sra ", H) + op(0x2d, "sra ", L) + op(0x2e, "sra ", IHL) + op(0x2f, "sra ", A) + op(0x30, "sll ", B) + op(0x31, "sll ", C) + op(0x32, "sll ", D) + op(0x33, "sll ", E) + op(0x34, "sll ", H) + op(0x35, "sll ", L) + op(0x36, "sll ", IHL) + op(0x37, "sll ", A) + op(0x38, "srl ", B) + op(0x39, "srl ", C) + op(0x3a, "srl ", D) + op(0x3b, "srl ", E) + op(0x3c, "srl ", H) + op(0x3d, "srl ", L) + op(0x3e, "srl ", IHL) + op(0x3f, "srl ", A) + op(0x40, "bit ", "0", B) + op(0x41, "bit ", "0", C) + op(0x42, "bit ", "0", D) + op(0x43, "bit ", "0", E) + op(0x44, "bit ", "0", H) + op(0x45, "bit ", "0", L) + op(0x46, "bit ", "0", IHL) + op(0x47, "bit ", "0", A) + op(0x48, "bit ", "1", B) + op(0x49, "bit ", "1", C) + op(0x4a, "bit ", "1", D) + op(0x4b, "bit ", "1", E) + op(0x4c, "bit ", "1", H) + op(0x4d, "bit ", "1", L) + op(0x4e, "bit ", "1", IHL) + op(0x4f, "bit ", "1", A) + op(0x50, "bit ", "2", B) + op(0x51, "bit ", "2", C) + op(0x52, "bit ", "2", D) + op(0x53, "bit ", "2", E) + op(0x54, "bit ", "2", H) + op(0x55, "bit ", "2", L) + op(0x56, "bit ", "2", IHL) + op(0x57, "bit ", "2", A) + op(0x58, "bit ", "3", B) + op(0x59, "bit ", "3", C) + op(0x5a, "bit ", "3", D) + op(0x5b, "bit ", "3", E) + op(0x5c, "bit ", "3", H) + op(0x5d, "bit ", "3", L) + op(0x5e, "bit ", "3", IHL) + op(0x5f, "bit ", "3", A) + op(0x60, "bit ", "4", B) + op(0x61, "bit ", "4", C) + op(0x62, "bit ", "4", D) + op(0x63, "bit ", "4", E) + op(0x64, "bit ", "4", H) + op(0x65, "bit ", "4", L) + op(0x66, "bit ", "4", IHL) + op(0x67, "bit ", "4", A) + op(0x68, "bit ", "5", B) + op(0x69, "bit ", "5", C) + op(0x6a, "bit ", "5", D) + op(0x6b, "bit ", "5", E) + op(0x6c, "bit ", "5", H) + op(0x6d, "bit ", "5", L) + op(0x6e, "bit ", "5", IHL) + op(0x6f, "bit ", "5", A) + op(0x70, "bit ", "6", B) + op(0x71, "bit ", "6", C) + op(0x72, "bit ", "6", D) + op(0x73, "bit ", "6", E) + op(0x74, "bit ", "6", H) + op(0x75, "bit ", "6", L) + op(0x76, "bit ", "6", IHL) + op(0x77, "bit ", "6", A) + op(0x78, "bit ", "7", B) + op(0x79, "bit ", "7", C) + op(0x7a, "bit ", "7", D) + op(0x7b, "bit ", "7", E) + op(0x7c, "bit ", "7", H) + op(0x7d, "bit ", "7", L) + op(0x7e, "bit ", "7", IHL) + op(0x7f, "bit ", "7", A) + op(0x80, "res ", "0", B) + op(0x81, "res ", "0", C) + op(0x82, "res ", "0", D) + op(0x83, "res ", "0", E) + op(0x84, "res ", "0", H) + op(0x85, "res ", "0", L) + op(0x86, "res ", "0", IHL) + op(0x87, "res ", "0", A) + op(0x88, "res ", "1", B) + op(0x89, "res ", "1", C) + op(0x8a, "res ", "1", D) + op(0x8b, "res ", "1", E) + op(0x8c, "res ", "1", H) + op(0x8d, "res ", "1", L) + op(0x8e, "res ", "1", IHL) + op(0x8f, "res ", "1", A) + op(0x90, "res ", "2", B) + op(0x91, "res ", "2", C) + op(0x92, "res ", "2", D) + op(0x93, "res ", "2", E) + op(0x94, "res ", "2", H) + op(0x95, "res ", "2", L) + op(0x96, "res ", "2", IHL) + op(0x97, "res ", "2", A) + op(0x98, "res ", "3", B) + op(0x99, "res ", "3", C) + op(0x9a, "res ", "3", D) + op(0x9b, "res ", "3", E) + op(0x9c, "res ", "3", H) + op(0x9d, "res ", "3", L) + op(0x9e, "res ", "3", IHL) + op(0x9f, "res ", "3", A) + op(0xa0, "res ", "4", B) + op(0xa1, "res ", "4", C) + op(0xa2, "res ", "4", D) + op(0xa3, "res ", "4", E) + op(0xa4, "res ", "4", H) + op(0xa5, "res ", "4", L) + op(0xa6, "res ", "4", IHL) + op(0xa7, "res ", "4", A) + op(0xa8, "res ", "5", B) + op(0xa9, "res ", "5", C) + op(0xaa, "res ", "5", D) + op(0xab, "res ", "5", E) + op(0xac, "res ", "5", H) + op(0xad, "res ", "5", L) + op(0xae, "res ", "5", IHL) + op(0xaf, "res ", "5", A) + op(0xb0, "res ", "6", B) + op(0xb1, "res ", "6", C) + op(0xb2, "res ", "6", D) + op(0xb3, "res ", "6", E) + op(0xb4, "res ", "6", H) + op(0xb5, "res ", "6", L) + op(0xb6, "res ", "6", IHL) + op(0xb7, "res ", "6", A) + op(0xb8, "res ", "7", B) + op(0xb9, "res ", "7", C) + op(0xba, "res ", "7", D) + op(0xbb, "res ", "7", E) + op(0xbc, "res ", "7", H) + op(0xbd, "res ", "7", L) + op(0xbe, "res ", "7", IHL) + op(0xbf, "res ", "7", A) + op(0xc0, "set ", "0", B) + op(0xc1, "set ", "0", C) + op(0xc2, "set ", "0", D) + op(0xc3, "set ", "0", E) + op(0xc4, "set ", "0", H) + op(0xc5, "set ", "0", L) + op(0xc6, "set ", "0", IHL) + op(0xc7, "set ", "0", A) + op(0xc8, "set ", "1", B) + op(0xc9, "set ", "1", C) + op(0xca, "set ", "1", D) + op(0xcb, "set ", "1", E) + op(0xcc, "set ", "1", H) + op(0xcd, "set ", "1", L) + op(0xce, "set ", "1", IHL) + op(0xcf, "set ", "1", A) + op(0xd0, "set ", "2", B) + op(0xd1, "set ", "2", C) + op(0xd2, "set ", "2", D) + op(0xd3, "set ", "2", E) + op(0xd4, "set ", "2", H) + op(0xd5, "set ", "2", L) + op(0xd6, "set ", "2", IHL) + op(0xd7, "set ", "2", A) + op(0xd8, "set ", "3", B) + op(0xd9, "set ", "3", C) + op(0xda, "set ", "3", D) + op(0xdb, "set ", "3", E) + op(0xdc, "set ", "3", H) + op(0xdd, "set ", "3", L) + op(0xde, "set ", "3", IHL) + op(0xdf, "set ", "3", A) + op(0xe0, "set ", "4", B) + op(0xe1, "set ", "4", C) + op(0xe2, "set ", "4", D) + op(0xe3, "set ", "4", E) + op(0xe4, "set ", "4", H) + op(0xe5, "set ", "4", L) + op(0xe6, "set ", "4", IHL) + op(0xe7, "set ", "4", A) + op(0xe8, "set ", "5", B) + op(0xe9, "set ", "5", C) + op(0xea, "set ", "5", D) + op(0xeb, "set ", "5", E) + op(0xec, "set ", "5", H) + op(0xed, "set ", "5", L) + op(0xee, "set ", "5", IHL) + op(0xef, "set ", "5", A) + op(0xf0, "set ", "6", B) + op(0xf1, "set ", "6", C) + op(0xf2, "set ", "6", D) + op(0xf3, "set ", "6", E) + op(0xf4, "set ", "6", H) + op(0xf5, "set ", "6", L) + op(0xf6, "set ", "6", IHL) + op(0xf7, "set ", "6", A) + op(0xf8, "set ", "7", B) + op(0xf9, "set ", "7", C) + op(0xfa, "set ", "7", D) + op(0xfb, "set ", "7", E) + op(0xfc, "set ", "7", H) + op(0xfd, "set ", "7", L) + op(0xfe, "set ", "7", IHL) + op(0xff, "set ", "7", A) + } + + unreachable; +} + +auto APU::disassembleCBd(uint16 pc, uint8 prefix, int8 d, uint8 code) -> string { + auto displace = [&] { + return d >= 0 ? string{"+$", hex(d, 2L)} : string{"-$", hex(-d, 2L)}; + }; + + switch(code) { + op(0x00, "rlc ", IHL, B) + op(0x01, "rlc ", IHL, C) + op(0x02, "rlc ", IHL, D) + op(0x03, "rlc ", IHL, E) + op(0x04, "rlc ", IHL, H) + op(0x05, "rlc ", IHL, L) + op(0x06, "rlc ", IHL) + op(0x07, "rlc ", IHL, A) + op(0x08, "rrc ", IHL, B) + op(0x09, "rrc ", IHL, C) + op(0x0a, "rrc ", IHL, D) + op(0x0b, "rrc ", IHL, E) + op(0x0c, "rrc ", IHL, H) + op(0x0d, "rrc ", IHL, L) + op(0x0e, "rrc ", IHL) + op(0x0f, "rrc ", IHL, A) + op(0x10, "rl ", IHL, B) + op(0x11, "rl ", IHL, C) + op(0x12, "rl ", IHL, D) + op(0x13, "rl ", IHL, E) + op(0x14, "rl ", IHL, H) + op(0x15, "rl ", IHL, L) + op(0x16, "rl ", IHL) + op(0x17, "rl ", IHL, A) + op(0x18, "rr ", IHL, B) + op(0x19, "rr ", IHL, C) + op(0x1a, "rr ", IHL, D) + op(0x1b, "rr ", IHL, E) + op(0x1c, "rr ", IHL, H) + op(0x1d, "rr ", IHL, L) + op(0x1e, "rr ", IHL) + op(0x1f, "rr ", IHL, A) + op(0x20, "sla ", IHL, B) + op(0x21, "sla ", IHL, C) + op(0x22, "sla ", IHL, D) + op(0x23, "sla ", IHL, E) + op(0x24, "sla ", IHL, H) + op(0x25, "sla ", IHL, L) + op(0x26, "sla ", IHL) + op(0x27, "sla ", IHL, A) + op(0x28, "sra ", IHL, B) + op(0x29, "sra ", IHL, C) + op(0x2a, "sra ", IHL, D) + op(0x2b, "sra ", IHL, E) + op(0x2c, "sra ", IHL, H) + op(0x2d, "sra ", IHL, L) + op(0x2e, "sra ", IHL) + op(0x2f, "sra ", IHL, A) + op(0x30, "sll ", IHL, B) + op(0x31, "sll ", IHL, C) + op(0x32, "sll ", IHL, D) + op(0x33, "sll ", IHL, E) + op(0x34, "sll ", IHL, H) + op(0x35, "sll ", IHL, L) + op(0x36, "sll ", IHL) + op(0x37, "sll ", IHL, A) + op(0x38, "srl ", IHL, B) + op(0x39, "srl ", IHL, C) + op(0x3a, "srl ", IHL, D) + op(0x3b, "srl ", IHL, E) + op(0x3c, "srl ", IHL, H) + op(0x3d, "srl ", IHL, L) + op(0x3e, "srl ", IHL) + op(0x3f, "srl ", IHL, A) + op(0x40, "bit ", "0", IHL, B) + op(0x41, "bit ", "0", IHL, C) + op(0x42, "bit ", "0", IHL, D) + op(0x43, "bit ", "0", IHL, E) + op(0x44, "bit ", "0", IHL, H) + op(0x45, "bit ", "0", IHL, L) + op(0x46, "bit ", "0", IHL) + op(0x47, "bit ", "0", IHL, A) + op(0x48, "bit ", "1", IHL, B) + op(0x49, "bit ", "1", IHL, C) + op(0x4a, "bit ", "1", IHL, D) + op(0x4b, "bit ", "1", IHL, E) + op(0x4c, "bit ", "1", IHL, H) + op(0x4d, "bit ", "1", IHL, L) + op(0x4e, "bit ", "1", IHL) + op(0x4f, "bit ", "1", IHL, A) + op(0x50, "bit ", "2", IHL, B) + op(0x51, "bit ", "2", IHL, C) + op(0x52, "bit ", "2", IHL, D) + op(0x53, "bit ", "2", IHL, E) + op(0x54, "bit ", "2", IHL, H) + op(0x55, "bit ", "2", IHL, L) + op(0x56, "bit ", "2", IHL) + op(0x57, "bit ", "2", IHL, A) + op(0x58, "bit ", "3", IHL, B) + op(0x59, "bit ", "3", IHL, C) + op(0x5a, "bit ", "3", IHL, D) + op(0x5b, "bit ", "3", IHL, E) + op(0x5c, "bit ", "3", IHL, H) + op(0x5d, "bit ", "3", IHL, L) + op(0x5e, "bit ", "3", IHL) + op(0x5f, "bit ", "3", IHL, A) + op(0x60, "bit ", "4", IHL, B) + op(0x61, "bit ", "4", IHL, C) + op(0x62, "bit ", "4", IHL, D) + op(0x63, "bit ", "4", IHL, E) + op(0x64, "bit ", "4", IHL, H) + op(0x65, "bit ", "4", IHL, L) + op(0x66, "bit ", "4", IHL) + op(0x67, "bit ", "4", IHL, A) + op(0x68, "bit ", "5", IHL, B) + op(0x69, "bit ", "5", IHL, C) + op(0x6a, "bit ", "5", IHL, D) + op(0x6b, "bit ", "5", IHL, E) + op(0x6c, "bit ", "5", IHL, H) + op(0x6d, "bit ", "5", IHL, L) + op(0x6e, "bit ", "5", IHL) + op(0x6f, "bit ", "5", IHL, A) + op(0x70, "bit ", "6", IHL, B) + op(0x71, "bit ", "6", IHL, C) + op(0x72, "bit ", "6", IHL, D) + op(0x73, "bit ", "6", IHL, E) + op(0x74, "bit ", "6", IHL, H) + op(0x75, "bit ", "6", IHL, L) + op(0x76, "bit ", "6", IHL) + op(0x77, "bit ", "6", IHL, A) + op(0x78, "bit ", "7", IHL, B) + op(0x79, "bit ", "7", IHL, C) + op(0x7a, "bit ", "7", IHL, D) + op(0x7b, "bit ", "7", IHL, E) + op(0x7c, "bit ", "7", IHL, H) + op(0x7d, "bit ", "7", IHL, L) + op(0x7e, "bit ", "7", IHL) + op(0x7f, "bit ", "7", IHL, A) + op(0x80, "res ", "0", IHL, B) + op(0x81, "res ", "0", IHL, C) + op(0x82, "res ", "0", IHL, D) + op(0x83, "res ", "0", IHL, E) + op(0x84, "res ", "0", IHL, H) + op(0x85, "res ", "0", IHL, L) + op(0x86, "res ", "0", IHL) + op(0x87, "res ", "0", IHL, A) + op(0x88, "res ", "1", IHL, B) + op(0x89, "res ", "1", IHL, C) + op(0x8a, "res ", "1", IHL, D) + op(0x8b, "res ", "1", IHL, E) + op(0x8c, "res ", "1", IHL, H) + op(0x8d, "res ", "1", IHL, L) + op(0x8e, "res ", "1", IHL) + op(0x8f, "res ", "1", IHL, A) + op(0x90, "res ", "2", IHL, B) + op(0x91, "res ", "2", IHL, C) + op(0x92, "res ", "2", IHL, D) + op(0x93, "res ", "2", IHL, E) + op(0x94, "res ", "2", IHL, H) + op(0x95, "res ", "2", IHL, L) + op(0x96, "res ", "2", IHL) + op(0x97, "res ", "2", IHL, A) + op(0x98, "res ", "3", IHL, B) + op(0x99, "res ", "3", IHL, C) + op(0x9a, "res ", "3", IHL, D) + op(0x9b, "res ", "3", IHL, E) + op(0x9c, "res ", "3", IHL, H) + op(0x9d, "res ", "3", IHL, L) + op(0x9e, "res ", "3", IHL) + op(0x9f, "res ", "3", IHL, A) + op(0xa0, "res ", "4", IHL, B) + op(0xa1, "res ", "4", IHL, C) + op(0xa2, "res ", "4", IHL, D) + op(0xa3, "res ", "4", IHL, E) + op(0xa4, "res ", "4", IHL, H) + op(0xa5, "res ", "4", IHL, L) + op(0xa6, "res ", "4", IHL) + op(0xa7, "res ", "4", IHL, A) + op(0xa8, "res ", "5", IHL, B) + op(0xa9, "res ", "5", IHL, C) + op(0xaa, "res ", "5", IHL, D) + op(0xab, "res ", "5", IHL, E) + op(0xac, "res ", "5", IHL, H) + op(0xad, "res ", "5", IHL, L) + op(0xae, "res ", "5", IHL) + op(0xaf, "res ", "5", IHL, A) + op(0xb0, "res ", "6", IHL, B) + op(0xb1, "res ", "6", IHL, C) + op(0xb2, "res ", "6", IHL, D) + op(0xb3, "res ", "6", IHL, E) + op(0xb4, "res ", "6", IHL, H) + op(0xb5, "res ", "6", IHL, L) + op(0xb6, "res ", "6", IHL) + op(0xb7, "res ", "6", IHL, A) + op(0xb8, "res ", "7", IHL, B) + op(0xb9, "res ", "7", IHL, C) + op(0xba, "res ", "7", IHL, D) + op(0xbb, "res ", "7", IHL, E) + op(0xbc, "res ", "7", IHL, H) + op(0xbd, "res ", "7", IHL, L) + op(0xbe, "res ", "7", IHL) + op(0xbf, "res ", "7", IHL, A) + op(0xc0, "set ", "0", IHL, B) + op(0xc1, "set ", "0", IHL, C) + op(0xc2, "set ", "0", IHL, D) + op(0xc3, "set ", "0", IHL, E) + op(0xc4, "set ", "0", IHL, H) + op(0xc5, "set ", "0", IHL, L) + op(0xc6, "set ", "0", IHL) + op(0xc7, "set ", "0", IHL, A) + op(0xc8, "set ", "1", IHL, B) + op(0xc9, "set ", "1", IHL, C) + op(0xca, "set ", "1", IHL, D) + op(0xcb, "set ", "1", IHL, E) + op(0xcc, "set ", "1", IHL, H) + op(0xcd, "set ", "1", IHL, L) + op(0xce, "set ", "1", IHL) + op(0xcf, "set ", "1", IHL, A) + op(0xd0, "set ", "2", IHL, B) + op(0xd1, "set ", "2", IHL, C) + op(0xd2, "set ", "2", IHL, D) + op(0xd3, "set ", "2", IHL, E) + op(0xd4, "set ", "2", IHL, H) + op(0xd5, "set ", "2", IHL, L) + op(0xd6, "set ", "2", IHL) + op(0xd7, "set ", "2", IHL, A) + op(0xd8, "set ", "3", IHL, B) + op(0xd9, "set ", "3", IHL, C) + op(0xda, "set ", "3", IHL, D) + op(0xdb, "set ", "3", IHL, E) + op(0xdc, "set ", "3", IHL, H) + op(0xdd, "set ", "3", IHL, L) + op(0xde, "set ", "3", IHL) + op(0xdf, "set ", "3", IHL, A) + op(0xe0, "set ", "4", IHL, B) + op(0xe1, "set ", "4", IHL, C) + op(0xe2, "set ", "4", IHL, D) + op(0xe3, "set ", "4", IHL, E) + op(0xe4, "set ", "4", IHL, H) + op(0xe5, "set ", "4", IHL, L) + op(0xe6, "set ", "4", IHL) + op(0xe7, "set ", "4", IHL, A) + op(0xe8, "set ", "5", IHL, B) + op(0xe9, "set ", "5", IHL, C) + op(0xea, "set ", "5", IHL, D) + op(0xeb, "set ", "5", IHL, E) + op(0xec, "set ", "5", IHL, H) + op(0xed, "set ", "5", IHL, L) + op(0xee, "set ", "5", IHL) + op(0xef, "set ", "5", IHL, A) + op(0xf0, "set ", "6", IHL, B) + op(0xf1, "set ", "6", IHL, C) + op(0xf2, "set ", "6", IHL, D) + op(0xf3, "set ", "6", IHL, E) + op(0xf4, "set ", "6", IHL, H) + op(0xf5, "set ", "6", IHL, L) + op(0xf6, "set ", "6", IHL) + op(0xf7, "set ", "6", IHL, A) + op(0xf8, "set ", "7", IHL, B) + op(0xf9, "set ", "7", IHL, C) + op(0xfa, "set ", "7", IHL, D) + op(0xfb, "set ", "7", IHL, E) + op(0xfc, "set ", "7", IHL, H) + op(0xfd, "set ", "7", IHL, L) + op(0xfe, "set ", "7", IHL) + op(0xff, "set ", "7", IHL, A) + } + + unreachable; +} + +auto APU::disassembleED(uint16 pc, uint8 prefix, uint8 code) -> string { + auto byte = [&] { + return read(pc++); + }; + + auto word = [&] { + uint16 data = byte() << 0; + return data | byte() << 8; + }; + + auto branch = [&] { + auto d = byte(); + return pc + (int8)d; + }; + + auto displace = [&] { + if(!prefix) return string{}; + auto d = (int8)byte(); + return d >= 0 ? string{"+$", hex(d, 2L)} : string{"-$", hex(-d, 2L)}; + }; + + switch(code) { + op(0x40, "in ", B, IC) + op(0x41, "out ", IC, B) + op(0x42, "sbc ", HL, BC) + op(0x43, "ld ", INN, BC) + op(0x44, "neg ") + op(0x45, "retn") + op(0x46, "im ", "0") + op(0x47, "ld ", I, A) + op(0x48, "in ", C, IC) + op(0x49, "out ", IC, C) + op(0x4a, "adc ", HL, BC) + op(0x4b, "ld ", BC, INN) + op(0x4c, "neg ") + op(0x4d, "reti") + op(0x4e, "im ", "0") + op(0x4f, "ld ", R, A) + op(0x50, "in ", D, IC) + op(0x51, "out ", IC, D) + op(0x52, "sbc ", HL, DE) + op(0x53, "ld ", INN, DE) + op(0x54, "neg ") + op(0x55, "retn") + op(0x56, "im ", "1") + op(0x57, "ld ", A, I) + op(0x58, "in ", E, IC) + op(0x59, "out ", IC, E) + op(0x5a, "adc ", HL, DE) + op(0x5b, "ld ", DE, INN) + op(0x5c, "neg ") + op(0x5d, "reti") + op(0x5e, "im ", "2") + op(0x5f, "ld ", A, R) + op(0x60, "in ", H, IC) + op(0x61, "out ", IC, H) + op(0x62, "sbc ", HL, HL) + op(0x63, "ld ", INN, HL) + op(0x64, "neg ") + op(0x65, "retn") + op(0x66, "im ", "0") + op(0x67, "rrd ") + op(0x68, "in ", L, IC) + op(0x69, "out ", IC, L) + op(0x6a, "adc ", HL, HL) + op(0x6b, "ld ", HL, INN) + op(0x6c, "neg ") + op(0x6d, "reti") + op(0x6e, "im ", "0") + op(0x6f, "rld ") + op(0x70, "in ", IC) + op(0x71, "out ", IC) + op(0x72, "sbc ", HL, SP) + op(0x73, "ld ", INN, SP) + op(0x74, "neg ") + op(0x75, "retn") + op(0x76, "im ", "1") + op(0x77, "nop ") + op(0x78, "in ", A, IC) + op(0x79, "out ", IC, A) + op(0x7a, "adc ", HL, SP) + op(0x7b, "ld ", SP, INN) + op(0x7c, "neg ") + op(0x7d, "reti") + op(0x7e, "im ", "2") + op(0x7f, "nop ") + op(0xa0, "ldi ") + op(0xa1, "cpi ") + op(0xa2, "ini ") + op(0xa3, "outi") + op(0xa8, "ldd ") + op(0xa9, "cpd ") + op(0xaa, "ind ") + op(0xab, "outd") + op(0xb0, "ldir") + op(0xb1, "cpir") + op(0xb2, "inir") + op(0xb3, "otir") + op(0xb8, "lddr") + op(0xb9, "cpdr") + op(0xba, "indr") + op(0xbb, "otdr") + } + + return {"nop ", "(ed ", hex(code, 2L), ")"}; +} + +#undef op + +#undef N +#undef IN +#undef NN +#undef INN +#undef REL + +#undef A +#undef F +#undef B +#undef C +#undef D +#undef E +#undef H +#undef L +#undef _H +#undef _L +#undef _HL + +#undef AF +#undef BC +#undef DE +#undef HL + +#undef AF_ +#undef BC_ +#undef DE_ +#undef HL_ + +#undef SP +#undef PC + +#undef I +#undef R + +#undef IC +#undef IBC +#undef IDE +#undef IHL +#undef ISP diff --git a/higan/md/apu/instruction.cpp b/higan/md/apu/instruction.cpp new file mode 100644 index 0000000..ef47567 --- /dev/null +++ b/higan/md/apu/instruction.cpp @@ -0,0 +1,916 @@ +auto APU::instruction() -> void { + P = 0; + + if(EI) { + EI = 0; + IFF1 = 1; + IFF2 = 1; + } + + if(HALT) { + return wait(1); + } + + uint8 code; + while(true) { + R.bit(0,6)++; + code = opcode(); + if(code == 0xdd) { prefix = Prefix::ix; continue; } + if(code == 0xfd) { prefix = Prefix::iy; continue; } + break; + } + + if(code == 0xcb && prefix != Prefix::hl) { + WZ = HL + (int8)operand(); + wait(2); // +1 fetch, +1 memory read + //R is not incremented here + instructionCBd(WZ, opcode()); + } else if(code == 0xcb) { + R.bit(0,6)++; + instructionCB(opcode()); + } else if(code == 0xed) { + R.bit(0,6)++; + instructionED(opcode()); + } else { + instruction(code); + } + + prefix = Prefix::hl; +} + +#define op(id, name, ...) case id: return instruction##name(__VA_ARGS__); + +auto APU::instruction(uint8 code) -> void { + switch(code) { + op(0x00, NOP) + op(0x01, LD_rr_nn, BC) + op(0x02, LD_irr_a, BC) + op(0x03, INC_rr, BC) + op(0x04, INC_r, B) + op(0x05, DEC_r, B) + op(0x06, LD_r_n, B) + op(0x07, RLCA) + op(0x08, EX_rr_rr, AF, AF_) + op(0x09, ADD_hl_rr, BC) + op(0x0a, LD_a_irr, BC) + op(0x0b, DEC_rr, BC) + op(0x0c, INC_r, C) + op(0x0d, DEC_r, C) + op(0x0e, LD_r_n, C) + op(0x0f, RRCA) + op(0x10, DJNZ_e) + op(0x11, LD_rr_nn, DE) + op(0x12, LD_irr_a, DE) + op(0x13, INC_rr, DE) + op(0x14, INC_r, D) + op(0x15, DEC_r, D) + op(0x16, LD_r_n, D) + op(0x17, RLA) + op(0x18, JR_c_e, 1) + op(0x19, ADD_hl_rr, DE) + op(0x1a, LD_a_irr, DE) + op(0x1b, DEC_rr, DE) + op(0x1c, INC_r, E) + op(0x1d, DEC_r, E) + op(0x1e, LD_r_n, E) + op(0x1f, RRA) + op(0x20, JR_c_e, ZF == 0) + op(0x21, LD_rr_nn, HL) + op(0x22, LD_inn_rr, HL) + op(0x23, INC_rr, HL) + op(0x24, INC_r, H) + op(0x25, DEC_r, H) + op(0x26, LD_r_n, H) + op(0x27, DAA) + op(0x28, JR_c_e, ZF == 1) + op(0x29, ADD_hl_rr, HL) + op(0x2a, LD_rr_inn, HL) + op(0x2b, DEC_rr, HL) + op(0x2c, INC_r, L) + op(0x2d, DEC_r, L) + op(0x2e, LD_r_n, L) + op(0x2f, CPL) + op(0x30, JR_c_e, CF == 0) + op(0x31, LD_rr_nn, SP) + op(0x32, LD_inn_a) + op(0x33, INC_rr, SP) + op(0x34, INC_irr, HL) + op(0x35, DEC_irr, HL) + op(0x36, LD_irr_n, HL) + op(0x37, SCF) + op(0x38, JR_c_e, CF == 1) + op(0x39, ADD_hl_rr, SP) + op(0x3a, LD_a_inn) + op(0x3b, DEC_rr, SP) + op(0x3c, INC_r, A) + op(0x3d, DEC_r, A) + op(0x3e, LD_r_n, A) + op(0x3f, CCF) + op(0x40, LD_r_r, B, B) + op(0x41, LD_r_r, B, C) + op(0x42, LD_r_r, B, D) + op(0x43, LD_r_r, B, E) + op(0x44, LD_r_r, B, H) + op(0x45, LD_r_r, B, L) + op(0x46, LD_r_irr, B, HL) + op(0x47, LD_r_r, B, A) + op(0x48, LD_r_r, C, B) + op(0x49, LD_r_r, C, C) + op(0x4a, LD_r_r, C, D) + op(0x4b, LD_r_r, C, E) + op(0x4c, LD_r_r, C, H) + op(0x4d, LD_r_r, C, L) + op(0x4e, LD_r_irr, C, HL) + op(0x4f, LD_r_r, C, A) + op(0x50, LD_r_r, D, B) + op(0x51, LD_r_r, D, C) + op(0x52, LD_r_r, D, D) + op(0x53, LD_r_r, D, E) + op(0x54, LD_r_r, D, H) + op(0x55, LD_r_r, D, L) + op(0x56, LD_r_irr, D, HL) + op(0x57, LD_r_r, D, A) + op(0x58, LD_r_r, E, B) + op(0x59, LD_r_r, E, C) + op(0x5a, LD_r_r, E, D) + op(0x5b, LD_r_r, E, E) + op(0x5c, LD_r_r, E, H) + op(0x5d, LD_r_r, E, L) + op(0x5e, LD_r_irr, E, HL) + op(0x5f, LD_r_r, E, A) + op(0x60, LD_r_r, H, B) + op(0x61, LD_r_r, H, C) + op(0x62, LD_r_r, H, D) + op(0x63, LD_r_r, H, E) + op(0x64, LD_r_r, H, H) + op(0x65, LD_r_r, H, L) + op(0x66, LD_r_irr, _H, HL) + op(0x67, LD_r_r, H, A) + op(0x68, LD_r_r, L, B) + op(0x69, LD_r_r, L, C) + op(0x6a, LD_r_r, L, D) + op(0x6b, LD_r_r, L, E) + op(0x6c, LD_r_r, L, H) + op(0x6d, LD_r_r, L, L) + op(0x6e, LD_r_irr, _L, HL) + op(0x6f, LD_r_r, L, A) + op(0x70, LD_irr_r, HL, B) + op(0x71, LD_irr_r, HL, C) + op(0x72, LD_irr_r, HL, D) + op(0x73, LD_irr_r, HL, E) + op(0x74, LD_irr_r, HL, _H) + op(0x75, LD_irr_r, HL, _L) + op(0x76, HALT) + op(0x77, LD_irr_r, HL, A) + op(0x78, LD_r_r, A, B) + op(0x79, LD_r_r, A, C) + op(0x7a, LD_r_r, A, D) + op(0x7b, LD_r_r, A, E) + op(0x7c, LD_r_r, A, H) + op(0x7d, LD_r_r, A, L) + op(0x7e, LD_r_irr, A, HL) + op(0x7f, LD_r_r, A, A) + op(0x80, ADD_a_r, B) + op(0x81, ADD_a_r, C) + op(0x82, ADD_a_r, D) + op(0x83, ADD_a_r, E) + op(0x84, ADD_a_r, H) + op(0x85, ADD_a_r, L) + op(0x86, ADD_a_irr, HL) + op(0x87, ADD_a_r, A) + op(0x88, ADC_a_r, B) + op(0x89, ADC_a_r, C) + op(0x8a, ADC_a_r, D) + op(0x8b, ADC_a_r, E) + op(0x8c, ADC_a_r, H) + op(0x8d, ADC_a_r, L) + op(0x8e, ADC_a_irr, HL) + op(0x8f, ADC_a_r, A) + op(0x90, SUB_a_r, B) + op(0x91, SUB_a_r, C) + op(0x92, SUB_a_r, D) + op(0x93, SUB_a_r, E) + op(0x94, SUB_a_r, H) + op(0x95, SUB_a_r, L) + op(0x96, SUB_a_irr, HL) + op(0x97, SUB_a_r, A) + op(0x98, SBC_a_r, B) + op(0x99, SBC_a_r, C) + op(0x9a, SBC_a_r, D) + op(0x9b, SBC_a_r, E) + op(0x9c, SBC_a_r, H) + op(0x9d, SBC_a_r, L) + op(0x9e, SBC_a_irr, HL) + op(0x9f, SBC_a_r, A) + op(0xa0, AND_a_r, B) + op(0xa1, AND_a_r, C) + op(0xa2, AND_a_r, D) + op(0xa3, AND_a_r, E) + op(0xa4, AND_a_r, H) + op(0xa5, AND_a_r, L) + op(0xa6, AND_a_irr, HL) + op(0xa7, AND_a_r, A) + op(0xa8, XOR_a_r, B) + op(0xa9, XOR_a_r, C) + op(0xaa, XOR_a_r, D) + op(0xab, XOR_a_r, E) + op(0xac, XOR_a_r, H) + op(0xad, XOR_a_r, L) + op(0xae, XOR_a_irr, HL) + op(0xaf, XOR_a_r, A) + op(0xb0, OR_a_r, B) + op(0xb1, OR_a_r, C) + op(0xb2, OR_a_r, D) + op(0xb3, OR_a_r, E) + op(0xb4, OR_a_r, H) + op(0xb5, OR_a_r, L) + op(0xb6, OR_a_irr, HL) + op(0xb7, OR_a_r, A) + op(0xb8, CP_a_r, B) + op(0xb9, CP_a_r, C) + op(0xba, CP_a_r, D) + op(0xbb, CP_a_r, E) + op(0xbc, CP_a_r, H) + op(0xbd, CP_a_r, L) + op(0xbe, CP_a_irr, HL) + op(0xbf, CP_a_r, A) + op(0xc0, RET_c, ZF == 0) + op(0xc1, POP_rr, BC) + op(0xc2, JP_c_nn, ZF == 0) + op(0xc3, JP_c_nn, 1) + op(0xc4, CALL_c_nn, ZF == 0) + op(0xc5, PUSH_rr, BC) + op(0xc6, ADD_a_n) + op(0xc7, RST_o, 0) + op(0xc8, RET_c, ZF == 1) + op(0xc9, RET) + op(0xca, JP_c_nn, ZF == 1) +//op(0xcb, cb:) + op(0xcc, CALL_c_nn, ZF == 1) + op(0xcd, CALL_nn) + op(0xce, ADC_a_n) + op(0xcf, RST_o, 1) + op(0xd0, RET_c, CF == 0) + op(0xd1, POP_rr, DE) + op(0xd2, JP_c_nn, CF == 0) + op(0xd3, OUT_in_a) + op(0xd4, CALL_c_nn, CF == 0) + op(0xd5, PUSH_rr, DE) + op(0xd6, SUB_a_n) + op(0xd7, RST_o, 2) + op(0xd8, RET_c, CF == 1) + op(0xd9, EXX) + op(0xda, JP_c_nn, CF == 1) + op(0xdb, IN_a_in) + op(0xdc, CALL_c_nn, CF == 1) +//op(0xdd, ix:) + op(0xde, SBC_a_n) + op(0xdf, RST_o, 3) + op(0xe0, RET_c, PF == 0) + op(0xe1, POP_rr, HL) + op(0xe2, JP_c_nn, PF == 0) + op(0xe3, EX_irr_rr, SP, HL) + op(0xe4, CALL_c_nn, PF == 0) + op(0xe5, PUSH_rr, HL) + op(0xe6, AND_a_n) + op(0xe7, RST_o, 4) + op(0xe8, RET_c, PF == 1) + op(0xe9, JP_rr, HL) + op(0xea, JP_c_nn, PF == 1) + op(0xeb, EX_rr_rr, DE, _HL) + op(0xec, CALL_c_nn, PF == 1) +//op(0xed, ed:) + op(0xee, XOR_a_n) + op(0xef, RST_o, 5) + op(0xf0, RET_c, SF == 0) + op(0xf1, POP_rr, AF) + op(0xf2, JP_c_nn, SF == 0) + op(0xf3, DI) + op(0xf4, CALL_c_nn, SF == 0) + op(0xf5, PUSH_rr, AF) + op(0xf6, OR_a_n) + op(0xf7, RST_o, 6) + op(0xf8, RET_c, SF == 1) + op(0xf9, LD_sp_rr, HL) + op(0xfa, JP_c_nn, SF == 1) + op(0xfb, EI) + op(0xfc, CALL_c_nn, SF == 1) +//op(0xfd, iy:) + op(0xfe, CP_a_n) + op(0xff, RST_o, 7) + } +} + +auto APU::instructionCB(uint8 code) -> void { + switch(code) { + op(0x00, RLC_r, B) + op(0x01, RLC_r, C) + op(0x02, RLC_r, D) + op(0x03, RLC_r, E) + op(0x04, RLC_r, H) + op(0x05, RLC_r, L) + op(0x06, RLC_irr, _HL) + op(0x07, RLC_r, A) + op(0x08, RRC_r, B) + op(0x09, RRC_r, C) + op(0x0a, RRC_r, D) + op(0x0b, RRC_r, E) + op(0x0c, RRC_r, H) + op(0x0d, RRC_r, L) + op(0x0e, RRC_irr, _HL) + op(0x0f, RRC_r, A) + op(0x10, RL_r, B) + op(0x11, RL_r, C) + op(0x12, RL_r, D) + op(0x13, RL_r, E) + op(0x14, RL_r, H) + op(0x15, RL_r, L) + op(0x16, RL_irr, _HL) + op(0x17, RL_r, A) + op(0x18, RR_r, B) + op(0x19, RR_r, C) + op(0x1a, RR_r, D) + op(0x1b, RR_r, E) + op(0x1c, RR_r, H) + op(0x1d, RR_r, L) + op(0x1e, RR_irr, _HL) + op(0x1f, RR_r, A) + op(0x20, SLA_r, B) + op(0x21, SLA_r, C) + op(0x22, SLA_r, D) + op(0x23, SLA_r, E) + op(0x24, SLA_r, H) + op(0x25, SLA_r, L) + op(0x26, SLA_irr, _HL) + op(0x27, SLA_r, A) + op(0x28, SRA_r, B) + op(0x29, SRA_r, C) + op(0x2a, SRA_r, D) + op(0x2b, SRA_r, E) + op(0x2c, SRA_r, H) + op(0x2d, SRA_r, L) + op(0x2e, SRA_irr, _HL) + op(0x2f, SRA_r, A) + op(0x30, SLL_r, B) + op(0x31, SLL_r, C) + op(0x32, SLL_r, D) + op(0x33, SLL_r, E) + op(0x34, SLL_r, H) + op(0x35, SLL_r, L) + op(0x36, SLL_irr, _HL) + op(0x37, SLL_r, A) + op(0x38, SRL_r, B) + op(0x39, SRL_r, C) + op(0x3a, SRL_r, D) + op(0x3b, SRL_r, E) + op(0x3c, SRL_r, H) + op(0x3d, SRL_r, L) + op(0x3e, SRL_irr, _HL) + op(0x3f, SRL_r, A) + op(0x40, BIT_o_r, 0, B) + op(0x41, BIT_o_r, 0, C) + op(0x42, BIT_o_r, 0, D) + op(0x43, BIT_o_r, 0, E) + op(0x44, BIT_o_r, 0, H) + op(0x45, BIT_o_r, 0, L) + op(0x46, BIT_o_irr, 0, _HL) + op(0x47, BIT_o_r, 0, A) + op(0x48, BIT_o_r, 1, B) + op(0x49, BIT_o_r, 1, C) + op(0x4a, BIT_o_r, 1, D) + op(0x4b, BIT_o_r, 1, E) + op(0x4c, BIT_o_r, 1, H) + op(0x4d, BIT_o_r, 1, L) + op(0x4e, BIT_o_irr, 1, _HL) + op(0x4f, BIT_o_r, 1, A) + op(0x50, BIT_o_r, 2, B) + op(0x51, BIT_o_r, 2, C) + op(0x52, BIT_o_r, 2, D) + op(0x53, BIT_o_r, 2, E) + op(0x54, BIT_o_r, 2, H) + op(0x55, BIT_o_r, 2, L) + op(0x56, BIT_o_irr, 2, _HL) + op(0x57, BIT_o_r, 2, A) + op(0x58, BIT_o_r, 3, B) + op(0x59, BIT_o_r, 3, C) + op(0x5a, BIT_o_r, 3, D) + op(0x5b, BIT_o_r, 3, E) + op(0x5c, BIT_o_r, 3, H) + op(0x5d, BIT_o_r, 3, L) + op(0x5e, BIT_o_irr, 3, _HL) + op(0x5f, BIT_o_r, 3, A) + op(0x60, BIT_o_r, 4, B) + op(0x61, BIT_o_r, 4, C) + op(0x62, BIT_o_r, 4, D) + op(0x63, BIT_o_r, 4, E) + op(0x64, BIT_o_r, 4, H) + op(0x65, BIT_o_r, 4, L) + op(0x66, BIT_o_irr, 4, _HL) + op(0x67, BIT_o_r, 4, A) + op(0x68, BIT_o_r, 5, B) + op(0x69, BIT_o_r, 5, C) + op(0x6a, BIT_o_r, 5, D) + op(0x6b, BIT_o_r, 5, E) + op(0x6c, BIT_o_r, 5, H) + op(0x6d, BIT_o_r, 5, L) + op(0x6e, BIT_o_irr, 5, _HL) + op(0x6f, BIT_o_r, 5, A) + op(0x70, BIT_o_r, 6, B) + op(0x71, BIT_o_r, 6, C) + op(0x72, BIT_o_r, 6, D) + op(0x73, BIT_o_r, 6, E) + op(0x74, BIT_o_r, 6, H) + op(0x75, BIT_o_r, 6, L) + op(0x76, BIT_o_irr, 6, _HL) + op(0x77, BIT_o_r, 6, A) + op(0x78, BIT_o_r, 7, B) + op(0x79, BIT_o_r, 7, C) + op(0x7a, BIT_o_r, 7, D) + op(0x7b, BIT_o_r, 7, E) + op(0x7c, BIT_o_r, 7, H) + op(0x7d, BIT_o_r, 7, L) + op(0x7e, BIT_o_irr, 7, _HL) + op(0x7f, BIT_o_r, 7, A) + op(0x80, RES_o_r, 0, B) + op(0x81, RES_o_r, 0, C) + op(0x82, RES_o_r, 0, D) + op(0x83, RES_o_r, 0, E) + op(0x84, RES_o_r, 0, H) + op(0x85, RES_o_r, 0, L) + op(0x86, RES_o_irr, 0, _HL) + op(0x87, RES_o_r, 0, A) + op(0x88, RES_o_r, 1, B) + op(0x89, RES_o_r, 1, C) + op(0x8a, RES_o_r, 1, D) + op(0x8b, RES_o_r, 1, E) + op(0x8c, RES_o_r, 1, H) + op(0x8d, RES_o_r, 1, L) + op(0x8e, RES_o_irr, 1, _HL) + op(0x8f, RES_o_r, 1, A) + op(0x90, RES_o_r, 2, B) + op(0x91, RES_o_r, 2, C) + op(0x92, RES_o_r, 2, D) + op(0x93, RES_o_r, 2, E) + op(0x94, RES_o_r, 2, H) + op(0x95, RES_o_r, 2, L) + op(0x96, RES_o_irr, 2, _HL) + op(0x97, RES_o_r, 2, A) + op(0x98, RES_o_r, 3, B) + op(0x99, RES_o_r, 3, C) + op(0x9a, RES_o_r, 3, D) + op(0x9b, RES_o_r, 3, E) + op(0x9c, RES_o_r, 3, H) + op(0x9d, RES_o_r, 3, L) + op(0x9e, RES_o_irr, 3, _HL) + op(0x9f, RES_o_r, 3, A) + op(0xa0, RES_o_r, 4, B) + op(0xa1, RES_o_r, 4, C) + op(0xa2, RES_o_r, 4, D) + op(0xa3, RES_o_r, 4, E) + op(0xa4, RES_o_r, 4, H) + op(0xa5, RES_o_r, 4, L) + op(0xa6, RES_o_irr, 4, _HL) + op(0xa7, RES_o_r, 4, A) + op(0xa8, RES_o_r, 5, B) + op(0xa9, RES_o_r, 5, C) + op(0xaa, RES_o_r, 5, D) + op(0xab, RES_o_r, 5, E) + op(0xac, RES_o_r, 5, H) + op(0xad, RES_o_r, 5, L) + op(0xae, RES_o_irr, 5, _HL) + op(0xaf, RES_o_r, 5, A) + op(0xb0, RES_o_r, 6, B) + op(0xb1, RES_o_r, 6, C) + op(0xb2, RES_o_r, 6, D) + op(0xb3, RES_o_r, 6, E) + op(0xb4, RES_o_r, 6, H) + op(0xb5, RES_o_r, 6, L) + op(0xb6, RES_o_irr, 6, _HL) + op(0xb7, RES_o_r, 6, A) + op(0xb8, RES_o_r, 7, B) + op(0xb9, RES_o_r, 7, C) + op(0xba, RES_o_r, 7, D) + op(0xbb, RES_o_r, 7, E) + op(0xbc, RES_o_r, 7, H) + op(0xbd, RES_o_r, 7, L) + op(0xbe, RES_o_irr, 7, _HL) + op(0xbf, RES_o_r, 7, A) + op(0xc0, SET_o_r, 0, B) + op(0xc1, SET_o_r, 0, C) + op(0xc2, SET_o_r, 0, D) + op(0xc3, SET_o_r, 0, E) + op(0xc4, SET_o_r, 0, H) + op(0xc5, SET_o_r, 0, L) + op(0xc6, SET_o_irr, 0, _HL) + op(0xc7, SET_o_r, 0, A) + op(0xc8, SET_o_r, 1, B) + op(0xc9, SET_o_r, 1, C) + op(0xca, SET_o_r, 1, D) + op(0xcb, SET_o_r, 1, E) + op(0xcc, SET_o_r, 1, H) + op(0xcd, SET_o_r, 1, L) + op(0xce, SET_o_irr, 1, _HL) + op(0xcf, SET_o_r, 1, A) + op(0xd0, SET_o_r, 2, B) + op(0xd1, SET_o_r, 2, C) + op(0xd2, SET_o_r, 2, D) + op(0xd3, SET_o_r, 2, E) + op(0xd4, SET_o_r, 2, H) + op(0xd5, SET_o_r, 2, L) + op(0xd6, SET_o_irr, 2, _HL) + op(0xd7, SET_o_r, 2, A) + op(0xd8, SET_o_r, 3, B) + op(0xd9, SET_o_r, 3, C) + op(0xda, SET_o_r, 3, D) + op(0xdb, SET_o_r, 3, E) + op(0xdc, SET_o_r, 3, H) + op(0xdd, SET_o_r, 3, L) + op(0xde, SET_o_irr, 3, _HL) + op(0xdf, SET_o_r, 3, A) + op(0xe0, SET_o_r, 4, B) + op(0xe1, SET_o_r, 4, C) + op(0xe2, SET_o_r, 4, D) + op(0xe3, SET_o_r, 4, E) + op(0xe4, SET_o_r, 4, H) + op(0xe5, SET_o_r, 4, L) + op(0xe6, SET_o_irr, 4, _HL) + op(0xe7, SET_o_r, 4, A) + op(0xe8, SET_o_r, 5, B) + op(0xe9, SET_o_r, 5, C) + op(0xea, SET_o_r, 5, D) + op(0xeb, SET_o_r, 5, E) + op(0xec, SET_o_r, 5, H) + op(0xed, SET_o_r, 5, L) + op(0xee, SET_o_irr, 5, _HL) + op(0xef, SET_o_r, 5, A) + op(0xf0, SET_o_r, 6, B) + op(0xf1, SET_o_r, 6, C) + op(0xf2, SET_o_r, 6, D) + op(0xf3, SET_o_r, 6, E) + op(0xf4, SET_o_r, 6, H) + op(0xf5, SET_o_r, 6, L) + op(0xf6, SET_o_irr, 6, _HL) + op(0xf7, SET_o_r, 6, A) + op(0xf8, SET_o_r, 7, B) + op(0xf9, SET_o_r, 7, C) + op(0xfa, SET_o_r, 7, D) + op(0xfb, SET_o_r, 7, E) + op(0xfc, SET_o_r, 7, H) + op(0xfd, SET_o_r, 7, L) + op(0xfe, SET_o_irr, 7, _HL) + op(0xff, SET_o_r, 7, A) + } +} + +auto APU::instructionCBd(uint16 addr, uint8 code) -> void { + uint8 _; + + switch(code) { + op(0x00, RLC_irr_r, addr, B) + op(0x01, RLC_irr_r, addr, C) + op(0x02, RLC_irr_r, addr, D) + op(0x03, RLC_irr_r, addr, E) + op(0x04, RLC_irr_r, addr, H) + op(0x05, RLC_irr_r, addr, L) + op(0x06, RLC_irr_r, addr, _) + op(0x07, RLC_irr_r, addr, A) + op(0x08, RRC_irr_r, addr, B) + op(0x09, RRC_irr_r, addr, C) + op(0x0a, RRC_irr_r, addr, D) + op(0x0b, RRC_irr_r, addr, E) + op(0x0c, RRC_irr_r, addr, H) + op(0x0d, RRC_irr_r, addr, L) + op(0x0e, RRC_irr_r, addr, _) + op(0x0f, RRC_irr_r, addr, A) + op(0x10, RL_irr_r, addr, B) + op(0x11, RL_irr_r, addr, C) + op(0x12, RL_irr_r, addr, D) + op(0x13, RL_irr_r, addr, E) + op(0x14, RL_irr_r, addr, H) + op(0x15, RL_irr_r, addr, L) + op(0x16, RL_irr_r, addr, _) + op(0x17, RL_irr_r, addr, A) + op(0x18, RR_irr_r, addr, B) + op(0x19, RR_irr_r, addr, C) + op(0x1a, RR_irr_r, addr, D) + op(0x1b, RR_irr_r, addr, E) + op(0x1c, RR_irr_r, addr, H) + op(0x1d, RR_irr_r, addr, L) + op(0x1e, RR_irr_r, addr, _) + op(0x1f, RR_irr_r, addr, A) + op(0x20, SLA_irr_r, addr, B) + op(0x21, SLA_irr_r, addr, C) + op(0x22, SLA_irr_r, addr, D) + op(0x23, SLA_irr_r, addr, E) + op(0x24, SLA_irr_r, addr, H) + op(0x25, SLA_irr_r, addr, L) + op(0x26, SLA_irr_r, addr, _) + op(0x27, SLA_irr_r, addr, A) + op(0x28, SRA_irr_r, addr, B) + op(0x29, SRA_irr_r, addr, C) + op(0x2a, SRA_irr_r, addr, D) + op(0x2b, SRA_irr_r, addr, E) + op(0x2c, SRA_irr_r, addr, H) + op(0x2d, SRA_irr_r, addr, L) + op(0x2e, SRA_irr_r, addr, _) + op(0x2f, SRA_irr_r, addr, A) + op(0x30, SLL_irr_r, addr, B) + op(0x31, SLL_irr_r, addr, C) + op(0x32, SLL_irr_r, addr, D) + op(0x33, SLL_irr_r, addr, E) + op(0x34, SLL_irr_r, addr, H) + op(0x35, SLL_irr_r, addr, L) + op(0x36, SLL_irr_r, addr, _) + op(0x37, SLL_irr_r, addr, A) + op(0x38, SRL_irr_r, addr, B) + op(0x39, SRL_irr_r, addr, C) + op(0x3a, SRL_irr_r, addr, D) + op(0x3b, SRL_irr_r, addr, E) + op(0x3c, SRL_irr_r, addr, H) + op(0x3d, SRL_irr_r, addr, L) + op(0x3e, SRL_irr_r, addr, _) + op(0x3f, SRL_irr_r, addr, A) + op(0x40, BIT_o_irr_r, 0, addr, B) + op(0x41, BIT_o_irr_r, 0, addr, C) + op(0x42, BIT_o_irr_r, 0, addr, D) + op(0x43, BIT_o_irr_r, 0, addr, E) + op(0x44, BIT_o_irr_r, 0, addr, H) + op(0x45, BIT_o_irr_r, 0, addr, L) + op(0x46, BIT_o_irr_r, 0, addr, _) + op(0x47, BIT_o_irr_r, 0, addr, A) + op(0x48, BIT_o_irr_r, 1, addr, B) + op(0x49, BIT_o_irr_r, 1, addr, C) + op(0x4a, BIT_o_irr_r, 1, addr, D) + op(0x4b, BIT_o_irr_r, 1, addr, E) + op(0x4c, BIT_o_irr_r, 1, addr, H) + op(0x4d, BIT_o_irr_r, 1, addr, L) + op(0x4e, BIT_o_irr_r, 1, addr, _) + op(0x4f, BIT_o_irr_r, 1, addr, A) + op(0x50, BIT_o_irr_r, 2, addr, B) + op(0x51, BIT_o_irr_r, 2, addr, C) + op(0x52, BIT_o_irr_r, 2, addr, D) + op(0x53, BIT_o_irr_r, 2, addr, E) + op(0x54, BIT_o_irr_r, 2, addr, H) + op(0x55, BIT_o_irr_r, 2, addr, L) + op(0x56, BIT_o_irr_r, 2, addr, _) + op(0x57, BIT_o_irr_r, 2, addr, A) + op(0x58, BIT_o_irr_r, 3, addr, B) + op(0x59, BIT_o_irr_r, 3, addr, C) + op(0x5a, BIT_o_irr_r, 3, addr, D) + op(0x5b, BIT_o_irr_r, 3, addr, E) + op(0x5c, BIT_o_irr_r, 3, addr, H) + op(0x5d, BIT_o_irr_r, 3, addr, L) + op(0x5e, BIT_o_irr_r, 3, addr, _) + op(0x5f, BIT_o_irr_r, 3, addr, A) + op(0x60, BIT_o_irr_r, 4, addr, B) + op(0x61, BIT_o_irr_r, 4, addr, C) + op(0x62, BIT_o_irr_r, 4, addr, D) + op(0x63, BIT_o_irr_r, 4, addr, E) + op(0x64, BIT_o_irr_r, 4, addr, H) + op(0x65, BIT_o_irr_r, 4, addr, L) + op(0x66, BIT_o_irr_r, 4, addr, _) + op(0x67, BIT_o_irr_r, 4, addr, A) + op(0x68, BIT_o_irr_r, 5, addr, B) + op(0x69, BIT_o_irr_r, 5, addr, C) + op(0x6a, BIT_o_irr_r, 5, addr, D) + op(0x6b, BIT_o_irr_r, 5, addr, E) + op(0x6c, BIT_o_irr_r, 5, addr, H) + op(0x6d, BIT_o_irr_r, 5, addr, L) + op(0x6e, BIT_o_irr_r, 5, addr, _) + op(0x6f, BIT_o_irr_r, 5, addr, A) + op(0x70, BIT_o_irr_r, 6, addr, B) + op(0x71, BIT_o_irr_r, 6, addr, C) + op(0x72, BIT_o_irr_r, 6, addr, D) + op(0x73, BIT_o_irr_r, 6, addr, E) + op(0x74, BIT_o_irr_r, 6, addr, H) + op(0x75, BIT_o_irr_r, 6, addr, L) + op(0x76, BIT_o_irr_r, 6, addr, _) + op(0x77, BIT_o_irr_r, 6, addr, A) + op(0x78, BIT_o_irr_r, 7, addr, B) + op(0x79, BIT_o_irr_r, 7, addr, C) + op(0x7a, BIT_o_irr_r, 7, addr, D) + op(0x7b, BIT_o_irr_r, 7, addr, E) + op(0x7c, BIT_o_irr_r, 7, addr, H) + op(0x7d, BIT_o_irr_r, 7, addr, L) + op(0x7e, BIT_o_irr_r, 7, addr, _) + op(0x7f, BIT_o_irr_r, 7, addr, A) + op(0x80, RES_o_irr_r, 0, addr, B) + op(0x81, RES_o_irr_r, 0, addr, C) + op(0x82, RES_o_irr_r, 0, addr, D) + op(0x83, RES_o_irr_r, 0, addr, E) + op(0x84, RES_o_irr_r, 0, addr, H) + op(0x85, RES_o_irr_r, 0, addr, L) + op(0x86, RES_o_irr_r, 0, addr, _) + op(0x87, RES_o_irr_r, 0, addr, A) + op(0x88, RES_o_irr_r, 1, addr, B) + op(0x89, RES_o_irr_r, 1, addr, C) + op(0x8a, RES_o_irr_r, 1, addr, D) + op(0x8b, RES_o_irr_r, 1, addr, E) + op(0x8c, RES_o_irr_r, 1, addr, H) + op(0x8d, RES_o_irr_r, 1, addr, L) + op(0x8e, RES_o_irr_r, 1, addr, _) + op(0x8f, RES_o_irr_r, 1, addr, A) + op(0x90, RES_o_irr_r, 2, addr, B) + op(0x91, RES_o_irr_r, 2, addr, C) + op(0x92, RES_o_irr_r, 2, addr, D) + op(0x93, RES_o_irr_r, 2, addr, E) + op(0x94, RES_o_irr_r, 2, addr, H) + op(0x95, RES_o_irr_r, 2, addr, L) + op(0x96, RES_o_irr_r, 2, addr, _) + op(0x97, RES_o_irr_r, 2, addr, A) + op(0x98, RES_o_irr_r, 3, addr, B) + op(0x99, RES_o_irr_r, 3, addr, C) + op(0x9a, RES_o_irr_r, 3, addr, D) + op(0x9b, RES_o_irr_r, 3, addr, E) + op(0x9c, RES_o_irr_r, 3, addr, H) + op(0x9d, RES_o_irr_r, 3, addr, L) + op(0x9e, RES_o_irr_r, 3, addr, _) + op(0x9f, RES_o_irr_r, 3, addr, A) + op(0xa0, RES_o_irr_r, 4, addr, B) + op(0xa1, RES_o_irr_r, 4, addr, C) + op(0xa2, RES_o_irr_r, 4, addr, D) + op(0xa3, RES_o_irr_r, 4, addr, E) + op(0xa4, RES_o_irr_r, 4, addr, H) + op(0xa5, RES_o_irr_r, 4, addr, L) + op(0xa6, RES_o_irr_r, 4, addr, _) + op(0xa7, RES_o_irr_r, 4, addr, A) + op(0xa8, RES_o_irr_r, 5, addr, B) + op(0xa9, RES_o_irr_r, 5, addr, C) + op(0xaa, RES_o_irr_r, 5, addr, D) + op(0xab, RES_o_irr_r, 5, addr, E) + op(0xac, RES_o_irr_r, 5, addr, H) + op(0xad, RES_o_irr_r, 5, addr, L) + op(0xae, RES_o_irr_r, 5, addr, _) + op(0xaf, RES_o_irr_r, 5, addr, A) + op(0xb0, RES_o_irr_r, 6, addr, B) + op(0xb1, RES_o_irr_r, 6, addr, C) + op(0xb2, RES_o_irr_r, 6, addr, D) + op(0xb3, RES_o_irr_r, 6, addr, E) + op(0xb4, RES_o_irr_r, 6, addr, H) + op(0xb5, RES_o_irr_r, 6, addr, L) + op(0xb6, RES_o_irr_r, 6, addr, _) + op(0xb7, RES_o_irr_r, 6, addr, A) + op(0xb8, RES_o_irr_r, 7, addr, B) + op(0xb9, RES_o_irr_r, 7, addr, C) + op(0xba, RES_o_irr_r, 7, addr, D) + op(0xbb, RES_o_irr_r, 7, addr, E) + op(0xbc, RES_o_irr_r, 7, addr, H) + op(0xbd, RES_o_irr_r, 7, addr, L) + op(0xbe, RES_o_irr_r, 7, addr, _) + op(0xbf, RES_o_irr_r, 7, addr, A) + op(0xc0, SET_o_irr_r, 0, addr, B) + op(0xc1, SET_o_irr_r, 0, addr, C) + op(0xc2, SET_o_irr_r, 0, addr, D) + op(0xc3, SET_o_irr_r, 0, addr, E) + op(0xc4, SET_o_irr_r, 0, addr, H) + op(0xc5, SET_o_irr_r, 0, addr, L) + op(0xc6, SET_o_irr_r, 0, addr, _) + op(0xc7, SET_o_irr_r, 0, addr, A) + op(0xc8, SET_o_irr_r, 1, addr, B) + op(0xc9, SET_o_irr_r, 1, addr, C) + op(0xca, SET_o_irr_r, 1, addr, D) + op(0xcb, SET_o_irr_r, 1, addr, E) + op(0xcc, SET_o_irr_r, 1, addr, H) + op(0xcd, SET_o_irr_r, 1, addr, L) + op(0xce, SET_o_irr_r, 1, addr, _) + op(0xcf, SET_o_irr_r, 1, addr, A) + op(0xd0, SET_o_irr_r, 2, addr, B) + op(0xd1, SET_o_irr_r, 2, addr, C) + op(0xd2, SET_o_irr_r, 2, addr, D) + op(0xd3, SET_o_irr_r, 2, addr, E) + op(0xd4, SET_o_irr_r, 2, addr, H) + op(0xd5, SET_o_irr_r, 2, addr, L) + op(0xd6, SET_o_irr_r, 2, addr, _) + op(0xd7, SET_o_irr_r, 2, addr, A) + op(0xd8, SET_o_irr_r, 3, addr, B) + op(0xd9, SET_o_irr_r, 3, addr, C) + op(0xda, SET_o_irr_r, 3, addr, D) + op(0xdb, SET_o_irr_r, 3, addr, E) + op(0xdc, SET_o_irr_r, 3, addr, H) + op(0xdd, SET_o_irr_r, 3, addr, L) + op(0xde, SET_o_irr_r, 3, addr, _) + op(0xdf, SET_o_irr_r, 3, addr, A) + op(0xe0, SET_o_irr_r, 4, addr, B) + op(0xe1, SET_o_irr_r, 4, addr, C) + op(0xe2, SET_o_irr_r, 4, addr, D) + op(0xe3, SET_o_irr_r, 4, addr, E) + op(0xe4, SET_o_irr_r, 4, addr, H) + op(0xe5, SET_o_irr_r, 4, addr, L) + op(0xe6, SET_o_irr_r, 4, addr, _) + op(0xe7, SET_o_irr_r, 4, addr, A) + op(0xe8, SET_o_irr_r, 5, addr, B) + op(0xe9, SET_o_irr_r, 5, addr, C) + op(0xea, SET_o_irr_r, 5, addr, D) + op(0xeb, SET_o_irr_r, 5, addr, E) + op(0xec, SET_o_irr_r, 5, addr, H) + op(0xed, SET_o_irr_r, 5, addr, L) + op(0xee, SET_o_irr_r, 5, addr, _) + op(0xef, SET_o_irr_r, 5, addr, A) + op(0xf0, SET_o_irr_r, 6, addr, B) + op(0xf1, SET_o_irr_r, 6, addr, C) + op(0xf2, SET_o_irr_r, 6, addr, D) + op(0xf3, SET_o_irr_r, 6, addr, E) + op(0xf4, SET_o_irr_r, 6, addr, H) + op(0xf5, SET_o_irr_r, 6, addr, L) + op(0xf6, SET_o_irr_r, 6, addr, _) + op(0xf7, SET_o_irr_r, 6, addr, A) + op(0xf8, SET_o_irr_r, 7, addr, B) + op(0xf9, SET_o_irr_r, 7, addr, C) + op(0xfa, SET_o_irr_r, 7, addr, D) + op(0xfb, SET_o_irr_r, 7, addr, E) + op(0xfc, SET_o_irr_r, 7, addr, H) + op(0xfd, SET_o_irr_r, 7, addr, L) + op(0xfe, SET_o_irr_r, 7, addr, _) + op(0xff, SET_o_irr_r, 7, addr, A) + } +} + +auto APU::instructionED(uint8 code) -> void { + switch(code) { + op(0x40, IN_r_ic, B) + op(0x41, OUT_ic_r, B) + op(0x42, SBC_hl_rr, BC) + op(0x43, LD_inn_rr, BC) + op(0x44, NEG) + op(0x45, RETN) + op(0x46, IM_o, 0) + op(0x47, LD_r_r1, I, A) + op(0x48, IN_r_ic, C) + op(0x49, OUT_ic_r, C) + op(0x4a, ADC_hl_rr, BC) + op(0x4b, LD_rr_inn, BC) + op(0x4c, NEG) + op(0x4d, RETI) + op(0x4e, IM_o, 0) + op(0x4f, LD_r_r1, R, A) + op(0x50, IN_r_ic, D) + op(0x51, OUT_ic_r, D) + op(0x52, SBC_hl_rr, DE) + op(0x53, LD_inn_rr, DE) + op(0x54, NEG) + op(0x55, RETN) + op(0x56, IM_o, 1) + op(0x57, LD_r_r2, A, I) + op(0x58, IN_r_ic, E) + op(0x59, OUT_ic_r, E) + op(0x5a, ADC_hl_rr, DE) + op(0x5b, LD_rr_inn, DE) + op(0x5c, NEG) + op(0x5d, RETI) + op(0x5e, IM_o, 2) + op(0x5f, LD_r_r2, A, R) + op(0x60, IN_r_ic, H) + op(0x61, OUT_ic_r, H) + op(0x62, SBC_hl_rr, HL) + op(0x63, LD_inn_rr, HL) + op(0x64, NEG) + op(0x65, RETN) + op(0x66, IM_o, 0) + op(0x67, RRD) + op(0x68, IN_r_ic, L) + op(0x69, OUT_ic_r, L) + op(0x6a, ADC_hl_rr, HL) + op(0x6b, LD_rr_inn, HL) + op(0x6c, NEG) + op(0x6d, RETI) + op(0x6e, IM_o, 0) + op(0x6f, RLD) + op(0x70, IN_ic) + op(0x71, OUT_ic) + op(0x72, SBC_hl_rr, SP) + op(0x73, LD_inn_rr, SP) + op(0x74, NEG) + op(0x75, RETN) + op(0x76, IM_o, 1) + op(0x77, NOP) + op(0x78, IN_r_ic, A) + op(0x79, OUT_ic_r, A) + op(0x7a, ADC_hl_rr, SP) + op(0x7b, LD_rr_inn, SP) + op(0x7c, NEG) + op(0x7d, RETI) + op(0x7e, IM_o, 2) + op(0x7f, NOP) + op(0xa0, LDI) + op(0xa1, CPI) + op(0xa2, INI) + op(0xa3, OUTI) + op(0xa8, LDD) + op(0xa9, CPD) + op(0xaa, IND) + op(0xab, OUTD) + op(0xb0, LDIR) + op(0xb1, CPIR) + op(0xb2, INIR) + op(0xb3, OTIR) + op(0xb8, LDDR) + op(0xb9, CPDR) + op(0xba, INDR) + op(0xbb, OTDR) + } + + //undefined instructions are NOP + return instructionNOP(); +} + +#undef op diff --git a/higan/md/apu/instructions.cpp b/higan/md/apu/instructions.cpp new file mode 100644 index 0000000..986bad3 --- /dev/null +++ b/higan/md/apu/instructions.cpp @@ -0,0 +1,912 @@ +//legend: +// a = register A +// c = condition +// e = relative operand +// in = (operand) +// inn = (operand-word) +// irr = (register-word) +// o = opcode bits +// n = operand +// nn = operand-word +// r = register + +auto APU::instructionADC_a_irr(uint16& x) -> void { Q = 1; + A = ADD(A, read(displace(x)), CF); +} + +auto APU::instructionADC_a_n() -> void { Q = 1; + A = ADD(A, operand(), CF); +} + +auto APU::instructionADC_a_r(uint8& x) -> void { Q = 1; + A = ADD(A, x, CF); +} + +auto APU::instructionADC_hl_rr(uint16& x) -> void { Q = 1; + WZ = HL + 1; + wait(4); + auto lo = ADD(HL >> 0, x >> 0, CF); + wait(3); + auto hi = ADD(HL >> 8, x >> 8, CF); + HL = hi << 8 | lo << 0; + ZF = HL == 0; +} + +auto APU::instructionADD_a_irr(uint16& x) -> void { Q = 1; + A = ADD(A, read(displace(x))); +} + +auto APU::instructionADD_a_n() -> void { Q = 1; + A = ADD(A, operand()); +} + +auto APU::instructionADD_a_r(uint8& x) -> void { Q = 1; + A = ADD(A, x); +} + +auto APU::instructionADD_hl_rr(uint16& x) -> void { Q = 1; + WZ = HL + 1; + bool vf = VF, zf = ZF, sf = SF; + wait(4); + auto lo = ADD(HL >> 0, x >> 0); + wait(3); + auto hi = ADD(HL >> 8, x >> 8, CF); + HL = hi << 8 | lo << 0; + VF = vf, ZF = zf, SF = sf; //restore unaffected flags +} + +auto APU::instructionAND_a_irr(uint16& x) -> void { Q = 1; + A = AND(A, read(displace(x))); +} + +auto APU::instructionAND_a_n() -> void { Q = 1; + A = AND(A, operand()); +} + +auto APU::instructionAND_a_r(uint8& x) -> void { Q = 1; + A = AND(A, x); +} + +auto APU::instructionBIT_o_irr(uint3 bit, uint16& addr) -> void { Q = 1; + BIT(bit, read(addr)); + XF = WZH.bit(3); + YF = WZH.bit(5); +} + +auto APU::instructionBIT_o_irr_r(uint3 bit, uint16& addr, uint8& x) -> void { Q = 1; + x = BIT(bit, read(addr)); + XF = WZH.bit(3); + YF = WZH.bit(5); +} + +auto APU::instructionBIT_o_r(uint3 bit, uint8& x) -> void { Q = 1; + BIT(bit, x); +} + +auto APU::instructionCALL_c_nn(bool c) -> void { Q = 0; + WZ = operands(); + if(!c) { + if(state.interruptPending) { + irq(); + state.interruptPending = false; + } + + return; + } + + wait(1); + push(PC); + PC = WZ; + + if(state.interruptPending) { + irq(); + state.interruptPending = false; + } +} + +auto APU::instructionCALL_nn() -> void { Q = 0; + WZ = operands(); + wait(1); + push(PC); + PC = WZ; + + if(state.interruptPending) { + irq(); + state.interruptPending = false; + } +} + +auto APU::instructionCCF() -> void { + if(Q) XF = 0, YF = 0; + HF = CF; + CF = !CF; + NF = 0; + XF = XF | A.bit(3); + YF = YF | A.bit(5); + Q = 1; +} + +auto APU::instructionCP_a_irr(uint16& x) -> void { Q = 1; + CP(A, read(displace(x))); +} + +auto APU::instructionCP_a_n() -> void { Q = 1; + CP(A, operand()); +} + +auto APU::instructionCP_a_r(uint8& x) -> void { Q = 1; + CP(A, x); +} + +auto APU::instructionCPD() -> void { Q = 1; + WZ--; + uint8 data = read(_HL--); + wait(5); + uint8 n = A - data; + NF = 1; + VF = --BC != 0; + HF = uint8(A ^ data ^ n).bit(4); + XF = uint8(n - HF).bit(3); + YF = uint8(n - HF).bit(1); + ZF = n == 0; + SF = n.bit(7); +} + +auto APU::instructionCPDR() -> void { Q = 1; + instructionCPD(); + if(!BC || ZF) return; + wait(5); + PC -= 2; + WZ = PC + 1; +} + +auto APU::instructionCPI() -> void { Q = 1; + WZ++; + uint8 data = read(_HL++); + wait(5); + uint8 n = A - data; + NF = 1; + VF = --BC != 0; + HF = uint8(A ^ data ^ n).bit(4); + XF = uint8(n - HF).bit(3); + YF = uint8(n - HF).bit(1); + ZF = n == 0; + SF = n.bit(7); +} + +auto APU::instructionCPIR() -> void { Q = 1; + instructionCPI(); + if(!BC || ZF) return; + wait(5); + PC -= 2; + WZ = PC + 1; +} + +auto APU::instructionCPL() -> void { Q = 1; + A = ~A; + + NF = 1; + XF = A.bit(3); + HF = 1; + YF = A.bit(5); +} + +auto APU::instructionDAA() -> void { Q = 1; + auto a = A; + if(CF || (A.bit(0,7) > 0x99)) { A += NF ? -0x60 : 0x60; CF = 1; } + if(HF || (A.bit(0,3) > 0x09)) { A += NF ? -0x06 : 0x06; } + + PF = parity(A); + XF = A.bit(3); + HF = uint8(A ^ a).bit(4); + YF = A.bit(5); + ZF = A == 0; + SF = A.bit(7); +} + +auto APU::instructionDEC_irr(uint16& x) -> void { Q = 1; + auto addr = displace(x); + auto data = read(addr); + wait(1); + write(addr, DEC(data)); +} + +auto APU::instructionDEC_r(uint8& x) -> void { Q = 1; + x = DEC(x); +} + +auto APU::instructionDEC_rr(uint16& x) -> void { Q = 0; + wait(2); + x--; +} + +auto APU::instructionDI() -> void { Q = 0; + IFF1 = 0; + IFF2 = 0; +} + +auto APU::instructionDJNZ_e() -> void { Q = 0; + wait(1); + auto displacement = (int8)operand(); + if(!--B) return; + wait(5); + WZ = PC + displacement; + PC = WZ; +} + +auto APU::instructionEI() -> void { Q = 0; + EI = 1; //raise IFF1, IFF2 after the next instruction +} + +auto APU::instructionEX_irr_rr(uint16& x, uint16& y) -> void { Q = 0; + WZL = read(x + 0); + WZH = read(x + 1); + write(x + 0, y >> 0); + write(x + 1, y >> 8); + y = WZ; +} + +auto APU::instructionEX_rr_rr(uint16& x, uint16& y) -> void { Q = 0; + swap(x, y); +} + +auto APU::instructionEXX() -> void { Q = 0; + swap(BC, BC_); + swap(DE, DE_); + swap(_HL, HL_); +} + +auto APU::instructionHALT() -> void { Q = 0; + HALT = 1; +} + +auto APU::instructionIM_o(uint2 code) -> void { Q = 0; + wait(4); + IM = code; +} + +auto APU::instructionIN_a_in() -> void { Q = 0; + WZL = operand(); + WZH = A; + A = in(WZ++); +} + +auto APU::instructionIN_r_ic(uint8& x) -> void { Q = 1; + x = IN(in(BC)); + WZ = BC + 1; +} + +auto APU::instructionIN_ic() -> void { Q = 1; + IN(in(BC)); + WZ = BC + 1; +} + +auto APU::instructionINC_irr(uint16& x) -> void { Q = 1; + auto addr = displace(x); + auto data = read(addr); + wait(1); + write(addr, INC(data)); +} + +auto APU::instructionINC_r(uint8& x) -> void { Q = 1; + x = INC(x); +} + +auto APU::instructionINC_rr(uint16& x) -> void { Q = 0; + wait(2); + x++; +} + +auto APU::instructionIND() -> void { Q = 1; + WZ = BC - 1; + B--; + wait(1); + auto data = in(BC); + write(_HL--, data); + CF = uint9(uint8(C - 1) + data).bit(8); + NF = data.bit(7); + PF = parity(uint8(C - 1) + data & 7 ^ B); + XF = B.bit(3); + HF = CF; + YF = B.bit(5); + ZF = B == 0; + SF = B.bit(7); +} + +auto APU::instructionINDR() -> void { Q = 1; + instructionIND(); + if(!B) return; + wait(5); + PC -= 2; +} + +auto APU::instructionINI() -> void { Q = 1; + WZ = BC + 1; + B--; + wait(1); + auto data = in(BC); + write(_HL++, data); + CF = uint9(uint8(C + 1) + data).bit(8); + NF = data.bit(7); + PF = parity(uint8(C + 1) + data & 7 ^ B); + XF = B.bit(3); + HF = CF; + YF = B.bit(5); + ZF = B == 0; + SF = B.bit(7); +} + +auto APU::instructionINIR() -> void { Q = 1; + instructionINI(); + if(!B) return; + wait(5); + PC -= 2; +} + +auto APU::instructionJP_c_nn(bool c) -> void { Q = 0; + WZ = operands(); + if(c) PC = WZ; + + if(state.interruptPending) { + irq(); + state.interruptPending = false; + } +} + +auto APU::instructionJP_rr(uint16& x) -> void { Q = 0; + PC = x; + + if(state.interruptPending) { + irq(); + state.interruptPending = false; + } +} + +auto APU::instructionJR_c_e(bool c) -> void { Q = 0; + auto displacement = (int8)operand(); + if(!c) { + if(state.interruptPending) { + irq(); + state.interruptPending = false; + } + return; + } + wait(5); + WZ = PC + displacement; + PC = WZ; + + if(state.interruptPending) { + irq(); + state.interruptPending = false; + } +} + +auto APU::instructionLD_a_inn() -> void { Q = 0; + WZ = operands(); + A = read(WZ++); +} + +auto APU::instructionLD_a_irr(uint16& x) -> void { Q = 0; + WZ = x; + A = read(displace(WZ)); + WZ++; +} + +auto APU::instructionLD_inn_a() -> void { Q = 0; + WZ = operands(); + write(WZ++, A); + WZH = A; +} + +auto APU::instructionLD_inn_rr(uint16& x) -> void { Q = 0; + WZ = operands(); + write(WZ + 0, x >> 0); + write(WZ + 1, x >> 8); + WZ++; +} + +auto APU::instructionLD_irr_a(uint16& x) -> void { Q = 0; + WZ = x; + write(displace(WZ), A); + WZL++; + WZH = A; +} + +auto APU::instructionLD_irr_n(uint16& x) -> void { Q = 0; + auto addr = displace(x); + write(addr, operand()); +} + +auto APU::instructionLD_irr_r(uint16& x, uint8& y) -> void { Q = 0; + write(displace(x), y); +} + +auto APU::instructionLD_r_n(uint8& x) -> void { Q = 0; + x = operand(); +} + +auto APU::instructionLD_r_irr(uint8& x, uint16& y) -> void { Q = 0; + x = read(displace(y)); +} + +auto APU::instructionLD_r_r(uint8& x, uint8& y) -> void { Q = 0; + x = y; +} + +//LD to/from I/R requires an extra T-cycle +auto APU::instructionLD_r_r1(uint8& x, uint8& y) -> void { Q = 0; + wait(1); + x = y; +} + +//LD from I/R sets status flags +auto APU::instructionLD_r_r2(uint8& x, uint8& y) -> void { Q = 1; + wait(1); + x = y; + NF = 0; + PF = IFF2; + XF = x.bit(3); + HF = 0; + YF = x.bit(5); + ZF = x == 0; + SF = x.bit(7); + P = mosfet == MOSFET::NMOS; +} + +auto APU::instructionLD_rr_inn(uint16& x) -> void { Q = 0; + auto addr = operands(); + x.byte(0) = read(addr + 0); + x.byte(1) = read(addr + 1); +} + +auto APU::instructionLD_rr_nn(uint16& x) -> void { Q = 0; + x = operands(); +} + +auto APU::instructionLD_sp_rr(uint16& x) -> void { Q = 0; + wait(2); + SP = x; +} + +auto APU::instructionLDD() -> void { Q = 1; + auto data = read(_HL--); + write(DE--, data); + wait(2); + NF = 0; + VF = --BC != 0; + XF = uint8(A + data).bit(3); + HF = 0; + YF = uint8(A + data).bit(1); +} + +auto APU::instructionLDDR() -> void { Q = 1; + instructionLDD(); + if(!BC) return; + wait(5); + PC -= 2; + WZ = PC + 1; +} + +auto APU::instructionLDI() -> void { Q = 1; + auto data = read(_HL++); + write(DE++, data); + wait(2); + NF = 0; + VF = --BC != 0; + XF = uint8(A + data).bit(3); + HF = 0; + YF = uint8(A + data).bit(1); +} + +auto APU::instructionLDIR() -> void { Q = 1; + instructionLDI(); + if(!BC) return; + wait(5); + PC -= 2; + WZ = PC + 1; +} + +auto APU::instructionNEG() -> void { Q = 1; + A = SUB(0, A); +} + +auto APU::instructionNOP() -> void { Q = 0; +} + +auto APU::instructionOR_a_irr(uint16& x) -> void { Q = 1; + A = OR(A, read(displace(x))); +} + +auto APU::instructionOR_a_n() -> void { Q = 1; + A = OR(A, operand()); +} + +auto APU::instructionOR_a_r(uint8& x) -> void { Q = 1; + A = OR(A, x); +} + +auto APU::instructionOTDR() -> void { Q = 1; + instructionOUTD(); + if(!B) return; + wait(5); + PC -= 2; +} + +auto APU::instructionOTIR() -> void { Q = 1; + instructionOUTI(); + if(!B) return; + wait(5); + PC -= 2; +} + +auto APU::instructionOUT_ic_r(uint8& x) -> void { Q = 0; + out(BC, x); + WZ = BC + 1; +} + +auto APU::instructionOUT_ic() -> void { Q = 0; + if(mosfet == MOSFET::NMOS) out(BC, 0x00); + if(mosfet == MOSFET::CMOS) out(BC, 0xff); +} + +auto APU::instructionOUT_in_a() -> void { Q = 0; + WZL = operand(); + WZH = A; + out(WZ, A); + WZL++; +} + +auto APU::instructionOUTD() -> void { Q = 1; + wait(1); + auto data = read(_HL--); + out(BC, data); + B--; + WZ = BC - 1; + CF = uint9(L + data).bit(8); + NF = data.bit(7); + PF = parity(L + data & 7 ^ B); + XF = B.bit(3); + HF = CF; + YF = B.bit(5); + ZF = B == 0; + SF = B.bit(7); +} + +auto APU::instructionOUTI() -> void { Q = 1; + wait(1); + auto data = read(_HL++); + out(BC, data); + B--; + WZ = BC + 1; + CF = uint9(L + data).bit(8); + NF = data.bit(7); + PF = parity(L + data & 7 ^ B); + XF = B.bit(3); + HF = CF; + YF = B.bit(5); + ZF = B == 0; + SF = B.bit(7); +} + +//note: even though "pop af" affects flags, it does not set Q +auto APU::instructionPOP_rr(uint16& x) -> void { Q = 0; + x = pop(); +} + +auto APU::instructionPUSH_rr(uint16& x) -> void { Q = 0; + wait(1); + push(x); +} + +auto APU::instructionRES_o_irr(uint3 bit, uint16& addr) -> void { Q = 1; + write(addr, RES(bit, read(addr))); +} + +auto APU::instructionRES_o_irr_r(uint3 bit, uint16& addr, uint8& x) -> void { Q = 1; + write(addr, x = RES(bit, read(addr))); +} + +auto APU::instructionRES_o_r(uint3 bit, uint8& x) -> void { Q = 1; + x = RES(bit, x); +} + +auto APU::instructionRET() -> void { Q = 0; + wait(1); + WZ = pop(); + PC = WZ; + + if(state.interruptPending) { + irq(); + state.interruptPending = false; + } +} + +auto APU::instructionRET_c(bool c) -> void { Q = 0; + wait(1); + if(!c) { + if(state.interruptPending) { + irq(); + state.interruptPending = false; + } + return; + } + + WZ = pop(); + PC = WZ; + + if(state.interruptPending) { + irq(); + state.interruptPending = false; + } +} + +auto APU::instructionRETI() -> void { Q = 0; + WZ = pop(); + PC = WZ; + IFF1 = IFF2; + + if(state.interruptPending) { + irq(); + state.interruptPending = false; + } +} + +auto APU::instructionRETN() -> void { Q = 0; + WZ = pop(); + PC = WZ; + IFF1 = IFF2; + + if(state.interruptPending) { + irq(); + state.interruptPending = false; + } +} + +auto APU::instructionRL_irr(uint16& addr) -> void { Q = 1; + write(addr, RL(read(addr))); +} + +auto APU::instructionRL_irr_r(uint16& addr, uint8& x) -> void { Q = 1; + write(addr, x = RL(read(addr))); +} + +auto APU::instructionRL_r(uint8& x) -> void { Q = 1; + x = RL(x); +} + +auto APU::instructionRLA() -> void { Q = 1; + bool c = A.bit(7); + A = A << 1 | CF; + + CF = c; + NF = 0; + XF = A.bit(3); + HF = 0; + YF = A.bit(5); +} + +auto APU::instructionRLC_irr(uint16& addr) -> void { Q = 1; + write(addr, RLC(read(addr))); +} + +auto APU::instructionRLC_irr_r(uint16& addr, uint8& x) -> void { Q = 1; + write(addr, x = RLC(read(addr))); +} + +auto APU::instructionRLC_r(uint8& x) -> void { Q = 1; + x = RLC(x); +} + +auto APU::instructionRLCA() -> void { Q = 1; + bool c = A.bit(7); + A = A << 1 | c; + + CF = c; + NF = 0; + XF = A.bit(3); + HF = 0; + YF = A.bit(5); +} + +auto APU::instructionRLD() -> void { Q = 1; + WZ = HL + 1; + auto data = read(HL); + wait(1); + write(HL, (data << 4) | (A & 0x0f)); + wait(3); + A = (A & 0xf0) | (data >> 4); + + NF = 0; + PF = parity(A); + XF = A.bit(3); + HF = 0; + YF = A.bit(5); + ZF = A == 0; + SF = A.bit(7); +} + +auto APU::instructionRR_irr(uint16& addr) -> void { Q = 1; + write(addr, RR(read(addr))); +} + +auto APU::instructionRR_irr_r(uint16& addr, uint8& x) -> void { Q = 1; + write(addr, x = RR(read(addr))); +} + +auto APU::instructionRR_r(uint8& x) -> void { Q = 1; + x = RR(x); +} + +auto APU::instructionRRA() -> void { Q = 1; + bool c = A.bit(0); + A = CF << 7 | A >> 1; + + CF = c; + NF = 0; + XF = A.bit(3); + HF = 0; + YF = A.bit(5); +} + +auto APU::instructionRRC_irr(uint16& addr) -> void { Q = 1; + write(addr, RRC(read(addr))); +} + +auto APU::instructionRRC_irr_r(uint16& addr, uint8& x) -> void { Q = 1; + write(addr, x = RRC(read(addr))); +} + +auto APU::instructionRRC_r(uint8& x) -> void { Q = 1; + x = RRC(x); +} + +auto APU::instructionRRCA() -> void { Q = 1; + bool c = A.bit(0); + A = c << 7 | A >> 1; + + CF = c; + NF = 0; + XF = A.bit(3); + HF = 0; + YF = A.bit(5); +} + +auto APU::instructionRRD() -> void { Q = 1; + WZ = HL + 1; + auto data = read(HL); + wait(1); + write(HL, (data >> 4) | (A << 4)); + wait(3); + A = (A & 0xf0) | (data & 0x0f); + + NF = 0; + PF = parity(A); + XF = A.bit(3); + HF = 0; + YF = A.bit(5); + ZF = A == 0; + SF = A.bit(7); +} + +auto APU::instructionRST_o(uint3 vector) -> void { Q = 0; + wait(1); + push(PC); + WZ = vector << 3; + PC = WZ; +} + +auto APU::instructionSBC_a_irr(uint16& x) -> void { Q = 1; + A = SUB(A, read(displace(x)), CF); +} + +auto APU::instructionSBC_a_n() -> void { Q = 1; + A = SUB(A, operand(), CF); +} + +auto APU::instructionSBC_a_r(uint8& x) -> void { Q = 1; + A = SUB(A, x, CF); +} + +auto APU::instructionSBC_hl_rr(uint16& x) -> void { Q = 1; + WZ = HL + 1; + wait(4); + auto lo = SUB(HL >> 0, x >> 0, CF); + wait(3); + auto hi = SUB(HL >> 8, x >> 8, CF); + HL = hi << 8 | lo << 0; + ZF = HL == 0; +} + +auto APU::instructionSCF() -> void { + if(Q) XF = 0, YF = 0; + CF = 1; + NF = 0; + XF = XF | A.bit(3); + HF = 0; + YF = YF | A.bit(5); + Q = 1; +} + +auto APU::instructionSET_o_irr(uint3 bit, uint16& addr) -> void { Q = 1; + write(addr, SET(bit, read(addr))); +} + +auto APU::instructionSET_o_irr_r(uint3 bit, uint16& addr, uint8& x) -> void { Q = 1; + write(addr, x = SET(bit, read(addr))); +} + +auto APU::instructionSET_o_r(uint3 bit, uint8& x) -> void { Q = 1; + x = SET(bit, x); +} + +auto APU::instructionSLA_irr(uint16& addr) -> void { Q = 1; + write(addr, SLA(read(addr))); +} + +auto APU::instructionSLA_irr_r(uint16& addr, uint8& x) -> void { Q = 1; + write(addr, x = SLA(read(addr))); +} + +auto APU::instructionSLA_r(uint8& x) -> void { Q = 1; + x = SLA(x); +} + +auto APU::instructionSLL_irr(uint16& addr) -> void { Q = 1; + write(addr, SLL(read(addr))); +} + +auto APU::instructionSLL_irr_r(uint16& addr, uint8& x) -> void { Q = 1; + write(addr, x = SLL(read(addr))); +} + +auto APU::instructionSLL_r(uint8& x) -> void { Q = 1; + x = SLL(x); +} + +auto APU::instructionSRA_irr(uint16& addr) -> void { Q = 1; + write(addr, SRA(read(addr))); +} + +auto APU::instructionSRA_irr_r(uint16& addr, uint8& x) -> void { Q = 1; + write(addr, x = SRA(read(addr))); +} + +auto APU::instructionSRA_r(uint8& x) -> void { Q = 1; + x = SRA(x); +} + +auto APU::instructionSRL_irr(uint16& addr) -> void { Q = 1; + write(addr, SRL(read(addr))); +} + +auto APU::instructionSRL_irr_r(uint16& addr, uint8& x) -> void { Q = 1; + write(addr, x = SRL(read(addr))); +} + +auto APU::instructionSRL_r(uint8& x) -> void { Q = 1; + x = SRL(x); +} + +auto APU::instructionSUB_a_irr(uint16& x) -> void { Q = 1; + A = SUB(A, read(displace(x))); +} + +auto APU::instructionSUB_a_n() -> void { Q = 1; + A = SUB(A, operand()); +} + +auto APU::instructionSUB_a_r(uint8& x) -> void { Q = 1; + A = SUB(A, x); +} + +auto APU::instructionXOR_a_irr(uint16& x) -> void { Q = 1; + A = XOR(A, read(displace(x))); +} + +auto APU::instructionXOR_a_n() -> void { Q = 1; + A = XOR(A, operand()); +} + +auto APU::instructionXOR_a_r(uint8& x) -> void { Q = 1; + A = XOR(A, x); +} diff --git a/higan/md/apu/registers.cpp b/higan/md/apu/registers.cpp new file mode 100644 index 0000000..0645bf6 --- /dev/null +++ b/higan/md/apu/registers.cpp @@ -0,0 +1,63 @@ +#define AF r.af.word +#define BC r.bc.word +#define DE r.de.word +#define HL (prefix == Prefix::ix ? r.ix.word : prefix == Prefix::iy ? r.iy.word : r.hl.word) + +#define A r.af.byte.hi +#define F r.af.byte.lo +#define B r.bc.byte.hi +#define C r.bc.byte.lo +#define D r.de.byte.hi +#define E r.de.byte.lo +#define H (prefix == Prefix::ix ? r.ix.byte.hi : prefix == Prefix::iy ? r.iy.byte.hi : r.hl.byte.hi) +#define L (prefix == Prefix::ix ? r.ix.byte.lo : prefix == Prefix::iy ? r.iy.byte.lo : r.hl.byte.lo) + +#define _HL r.hl.word //true HL (ignores IX/IY prefixes) +#define _H r.hl.byte.hi +#define _L r.hl.byte.lo + +#define AF_ r.af_.word //shadow registers +#define BC_ r.bc_.word +#define DE_ r.de_.word +#define HL_ r.hl_.word + +#define A_ r.af_.byte.hi +#define F_ r.af_.byte.lo +#define B_ r.bc_.byte.hi +#define C_ r.bc_.byte.lo +#define D_ r.de_.byte.hi +#define E_ r.de_.byte.lo +#define H_ r.hl_.byte.hi +#define L_ r.hl_.byte.lo + +#define SP r.sp +#define PC r.pc + +#define IX r.ix.word +#define IY r.iy.word +#define IR r.ir.word +#define WZ r.wz.word +#define WZH r.wz.byte.hi +#define WZL r.wz.byte.lo + +#define I r.ir.byte.hi +#define R r.ir.byte.lo + +#define CF r.af.byte.lo.bit(0) +#define NF r.af.byte.lo.bit(1) +#define PF r.af.byte.lo.bit(2) +#define VF r.af.byte.lo.bit(2) +#define XF r.af.byte.lo.bit(3) +#define HF r.af.byte.lo.bit(4) +#define YF r.af.byte.lo.bit(5) +#define ZF r.af.byte.lo.bit(6) +#define SF r.af.byte.lo.bit(7) + +#define EI r.ei +#define P r.p +#define Q r.q +#define QL r.ql +#define HALT r.halt +#define IFF1 r.iff1 +#define IFF2 r.iff2 +#define IM r.im diff --git a/higan/md/apu/serialization.cpp b/higan/md/apu/serialization.cpp index 1cffe63..041c06f 100644 --- a/higan/md/apu/serialization.cpp +++ b/higan/md/apu/serialization.cpp @@ -1,6 +1,31 @@ auto APU::serialize(serializer& s) -> void { - Z80::serialize(s); - Z80::Bus::serialize(s); + s.integer((uint&)mosfet); + s.integer((uint&)prefix); + s.integer(r.af.word); + s.integer(r.bc.word); + s.integer(r.de.word); + s.integer(r.hl.word); + s.integer(r.ix.word); + s.integer(r.iy.word); + s.integer(r.ir.word); + s.integer(r.wz.word); + s.integer(r.sp); + s.integer(r.pc); + s.integer(r.af_.word); + s.integer(r.bc_.word); + s.integer(r.de_.word); + s.integer(r.hl_.word); + s.boolean(r.ei); + s.boolean(r.p); + s.boolean(r.q); + s.boolean(r.halt); + s.boolean(r.iff1); + s.boolean(r.iff2); + s.boolean(r.im); + + s.integer(_requested); + s.integer(_granted); + Thread::serialize(s); ram.serialize(s); diff --git a/higan/md/cartridge/cartridge.cpp b/higan/md/cartridge/cartridge.cpp index 484b5cc..c115fbf 100644 --- a/higan/md/cartridge/cartridge.cpp +++ b/higan/md/cartridge/cartridge.cpp @@ -41,6 +41,9 @@ auto Cartridge::connect(Node::Peripheral with) -> void { information.regions = document["game/region"].text().split(",").strip(); information.bootable = (bool)document["game/bootable"]; +/* Reduce cartridge read/write overhead by hard-coding handlers + NOTE: This will break Super Street Fighter II, but *all* other games will work fine. + read = {&Cartridge::readLinear, this}; write = {&Cartridge::writeLinear, this}; @@ -60,10 +63,11 @@ auto Cartridge::connect(Node::Peripheral with) -> void { read = {&Cartridge::readGameGenie, this}; write = {&Cartridge::writeGameGenie, this}; } - } + }*/ //easter egg: power draw increases with each successively stacked cartridge //simulate increasing address/data line errors as stacking increases + /* if(depth >= 3) { auto reader = read; auto writer = write; @@ -78,7 +82,7 @@ auto Cartridge::connect(Node::Peripheral with) -> void { write = [=](uint1 upper, uint1 lower, uint22 address, uint16 data) -> void { writer(upper, lower, scramble(address), scramble(data)); }; - } + } */ power(); } @@ -89,8 +93,8 @@ auto Cartridge::disconnect() -> void { patch.reset(); wram.reset(); bram.reset(); - read.reset(); - write.reset(); + //read.reset(); + //write.reset(); if(slot) slot->disconnect(); slot.reset(); node = {}; @@ -207,11 +211,18 @@ auto Cartridge::writeIO(uint1 upper, uint1 lower, uint24 address, uint16 data) - // auto Cartridge::readLinear(uint1 upper, uint1 lower, uint22 address, uint16 data) -> uint16 { + address >>= 1; + if(address >= 0x200000 && ramEnable) { - if(wram) return data = wram[address >> 1]; - if(bram) return data = bram[address >> 1] * 0x0101; //todo: unconfirmed + if(wram) return data = wram[address]; + if(bram) return data = bram[address] * 0x0101; //todo: unconfirmed } - return data = rom[address >> 1]; + + if (address >= rom.size()) { + return data; + } + + return data = rom[address]; } auto Cartridge::writeLinear(uint1 upper, uint1 lower, uint22 address, uint16 data) -> void { diff --git a/higan/md/cartridge/cartridge.hpp b/higan/md/cartridge/cartridge.hpp index 702d940..336f3e8 100644 --- a/higan/md/cartridge/cartridge.hpp +++ b/higan/md/cartridge/cartridge.hpp @@ -71,8 +71,13 @@ struct Cartridge { } codes[5]; } gameGenie; - function read; - function write; + alwaysinline auto read(uint1 upper, uint1 lower, uint22 address, uint16 data) -> uint16 { + return data = readLinear(upper, lower, address, data); + } + + alwaysinline auto write(uint1 upper, uint1 lower, uint22 address, uint16 data) -> void { + return writeLinear(upper, lower, address, data); + } unique_pointer slot; const uint depth = 0; diff --git a/higan/md/cpu/algorithms.cpp b/higan/md/cpu/algorithms.cpp new file mode 100644 index 0000000..4994b1e --- /dev/null +++ b/higan/md/cpu/algorithms.cpp @@ -0,0 +1,205 @@ +template auto CPU::ADD(const uint32 source, const uint32 target) -> uint32 { + auto result = (uint64)source + target; + if(extend) result += r.x; + + r.c = sign(result >> 1) < 0; + r.v = sign(~(target ^ source) & (target ^ result)) < 0; + r.z = clip(result) ? 0 : (extend ? r.z : 1); + r.n = sign(result) < 0; + r.x = r.c; + + return clip(result); +} + +template auto CPU::AND(const uint32 source, const uint32 target) -> uint32 { + const uint32 result = target & source; + + r.c = 0; + r.v = 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + + return clip(result); +} + +template auto CPU::ASL(uint32 result, const uint shift) -> uint32 { + bool carry = false; + uint32 overflow = 0; + for(auto _ : range(shift)) { + carry = result & msb(); + const uint32 before = result; + result <<= 1; + overflow |= before ^ result; + } + + r.c = carry; + r.v = sign(overflow) < 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + if(shift) r.x = r.c; + + return clip(result); +} + +template auto CPU::ASR(uint32 result, const uint shift) -> uint32 { + bool carry = false; + uint32 overflow = 0; + for(auto _ : range(shift)) { + carry = result & lsb(); + const uint32 before = result; + result = sign(result) >> 1; + overflow |= before ^ result; + } + + r.c = carry; + r.v = sign(overflow) < 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + if(shift) r.x = r.c; + + return clip(result); +} + +template auto CPU::CMP(const uint32 source, const uint32 target) -> uint32 { + const auto result = (uint64)target - source; + + r.c = sign(result >> 1) < 0; + r.v = sign((target ^ source) & (target ^ result)) < 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + + return clip(result); +} + +template auto CPU::EOR(const uint32 source, const uint32 target) -> uint32 { + const uint32 result = target ^ source; + + r.c = 0; + r.v = 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + + return clip(result); +} + +template auto CPU::LSL(uint32 result, const uint shift) -> uint32 { + bool carry = false; + for(auto _ : range(shift)) { + carry = result & msb(); + result <<= 1; + } + + r.c = carry; + r.v = 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + if(shift) r.x = r.c; + + return clip(result); +} + +template auto CPU::LSR(uint32 result, const uint shift) -> uint32 { + bool carry = false; + for(auto _ : range(shift)) { + carry = result & lsb(); + result >>= 1; + } + + r.c = carry; + r.v = 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + if(shift) r.x = r.c; + + return clip(result); +} + +template auto CPU::OR(const uint32 source, const uint32 target) -> uint32 { + const auto result = target | source; + + r.c = 0; + r.v = 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + + return clip(result); +} + +template auto CPU::ROL(uint32 result, const uint shift) -> uint32 { + bool carry = false; + for(auto _ : range(shift)) { + carry = result & msb(); + result = result << 1 | carry; + } + + r.c = carry; + r.v = 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + + return clip(result); +} + +template auto CPU::ROR(uint32 result, const const uint shift) -> uint32 { + bool carry = false; + for(auto _ : range(shift)) { + carry = result & lsb(); + result >>= 1; + if(carry) result |= msb(); + } + + r.c = carry; + r.v = 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + + return clip(result); +} + +template auto CPU::ROXL(uint32 result, const uint shift) -> uint32 { + bool carry = r.x; + for(auto _ : range(shift)) { + const bool extend = carry; + carry = result & msb(); + result = result << 1 | extend; + } + + r.c = carry; + r.v = 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + r.x = r.c; + + return clip(result); +} + +template auto CPU::ROXR(uint32 result, uint shift) -> uint32 { + bool carry = r.x; + for(auto _ : range(shift)) { + const bool extend = carry; + carry = result & lsb(); + result >>= 1; + if(extend) result |= msb(); + } + + r.c = carry; + r.v = 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + r.x = r.c; + + return clip(result); +} + +template auto CPU::SUB(const uint32 source, const uint32 target) -> uint32 { + auto result = (uint64)target - source; + if(extend) result -= r.x; + + r.c = sign(result >> 1) < 0; + r.v = sign((target ^ source) & (target ^ result)) < 0; + r.z = clip(result) ? 0 : (extend ? r.z : 1); + r.n = sign(result) < 0; + r.x = r.c; + + return result; +} diff --git a/higan/md/cpu/bus.cpp b/higan/md/cpu/bus.cpp index 22435c1..4ff3c3f 100644 --- a/higan/md/cpu/bus.cpp +++ b/higan/md/cpu/bus.cpp @@ -3,110 +3,88 @@ * byte writes to word memory areas that are addressable as bytes cannot enjoy this optimization. */ -auto CPU::read(uint1 upper, uint1 lower, uint24 address, uint16 data) -> uint16 { - if(address >= 0x000000 && address <= 0x3fffff) { - //if(refresh.external >= 126) idle(min(2, 128 - refresh.external)); - if(!io.romEnable) return tmss[address >> 1]; - if(cartridge.bootable()) { - if(cartridge.node) return cartridge.read(upper, lower, address, data); - } else { - if(expansion.node) return expansion.read(upper, lower, address, data); - } - return data; - } - - if(address >= 0x400000 && address <= 0x7fffff) { - //if(refresh.external >= 126) idle(min(2, 128 - refresh.external)); - if(!cartridge.bootable()) { - if(cartridge.node) return cartridge.read(upper, lower, address, data); - } else { - if(expansion.node) return expansion.read(upper, lower, address, data); - } - return data; +auto CPU::read(const uint1 upper, const uint1 lower, const uint24 address, uint16 data) -> uint16 { + if ((address & 0xc00000) == 0) { + return cartridge.read(upper, lower, address, data); } - if(address >= 0xa00000 && address <= 0xa0ffff) { - if(apu.busStatus()) return data; - address.bit(15) = 0; //a080000-a0ffff mirrors a00000-a07fff - //word reads load the even input byte into both output bytes - auto byte = apu.read(address | !upper); //upper==0 only on odd byte reads - return byte << 8 | byte << 0; + if ((address & 0xe00000) == 0xe00000) { + return ram[address >> 1]; } - if(address >= 0xa10000 && address <= 0xbfffff) { - data = cartridge.readIO(upper, lower, address, data); - data = expansion.readIO(upper, lower, address, data); - data = readIO(upper, lower, address, data); - return data; - } + switch(address & 0xf00000) { + case 0xa00000: case 0xb00000: + if(address <= 0xa0ffff) { + auto addrCopy = address; + if(apu.busStatus()) return data; + addrCopy.bit(15) = 0; //a080000-a0ffff mirrors a00000-a07fff + //word reads load the even input byte into both output bytes + auto byte = apu.read(addrCopy | !upper); //upper==0 only on odd byte reads + return byte << 8 | byte << 0; + } - if(address >= 0xc00000 && address <= 0xdfffff) { - if(address.bit(5,7)) return cpu.ird(); //should deadlock the machine - if(address.bit(16,18)) return cpu.ird(); //should deadlock the machine - address.bit(8,15) = 0; //mirrors - if(address.bit(2,3) == 3) return cpu.ird(); //should return VDP open bus - if(address.bit(4)) return cpu.ird(); //reading the PSG should deadlock the machine - return vdp.read(address, data); - } + if(address <= 0xbfffff) { + data = cartridge.readIO(upper, lower, address, data); + //data = expansion.readIO(upper, lower, address, data); + data = readIO(upper, lower, address, data); + return data; + } + break; + case 0xc00000: case 0xd00000: { + if(address.bit(5,7)) return cpu.ird(); //should deadlock the machine + if(address.bit(16,18)) return cpu.ird(); //should deadlock the machine + if(address.bit(2,3) == 3) return cpu.ird(); //should return VDP open bus + if(address.bit(4)) return cpu.ird(); //reading the PSG should deadlock the machine - if(address >= 0xe00000 && address <= 0xffffff) { - //if(refresh.ram >= 116) idle(min(3, 133 - refresh.ram)); - return ram[address >> 1]; + auto addrCopy = address; + addrCopy.bit(8,15) = 0; //mirrors + return vdp.read(addrCopy, data); + } } return data; } -auto CPU::write(uint1 upper, uint1 lower, uint24 address, uint16 data) -> void { - if(address >= 0x000000 && address <= 0x3fffff) { - //if(refresh.external >= 126) idle(min(2, 128 - refresh.external)); - if(cartridge.bootable()) { - if(cartridge.node) return cartridge.write(upper, lower, address, data); - } else { - if(expansion.node) return expansion.write(upper, lower, address, data); - } - return; - } - - if(address >= 0x400000 && address <= 0x7fffff) { - //if(refresh.external >= 126) idle(min(2, 128 - refresh.external)); - if(!cartridge.bootable()) { - if(cartridge.node) return cartridge.write(upper, lower, address, data); - } else { - if(expansion.node) return expansion.write(upper, lower, address, data); - } - return; +auto CPU::write(const uint1 upper, const uint1 lower, const uint24 address, const uint16 data) -> void { + if ((address & 0xe00000) == 0xe00000) { + if(upper) ram[address >> 1].byte(1) = data.byte(1); + if(lower) ram[address >> 1].byte(0) = data.byte(0); + return; } - if(address >= 0xa00000 && address <= 0xa0ffff) { - if(apu.busStatus()) return; - address.bit(15) = 0; //a08000-a0ffff mirrors a00000-a07fff - //word writes store the upper input byte into the lower output byte - return apu.write(address | !upper, data.byte(upper)); //upper==0 only on odd byte reads + if ((address & 0xc00000) == 0) { + return cartridge.write(upper, lower, address, data); } - if(address >= 0xa10000 && address <= 0xbfffff) { - cartridge.writeIO(upper, lower, address, data); - expansion.writeIO(upper, lower, address, data); - writeIO(upper, lower, address, data); - return; - } + switch(address & 0xf00000) { + case 0xa00000: case 0xb00000: + if(address <= 0xa0ffff) { + auto addrCopy = address; + if(apu.busStatus()) return; + addrCopy.bit(15) = 0; //a08000-a0ffff mirrors a00000-a07fff + //word writes store the upper input byte into the lower output byte + return apu.write(addrCopy | !upper, data.byte(upper)); //upper==0 only on odd byte reads + } - if(address >= 0xc00000 && address <= 0xdfffff) { - if(address.bit(5,7)) return; //should deadlock the machine - if(address.bit(16,18)) return; //should deadlock the machine - address.bit(8,15) = 0; //mirrors - if(address.bit(4)) { - if(!lower) return; //byte writes to even PSG registers has no effect - return psg.write(data.byte(0)); + if(address <= 0xbfffff) { + cartridge.writeIO(upper, lower, address, data); + //expansion.writeIO(upper, lower, address, data); + writeIO(upper, lower, address, data); + return; + } + break; + case 0xc00000: case 0xd00000: { + if(address.bit(5,7)) return; //should deadlock the machine + if(address.bit(16,18)) return; //should deadlock the machine + if(address.bit(4)) { + if(!lower) return; //byte writes to even PSG registers has no effect + return psg.write(data.byte(0)); + } + + auto addrCopy = address; + addrCopy.bit(8,15) = 0; //mirrors + return vdp.write(addrCopy, data); } - return vdp.write(address, data); - } - - if(address >= 0xe00000 && address <= 0xffffff) { - //if(refresh.ram >= 116) idle(min(3, 133 - refresh.ram)); - if(upper) ram[address >> 1].byte(1) = data.byte(1); - if(lower) ram[address >> 1].byte(0) = data.byte(0); - return; } + } diff --git a/higan/md/cpu/cpu.cpp b/higan/md/cpu/cpu.cpp index f6954b6..bc38e97 100644 --- a/higan/md/cpu/cpu.cpp +++ b/higan/md/cpu/cpu.cpp @@ -1,5 +1,15 @@ #include +// Underclock the CPU by increasing this divisor +// Default (original clock speed) is 7.0 +// Higher values = slower emulated cpu (worse game performance) +// but higher emulator frame-rate (less audio stutter, etc) +// FIXME: Tweak to the best value for target device/game +// This can be overidden with -DMD_M68K_DIVISOR compiler flag, or changed here +#ifndef MD_M68K_DIVISOR + #define MD_M68K_DIVISOR 14.0 +#endif + namespace higan::MegaDrive { CPU cpu; @@ -7,6 +17,22 @@ CPU cpu; #include "io.cpp" #include "serialization.cpp" +enum : uint { Byte, Word, Long }; +enum : bool { Reverse = 1 }; + +#include "registers.cpp" +#include "memory.cpp" +#include "effective-address.cpp" +#include "traits.cpp" +#include "algorithms.cpp" +#include "instructions.cpp" + +#if !defined(NO_EVENTINSTRUCTION_NOTIFY) +#include "disassembler.cpp" +#endif + +#include "instruction.cpp" + auto CPU::load(Node::Object parent, Node::Object from) -> void { node = Node::append(parent, from, "CPU"); from = Node::scan(parent = node, from); @@ -23,6 +49,40 @@ auto CPU::unload() -> void { node = {}; } + +auto CPU::supervisor() -> bool { + if(r.s) return true; + + r.pc -= 4; + exception(Exception::Unprivileged, Vector::Unprivileged); + return false; +} + +auto CPU::exception(uint exception, uint vector, uint priority) -> void { + idle(10); //todo: not accurate + + auto pc = r.pc; + auto sr = readSR(); + + if(exception != Exception::Illegal) { + if(!r.s) swap(r.a[7], r.sp); + r.i = priority; + r.s = 1; + r.t = 0; + } + + push(pc - 4); + push(sr); + + r.pc = read(vector << 2); + prefetch(); + prefetch(); +} + +auto CPU::interrupt(uint vector, uint priority) -> void { + return exception(Exception::Interrupt, vector, priority); +} + auto CPU::main() -> void { #if defined(SCHEDULER_SYNCHRO) if (vdp.hasRendered) { @@ -32,33 +92,6 @@ auto CPU::main() -> void { } #endif - if(state.interruptPending) { - if(state.interruptPending.bit((uint)Interrupt::Reset)) { - state.interruptPending.bit((uint)Interrupt::Reset) = 0; - r.a[7] = read(1, 1, 0) << 16 | read(1, 1, 2) << 0; - r.pc = read(1, 1, 4) << 16 | read(1, 1, 6) << 0; - prefetch(); - prefetch(); - if(eventInterrupt->enabled()) eventInterrupt->notify("Reset"); - } - - if(state.interruptPending.bit((uint)Interrupt::HorizontalBlank)) { - if(4 > r.i) { - state.interruptPending.bit((uint)Interrupt::HorizontalBlank) = 0; - if(eventInterrupt->enabled()) eventInterrupt->notify("Hblank"); - return interrupt(Vector::Level4, 4); - } - } - - if(state.interruptPending.bit((uint)Interrupt::VerticalBlank)) { - if(6 > r.i) { - state.interruptPending.bit((uint)Interrupt::VerticalBlank) = 0; - if(eventInterrupt->enabled()) eventInterrupt->notify("Vblank"); - return interrupt(Vector::Level6, 6); - } - } - } - #if !defined(NO_EVENTINSTRUCTION_NOTIFY) if(eventInstruction->enabled() && eventInstruction->address(r.pc - 4)) { eventInstruction->notify(disassembleInstruction(r.pc - 4), disassembleContext()); @@ -68,17 +101,6 @@ auto CPU::main() -> void { instruction(); } -auto CPU::step(uint clocks) -> void { - refresh.ram += clocks; - while(refresh.ram >= 133) refresh.ram -= 133; - refresh.external += clocks; - Thread::step(clocks); -} - -auto CPU::idle(uint clocks) -> void { - step(clocks); -} - auto CPU::wait(uint clocks) -> void { while(vdp.dma.active) { Thread::step(1); @@ -102,8 +124,32 @@ auto CPU::lower(Interrupt interrupt) -> void { } auto CPU::power(bool reset) -> void { - M68K::power(); + for(auto& dr : r.d) dr = 0; + for(auto& ar : r.a) ar = 0; + r.sp = 0; + r.pc = 0; + + r.c = 0; + r.v = 0; + r.z = 0; + r.n = 0; + r.x = 0; + r.i = 7; + r.s = 1; + r.t = 0; + + r.irc = 0x4e71; //nop + r.ir = 0x4e71; //nop + r.ird = 0x4e71; //nop + + r.stop = false; + r.reset = false; + +#ifdef MD_M68K_DIVISOR + Thread::create(system.frequency() / MD_M68K_DIVISOR, {&CPU::main, this}); +#else Thread::create(system.frequency() / 7.0, {&CPU::main, this}); +#endif ram.allocate(64_KiB >> 1); @@ -127,7 +173,12 @@ auto CPU::power(bool reset) -> void { refresh = {}; state = {}; - state.interruptPending.bit((uint)Interrupt::Reset) = 1; + + // Start execution by calling the reset vector + r.a[7] = read(1, 1, 0) << 16 | read(1, 1, 2) << 0; + r.pc = read(1, 1, 4) << 16 | read(1, 1, 6) << 0; + prefetch(); + prefetch(); } } diff --git a/higan/md/cpu/cpu.hpp b/higan/md/cpu/cpu.hpp index c45d4b4..2b2d88c 100644 --- a/higan/md/cpu/cpu.hpp +++ b/higan/md/cpu/cpu.hpp @@ -1,6 +1,8 @@ //Motorola 68000 -struct CPU : M68K, Thread { +struct CPU : Thread { + CPU(); + Node::Component node; Node::Instruction eventInstruction; Node::Notification eventInterrupt; @@ -11,14 +13,98 @@ struct CPU : M68K, Thread { VerticalBlank, }; + + inline auto ird() const -> uint16 { return r.ird; } + + enum : bool { User, Supervisor }; + enum : uint { Byte, Word, Long }; + enum : bool { Reverse = 1, Extend = 1, Hold = 1, Fast = 1 }; + + enum : uint { + /* 0,n */ DataRegisterDirect, + /* 1,n */ AddressRegisterDirect, + /* 2,n */ AddressRegisterIndirect, + /* 3,n */ AddressRegisterIndirectWithPostIncrement, + /* 4,n */ AddressRegisterIndirectWithPreDecrement, + /* 5,n */ AddressRegisterIndirectWithDisplacement, + /* 6,n */ AddressRegisterIndirectWithIndex, + /* 7,0 */ AbsoluteShortIndirect, + /* 7,1 */ AbsoluteLongIndirect, + /* 7,2 */ ProgramCounterIndirectWithDisplacement, + /* 7,3 */ ProgramCounterIndirectWithIndex, + /* 7,4 */ Immediate, + }; + + struct Exception { enum : uint { + Illegal, + DivisionByZero, + BoundsCheck, + Overflow, + Unprivileged, + + Trap, + Interrupt, + };}; + + struct Vector { enum : uint { + Reset = 1, + BusError = 2, + AddressError = 3, + IllegalInstruction = 4, + DivisionByZero = 5, + BoundsCheck = 6, + Overflow = 7, + Unprivileged = 8, + Trace = 9, + IllegalLineA = 10, + IllegalLineF = 11, + Level1 = 25, + Level2 = 26, + Level3 = 27, + Level4 = 28, + Level5 = 29, + Level6 = 30, + Level7 = 31, + };}; + //cpu.cpp auto load(Node::Object, Node::Object) -> void; auto unload() -> void; auto main() -> void; - inline auto step(uint clocks) -> void; - auto idle(uint clocks) -> void override; - auto wait(uint clocks) -> void override; + + inline auto checkForInterrupts() -> void { + if(state.interruptPending) { + if(state.interruptPending.bit((uint)Interrupt::HorizontalBlank)) { + if(4 > r.i) { + state.interruptPending.bit((uint)Interrupt::HorizontalBlank) = 0; + //if(eventInterrupt->enabled()) eventInterrupt->notify("Hblank"); + return interrupt(Vector::Level4, 4); + } + } + + if(state.interruptPending.bit((uint)Interrupt::VerticalBlank)) { + if(6 > r.i) { + state.interruptPending.bit((uint)Interrupt::VerticalBlank) = 0; + //if(eventInterrupt->enabled()) eventInterrupt->notify("Vblank"); + return interrupt(Vector::Level6, 6); + } + } + } + } + + alwaysinline auto step(const uint clocks) -> void { + refresh.ram += clocks; + while(refresh.ram >= 133) refresh.ram -= 133; + refresh.external += clocks; + Thread::step(clocks); + } + + alwaysinline auto idle(const uint clocks) -> void { + step(clocks); + } + + alwaysinline auto wait(const uint clocks) -> void; auto raise(Interrupt) -> void; auto lower(Interrupt) -> void; @@ -26,16 +112,421 @@ struct CPU : M68K, Thread { auto power(bool reset) -> void; //bus.cpp - auto read(uint1 upper, uint1 lower, uint24 address, uint16 data = 0) -> uint16 override; - auto write(uint1 upper, uint1 lower, uint24 address, uint16 data) -> void override; + inline auto read(const uint1 upper, const uint1 lower, const uint24 address, uint16 data = 0) -> uint16; + inline auto write(const uint1 upper, const uint1 lower, const uint24 address, const uint16 data) -> void; //io.cpp - auto readIO(uint1 upper, uint1 lower, uint24 address, uint16 data) -> uint16; - auto writeIO(uint1 upper, uint1 lower, uint24 address, uint16 data) -> void; + inline auto readIO(uint1 upper, uint1 lower, uint24 address, uint16 data) -> uint16; + inline auto writeIO(uint1 upper, uint1 lower, uint24 address, uint16 data) -> void; //serialization.cpp auto serialize(serializer&) -> void; + auto supervisor() -> bool; + auto exception(uint exception, uint vector, uint priority = 7) -> void; + auto interrupt(uint vector, uint priority = 7) -> void; + + //registers.cpp + struct DataRegister { + explicit DataRegister(uint64 number_) : number(number_) {} + uint3 number; + }; + template auto read(DataRegister reg) -> uint32; + template auto write(DataRegister reg, uint32 data) -> void; + + struct AddressRegister { + explicit AddressRegister(uint64 number_) : number(number_) {} + uint3 number; + }; + template auto read(AddressRegister reg) -> uint32; + template auto write(AddressRegister reg, uint32 data) -> void; + + auto readCCR() -> uint8; + auto readSR() -> uint16; + auto writeCCR(uint8 ccr) -> void; + auto writeSR(uint16 sr) -> void; + + //memory.cpp + template auto read(uint32 addr) -> uint32; + template auto write(uint32 addr, uint32 data) -> void; + template auto extension() -> uint32; +// + alwaysinline auto prefetch() -> uint16 { + wait(4); + r.ir = r.irc; + r.irc = read(1, 1, r.pc & ~1); + r.pc += 2; + return r.ir; + } + + //take the prefetched value without reloading the prefetch. + //this is used by instructions such as JMP and JSR. + alwaysinline auto prefetched() -> uint16 { + r.ir = r.irc; + r.irc = 0x0000; + r.pc += 2; + return r.ir; + } + + template auto pop() -> uint32; + template auto push(uint32 data) -> void; + + //effective-address.cpp + struct EffectiveAddress { + explicit EffectiveAddress(uint4 mode_, uint3 reg_) : mode(mode_), reg(reg_) { + if(mode == 7) mode += reg; //optimization: convert modes {7; 0-4} to {7-11} + } + + uint4 mode; + uint3 reg; + + boolean valid; + uint32 address; + }; + + auto prefetched(EffectiveAddress& ea) -> uint32; + template auto fetch(EffectiveAddress& ea) -> uint32; + template auto read(EffectiveAddress& ea) -> uint32; + template auto write(EffectiveAddress& ea, uint32 data) -> void; + + //instruction.cpp + alwaysinline auto instruction() -> void { + r.ird = r.ir; + return instructionTable[r.ird](); + } + + //traits.cpp + template auto bytes() -> uint; + template auto bits() -> uint; + template auto lsb() -> uint32; + template auto msb() -> uint32; + template auto mask() -> uint32; + template auto clip(uint32 data) -> uint32; + template auto sign(uint32 data) -> int32; + + //conditions.cpp + alwaysinline auto condition(const uint4 condition) -> const bool { + switch(condition) { + case 0: return true; //T + case 1: return false; //F + case 2: return !r.c && !r.z; //HI + case 3: return r.c || r.z; //LS + case 4: return !r.c; //CC,HS + case 5: return r.c; //CS,LO + case 6: return !r.z; //NE + case 7: return r.z; //EQ + case 8: return !r.v; //VC + case 9: return r.v; //VS + case 10: return !r.n; //PL + case 11: return r.n; //MI + case 12: return r.n == r.v; //GE + case 13: return r.n != r.v; //LT + case 14: return r.n == r.v && !r.z; //GT + case 15: return r.n != r.v || r.z; //LE + } + unreachable; + } + + //algorithms.cpp + template alwaysinline auto ADD(uint32 source, uint32 target) -> uint32; + template alwaysinline auto AND(uint32 source, uint32 target) -> uint32; + template alwaysinline auto ASL(uint32 result, uint shift) -> uint32; + template alwaysinline auto ASR(uint32 result, uint shift) -> uint32; + template alwaysinline auto CMP(uint32 source, uint32 target) -> uint32; + template alwaysinline auto EOR(uint32 source, uint32 target) -> uint32; + template alwaysinline auto LSL(uint32 result, uint shift) -> uint32; + template alwaysinline auto LSR(uint32 result, uint shift) -> uint32; + template alwaysinline auto OR(uint32 source, uint32 target) -> uint32; + template alwaysinline auto ROL(uint32 result, uint shift) -> uint32; + template alwaysinline auto ROR(uint32 result, uint shift) -> uint32; + template alwaysinline auto ROXL(uint32 result, uint shift) -> uint32; + template alwaysinline auto ROXR(uint32 result, uint shift) -> uint32; + template alwaysinline auto SUB(uint32 source, uint32 target) -> uint32; + + //instructions.cpp + auto instructionABCD(EffectiveAddress from, EffectiveAddress with) -> void; + template auto instructionADD(EffectiveAddress from, DataRegister with) -> void; + template auto instructionADD(DataRegister from, EffectiveAddress with) -> void; + template auto instructionADDA(EffectiveAddress from, AddressRegister with) -> void; + template auto instructionADDI(EffectiveAddress with) -> void; + template auto instructionADDQ(uint4 immediate, EffectiveAddress with) -> void; + template auto instructionADDQ(uint4 immediate, AddressRegister with) -> void; + template auto instructionADDX(EffectiveAddress with, EffectiveAddress from) -> void; + template auto instructionAND(EffectiveAddress from, DataRegister with) -> void; + template auto instructionAND(DataRegister from, EffectiveAddress with) -> void; + template auto instructionANDI(EffectiveAddress with) -> void; + auto instructionANDI_TO_CCR() -> void; + auto instructionANDI_TO_SR() -> void; + template auto instructionASL(uint4 count, DataRegister modify) -> void; + template auto instructionASL(DataRegister from, DataRegister modify) -> void; + auto instructionASL(EffectiveAddress modify) -> void; + template auto instructionASR(uint4 count, DataRegister modify) -> void; + template auto instructionASR(DataRegister from, DataRegister modify) -> void; + auto instructionASR(EffectiveAddress modify) -> void; + auto instructionBCC(uint4 test, uint8 displacement) -> void; + template auto instructionBCHG(DataRegister bit, EffectiveAddress with) -> void; + template auto instructionBCHG(EffectiveAddress with) -> void; + template auto instructionBCLR(DataRegister bit, EffectiveAddress with) -> void; + template auto instructionBCLR(EffectiveAddress with) -> void; + auto instructionBRA(uint8 displacement) -> void; + template auto instructionBSET(DataRegister bit, EffectiveAddress with) -> void; + template auto instructionBSET(EffectiveAddress with) -> void; + auto instructionBSR(uint8 displacement) -> void; + template auto instructionBTST(DataRegister bit, EffectiveAddress with) -> void; + template auto instructionBTST(EffectiveAddress with) -> void; + auto instructionCHK(DataRegister compare, EffectiveAddress maximum) -> void; + template auto instructionCLR(EffectiveAddress with) -> void; + template auto instructionCMP(EffectiveAddress from, DataRegister with) -> void; + template auto instructionCMPA(EffectiveAddress from, AddressRegister with) -> void; + template auto instructionCMPI(EffectiveAddress with) -> void; + template auto instructionCMPM(EffectiveAddress from, EffectiveAddress with) -> void; + auto instructionDBCC(uint4 condition, DataRegister with) -> void; + auto instructionDIVS(EffectiveAddress from, DataRegister with) -> void; + auto instructionDIVU(EffectiveAddress from, DataRegister with) -> void; + template auto instructionEOR(DataRegister from, EffectiveAddress with) -> void; + template auto instructionEORI(EffectiveAddress with) -> void; + auto instructionEORI_TO_CCR() -> void; + auto instructionEORI_TO_SR() -> void; + auto instructionEXG(DataRegister x, DataRegister y) -> void; + auto instructionEXG(AddressRegister x, AddressRegister y) -> void; + auto instructionEXG(DataRegister x, AddressRegister y) -> void; + template auto instructionEXT(DataRegister with) -> void; + auto instructionILLEGAL(uint16 code) -> void; + auto instructionJMP(EffectiveAddress from) -> void; + auto instructionJSR(EffectiveAddress from) -> void; + auto instructionLEA(EffectiveAddress from, AddressRegister to) -> void; + auto instructionLINK(AddressRegister with) -> void; + template auto instructionLSL(uint4 count, DataRegister with) -> void; + template auto instructionLSL(DataRegister from, DataRegister with) -> void; + auto instructionLSL(EffectiveAddress with) -> void; + template auto instructionLSR(uint4 count, DataRegister with) -> void; + template auto instructionLSR(DataRegister from, DataRegister with) -> void; + auto instructionLSR(EffectiveAddress with) -> void; + template auto instructionMOVE(EffectiveAddress from, EffectiveAddress to) -> void; + template auto instructionMOVEA(EffectiveAddress from, AddressRegister to) -> void; + template auto instructionMOVEM_TO_MEM(EffectiveAddress to) -> void; + template auto instructionMOVEM_TO_REG(EffectiveAddress from) -> void; + template auto instructionMOVEP(DataRegister from, EffectiveAddress to) -> void; + template auto instructionMOVEP(EffectiveAddress from, DataRegister to) -> void; + auto instructionMOVEQ(uint8 immediate, DataRegister to) -> void; + auto instructionMOVE_FROM_SR(EffectiveAddress to) -> void; + auto instructionMOVE_TO_CCR(EffectiveAddress from) -> void; + auto instructionMOVE_TO_SR(EffectiveAddress from) -> void; + auto instructionMOVE_FROM_USP(AddressRegister to) -> void; + auto instructionMOVE_TO_USP(AddressRegister from) -> void; + auto instructionMULS(EffectiveAddress from, DataRegister with) -> void; + auto instructionMULU(EffectiveAddress from, DataRegister with) -> void; + auto instructionNBCD(EffectiveAddress with) -> void; + template auto instructionNEG(EffectiveAddress with) -> void; + template auto instructionNEGX(EffectiveAddress with) -> void; + auto instructionNOP() -> void; + template auto instructionNOT(EffectiveAddress with) -> void; + template auto instructionOR(EffectiveAddress from, DataRegister with) -> void; + template auto instructionOR(DataRegister from, EffectiveAddress with) -> void; + template auto instructionORI(EffectiveAddress with) -> void; + auto instructionORI_TO_CCR() -> void; + auto instructionORI_TO_SR() -> void; + auto instructionPEA(EffectiveAddress from) -> void; + auto instructionRESET() -> void; + template auto instructionROL(uint4 count, DataRegister with) -> void; + template auto instructionROL(DataRegister from, DataRegister with) -> void; + auto instructionROL(EffectiveAddress with) -> void; + template auto instructionROR(uint4 count, DataRegister with) -> void; + template auto instructionROR(DataRegister from, DataRegister with) -> void; + auto instructionROR(EffectiveAddress with) -> void; + template auto instructionROXL(uint4 count, DataRegister with) -> void; + template auto instructionROXL(DataRegister from, DataRegister with) -> void; + auto instructionROXL(EffectiveAddress with) -> void; + template auto instructionROXR(uint4 count, DataRegister with) -> void; + template auto instructionROXR(DataRegister from, DataRegister with) -> void; + auto instructionROXR(EffectiveAddress with) -> void; + auto instructionRTE() -> void; + auto instructionRTR() -> void; + auto instructionRTS() -> void; + auto instructionSBCD(EffectiveAddress with, EffectiveAddress from) -> void; + auto instructionSCC(uint4 test, EffectiveAddress to) -> void; + auto instructionSTOP() -> void; + template auto instructionSUB(EffectiveAddress from, DataRegister with) -> void; + template auto instructionSUB(DataRegister from, EffectiveAddress with) -> void; + template auto instructionSUBA(EffectiveAddress from, AddressRegister with) -> void; + template auto instructionSUBI(EffectiveAddress with) -> void; + template auto instructionSUBQ(uint4 immediate, EffectiveAddress with) -> void; + template auto instructionSUBQ(uint4 immediate, AddressRegister with) -> void; + template auto instructionSUBX(EffectiveAddress from, EffectiveAddress with) -> void; + auto instructionSWAP(DataRegister with) -> void; + auto instructionTAS(EffectiveAddress with) -> void; + auto instructionTRAP(uint4 vector) -> void; + auto instructionTRAPV() -> void; + template auto instructionTST(EffectiveAddress from) -> void; + auto instructionUNLK(AddressRegister with) -> void; + +//disassembler.cpp +#if !defined(NO_EVENTINSTRUCTION_NOTIFY) + auto disassembleInstruction(uint32 pc) -> string; + auto disassembleContext() -> string; +#endif + + struct Registers { + uint32 d[8]; //data registers + uint32 a[8]; //address registers (a7 = s ? ssp : usp) + uint32 sp; //inactive stack pointer (s ? usp : ssp) + uint32 pc; //program counter + + bool c; //carry + bool v; //overflow + bool z; //zero + bool n; //negative + bool x; //extend + uint3 i; //interrupt mask + bool s; //supervisor mode + bool t; //trace mode + + uint16 irc; //instruction prefetched from external memory + uint16 ir; //instruction currently being decoded + uint16 ird; //instruction currently being executed + + bool stop; + bool reset; + } r; + + function instructionTable[65536]; + +private: + //disassembler.cpp +#if !defined(NO_EVENTINSTRUCTION_NOTIFY) + auto disassembleABCD(EffectiveAddress from, EffectiveAddress with) -> string; + template auto disassembleADD(EffectiveAddress from, DataRegister with) -> string; + template auto disassembleADD(DataRegister from, EffectiveAddress with) -> string; + template auto disassembleADDA(EffectiveAddress from, AddressRegister with) -> string; + template auto disassembleADDI(EffectiveAddress with) -> string; + template auto disassembleADDQ(uint4 immediate, EffectiveAddress with) -> string; + template auto disassembleADDQ(uint4 immediate, AddressRegister with) -> string; + template auto disassembleADDX(EffectiveAddress from, EffectiveAddress with) -> string; + template auto disassembleAND(EffectiveAddress from, DataRegister with) -> string; + template auto disassembleAND(DataRegister from, EffectiveAddress with) -> string; + template auto disassembleANDI(EffectiveAddress with) -> string; + auto disassembleANDI_TO_CCR() -> string; + auto disassembleANDI_TO_SR() -> string; + template auto disassembleASL(uint4 count, DataRegister with) -> string; + template auto disassembleASL(DataRegister from, DataRegister with) -> string; + auto disassembleASL(EffectiveAddress with) -> string; + template auto disassembleASR(uint4 count, DataRegister with) -> string; + template auto disassembleASR(DataRegister from, DataRegister with) -> string; + auto disassembleASR(EffectiveAddress with) -> string; + auto disassembleBCC(uint4 condition, uint8 displacement) -> string; + template auto disassembleBCHG(DataRegister bit, EffectiveAddress with) -> string; + template auto disassembleBCHG(EffectiveAddress with) -> string; + template auto disassembleBCLR(DataRegister bit, EffectiveAddress with) -> string; + template auto disassembleBCLR(EffectiveAddress with) -> string; + auto disassembleBRA(uint8 displacement) -> string; + template auto disassembleBSET(DataRegister bit, EffectiveAddress with) -> string; + template auto disassembleBSET(EffectiveAddress with) -> string; + auto disassembleBSR(uint8 displacement) -> string; + template auto disassembleBTST(DataRegister bit, EffectiveAddress with) -> string; + template auto disassembleBTST(EffectiveAddress with) -> string; + auto disassembleCHK(DataRegister compare, EffectiveAddress maximum) -> string; + template auto disassembleCLR(EffectiveAddress with) -> string; + template auto disassembleCMP(EffectiveAddress from, DataRegister with) -> string; + template auto disassembleCMPA(EffectiveAddress from, AddressRegister with) -> string; + template auto disassembleCMPI(EffectiveAddress with) -> string; + template auto disassembleCMPM(EffectiveAddress from, EffectiveAddress with) -> string; + auto disassembleDBCC(uint4 test, DataRegister with) -> string; + auto disassembleDIVS(EffectiveAddress from, DataRegister with) -> string; + auto disassembleDIVU(EffectiveAddress from, DataRegister with) -> string; + template auto disassembleEOR(DataRegister from, EffectiveAddress with) -> string; + template auto disassembleEORI(EffectiveAddress with) -> string; + auto disassembleEORI_TO_CCR() -> string; + auto disassembleEORI_TO_SR() -> string; + auto disassembleEXG(DataRegister x, DataRegister y) -> string; + auto disassembleEXG(AddressRegister x, AddressRegister y) -> string; + auto disassembleEXG(DataRegister x, AddressRegister y) -> string; + template auto disassembleEXT(DataRegister with) -> string; + auto disassembleILLEGAL(uint16 code) -> string; + auto disassembleJMP(EffectiveAddress from) -> string; + auto disassembleJSR(EffectiveAddress from) -> string; + auto disassembleLEA(EffectiveAddress from, AddressRegister to) -> string; + auto disassembleLINK(AddressRegister with) -> string; + template auto disassembleLSL(uint4 count, DataRegister with) -> string; + template auto disassembleLSL(DataRegister from, DataRegister with) -> string; + auto disassembleLSL(EffectiveAddress with) -> string; + template auto disassembleLSR(uint4 count, DataRegister with) -> string; + template auto disassembleLSR(DataRegister from, DataRegister with) -> string; + auto disassembleLSR(EffectiveAddress with) -> string; + template auto disassembleMOVE(EffectiveAddress from, EffectiveAddress to) -> string; + template auto disassembleMOVEA(EffectiveAddress from, AddressRegister to) -> string; + template auto disassembleMOVEM_TO_MEM(EffectiveAddress to) -> string; + template auto disassembleMOVEM_TO_REG(EffectiveAddress from) -> string; + template auto disassembleMOVEP(DataRegister from, EffectiveAddress to) -> string; + template auto disassembleMOVEP(EffectiveAddress from, DataRegister to) -> string; + auto disassembleMOVEQ(uint8 immediate, DataRegister to) -> string; + auto disassembleMOVE_FROM_SR(EffectiveAddress to) -> string; + auto disassembleMOVE_TO_CCR(EffectiveAddress from) -> string; + auto disassembleMOVE_TO_SR(EffectiveAddress from) -> string; + auto disassembleMOVE_FROM_USP(AddressRegister to) -> string; + auto disassembleMOVE_TO_USP(AddressRegister from) -> string; + auto disassembleMULS(EffectiveAddress from, DataRegister with) -> string; + auto disassembleMULU(EffectiveAddress from, DataRegister with) -> string; + auto disassembleNBCD(EffectiveAddress with) -> string; + template auto disassembleNEG(EffectiveAddress with) -> string; + template auto disassembleNEGX(EffectiveAddress with) -> string; + auto disassembleNOP() -> string; + template auto disassembleNOT(EffectiveAddress with) -> string; + template auto disassembleOR(EffectiveAddress from, DataRegister with) -> string; + template auto disassembleOR(DataRegister from, EffectiveAddress with) -> string; + template auto disassembleORI(EffectiveAddress with) -> string; + auto disassembleORI_TO_CCR() -> string; + auto disassembleORI_TO_SR() -> string; + auto disassemblePEA(EffectiveAddress from) -> string; + auto disassembleRESET() -> string; + template auto disassembleROL(uint4 count, DataRegister with) -> string; + template auto disassembleROL(DataRegister from, DataRegister with) -> string; + auto disassembleROL(EffectiveAddress with) -> string; + template auto disassembleROR(uint4 count, DataRegister with) -> string; + template auto disassembleROR(DataRegister from, DataRegister with) -> string; + auto disassembleROR(EffectiveAddress with) -> string; + template auto disassembleROXL(uint4 count, DataRegister with) -> string; + template auto disassembleROXL(DataRegister from, DataRegister with) -> string; + auto disassembleROXL(EffectiveAddress with) -> string; + template auto disassembleROXR(uint4 count, DataRegister with) -> string; + template auto disassembleROXR(DataRegister from, DataRegister with) -> string; + auto disassembleROXR(EffectiveAddress with) -> string; + auto disassembleRTE() -> string; + auto disassembleRTR() -> string; + auto disassembleRTS() -> string; + auto disassembleSBCD(EffectiveAddress with, EffectiveAddress from) -> string; + auto disassembleSCC(uint4 test, EffectiveAddress to) -> string; + auto disassembleSTOP() -> string; + template auto disassembleSUB(EffectiveAddress from, DataRegister with) -> string; + template auto disassembleSUB(DataRegister from, EffectiveAddress with) -> string; + template auto disassembleSUBA(EffectiveAddress from, AddressRegister with) -> string; + template auto disassembleSUBI(EffectiveAddress with) -> string; + template auto disassembleSUBQ(uint4 immediate, EffectiveAddress with) -> string; + template auto disassembleSUBQ(uint4 immediate, AddressRegister with) -> string; + template auto disassembleSUBX(EffectiveAddress from, EffectiveAddress with) -> string; + auto disassembleSWAP(DataRegister with) -> string; + auto disassembleTAS(EffectiveAddress with) -> string; + auto disassembleTRAP(uint4 vector) -> string; + auto disassembleTRAPV() -> string; + template auto disassembleTST(EffectiveAddress from) -> string; + auto disassembleUNLK(AddressRegister with) -> string; +#endif + + template auto _read(uint32 addr) -> uint32; + template auto _readPC() -> uint32; + auto _readDisplacement(uint32 base) -> uint32; + auto _readIndex(uint32 base) -> uint32; + auto _dataRegister(DataRegister dr) -> string; + auto _addressRegister(AddressRegister ar) -> string; + template auto _immediate() -> string; + template auto _address(EffectiveAddress& ea) -> string; + template auto _effectiveAddress(EffectiveAddress& ea) -> string; + auto _branch(uint8 displacement) -> string; + template auto _suffix() -> string; + auto _condition(uint4 condition) -> string; + + uint32 _pc; + function disassembleTable[65536]; + private: Memory::Writable ram; Memory::Readable tmss; diff --git a/higan/md/cpu/disassembler.cpp b/higan/md/cpu/disassembler.cpp new file mode 100644 index 0000000..0f293d9 --- /dev/null +++ b/higan/md/cpu/disassembler.cpp @@ -0,0 +1,615 @@ +template<> auto CPU::_read(uint32 address) -> uint32 { + if(address & 1) { + return read(0, 1, address & ~1).byte(0); + } else { + return read(1, 0, address & ~1).byte(1); + } +} + +template<> auto CPU::_read(uint32 address) -> uint32 { + return read(1, 1, address & ~1); +} + +template<> auto CPU::_read(uint32 address) -> uint32 { + uint32 data = _read(address + 0) << 16; + return data | _read(address + 2) << 0; +} + +template auto CPU::_readPC() -> uint32 { + auto data = _read(_pc); + _pc += Size == Long ? 4 : 2; + return clip(data); +} + +auto CPU::_readDisplacement(uint32 base) -> uint32 { + return base + (int16)_readPC(); +} + +auto CPU::_readIndex(uint32 base) -> uint32 { + auto extension = _readPC(); + auto index = extension & 0x8000 + ? read(AddressRegister{extension >> 12}) + : read(DataRegister{extension >> 12}); + if(!(extension & 0x800)) index = (int16)index; + return base + index + (int8)extension; +} + +auto CPU::_dataRegister(DataRegister dr) -> string { + return {"d", dr.number}; +} + +auto CPU::_addressRegister(AddressRegister ar) -> string { + return {"a", ar.number}; +} + +template auto CPU::_immediate() -> string { + return {"#$", hex(_readPC(), 2 << Size)}; +} + +template auto CPU::_address(EffectiveAddress& ea) -> string { + if(ea.mode == 2) return {_addressRegister(AddressRegister{ea.reg})}; + if(ea.mode == 5) return {"$", hex(_readDisplacement(read(AddressRegister{ea.reg})), 6L)}; + if(ea.mode == 6) return {"$", hex(_readIndex(read(AddressRegister{ea.reg})), 6L)}; + if(ea.mode == 7) return {"$", hex((int16)_readPC(), 6L)}; + if(ea.mode == 8) return {"$", hex(_readPC(), 6L)}; + if(ea.mode == 9) return {"$", hex(_pc + (int16)_readPC(), 6L)}; + if(ea.mode == 10) return {"$", hex(_readIndex(_pc), 6L)}; + return "???"; //should never occur (modes 0, 1, 3, 4, 11 are not valid for LEA) +} + +template auto CPU::_effectiveAddress(EffectiveAddress& ea) -> string { + if(ea.mode == 0) return {_dataRegister(DataRegister{ea.reg})}; + if(ea.mode == 1) return {_addressRegister(AddressRegister{ea.reg})}; + if(ea.mode == 2) return {"(", _addressRegister(AddressRegister{ea.reg}), ")"}; + if(ea.mode == 3) return {"(", _addressRegister(AddressRegister{ea.reg}), ")+"}; + if(ea.mode == 4) return {"-(", _addressRegister(AddressRegister{ea.reg}), ")"}; + if(ea.mode == 5) return {"($", hex(_readDisplacement(read(AddressRegister{ea.reg})), 6L), ")"}; + if(ea.mode == 6) return {"($", hex(_readIndex(read(AddressRegister{ea.reg})), 6L), ")"}; + if(ea.mode == 7) return {"($", hex((int16)_readPC(), 6L), ")"}; + if(ea.mode == 8) return {"($", hex(_readPC(), 6L), ")"}; + if(ea.mode == 9) return {"($", hex(_readDisplacement(_pc), 6L), ")"}; + if(ea.mode == 10) return {"($", hex(_readIndex(_pc), 6L), ")"}; + if(ea.mode == 11) return {"#$", hex(_readPC(), 2 << Size)}; + return "???"; //should never occur +} + +auto CPU::_branch(uint8 displacement) -> string { + uint16 extension = _readPC(); + _pc -= 2; + int32 offset = displacement ? sign(displacement) : sign(extension); + return {"$", hex(_pc + offset, 6L)}; +} + +template auto CPU::_suffix() -> string { + return Size == Byte ? ".b" : Size == Word ? ".w" : ".l"; +} + +auto CPU::_condition(uint4 condition) -> string { + static const string conditions[16] = { + "t ", "f ", "hi", "ls", "cc", "cs", "ne", "eq", + "vc", "vs", "pl", "mi", "ge", "lt", "gt", "le", + }; + return conditions[condition]; +} + +auto CPU::disassembleInstruction(uint32 pc) -> string { + _pc = pc; + return pad(disassembleTable[_readPC()](), -60); //todo: exact maximum length unknown (and sub-optimal) +} + +auto CPU::disassembleContext() -> string { + return { + "d0:", hex(r.d[0], 8L), " ", + "d1:", hex(r.d[1], 8L), " ", + "d2:", hex(r.d[2], 8L), " ", + "d3:", hex(r.d[3], 8L), " ", + "d4:", hex(r.d[4], 8L), " ", + "d5:", hex(r.d[5], 8L), " ", + "d6:", hex(r.d[6], 8L), " ", + "d7:", hex(r.d[7], 8L), " ", + "a0:", hex(r.a[0], 8L), " ", + "a1:", hex(r.a[1], 8L), " ", + "a2:", hex(r.a[2], 8L), " ", + "a3:", hex(r.a[3], 8L), " ", + "a4:", hex(r.a[4], 8L), " ", + "a5:", hex(r.a[5], 8L), " ", + "a6:", hex(r.a[6], 8L), " ", + "a7:", hex(r.a[7], 8L), " ", + "sp:", hex(r.sp, 8L), " ", + r.t ? "T" : "t", + r.s ? "S" : "s", + (uint)r.i, + r.c ? "C" : "c", + r.v ? "V" : "v", + r.z ? "Z" : "z", + r.n ? "N" : "n", + r.x ? "X" : "x", " ", + }; +} + +// + +auto CPU::disassembleABCD(EffectiveAddress from, EffectiveAddress with) -> string { + return {"abcd ", _effectiveAddress(from), ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleADD(EffectiveAddress from, DataRegister with) -> string { + return {"add", _suffix(), " ", _effectiveAddress(from), ",", _dataRegister(with)}; +} + +template auto CPU::disassembleADD(DataRegister from, EffectiveAddress with) -> string { + return {"add", _suffix(), " ", _dataRegister(from), ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleADDA(EffectiveAddress from, AddressRegister with) -> string { + return {"adda", _suffix(), " ", _effectiveAddress(from), ",", _addressRegister(with)}; +} + +template auto CPU::disassembleADDI(EffectiveAddress with) -> string { + return {"addi", _suffix(), " ", _immediate(), ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleADDQ(uint4 immediate, EffectiveAddress with) -> string { + return {"addq", _suffix(), " #", immediate, ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleADDQ(uint4 immediate, AddressRegister with) -> string { + return {"addq", _suffix(), " #", immediate, ",", _addressRegister(with)}; +} + +template auto CPU::disassembleADDX(EffectiveAddress from, EffectiveAddress with) -> string { + return {"addx", _suffix(), " ", _effectiveAddress(from), ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleAND(EffectiveAddress from, DataRegister with) -> string { + return {"and", _suffix(), " ", _effectiveAddress(from), ",", _dataRegister(with)}; +} + +template auto CPU::disassembleAND(DataRegister from, EffectiveAddress with) -> string { + return {"and", _suffix(), " ", _dataRegister(from), ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleANDI(EffectiveAddress ea) -> string { + return {"andi", _suffix(), " ", _immediate(), ",", _effectiveAddress(ea)}; +} + +auto CPU::disassembleANDI_TO_CCR() -> string { + return {"andi ", _immediate(), ",ccr"}; +} + +auto CPU::disassembleANDI_TO_SR() -> string { + return {"andi ", _immediate(), ",sr"}; +} + +template auto CPU::disassembleASL(uint4 count, DataRegister with) -> string { + return {"asl", _suffix(), " #", count, ",", _dataRegister(with)}; +} + +template auto CPU::disassembleASL(DataRegister from, DataRegister with) -> string { + return {"asl", _suffix(), " ", _dataRegister(from), ",", _dataRegister(with)}; +} + +auto CPU::disassembleASL(EffectiveAddress with) -> string { + return {"asl", _suffix(), " ", _effectiveAddress(with)}; +} + +template auto CPU::disassembleASR(uint4 count, DataRegister modify) -> string { + return {"asr", _suffix(), " #", count, ",", _dataRegister(modify)}; +} + +template auto CPU::disassembleASR(DataRegister from, DataRegister modify) -> string { + return {"asr", _suffix(), " ", _dataRegister(from), ",", _dataRegister(modify)}; +} + +auto CPU::disassembleASR(EffectiveAddress with) -> string { + return {"asr", _suffix(), " ", _effectiveAddress(with)}; +} + +auto CPU::disassembleBCC(uint4 test, uint8 displacement) -> string { + auto cc = _condition(test); + return {"b", cc, " ", _branch(displacement)}; +} + +template auto CPU::disassembleBCHG(DataRegister bit, EffectiveAddress with) -> string { + return {"bchg", _suffix(), " ", _dataRegister(bit), ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleBCHG(EffectiveAddress with) -> string { + return {"bchg", _suffix(), " ", _immediate(), ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleBCLR(DataRegister bit, EffectiveAddress with) -> string { + return {"bclr", _suffix(), " ", _dataRegister(bit), ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleBCLR(EffectiveAddress with) -> string { + return {"bclr", _suffix(), " ", _immediate(), ",", _effectiveAddress(with)}; +} + +auto CPU::disassembleBRA(uint8 displacement) -> string { + return {"bra ", _branch(displacement)}; +} + +template auto CPU::disassembleBSET(DataRegister bit, EffectiveAddress with) -> string { + return {"bset", _suffix(), " ", _dataRegister(bit), ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleBSET(EffectiveAddress with) -> string { + return {"bset", _suffix(), " ", _immediate(), ",", _effectiveAddress(with)}; +} + +auto CPU::disassembleBSR(uint8 displacement) -> string { + return {"bsr ", _branch(displacement)}; +} + +template auto CPU::disassembleBTST(DataRegister bit, EffectiveAddress with) -> string { + return {"btst", _suffix(), " ", _dataRegister(bit), ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleBTST(EffectiveAddress with) -> string { + return {"btst", _suffix(), " ", _immediate(), ",", _effectiveAddress(with)}; +} + +auto CPU::disassembleCHK(DataRegister compare, EffectiveAddress maximum) -> string { + return {"chk", _suffix(), " ", _effectiveAddress(maximum), ",", _dataRegister(compare)}; +} + +template auto CPU::disassembleCLR(EffectiveAddress ea) -> string { + return {"clr", _suffix(), " ", _effectiveAddress(ea)}; +} + +template auto CPU::disassembleCMP(EffectiveAddress from, DataRegister with) -> string { + return {"cmp", _suffix(), " ", _effectiveAddress(from), ",", _dataRegister(with)}; +} + +template auto CPU::disassembleCMPA(EffectiveAddress from, AddressRegister with) -> string { + return {"cmpa", _suffix(), " ", _effectiveAddress(from), ",", _addressRegister(with)}; +} + +template auto CPU::disassembleCMPI(EffectiveAddress with) -> string { + return {"cmpi", _suffix(), " ", _immediate(), ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleCMPM(EffectiveAddress from, EffectiveAddress with) -> string { + return {"cmpm", _suffix(), " ", _effectiveAddress(from), ",", _effectiveAddress(with)}; +} + +auto CPU::disassembleDBCC(uint4 condition, DataRegister with) -> string { + auto base = _pc; + auto displacement = (int16)_readPC(); + return {"db", _condition(condition), " ", _dataRegister(with), ",$", hex(base + displacement, 6L)}; +} + +auto CPU::disassembleDIVS(EffectiveAddress from, DataRegister with) -> string { + return {"divs", _suffix(), " ", _effectiveAddress(from), ",", _dataRegister(with)}; +} + +auto CPU::disassembleDIVU(EffectiveAddress from, DataRegister with) -> string { + return {"divu", _suffix(), " ", _effectiveAddress(from), ",", _dataRegister(with)}; +} + +template auto CPU::disassembleEOR(DataRegister from, EffectiveAddress with) -> string { + return {"eor", _suffix(), " ", _dataRegister(from), ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleEORI(EffectiveAddress with) -> string { + return {"eori", _suffix(), " ", _immediate(), ",", _effectiveAddress(with)}; +} + +auto CPU::disassembleEORI_TO_CCR() -> string { + return {"eori ", _immediate(), ",ccr"}; +} + +auto CPU::disassembleEORI_TO_SR() -> string { + return {"eori ", _immediate(), ",sr"}; +} + +auto CPU::disassembleEXG(DataRegister x, DataRegister y) -> string { + return {"exg ", _dataRegister(x), ",", _dataRegister(y)}; +} + +auto CPU::disassembleEXG(AddressRegister x, AddressRegister y) -> string { + return {"exg ", _addressRegister(x), ",", _addressRegister(y)}; +} + +auto CPU::disassembleEXG(DataRegister x, AddressRegister y) -> string { + return {"exg ", _dataRegister(x), ",", _addressRegister(y)}; +} + +template auto CPU::disassembleEXT(DataRegister with) -> string { + return {"ext", _suffix(), " ", _dataRegister(with)}; +} + +auto CPU::disassembleILLEGAL(uint16 code) -> string { + if(code.bit(12,15) == 0xa) return {"linea $", hex(code.bit(0,11), 3L)}; + if(code.bit(12,15) == 0xf) return {"linef $", hex(code.bit(0,11), 3L)}; + return {"illegal "}; +} + +auto CPU::disassembleJMP(EffectiveAddress from) -> string { + return {"jmp ", _effectiveAddress(from)}; +} + +auto CPU::disassembleJSR(EffectiveAddress from) -> string { + return {"jsr ", _effectiveAddress(from)}; +} + +auto CPU::disassembleLEA(EffectiveAddress from, AddressRegister with) -> string { + return {"lea ", _address(from), ",", _addressRegister(with)}; +} + +auto CPU::disassembleLINK(AddressRegister with) -> string { + return {"link ", _addressRegister(with), ",", _immediate()}; +} + +template auto CPU::disassembleLSL(uint4 count, DataRegister with) -> string { + return {"lsl", _suffix(), " #", count, ",", _dataRegister(with)}; +} + +template auto CPU::disassembleLSL(DataRegister from, DataRegister with) -> string { + return {"lsl", _suffix(), " ", _dataRegister(from), ",", _dataRegister(with)}; +} + +auto CPU::disassembleLSL(EffectiveAddress with) -> string { + return {"lsl", _suffix(), " ", _effectiveAddress(with)}; +} + +template auto CPU::disassembleLSR(uint4 count, DataRegister with) -> string { + return {"lsr", _suffix(), " #", count, ",", _dataRegister(with)}; +} + +template auto CPU::disassembleLSR(DataRegister from, DataRegister with) -> string { + return {"lsr", _suffix(), " ", _dataRegister(from), ",", _dataRegister(with)}; +} + +auto CPU::disassembleLSR(EffectiveAddress with) -> string { + return {"lsr", _suffix(), " ", _effectiveAddress(with)}; +} + +template auto CPU::disassembleMOVE(EffectiveAddress from, EffectiveAddress to) -> string { + return {"move", _suffix(), " ", _effectiveAddress(from), ",", _effectiveAddress(to)}; +} + +template auto CPU::disassembleMOVEA(EffectiveAddress from, AddressRegister to) -> string { + return {"movea ", _effectiveAddress(from), ",", _addressRegister(to)}; +} + +template auto CPU::disassembleMOVEM_TO_MEM(EffectiveAddress to) -> string { + string op{"movem", _suffix(), " "}; + + uint16 list = _readPC(); + string regs; + for(uint n : range(8)) if(list.bit(0 + n)) regs.append(_dataRegister(DataRegister{n}), ","); + regs.trimRight(","); + if(regs && list >> 8) regs.append("/"); + for(uint n : range(8)) if(list.bit(8 + n)) regs.append(_addressRegister(AddressRegister{n}), ","); + regs.trimRight(","); + + return {op, regs, ",", _effectiveAddress(to)}; +} + +template auto CPU::disassembleMOVEM_TO_REG(EffectiveAddress from) -> string { + string op{"movem", _suffix(), " "}; + + uint16 list = _readPC(); + string regs; + for(uint n : range(8)) if(list.bit(0 + n)) regs.append(_dataRegister(DataRegister{n}), ","); + regs.trimRight(","); + if(regs && list >> 8) regs.append("/"); + for(uint n : range(8)) if(list.bit(8 + n)) regs.append(_addressRegister(AddressRegister{n}), ","); + regs.trimRight(","); + + return {op, _effectiveAddress(from), ",", regs}; +} + +template auto CPU::disassembleMOVEP(DataRegister from, EffectiveAddress to) -> string { + return {"movep", _suffix(), " ", _dataRegister(from), ",", _effectiveAddress(to)}; +} + +template auto CPU::disassembleMOVEP(EffectiveAddress from, DataRegister to) -> string { + return {"movep", _suffix(), " ", _effectiveAddress(from), ",", _dataRegister(to)}; +} + +auto CPU::disassembleMOVEQ(uint8 immediate, DataRegister to) -> string { + return {"moveq #$", hex(immediate, 2L), ",", _dataRegister(to)}; +} + +auto CPU::disassembleMOVE_FROM_SR(EffectiveAddress to) -> string { + return {"move sr,", _effectiveAddress(to)}; +} + +auto CPU::disassembleMOVE_TO_CCR(EffectiveAddress from) -> string { + return {"move ", _effectiveAddress(from), ",ccr"}; +} + +auto CPU::disassembleMOVE_TO_SR(EffectiveAddress from) -> string { + return {"move ", _effectiveAddress(from), ",sr"}; +} + +auto CPU::disassembleMOVE_FROM_USP(AddressRegister to) -> string { + return {"move usp,", _addressRegister(to)}; +} + +auto CPU::disassembleMOVE_TO_USP(AddressRegister from) -> string { + return {"move ", _addressRegister(from), ",usp"}; +} + +auto CPU::disassembleMULS(EffectiveAddress from, DataRegister with) -> string { + return {"muls", _suffix(), " ", _effectiveAddress(from), ",", _dataRegister(with)}; +} + +auto CPU::disassembleMULU(EffectiveAddress from, DataRegister with) -> string { + return {"mulu", _suffix(), " ", _effectiveAddress(from), ",", _dataRegister(with)}; +} + +auto CPU::disassembleNBCD(EffectiveAddress with) -> string { + return {"nbcd ", _effectiveAddress(with)}; +} + +template auto CPU::disassembleNEG(EffectiveAddress with) -> string { + return {"neg", _suffix(), " ", _effectiveAddress(with)}; +} + +template auto CPU::disassembleNEGX(EffectiveAddress with) -> string { + return {"negx", _suffix(), " ", _effectiveAddress(with)}; +} + +auto CPU::disassembleNOP() -> string { + return {"nop "}; +} + +template auto CPU::disassembleNOT(EffectiveAddress with) -> string { + return {"not", _suffix(), " ", _effectiveAddress(with)}; +} + +template auto CPU::disassembleOR(EffectiveAddress from, DataRegister with) -> string { + return {"or", _suffix(), " ", _effectiveAddress(from), ",", _dataRegister(with)}; +} + +template auto CPU::disassembleOR(DataRegister from, EffectiveAddress with) -> string { + return {"or", _suffix(), " ", _dataRegister(from), ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleORI(EffectiveAddress with) -> string { + return {"ori", _suffix(), " ", _immediate(), ",", _effectiveAddress(with)}; +} + +auto CPU::disassembleORI_TO_CCR() -> string { + return {"ori ", _immediate(), ",ccr"}; +} + +auto CPU::disassembleORI_TO_SR() -> string { + return {"ori ", _immediate(), ",sr"}; +} + +auto CPU::disassemblePEA(EffectiveAddress from) -> string { + return {"pea ", _effectiveAddress(from)}; +} + +auto CPU::disassembleRESET() -> string { + return {"reset "}; +} + +template auto CPU::disassembleROL(uint4 count, DataRegister with) -> string { + return {"rol", _suffix(), " #", count, ",", _dataRegister(with)}; +} + +template auto CPU::disassembleROL(DataRegister from, DataRegister with) -> string { + return {"rol", _suffix(), " ", _dataRegister(from), ",", _dataRegister(with)}; +} + +auto CPU::disassembleROL(EffectiveAddress with) -> string { + return {"rol", _suffix(), " ", _effectiveAddress(with)}; +} + +template auto CPU::disassembleROR(uint4 count, DataRegister with) -> string { + return {"ror", _suffix(), " #", count, ",", _dataRegister(with)}; +} + +template auto CPU::disassembleROR(DataRegister from, DataRegister with) -> string { + return {"ror", _suffix(), " ", _dataRegister(from) ,",", _dataRegister(with)}; +} + +auto CPU::disassembleROR(EffectiveAddress with) -> string { + return {"ror", _suffix(), " ", _effectiveAddress(with)}; +} + +template auto CPU::disassembleROXL(uint4 count, DataRegister with) -> string { + return {"roxl", _suffix(), " #", count, ",", _dataRegister(with)}; +} + +template auto CPU::disassembleROXL(DataRegister from, DataRegister with) -> string { + return {"roxl", _suffix(), " ", _dataRegister(from), ",", _dataRegister(with)}; +} + +auto CPU::disassembleROXL(EffectiveAddress with) -> string { + return {"roxl", _suffix(), " ", _effectiveAddress(with)}; +} + +template auto CPU::disassembleROXR(uint4 count, DataRegister with) -> string { + return {"roxr", _suffix(), " #", count, ",", _dataRegister(with)}; +} + +template auto CPU::disassembleROXR(DataRegister from, DataRegister with) -> string { + return {"roxr", _suffix(), " ", _dataRegister(from), ",", _dataRegister(with)}; +} + +auto CPU::disassembleROXR(EffectiveAddress with) -> string { + return {"roxr", _suffix(), " ", _effectiveAddress(with)}; +} + +auto CPU::disassembleRTE() -> string { + return {"rte "}; +} + +auto CPU::disassembleRTR() -> string { + return {"rtr "}; +} + +auto CPU::disassembleRTS() -> string { + return {"rts "}; +} + +auto CPU::disassembleSBCD(EffectiveAddress from, EffectiveAddress with) -> string { + return {"sbcd ", _effectiveAddress(from), ",", _effectiveAddress(with)}; +} + +auto CPU::disassembleSCC(uint4 test, EffectiveAddress to) -> string { + return {"s", _condition(test), " ", _effectiveAddress(to)}; +} + +auto CPU::disassembleSTOP() -> string { + return {"stop ", _immediate()}; +} + +template auto CPU::disassembleSUB(EffectiveAddress from, DataRegister with) -> string { + return {"sub", _suffix(), " ", _effectiveAddress(from), ",", _dataRegister(with)}; +} + +template auto CPU::disassembleSUB(DataRegister from, EffectiveAddress with) -> string { + return {"sub", _suffix(), " ", _dataRegister(from), ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleSUBA(EffectiveAddress from, AddressRegister with) -> string { + return {"suba", _suffix(), " ", _effectiveAddress(from), ",", _addressRegister(with)}; +} + +template auto CPU::disassembleSUBI(EffectiveAddress with) -> string { + return {"subi", _suffix(), " ", _immediate(), ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleSUBQ(uint4 immediate, EffectiveAddress with) -> string { + return {"subq", _suffix(), " #", immediate, ",", _effectiveAddress(with)}; +} + +template auto CPU::disassembleSUBQ(uint4 immediate, AddressRegister with) -> string { + return {"subq", _suffix(), " #", immediate, ",", _addressRegister(with)}; +} + +template auto CPU::disassembleSUBX(EffectiveAddress from, EffectiveAddress with) -> string { + return {"subx", _suffix(), " ", _effectiveAddress(from), ",", _effectiveAddress(with)}; +} + +auto CPU::disassembleSWAP(DataRegister with) -> string { + return {"swap ", _dataRegister(with)}; +} + +auto CPU::disassembleTAS(EffectiveAddress with) -> string { + return {"tas ", _effectiveAddress(with)}; +} + +auto CPU::disassembleTRAP(uint4 vector) -> string { + return {"trap #", vector}; +} + +auto CPU::disassembleTRAPV() -> string { + return {"trapv "}; +} + +template auto CPU::disassembleTST(EffectiveAddress from) -> string { + return {"tst", _suffix(), " ", _effectiveAddress(from)}; +} + +auto CPU::disassembleUNLK(AddressRegister with) -> string { + return {"unlk ", _addressRegister(with)}; +} diff --git a/higan/md/cpu/effective-address.cpp b/higan/md/cpu/effective-address.cpp new file mode 100644 index 0000000..0ad96a6 --- /dev/null +++ b/higan/md/cpu/effective-address.cpp @@ -0,0 +1,254 @@ +//used by JMP and JSR: as PC is guaranteed to change, avoid performing any unnecessary prefetches +auto CPU::prefetched(EffectiveAddress& ea) -> uint32 { + if(ea.valid) return ea.address; + ea.valid = true; + + switch(ea.mode) { + + case AddressRegisterIndirect: { + return ea.address = r.a[ea.reg]; + } + + case AddressRegisterIndirectWithDisplacement: { + idle(2); + return ea.address = r.a[ea.reg] + (int16)prefetched(); + } + + case AddressRegisterIndirectWithIndex: { + idle(6); + auto extension = prefetched(); + auto index = extension & 0x8000 ? r.a[extension >> 12 & 7] : r.d[extension >> 12 & 7]; + if(!(extension & 0x800)) index = (int16)index; + return ea.address = r.a[ea.reg] + index + (int8)extension; + } + + case AbsoluteShortIndirect: { + idle(2); + return ea.address = (int16)prefetched(); + } + + case AbsoluteLongIndirect: { + auto hi = prefetch(); + auto lo = prefetched(); + return ea.address = hi << 16 | lo << 0; + } + + case ProgramCounterIndirectWithDisplacement: { + idle(2); + auto base = r.pc - 2; + return ea.address = base + (int16)prefetched(); + } + + case ProgramCounterIndirectWithIndex: { + idle(6); + auto base = r.pc - 2; + auto extension = prefetched(); + auto index = extension & 0x8000 ? r.a[extension >> 12 & 7] : r.d[extension >> 12 & 7]; + if(!(extension & 0x800)) index = (int16)index; + return ea.address = base + index + (int8)extension; + } + + } + + return ea.address = 0; //should never occur +} + +template auto CPU::fetch(EffectiveAddress& ea) -> uint32 { + if(ea.valid) return ea.address; + ea.valid = true; + + switch(ea.mode) { + + case DataRegisterDirect: { + return ea.address = read(DataRegister{ea.reg}); + } + + case AddressRegisterDirect: { + return ea.address = read(AddressRegister{ea.reg}); + } + + case AddressRegisterIndirect: { + return ea.address = read(AddressRegister{ea.reg}); + } + + case AddressRegisterIndirectWithPostIncrement: { + return ea.address = read(AddressRegister{ea.reg}); + } + + case AddressRegisterIndirectWithPreDecrement: { + return ea.address = read(AddressRegister{ea.reg}); + } + + case AddressRegisterIndirectWithDisplacement: { + return ea.address = read(AddressRegister{ea.reg}) + (int16)extension(); + } + + case AddressRegisterIndirectWithIndex: { + idle(2); + auto data = extension(); + auto index = data & 0x8000 + ? read(AddressRegister{data >> 12}) + : read(DataRegister{data >> 12}); + if(!(data & 0x800)) index = (int16)index; + return ea.address = read(AddressRegister{ea.reg}) + index + (int8)data; + } + + case AbsoluteShortIndirect: { + return ea.address = (int16)extension(); + } + + case AbsoluteLongIndirect: { + return ea.address = extension(); + } + + case ProgramCounterIndirectWithDisplacement: { + auto base = r.pc - 2; + return ea.address = base + (int16)extension(); + } + + case ProgramCounterIndirectWithIndex: { + idle(2); + auto base = r.pc - 2; + auto data = extension(); + auto index = data & 0x8000 + ? read(AddressRegister{data >> 12}) + : read(DataRegister{data >> 12}); + if(!(data & 0x800)) index = (int16)index; + return ea.address = base + index + (int8)data; + } + + case Immediate: { + return ea.address = extension(); + } + + } + + return ea.address = 0; //should never occur +} + +template auto CPU::read(EffectiveAddress& ea) -> uint32 { + fetch(ea); + + switch(ea.mode) { + + case DataRegisterDirect: { + return clip(ea.address); + } + + case AddressRegisterDirect: { + return sign(ea.address); + } + + case AddressRegisterIndirect: { + return read(ea.address); + } + + case AddressRegisterIndirectWithPostIncrement: { + auto address = ea.address + (ea.reg == 7 && Size == Byte ? bytes() : bytes()); + auto data = read(ea.address); + if(!hold) write(AddressRegister{ea.reg}, ea.address = address); + return data; + } + + case AddressRegisterIndirectWithPreDecrement: { + if(!fast) idle(2); + auto address = ea.address - (ea.reg == 7 && Size == Byte ? bytes() : bytes()); + auto data = read(address); + if(!hold) write(AddressRegister{ea.reg}, ea.address = address); + return data; + } + + case AddressRegisterIndirectWithDisplacement: { + return read(ea.address); + } + + case AddressRegisterIndirectWithIndex: { + return read(ea.address); + } + + case AbsoluteShortIndirect: { + return read(ea.address); + } + + case AbsoluteLongIndirect: { + return read(ea.address); + } + + case ProgramCounterIndirectWithDisplacement: { + return read(ea.address); + } + + case ProgramCounterIndirectWithIndex: { + return read(ea.address); + } + + case Immediate: { + return clip(ea.address); + } + + } + + return 0; +} + +template auto CPU::write(EffectiveAddress& ea, uint32 data) -> void { + fetch(ea); + + switch(ea.mode) { + + case DataRegisterDirect: { + return write(DataRegister{ea.reg}, data); + } + + case AddressRegisterDirect: { + return write(AddressRegister{ea.reg}, data); + } + + case AddressRegisterIndirect: { + return write(ea.address, data); + } + + case AddressRegisterIndirectWithPostIncrement: { + auto address = ea.address + (ea.reg == 7 && Size == Byte ? bytes() : bytes()); + write(ea.address, data); + if(!hold) write(AddressRegister{ea.reg}, ea.address = address); + return; + } + + case AddressRegisterIndirectWithPreDecrement: { + auto address = ea.address - (ea.reg == 7 && Size == Byte ? bytes() : bytes()); + write(address, data); + if(!hold) write(AddressRegister{ea.reg}, ea.address = address); + return; + } + + case AddressRegisterIndirectWithDisplacement: { + return write(ea.address, data); + } + + case AddressRegisterIndirectWithIndex: { + return write(ea.address, data); + } + + case AbsoluteShortIndirect: { + return write(ea.address, data); + } + + case AbsoluteLongIndirect: { + return write(ea.address, data); + } + + case ProgramCounterIndirectWithDisplacement: { + return write(ea.address, data); + } + + case ProgramCounterIndirectWithIndex: { + return write(ea.address, data); + } + + case Immediate: { + return; + } + + } +} diff --git a/higan/md/cpu/instruction.cpp b/higan/md/cpu/instruction.cpp new file mode 100644 index 0000000..baa692c --- /dev/null +++ b/higan/md/cpu/instruction.cpp @@ -0,0 +1,1288 @@ +CPU::CPU() { +#if defined(NO_EVENTINSTRUCTION_NOTIFY) + #define bind(id, name, ...) { \ + assert(!instructionTable[id]); \ + instructionTable[id] = [=] { return instruction##name(__VA_ARGS__); }; \ + } + + #define unbind(id) { \ + instructionTable[id].reset(); \ + } +#else + #define bind(id, name, ...) { \ + assert(!instructionTable[id]); \ + instructionTable[id] = [=] { return instruction##name(__VA_ARGS__); }; \ + disassembleTable[id] = [=] { return disassemble##name(__VA_ARGS__); }; \ + } + + #define unbind(id) { \ + instructionTable[id].reset(); \ + disassembleTable[id].reset(); \ + } +#endif + + #define pattern(s) \ + std::integral_constant::value + + //ABCD + for(uint3 treg : range(8)) + for(uint3 sreg : range(8)) { + auto opcode = pattern("1100 ---1 0000 ----") | treg << 9 | sreg << 0; + + EffectiveAddress dataWith{DataRegisterDirect, treg}; + EffectiveAddress dataFrom{DataRegisterDirect, sreg}; + bind(opcode | 0 << 3, ABCD, dataFrom, dataWith); + + EffectiveAddress addressWith{AddressRegisterIndirectWithPreDecrement, treg}; + EffectiveAddress addressFrom{AddressRegisterIndirectWithPreDecrement, sreg}; + bind(opcode | 1 << 3, ABCD, addressFrom, addressWith); + } + + //ADD + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1101 ---0 ++-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode == 7 && reg >= 5) continue; + + EffectiveAddress from{mode, reg}; + DataRegister with{dreg}; + bind(opcode | 0 << 6, ADD, from, with); + bind(opcode | 1 << 6, ADD, from, with); + bind(opcode | 2 << 6, ADD, from, with); + + if(mode == 1) unbind(opcode | 0 << 6); + } + + //ADD + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1101 ---1 ++-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode <= 1 || (mode == 7 && reg >= 2)) continue; + + DataRegister from{dreg}; + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, ADD, from, with); + bind(opcode | 1 << 6, ADD, from, with); + bind(opcode | 2 << 6, ADD, from, with); + } + + //ADDA + for(uint3 areg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1101 ---+ 11-- ----") | areg << 9 | mode << 3 | reg << 0; + if(mode == 7 && reg >= 5) continue; + + AddressRegister with{areg}; + EffectiveAddress from{mode, reg}; + bind(opcode | 0 << 8, ADDA, from, with); + bind(opcode | 1 << 8, ADDA, from, with); + } + + //ADDI + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0000 0110 ++-- ----") | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, ADDI, with); + bind(opcode | 1 << 6, ADDI, with); + bind(opcode | 2 << 6, ADDI, with); + } + + //ADDQ + for(uint3 data : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0101 ---0 ++-- ----") | data << 9 | mode << 3 | reg << 0; + if(mode == 7 && reg >= 2) continue; + + uint4 immediate = data ? (uint4)data : (uint4)8; + if(mode != 1) { + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, ADDQ, immediate, with); + bind(opcode | 1 << 6, ADDQ, immediate, with); + bind(opcode | 2 << 6, ADDQ, immediate, with); + } else { + AddressRegister with{reg}; + bind(opcode | 1 << 6, ADDQ, immediate, with); + bind(opcode | 2 << 6, ADDQ, immediate, with); + } + } + + //ADDX + for(uint3 xreg : range(8)) + for(uint3 yreg : range(8)) { + auto opcode = pattern("1101 ---1 ++00 ----") | xreg << 9 | yreg << 0; + + EffectiveAddress dataWith{DataRegisterDirect, xreg}; + EffectiveAddress dataFrom{DataRegisterDirect, yreg}; + bind(opcode | 0 << 6 | 0 << 3, ADDX, dataFrom, dataWith); + bind(opcode | 1 << 6 | 0 << 3, ADDX, dataFrom, dataWith); + bind(opcode | 2 << 6 | 0 << 3, ADDX, dataFrom, dataWith); + + EffectiveAddress addressWith{AddressRegisterIndirectWithPreDecrement, xreg}; + EffectiveAddress addressFrom{AddressRegisterIndirectWithPreDecrement, yreg}; + bind(opcode | 0 << 6 | 1 << 3, ADDX, addressFrom, addressWith); + bind(opcode | 1 << 6 | 1 << 3, ADDX, addressFrom, addressWith); + bind(opcode | 2 << 6 | 1 << 3, ADDX, addressFrom, addressWith); + } + + //AND + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1100 ---0 ++-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 5)) continue; + + EffectiveAddress from{mode, reg}; + DataRegister with{dreg}; + bind(opcode | 0 << 6, AND, from, with); + bind(opcode | 1 << 6, AND, from, with); + bind(opcode | 2 << 6, AND, from, with); + } + + //AND + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1100 ---1 ++-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode <= 1 || (mode == 7 && reg >= 2)) continue; + + DataRegister from{dreg}; + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, AND, from, with); + bind(opcode | 1 << 6, AND, from, with); + bind(opcode | 2 << 6, AND, from, with); + } + + //ANDI + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0000 0010 ++-- ----") | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, ANDI, with); + bind(opcode | 1 << 6, ANDI, with); + bind(opcode | 2 << 6, ANDI, with); + } + + //ANDI_TO_CCR + { auto opcode = pattern("0000 0010 0011 1100"); + + bind(opcode, ANDI_TO_CCR); + } + + //ANDI_TO_SR + { auto opcode = pattern("0000 0010 0111 1100"); + + bind(opcode, ANDI_TO_SR); + } + + //ASL (immediate) + for(uint3 immediate : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---1 ++00 0---") | immediate << 9 | dreg << 0; + + auto count = immediate ? (uint4)immediate : (uint4)8; + DataRegister with{dreg}; + bind(opcode | 0 << 6, ASL, count, with); + bind(opcode | 1 << 6, ASL, count, with); + bind(opcode | 2 << 6, ASL, count, with); + } + + //ASL (register) + for(uint3 sreg : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---1 ++10 0---") | sreg << 9 | dreg << 0; + + DataRegister from{sreg}; + DataRegister with{dreg}; + bind(opcode | 0 << 6, ASL, from, with); + bind(opcode | 1 << 6, ASL, from, with); + bind(opcode | 2 << 6, ASL, from, with); + } + + //ASL (effective address) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1110 0001 11-- ----") | mode << 3 | reg << 0; + if(mode <= 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode, ASL, with); + } + + //ASR (immediate) + for(uint3 immediate : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---0 ++00 0---") | immediate << 9 | dreg << 0; + + auto count = immediate ? (uint4)immediate : (uint4)8; + DataRegister with{dreg}; + bind(opcode | 0 << 6, ASR, count, with); + bind(opcode | 1 << 6, ASR, count, with); + bind(opcode | 2 << 6, ASR, count, with); + } + + //ASR (register) + for(uint3 sreg : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---0 ++10 0---") | sreg << 9 | dreg << 0; + + DataRegister from{sreg}; + DataRegister with{dreg}; + bind(opcode | 0 << 6, ASR, from, with); + bind(opcode | 1 << 6, ASR, from, with); + bind(opcode | 2 << 6, ASR, from, with); + } + + //ASR (effective address) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1110 0000 11-- ----") | mode << 3 | reg << 0; + if(mode <= 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode, ASR, with); + } + + //BCC + for(uint4 test : range( 16)) + for(uint8 displacement : range(256)) { + if(test <= 1) continue; + + auto opcode = pattern("0110 ---- ---- ----") | test << 8 | displacement << 0; + + bind(opcode, BCC, test, displacement); + } + + //BCHG (register) + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0000 ---1 01-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + DataRegister bit{dreg}; + EffectiveAddress with{mode, reg}; + if(mode == 0) bind(opcode, BCHG, bit, with); + if(mode != 0) bind(opcode, BCHG, bit, with); + } + + //BCHG (immediate) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0000 1000 01-- ----") | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + if(mode == 0) bind(opcode, BCHG, with); + if(mode != 0) bind(opcode, BCHG, with); + } + + //BCLR (register) + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0000 ---1 10-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + DataRegister bit{dreg}; + EffectiveAddress with{mode, reg}; + if(mode == 0) bind(opcode, BCLR, bit, with); + if(mode != 0) bind(opcode, BCLR, bit, with); + } + + //BCLR (immediate) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0000 1000 10-- ----") | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + if(mode == 0) bind(opcode, BCLR, with); + if(mode != 0) bind(opcode, BCLR, with); + } + + //BRA + for(uint8 displacement : range(256)) { + auto opcode = pattern("0110 0000 ---- ----") | displacement << 0; + + bind(opcode, BRA, displacement); + } + + //BSET (register) + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0000 ---1 11-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + DataRegister bit{dreg}; + EffectiveAddress with{mode, reg}; + if(mode == 0) bind(opcode, BSET, bit, with); + if(mode != 0) bind(opcode, BSET, bit, with); + } + + //BSET (immediate) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0000 1000 11-- ----") | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + if(mode == 0) bind(opcode, BSET, with); + if(mode != 0) bind(opcode, BSET, with); + } + + //BSR + for(uint8 displacement : range(256)) { + auto opcode = pattern("0110 0001 ---- ----") | displacement << 0; + + bind(opcode, BSR, 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 || (mode == 7 && reg >= 5)) continue; + + DataRegister bit{dreg}; + EffectiveAddress with{mode, reg}; + if(mode == 0) bind(opcode, BTST, bit, with); + if(mode != 0) bind(opcode, BTST, bit, with); + } + + //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 >= 4)) continue; + + EffectiveAddress with{mode, reg}; + if(mode == 0) bind(opcode, BTST, with); + if(mode != 0) bind(opcode, BTST, with); + } + + //CHK + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0100 ---1 10-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 5)) continue; + + DataRegister compare{dreg}; + EffectiveAddress maximum{mode, reg}; + bind(opcode, CHK, compare, maximum); + } + + //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; + + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, CLR, with); + bind(opcode | 1 << 6, CLR, with); + bind(opcode | 2 << 6, CLR, with); + } + + //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; + if(mode == 7 && reg >= 5) continue; + + DataRegister with{dreg}; + EffectiveAddress from{mode, reg}; + bind(opcode | 0 << 6, CMP, from, with); + bind(opcode | 1 << 6, CMP, from, with); + bind(opcode | 2 << 6, CMP, from, with); + + if(mode == 1) unbind(opcode | 0 << 6); + } + + //CMPA + for(uint3 areg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1011 ---+ 11-- ----") | areg << 9 | mode << 3 | reg << 0; + + AddressRegister with{areg}; + EffectiveAddress from{mode, reg}; + bind(opcode | 0 << 8, CMPA, from, with); + bind(opcode | 1 << 8, CMPA, from, with); + } + + //CMPI + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0000 1100 ++-- ----") | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, CMPI, with); + bind(opcode | 1 << 6, CMPI, with); + bind(opcode | 2 << 6, CMPI, with); + } + + //CMPM + for(uint3 xreg : range(8)) + for(uint3 yreg : range(8)) { + auto opcode = pattern("1011 ---1 ++00 1---") | xreg << 9 | yreg << 0; + + EffectiveAddress with{AddressRegisterIndirectWithPostIncrement, xreg}; + EffectiveAddress from{AddressRegisterIndirectWithPostIncrement, yreg}; + bind(opcode | 0 << 6, CMPM, from, with); + bind(opcode | 1 << 6, CMPM, from, with); + bind(opcode | 2 << 6, CMPM, from, with); + } + + //DBCC + for(uint4 condition : range(16)) + for(uint3 dreg : range( 8)) { + auto opcode = pattern("0101 ---- 1100 1---") | condition << 8 | dreg << 0; + + DataRegister with{dreg}; + bind(opcode, DBCC, condition, with); + } + + //DIVS + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1000 ---1 11-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 5)) continue; + + DataRegister with{dreg}; + EffectiveAddress from{mode, reg}; + bind(opcode, DIVS, from, with); + } + + //DIVU + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1000 ---0 11-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 5)) continue; + + DataRegister with{dreg}; + EffectiveAddress from{mode, reg}; + bind(opcode, DIVU, from, with); + } + + //EOR + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1011 ---1 ++-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + DataRegister from{dreg}; + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, EOR, from, with); + bind(opcode | 1 << 6, EOR, from, with); + bind(opcode | 2 << 6, EOR, from, with); + } + + //EORI + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0000 1010 ++-- ----") | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, EORI, with); + bind(opcode | 1 << 6, EORI, with); + bind(opcode | 2 << 6, EORI, with); + } + + //EORI_TO_CCR + { auto opcode = pattern("0000 1010 0011 1100"); + + bind(opcode, EORI_TO_CCR); + } + + //EORI_TO_SR + { auto opcode = pattern("0000 1010 0111 1100"); + + bind(opcode, EORI_TO_SR); + } + + //EXG + for(uint3 xreg : range(8)) + for(uint3 yreg : range(8)) { + auto opcode = pattern("1100 ---1 0100 0---") | xreg << 9 | yreg << 0; + + DataRegister x{xreg}; + DataRegister y{yreg}; + bind(opcode, EXG, x, y); + } + + //EXG + for(uint3 xreg : range(8)) + for(uint3 yreg : range(8)) { + auto opcode = pattern("1100 ---1 0100 1---") | xreg << 9 | yreg << 0; + + AddressRegister x{xreg}; + AddressRegister y{yreg}; + bind(opcode, EXG, x, y); + } + + //EXG + for(uint3 xreg : range(8)) + for(uint3 yreg : range(8)) { + auto opcode = pattern("1100 ---1 1000 1---") | xreg << 9 | yreg << 0; + + DataRegister x{xreg}; + AddressRegister y{yreg}; + bind(opcode, EXG, x, y); + } + + //EXT + for(uint3 dreg : range(8)) { + auto opcode = pattern("0100 1000 1+00 0---") | dreg << 0; + + DataRegister with{dreg}; + bind(opcode | 0 << 6, EXT, with); + bind(opcode | 1 << 6, EXT, with); + } + + //ILLEGAL + { auto opcode = pattern("0100 1010 1111 1100"); + + bind(opcode, ILLEGAL, opcode); + } + + //JMP + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0100 1110 11-- ----") | mode << 3 | reg << 0; + if(mode <= 1 || mode == 3 || mode == 4 || (mode == 7 && reg >= 4)) continue; + + EffectiveAddress from{mode, reg}; + bind(opcode, JMP, from); + } + + //JSR + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0100 1110 10-- ----") | mode << 3 | reg << 0; + if(mode <= 1 || mode == 3 || mode == 4 || (mode == 7 && reg >= 4)) continue; + + EffectiveAddress from{mode, reg}; + bind(opcode, JSR, from); + } + + //LEA + for(uint3 areg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0100 ---1 11-- ----") | areg << 9 | mode << 3 | reg << 0; + if(mode <= 1 || mode == 3 || mode == 4 || (mode == 7 && reg >= 4)) continue; + + AddressRegister to{areg}; + EffectiveAddress from{mode, reg}; + bind(opcode, LEA, from, to); + } + + //LINK + for(uint3 areg : range(8)) { + auto opcode = pattern("0100 1110 0101 0---") | areg << 0; + + AddressRegister with{areg}; + bind(opcode, LINK, with); + } + + //LSL (immediate) + for(uint3 immediate : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---1 ++00 1---") | immediate << 9 | dreg << 0; + + auto count = immediate ? (uint4)immediate : (uint4)8; + DataRegister with{dreg}; + bind(opcode | 0 << 6, LSL, count, with); + bind(opcode | 1 << 6, LSL, count, with); + bind(opcode | 2 << 6, LSL, count, with); + } + + //LSL (register) + for(uint3 sreg : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---1 ++10 1---") | sreg << 9 | dreg << 0; + + DataRegister from{sreg}; + DataRegister with{dreg}; + bind(opcode | 0 << 6, LSL, from, with); + bind(opcode | 1 << 6, LSL, from, with); + bind(opcode | 2 << 6, LSL, from, with); + } + + //LSL (effective address) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1110 0011 11-- ----") | mode << 3 | reg << 0; + if(mode <= 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode, LSL, with); + } + + //LSR (immediate) + for(uint3 immediate : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---0 ++00 1---") | immediate << 9 | dreg << 0; + + auto count = immediate ? (uint4)immediate : (uint4)8; + DataRegister with{dreg}; + bind(opcode | 0 << 6, LSR, count, with); + bind(opcode | 1 << 6, LSR, count, with); + bind(opcode | 2 << 6, LSR, count, with); + } + + //LSR (register) + for(uint3 sreg : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---0 ++10 1---") | sreg << 9 | dreg << 0; + + DataRegister from{sreg}; + DataRegister with{dreg}; + bind(opcode | 0 << 6, LSR, from, with); + bind(opcode | 1 << 6, LSR, from, with); + bind(opcode | 2 << 6, LSR, from, with); + } + + //LSR (effective address) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1110 0010 11-- ----") | mode << 3 | reg << 0; + if(mode <= 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode, LSR, with); + } + + //MOVE + for(uint3 toReg : range(8)) + for(uint3 toMode : range(8)) + for(uint3 fromMode : range(8)) + for(uint3 fromReg : range(8)) { + auto opcode = pattern("00++ ---- ---- ----") | toReg << 9 | toMode << 6 | fromMode << 3 | fromReg << 0; + if(toMode == 1 || (toMode == 7 && toReg >= 2)) continue; + if(fromMode == 7 && fromReg >= 5) continue; + + EffectiveAddress to{toMode, toReg}; + EffectiveAddress from{fromMode, fromReg}; + bind(opcode | 1 << 12, MOVE, from, to); + bind(opcode | 3 << 12, MOVE, from, to); + bind(opcode | 2 << 12, MOVE, from, to); + + if(fromMode == 1) unbind(opcode | 1 << 12); + } + + //MOVEA + for(uint3 areg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("00++ ---0 01-- ----") | areg << 9 | mode << 3 | reg << 0; + if(mode == 7 && reg >= 5) continue; + + AddressRegister to{areg}; + EffectiveAddress from{mode, reg}; + bind(opcode | 3 << 12, MOVEA, from, to); + bind(opcode | 2 << 12, MOVEA, from, to); + } + + //MOVEM + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0100 1000 1+-- ----") | mode << 3 | reg << 0; + if(mode <= 1 || mode == 3 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress to{mode, reg}; + bind(opcode | 0 << 6, MOVEM_TO_MEM, to); + bind(opcode | 1 << 6, MOVEM_TO_MEM, to); + } + + //MOVEM + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0100 1100 1+-- ----") | mode << 3 | reg << 0; + if(mode <= 1 || mode == 4 || (mode == 7 && reg >= 4)) continue; + + EffectiveAddress from{mode, reg}; + bind(opcode | 0 << 6, MOVEM_TO_REG, from); + bind(opcode | 1 << 6, MOVEM_TO_REG, from); + } + + //MOVEP + for(uint3 dreg : range(8)) + for(uint3 areg : range(8)) { + auto opcode = pattern("0000 ---1 1+00 1---") | dreg << 9 | areg << 0; + + DataRegister from{dreg}; + EffectiveAddress to{AddressRegisterIndirectWithDisplacement, areg}; + bind(opcode | 0 << 6, MOVEP, from, to); + bind(opcode | 1 << 6, MOVEP, from, to); + } + + //MOVEP + for(uint3 dreg : range(8)) + for(uint3 areg : range(8)) { + auto opcode = pattern("0000 ---1 0+00 1---") | dreg << 9 | areg << 0; + + DataRegister to{dreg}; + EffectiveAddress from{AddressRegisterIndirectWithDisplacement, areg}; + bind(opcode | 0 << 6, MOVEP, from, to); + bind(opcode | 1 << 6, MOVEP, from, to); + } + + //MOVEQ + for(uint3 dreg : range( 8)) + for(uint8 immediate : range(256)) { + auto opcode = pattern("0111 ---0 ---- ----") | dreg << 9 | immediate << 0; + + DataRegister to{dreg}; + bind(opcode, MOVEQ, immediate, to); + } + + //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; + + EffectiveAddress to{mode, reg}; + bind(opcode, MOVE_FROM_SR, to); + } + + //MOVE_TO_CCR + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0100 0100 11-- ----") | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 5)) continue; + + EffectiveAddress from{mode, reg}; + bind(opcode, MOVE_TO_CCR, from); + } + + //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 || (mode == 7 && reg >= 5)) continue; + + EffectiveAddress from{mode, reg}; + bind(opcode, MOVE_TO_SR, from); + } + + //MOVE_FROM_USP + for(uint3 areg : range(8)) { + auto opcode = pattern("0100 1110 0110 1---") | areg << 0; + + AddressRegister to{areg}; + bind(opcode, MOVE_FROM_USP, to); + } + + //MOVE_TO_USP + for(uint3 areg : range(8)) { + auto opcode = pattern("0100 1110 0110 0---") | areg << 0; + + AddressRegister from{areg}; + bind(opcode, MOVE_TO_USP, from); + } + + //MULS + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1100 ---1 11-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 5)) continue; + + DataRegister with{dreg}; + EffectiveAddress from{mode, reg}; + bind(opcode, MULS, from, with); + } + + //MULU + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1100 ---0 11-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 5)) continue; + + DataRegister with{dreg}; + EffectiveAddress from{mode, reg}; + bind(opcode, MULU, from, with); + } + + //NBCD + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0100 1000 00-- ----") | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode, NBCD, with); + } + + //NEG + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0100 0100 ++-- ----") | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, NEG, with); + bind(opcode | 1 << 6, NEG, with); + bind(opcode | 2 << 6, NEG, with); + } + + //NEGX + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0100 0000 ++-- ----") | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, NEGX, with); + bind(opcode | 1 << 6, NEGX, with); + bind(opcode | 2 << 6, NEGX, with); + } + + //NOP + { auto opcode = pattern("0100 1110 0111 0001"); + + bind(opcode, NOP); + } + + //NOT + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0100 0110 ++-- ----") | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, NOT, with); + bind(opcode | 1 << 6, NOT, with); + bind(opcode | 2 << 6, NOT, with); + } + + //OR + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1000 ---0 ++-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 5)) continue; + + EffectiveAddress from{mode, reg}; + DataRegister with{dreg}; + bind(opcode | 0 << 6, OR, from, with); + bind(opcode | 1 << 6, OR, from, with); + bind(opcode | 2 << 6, OR, from, with); + } + + //OR + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1000 ---1 ++-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode <= 1 || (mode == 7 && reg >= 2)) continue; + + DataRegister from{dreg}; + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, OR, from, with); + bind(opcode | 1 << 6, OR, from, with); + bind(opcode | 2 << 6, OR, from, with); + } + + //ORI + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0000 0000 ++-- ----") | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, ORI, with); + bind(opcode | 1 << 6, ORI, with); + bind(opcode | 2 << 6, ORI, with); + } + + //ORI_TO_CCR + { auto opcode = pattern("0000 0000 0011 1100"); + + bind(opcode, ORI_TO_CCR); + } + + //ORI_TO_SR + { auto opcode = pattern("0000 0000 0111 1100"); + + bind(opcode, ORI_TO_SR); + } + + //PEA + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0100 1000 01-- ----") | mode << 3 | reg << 0; + if(mode <= 1 || mode == 3 || mode == 4 || (mode == 7 && reg >= 4)) continue; + + EffectiveAddress from{mode, reg}; + bind(opcode, PEA, from); + } + + //RESET + { auto opcode = pattern("0100 1110 0111 0000"); + + bind(opcode, RESET); + } + + //ROL (immediate) + for(uint3 immediate : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---1 ++01 1---") | immediate << 9 | dreg << 0; + + auto count = immediate ? (uint4)immediate : (uint4)8; + DataRegister with{dreg}; + bind(opcode | 0 << 6, ROL, count, with); + bind(opcode | 1 << 6, ROL, count, with); + bind(opcode | 2 << 6, ROL, count, with); + } + + //ROL (register) + for(uint3 sreg : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---1 ++11 1---") | sreg << 9 | dreg << 0; + + DataRegister from{sreg}; + DataRegister with{dreg}; + bind(opcode | 0 << 6, ROL, from, with); + bind(opcode | 1 << 6, ROL, from, with); + bind(opcode | 2 << 6, ROL, from, with); + } + + //ROL (effective address) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1110 0111 11-- ----") | mode << 3 | reg << 0; + if(mode <= 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode, ROL, with); + } + + //ROR (immediate) + for(uint3 immediate : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---0 ++01 1---") | immediate << 9 | dreg << 0; + + auto count = immediate ? (uint4)immediate : (uint4)8; + DataRegister with{dreg}; + bind(opcode | 0 << 6, ROR, count, with); + bind(opcode | 1 << 6, ROR, count, with); + bind(opcode | 2 << 6, ROR, count, with); + } + + //ROR (register) + for(uint3 sreg : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---0 ++11 1---") | sreg << 9 | dreg << 0; + + DataRegister from{sreg}; + DataRegister with{dreg}; + bind(opcode | 0 << 6, ROR, from, with); + bind(opcode | 1 << 6, ROR, from, with); + bind(opcode | 2 << 6, ROR, from, with); + } + + //ROR (effective address) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1110 0110 11-- ----") | mode << 3 | reg << 0; + if(mode <= 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode, ROR, with); + } + + //ROXL (immediate) + for(uint3 immediate : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---1 ++01 0---") | immediate << 9 | dreg << 0; + + auto count = immediate ? (uint4)immediate : (uint4)8; + DataRegister with{dreg}; + bind(opcode | 0 << 6, ROXL, count, with); + bind(opcode | 1 << 6, ROXL, count, with); + bind(opcode | 2 << 6, ROXL, count, with); + } + + //ROXL (register) + for(uint3 sreg : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---1 ++11 0---") | sreg << 9 | dreg << 0; + + DataRegister from{sreg}; + DataRegister with{dreg}; + bind(opcode | 0 << 6, ROXL, from, with); + bind(opcode | 1 << 6, ROXL, from, with); + bind(opcode | 2 << 6, ROXL, from, with); + } + + //ROXL (effective address) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1110 0101 11-- ----") | mode << 3 | reg << 0; + if(mode <= 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode, ROXL, with); + } + + //ROXR (immediate) + for(uint3 immediate : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---0 ++01 0---") | immediate << 9 | dreg << 0; + + auto count = immediate ? (uint4)immediate : (uint4)8; + DataRegister with{dreg}; + bind(opcode | 0 << 6, ROXR, count, with); + bind(opcode | 1 << 6, ROXR, count, with); + bind(opcode | 2 << 6, ROXR, count, with); + } + + //ROXR (register) + for(uint3 sreg : range(8)) + for(uint3 dreg : range(8)) { + auto opcode = pattern("1110 ---0 ++11 0---") | sreg << 9 | dreg << 0; + + DataRegister from{sreg}; + DataRegister with{dreg}; + bind(opcode | 0 << 6, ROXR, from, with); + bind(opcode | 1 << 6, ROXR, from, with); + bind(opcode | 2 << 6, ROXR, from, with); + } + + //ROXR (effective address) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1110 0100 11-- ----") | mode << 3 | reg << 0; + if(mode <= 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode, ROXR, with); + } + + //RTE + { auto opcode = pattern("0100 1110 0111 0011"); + + bind(opcode, RTE); + } + + //RTR + { auto opcode = pattern("0100 1110 0111 0111"); + + bind(opcode, RTR); + } + + //RTS + { auto opcode = pattern("0100 1110 0111 0101"); + + bind(opcode, RTS); + } + + //SBCD + for(uint3 treg : range(8)) + for(uint3 sreg : range(8)) { + auto opcode = pattern("1000 ---1 0000 ----") | treg << 9 | sreg << 0; + + EffectiveAddress dataWith{DataRegisterDirect, treg}; + EffectiveAddress dataFrom{DataRegisterDirect, sreg}; + bind(opcode | 0 << 3, SBCD, dataFrom, dataWith); + + EffectiveAddress addressWith{AddressRegisterIndirectWithPreDecrement, treg}; + EffectiveAddress addressFrom{AddressRegisterIndirectWithPreDecrement, sreg}; + bind(opcode | 1 << 3, SBCD, addressFrom, addressWith); + } + + //SCC + for(uint4 test : range(16)) + for(uint3 mode : range( 8)) + for(uint3 reg : range( 8)) { + auto opcode = pattern("0101 ---- 11-- ----") | test << 8 | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress to{mode, reg}; + bind(opcode, SCC, test, to); + } + + //STOP + { auto opcode = pattern("0100 1110 0111 0010"); + + bind(opcode, STOP); + } + + //SUB + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1001 ---0 ++-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode == 7 && reg >= 5) continue; + + EffectiveAddress from{mode, reg}; + DataRegister to{dreg}; + bind(opcode | 0 << 6, SUB, from, to); + bind(opcode | 1 << 6, SUB, from, to); + bind(opcode | 2 << 6, SUB, from, to); + + if(mode == 1) unbind(opcode | 0 << 6); + } + + //SUB + for(uint3 dreg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1001 ---1 ++-- ----") | dreg << 9 | mode << 3 | reg << 0; + if(mode <= 1 || (mode == 7 && reg >= 2)) continue; + + DataRegister from{dreg}; + EffectiveAddress to{mode, reg}; + bind(opcode | 0 << 6, SUB, from, to); + bind(opcode | 1 << 6, SUB, from, to); + bind(opcode | 2 << 6, SUB, from, to); + } + + //SUBA + for(uint3 areg : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("1001 ---+ 11-- ----") | areg << 9 | mode << 3 | reg << 0; + if(mode == 7 && reg >= 5) continue; + + AddressRegister to{areg}; + EffectiveAddress from{mode, reg}; + bind(opcode | 0 << 8, SUBA, from, to); + bind(opcode | 1 << 8, SUBA, from, to); + } + + //SUBI + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0000 0100 ++-- ----") | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, SUBI, with); + bind(opcode | 1 << 6, SUBI, with); + bind(opcode | 2 << 6, SUBI, with); + } + + //SUBQ + for(uint3 data : range(8)) + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0101 ---1 ++-- ----") | data << 9 | mode << 3 | reg << 0; + if(mode == 7 && reg >= 2) continue; + + auto immediate = data ? (uint4)data : (uint4)8; + if(mode != 1) { + EffectiveAddress with{mode, reg}; + bind(opcode | 0 << 6, SUBQ, immediate, with); + bind(opcode | 1 << 6, SUBQ, immediate, with); + bind(opcode | 2 << 6, SUBQ, immediate, with); + } else { + AddressRegister with{reg}; + bind(opcode | 1 << 6, SUBQ, immediate, with); + bind(opcode | 2 << 6, SUBQ, immediate, with); + } + } + + //SUBX + for(uint3 treg : range(8)) + for(uint3 sreg : range(8)) { + auto opcode = pattern("1001 ---1 ++00 ----") | treg << 9 | sreg << 0; + + EffectiveAddress dataWith{DataRegisterDirect, treg}; + EffectiveAddress dataFrom{DataRegisterDirect, sreg}; + bind(opcode | 0 << 6 | 0 << 3, SUBX, dataFrom, dataWith); + bind(opcode | 1 << 6 | 0 << 3, SUBX, dataFrom, dataWith); + bind(opcode | 2 << 6 | 0 << 3, SUBX, dataFrom, dataWith); + + EffectiveAddress addressWith{AddressRegisterIndirectWithPreDecrement, treg}; + EffectiveAddress addressFrom{AddressRegisterIndirectWithPreDecrement, sreg}; + bind(opcode | 0 << 6 | 1 << 3, SUBX, addressFrom, addressWith); + bind(opcode | 1 << 6 | 1 << 3, SUBX, addressFrom, addressWith); + bind(opcode | 2 << 6 | 1 << 3, SUBX, addressFrom, addressWith); + } + + //SWAP + for(uint3 dreg : range(8)) { + auto opcode = pattern("0100 1000 0100 0---") | dreg << 0; + + DataRegister with{dreg}; + bind(opcode, SWAP, with); + } + + //TAS + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0100 1010 11-- ----") | mode << 3 | reg << 0; + if(mode == 1 || (mode == 7 && reg >= 2)) continue; + + EffectiveAddress with{mode, reg}; + bind(opcode, TAS, with); + } + + //TRAP + for(uint4 vector : range(16)) { + auto opcode = pattern("0100 1110 0100 ----") | vector << 0; + + bind(opcode, TRAP, vector); + } + + //TRAPV + { auto opcode = pattern("0100 1110 0111 0110"); + + bind(opcode, TRAPV); + } + + //TST + for(uint3 mode : range(8)) + for(uint3 reg : range(8)) { + auto opcode = pattern("0100 1010 ++-- ----") | mode << 3 | reg << 0; + if(mode == 7 && reg >= 2) continue; + + EffectiveAddress from{mode, reg}; + bind(opcode | 0 << 6, TST, from); + bind(opcode | 1 << 6, TST, from); + bind(opcode | 2 << 6, TST, from); + + if(mode == 1) unbind(opcode | 0 << 6); + } + + //UNLK + for(uint3 areg : range(8)) { + auto opcode = pattern("0100 1110 0101 1---") | areg << 0; + + AddressRegister with{areg}; + bind(opcode, UNLK, with); + } + + //ILLEGAL + for(uint16 opcode : range(65536)) { + if(instructionTable[opcode]) continue; + bind(opcode, ILLEGAL, opcode); + } + + #undef bind + #undef unbind + #undef pattern +} diff --git a/higan/md/cpu/instructions.cpp b/higan/md/cpu/instructions.cpp new file mode 100644 index 0000000..8bd81dd --- /dev/null +++ b/higan/md/cpu/instructions.cpp @@ -0,0 +1,1327 @@ +auto CPU::instructionABCD(EffectiveAddress from, EffectiveAddress with) -> void { + if(from.mode == DataRegisterDirect) idle(2); + auto target = read(with); + auto source = read(from); + auto result = source + target + r.x; + bool c = false; + bool v = false; + + if(((target ^ source ^ result) & 0x10) || (result & 0x0f) >= 0x0a) { + auto previous = result; + result += 0x06; + v |= ((~previous & 0x80) & (result & 0x80)); + } + + if(result >= 0xa0) { + auto previous = result; + result += 0x60; + c = true; + v |= ((~previous & 0x80) & (result & 0x80)); + } + + prefetch(); + write(with, result); + + r.c = c; + r.v = v; + r.z = clip(result) ? 0 : r.z; + r.n = sign(result) < 0; + r.x = r.c; +} + +template auto CPU::instructionADD(EffectiveAddress from, DataRegister with) -> void { + if constexpr(Size == Long) { + if(from.mode == DataRegisterDirect || from.mode == AddressRegisterDirect || from.mode == Immediate) { + idle(4); + } else { + idle(2); + } + } + auto source = read(from); + auto target = read(with); + auto result = ADD(source, target); + prefetch(); + write(with, result); +} + +template auto CPU::instructionADD(DataRegister from, EffectiveAddress with) -> void { + auto source = read(from); + auto target = read(with); + auto result = ADD(source, target); + prefetch(); + write(with, result); +} + +template auto CPU::instructionADDA(EffectiveAddress from, AddressRegister with) -> void { + if(Size != Long || from.mode == DataRegisterDirect || from.mode == AddressRegisterDirect || from.mode == Immediate) { + idle(4); + } else { + idle(2); + } + auto source = sign(read(from)); + auto target = read(with); + prefetch(); + write(with, source + target); +} + +template auto CPU::instructionADDI(EffectiveAddress with) -> void { + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(4); + } + auto source = extension(); + auto target = read(with); + auto result = ADD(source, target); + prefetch(); + write(with, result); +} + +template auto CPU::instructionADDQ(uint4 immediate, EffectiveAddress with) -> void { + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(4); + } + auto source = immediate; + auto target = read(with); + auto result = ADD(source, target); + prefetch(); + write(with, result); +} + +//Size is ignored: always uses Long +template auto CPU::instructionADDQ(uint4 immediate, AddressRegister with) -> void { + idle(4); + auto result = read(with) + immediate; + prefetch(); + write(with, result); +} + +template auto CPU::instructionADDX(EffectiveAddress from, EffectiveAddress with) -> void { + if constexpr(Size == Long) { + if(from.mode == DataRegisterDirect) idle(4); + } + auto target = read(with); + auto source = read(from); + auto result = ADD(source, target); + prefetch(); + write(with, result); +} + +template auto CPU::instructionAND(EffectiveAddress from, DataRegister with) -> void { + if constexpr(Size == Long) { + if(from.mode == DataRegisterDirect || from.mode == Immediate) { + idle(4); + } else { + idle(2); + } + } + auto source = read(from); + auto target = read(with); + auto result = AND(source, target); + prefetch(); + write(with, result); +} + +template auto CPU::instructionAND(DataRegister from, EffectiveAddress with) -> void { + auto source = read(from); + auto target = read(with); + auto result = AND(source, target); + prefetch(); + write(with, result); +} + +template auto CPU::instructionANDI(EffectiveAddress with) -> void { + if constexpr(Size == Long) { + //note: m68000um.pdf erroneously lists ANDI.L #,Dn as 14(3/0), but is in fact 16(3/0) + if(with.mode == DataRegisterDirect) idle(4); + } + auto source = extension(); + auto target = read(with); + auto result = AND(source, target); + prefetch(); + write(with, result); +} + +auto CPU::instructionANDI_TO_CCR() -> void { + auto data = extension(); + writeCCR(readCCR() & data); + idle(8); + read(r.pc); + prefetch(); +} + +auto CPU::instructionANDI_TO_SR() -> void { + if(supervisor()) { + auto data = extension(); + writeSR(readSR() & data); + idle(8); + read(r.pc); + } + prefetch(); +} + +template auto CPU::instructionASL(uint4 count, DataRegister with) -> void { + idle((Size != Long ? 2 : 4) + count * 2); + auto result = ASL(read(with), count); + prefetch(); + write(with, result); +} + +template auto CPU::instructionASL(DataRegister from, DataRegister with) -> void { + auto count = read(from) & 63; + idle((Size != Long ? 2 : 4) + count * 2); + auto result = ASL(read(with), count); + prefetch(); + write(with, result); +} + +auto CPU::instructionASL(EffectiveAddress with) -> void { + auto result = ASL(read(with), 1); + prefetch(); + write(with, result); +} + +template auto CPU::instructionASR(uint4 count, DataRegister with) -> void { + idle((Size != Long ? 2 : 4) + count * 2); + auto result = ASR(read(with), count); + prefetch(); + write(with, result); +} + +template auto CPU::instructionASR(DataRegister from, DataRegister with) -> void { + auto count = read(from) & 63; + idle((Size != Long ? 2 : 4) + count * 2); + auto result = ASR(read(with), count); + prefetch(); + write(with, result); +} + +auto CPU::instructionASR(EffectiveAddress with) -> void { + auto result = ASR(read(with), 1); + prefetch(); + write(with, result); +} + +auto CPU::instructionBCC(uint4 test, uint8 displacement) -> void { + if(!condition(test)) { + idle(4); + if(!displacement) prefetch(); + } else { + idle(2); + auto offset = displacement ? (int8_t)displacement : (int16_t)prefetched() - 2; + r.pc -= 2; + r.pc += offset; + prefetch(); + } + prefetch(); + + checkForInterrupts(); +} + +template auto CPU::instructionBCHG(DataRegister bit, EffectiveAddress with) -> void { + auto index = read(bit) & bits() - 1; + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(index < 16 ? 2 : 4); + } + auto test = read(with); + r.z = test.bit(index) == 0; + test.bit(index) ^= 1; + prefetch(); + write(with, test); +} + +template auto CPU::instructionBCHG(EffectiveAddress with) -> void { + auto index = extension() & bits() - 1; + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(index < 16 ? 2 : 4); + } + auto test = read(with); + r.z = test.bit(index) == 0; + test.bit(index) ^= 1; + prefetch(); + write(with, test); +} + +template auto CPU::instructionBCLR(DataRegister bit, EffectiveAddress with) -> void { + auto index = read(bit) & bits() - 1; + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(index < 16 ? 4 : 6); + } + auto test = read(with); + r.z = test.bit(index) == 0; + test.bit(index) = 0; + prefetch(); + write(with, test); +} + +template auto CPU::instructionBCLR(EffectiveAddress with) -> void { + auto index = extension() & bits() - 1; + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(index < 16 ? 4 : 6); + } + auto test = read(with); + r.z = test.bit(index) == 0; + test.bit(index) = 0; + prefetch(); + write(with, test); +} + +auto CPU::instructionBRA(uint8 displacement) -> void { + idle(2); + auto offset = displacement ? (int8_t)displacement : (int16_t)prefetched() - 2; + r.pc -= 2; + r.pc += offset; + prefetch(); + prefetch(); + + checkForInterrupts(); +} + +template auto CPU::instructionBSET(DataRegister bit, EffectiveAddress with) -> void { + auto index = read(bit) & bits() - 1; + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(index < 16 ? 2 : 4); + } + auto test = read(with); + r.z = test.bit(index) == 0; + test.bit(index) = 1; + prefetch(); + write(with, test); +} + +template auto CPU::instructionBSET(EffectiveAddress with) -> void { + auto index = extension() & bits() - 1; + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(index < 16 ? 2 : 4); + } + auto test = read(with); + r.z = test.bit(index) == 0; + test.bit(index) = 1; + prefetch(); + write(with, test); +} + +auto CPU::instructionBSR(uint8 displacement) -> void { + idle(2); + auto offset = displacement ? (int8_t)displacement : (int16_t)prefetched() - 2; + r.pc -= 2; + push(r.pc); + r.pc += offset; + prefetch(); + prefetch(); + checkForInterrupts(); +} + +template auto CPU::instructionBTST(DataRegister bit, EffectiveAddress with) -> void { + auto index = read(bit) & bits() - 1; + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(2); + } + auto test = read(with); + r.z = test.bit(index) == 0; + prefetch(); +} + +template auto CPU::instructionBTST(EffectiveAddress with) -> void { + auto index = extension() & bits() - 1; + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(2); + } + auto test = read(with); + r.z = test.bit(index) == 0; + prefetch(); +} + +auto CPU::instructionCHK(DataRegister compare, EffectiveAddress maximum) -> void { + idle(6); + auto source = read(maximum); + auto target = read(compare); + + r.z = clip(target) == 0; + r.n = sign(target) < 0; + if(r.n) return exception(Exception::BoundsCheck, Vector::BoundsCheck); + + auto result = (uint64)target - source; + r.c = sign(result >> 1) < 0; + r.v = sign((target ^ source) & (target ^ result)) < 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + if(r.n == r.v && !r.z) return exception(Exception::BoundsCheck, Vector::BoundsCheck); + prefetch(); +} + +template auto CPU::instructionCLR(EffectiveAddress with) -> void { + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect || with.mode == AddressRegisterDirect) idle(2); + } + read(with); + prefetch(); + write(with, 0); + r.c = 0; + r.v = 0; + r.z = 1; + r.n = 0; +} + +template auto CPU::instructionCMP(EffectiveAddress from, DataRegister with) -> void { + if constexpr(Size == Long) idle(2); + auto source = read(from); + auto target = read(with); + CMP(source, target); + prefetch(); +} + +template auto CPU::instructionCMPA(EffectiveAddress from, AddressRegister with) -> void { + idle(2); + auto source = sign(read(from)); + auto target = read(with); + CMP(source, target); + prefetch(); +} + +template auto CPU::instructionCMPI(EffectiveAddress with) -> void { + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(2); + } + auto source = extension(); + auto target = read(with); + CMP(source, target); + prefetch(); +} + +template auto CPU::instructionCMPM(EffectiveAddress from, EffectiveAddress with) -> void { + auto source = read(from); + auto target = read(with); + CMP(source, target); + prefetch(); +} + +auto CPU::instructionDBCC(uint4 test, DataRegister with) -> void { + auto displacement = extension(); + if(condition(test)) { + idle(4); + } else { + uint16 result = read(with); + write(with, result - 1); + if(result) { + idle(2); + r.pc -= 4; + r.pc += sign(displacement); + prefetch(); + } else { + idle(2); + } + } + prefetch(); + checkForInterrupts(); +} + +auto CPU::instructionDIVS(EffectiveAddress from, DataRegister with) -> void { + uint32 dividend = read(with), odividend = dividend; + uint32 divisor = read(from) << 16, odivisor = divisor; + + if(divisor == 0) { + return exception(Exception::DivisionByZero, Vector::DivisionByZero); + } + + if(divisor >> 31) { + divisor = -divisor; + } + + if(dividend >> 31) { + dividend = -dividend; + idle(2); + } + + r.c = 0; + if(r.v = dividend >= divisor) { + r.z = 0; + r.n = 1; + idle(14); + prefetch(); + return; + } + + uint16 quotient = 0; + bool carry = 0; + uint ticks = 12; + for(uint index : range(15)) { + dividend = dividend << 1; + quotient = quotient << 1 | carry; + if(carry = dividend >= divisor) dividend -= divisor; + ticks += !carry ? 8 : 6; + } + quotient = quotient << 1 | carry; + dividend = dividend << 1; + if(carry = dividend >= divisor) dividend -= divisor; + quotient = quotient << 1 | carry; + ticks += 4; + + if(odivisor >> 31) { + ticks += 16; + if(odividend >> 31) { + if(quotient >> 15) r.v = 1; + dividend = -dividend; + } else { + quotient = -quotient; + if(quotient && !(quotient >> 15)) r.v = 1; + } + } else if(odividend >> 31) { + ticks += 18; + quotient = -quotient; + if(quotient && !(quotient >> 15)) r.v = 1; + dividend = -dividend; + } else { + ticks += 14; + if(quotient >> 15) r.v = 1; + } + + if(r.v) { + r.z = 0; + r.n = 1; + idle(ticks); + prefetch(); + return; + } + + r.z = quotient == 0; + r.n = quotient < 0; + + idle(ticks); + write(with, dividend | quotient); + prefetch(); +} + +auto CPU::instructionDIVU(EffectiveAddress from, DataRegister with) -> void { + uint32 dividend = read(with); + uint32 divisor = read(from) << 16; + + if(divisor == 0) { + return exception(Exception::DivisionByZero, Vector::DivisionByZero); + } + + r.c = 0; + if(r.v = dividend >= divisor) { + r.z = 0; + r.n = 1; + idle(10); + prefetch(); + return; + } + + uint16 quotient = 0; + bool force = 0; + bool carry = 0; + uint ticks = 6; + for(uint index : range(16)) { + force = dividend >> 31; + dividend = dividend << 1; + quotient = quotient << 1 | carry; + if(carry = force || dividend >= divisor) dividend -= divisor; + ticks += !carry ? 8 : !force ? 6 : 4; + } + ticks += force ? 6 : carry ? 4 : 2; + quotient = quotient << 1 | carry; + + r.z = quotient == 0; + r.n = quotient < 0; + + idle(ticks); + write(with, dividend | quotient); + prefetch(); +} + +template auto CPU::instructionEOR(DataRegister from, EffectiveAddress with) -> void { + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(4); + } + auto source = read(from); + auto target = read(with); + auto result = EOR(source, target); + prefetch(); + write(with, result); +} + +template auto CPU::instructionEORI(EffectiveAddress with) -> void { + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(4); + } + auto source = extension(); + auto target = read(with); + auto result = EOR(source, target); + prefetch(); + write(with, result); +} + +auto CPU::instructionEORI_TO_CCR() -> void { + auto data = extension(); + writeCCR(readCCR() ^ data); + idle(8); + read(r.pc); + prefetch(); +} + +auto CPU::instructionEORI_TO_SR() -> void { + if(supervisor()) { + auto data = extension(); + writeSR(readSR() ^ data); + idle(8); + read(r.pc); + } + prefetch(); +} + +auto CPU::instructionEXG(DataRegister x, DataRegister y) -> void { + idle(2); + auto z = read(x); + write(x, read(y)); + write(y, z); + prefetch(); +} + +auto CPU::instructionEXG(AddressRegister x, AddressRegister y) -> void { + idle(2); + auto z = read(x); + write(x, read(y)); + write(y, z); + prefetch(); +} + +auto CPU::instructionEXG(DataRegister x, AddressRegister y) -> void { + idle(2); + auto z = read(x); + write(x, read(y)); + write(y, z); + prefetch(); +} + +template<> auto CPU::instructionEXT(DataRegister with) -> void { + auto result = (int8)read(with); + write(with, result); + + r.c = 0; + r.v = 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + prefetch(); +} + +template<> auto CPU::instructionEXT(DataRegister with) -> void { + auto result = (int16)read(with); + write(with, result); + + r.c = 0; + r.v = 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + prefetch(); +} + +auto CPU::instructionILLEGAL(uint16 code) -> void { + idle(6); + r.pc -= 2; + if(code.bit(12,15) == 0xa) return exception(Exception::Illegal, Vector::IllegalLineA); + if(code.bit(12,15) == 0xf) return exception(Exception::Illegal, Vector::IllegalLineF); + return exception(Exception::Illegal, Vector::IllegalInstruction); +} + +auto CPU::instructionJMP(EffectiveAddress from) -> void { + r.pc = prefetched(from); + prefetch(); + prefetch(); + checkForInterrupts(); +} + +auto CPU::instructionJSR(EffectiveAddress from) -> void { + auto ir = prefetched(from); + auto pc = r.pc; + r.pc = ir; + prefetch(); + push(pc - 2); + prefetch(); + checkForInterrupts(); +} + +auto CPU::instructionLEA(EffectiveAddress from, AddressRegister to) -> void { + if(from.mode == AddressRegisterIndirectWithIndex) idle(2); + write(to, fetch(from)); + prefetch(); +} + +auto CPU::instructionLINK(AddressRegister with) -> void { + auto displacement = (int16)extension(); + auto sp = AddressRegister{7}; + push(read(with)); + write(with, read(sp)); + write(sp, read(sp) + displacement); + prefetch(); +} + +template auto CPU::instructionLSL(uint4 count, DataRegister with) -> void { + idle((Size != Long ? 2 : 4) + count * 2); + auto result = LSL(read(with), count); + prefetch(); + write(with, result); +} + +template auto CPU::instructionLSL(DataRegister from, DataRegister with) -> void { + auto count = read(from) & 63; + idle((Size != Long ? 2 : 4) + count * 2); + auto result = LSL(read(with), count); + prefetch(); + write(with, result); +} + +auto CPU::instructionLSL(EffectiveAddress with) -> void { + auto result = LSL(read(with), 1); + prefetch(); + write(with, result); +} + +template auto CPU::instructionLSR(uint4 count, DataRegister with) -> void { + idle((Size != Long ? 2 : 4) + count * 2); + auto result = LSR(read(with), count); + prefetch(); + write(with, result); +} + +template auto CPU::instructionLSR(DataRegister from, DataRegister with) -> void { + auto count = read(from) & 63; + idle((Size != Long ? 2 : 4) + count * 2); + auto result = LSR(read(with), count); + prefetch(); + write(with, result); +} + +auto CPU::instructionLSR(EffectiveAddress with) -> void { + auto result = LSR(read(with), 1); + prefetch(); + write(with, result); +} + +//todo: move memory,(xxx).l should interleave extension fetches with writes +template auto CPU::instructionMOVE(EffectiveAddress from, EffectiveAddress to) -> void { + auto data = read(from); + r.c = 0; + r.v = 0; + r.z = clip(data) == 0; + r.n = sign(data) < 0; + + if(to.mode == AddressRegisterIndirectWithPreDecrement) { + prefetch(); + write(to, data); + } else { + write(to, data); + prefetch(); + } +} + +template auto CPU::instructionMOVEA(EffectiveAddress from, AddressRegister to) -> void { + auto data = sign(read(from)); + write(to, data); + prefetch(); +} + +template auto CPU::instructionMOVEM_TO_MEM(EffectiveAddress to) -> void { + auto list = extension(); + auto addr = fetch(to); + + for(uint n : range(16)) { + if(!list.bit(n)) continue; + //pre-decrement mode traverses registers in reverse order {A7-A0, D7-D0} + uint index = to.mode == AddressRegisterIndirectWithPreDecrement ? 15 - n : n; + + if(to.mode == AddressRegisterIndirectWithPreDecrement) addr -= bytes(); + auto data = index < 8 ? read(DataRegister{index}) : read(AddressRegister{index}); + write(addr, data); + if(to.mode != AddressRegisterIndirectWithPreDecrement) addr += bytes(); + } + + AddressRegister with{to.reg}; + if(to.mode == AddressRegisterIndirectWithPreDecrement ) write(with, addr); + if(to.mode == AddressRegisterIndirectWithPostIncrement) write(with, addr); + prefetch(); +} + +template auto CPU::instructionMOVEM_TO_REG(EffectiveAddress from) -> void { + auto list = extension(); + auto addr = fetch(from); + + for(uint n : range(16)) { + if(!list.bit(n)) continue; + uint index = from.mode == AddressRegisterIndirectWithPreDecrement ? 15 - n : n; + + if(from.mode == AddressRegisterIndirectWithPreDecrement) addr -= bytes(); + auto data = read(addr); + data = sign(data); + index < 8 ? write(DataRegister{index}, data) : write(AddressRegister{index}, data); + if(from.mode != AddressRegisterIndirectWithPreDecrement) addr += bytes(); + } + + //spurious extra word read cycle exclusive to MOVEM memory->register + if(from.mode == AddressRegisterIndirectWithPreDecrement) addr -= 2; + read(addr); + + AddressRegister with{from.reg}; + if(from.mode == AddressRegisterIndirectWithPreDecrement ) write(with, addr); + if(from.mode == AddressRegisterIndirectWithPostIncrement) write(with, addr); + prefetch(); +} + +template auto CPU::instructionMOVEP(DataRegister from, EffectiveAddress to) -> void { + auto address = fetch(to); + auto data = read(from); + uint shift = bits(); + for(auto _ : range(bytes())) { + shift -= 8; + write(address, data >> shift); + address += 2; + } + prefetch(); +} + +template auto CPU::instructionMOVEP(EffectiveAddress from, DataRegister to) -> void { + auto address = fetch(from); + auto data = read(to); + uint shift = bits(); + for(auto _ : range(bytes())) { + shift -= 8; + data &= ~(0xff << shift); + data |= read(address) << shift; + address += 2; + } + write(to, data); + prefetch(); +} + +auto CPU::instructionMOVEQ(uint8 immediate, DataRegister to) -> void { + write(to, sign(immediate)); + + r.c = 0; + r.v = 0; + r.z = clip(immediate) == 0; + r.n = sign(immediate) < 0; + prefetch(); +} + +auto CPU::instructionMOVE_FROM_SR(EffectiveAddress to) -> void { + if(to.mode == DataRegisterDirect) idle(2); + if(to.mode == AddressRegisterIndirectWithPreDecrement) idle(2); + if(to.mode != DataRegisterDirect) read(r.pc); + auto data = readSR(); + fetch(to); + prefetch(); + write(to, data); +} + +auto CPU::instructionMOVE_TO_CCR(EffectiveAddress from) -> void { + idle(8); + auto data = read(from); + writeCCR(data); + prefetch(); +} + +auto CPU::instructionMOVE_TO_SR(EffectiveAddress from) -> void { + if(supervisor()) { + idle(8); + auto data = read(from); + writeSR(data); + } + prefetch(); +} + +auto CPU::instructionMOVE_FROM_USP(AddressRegister to) -> void { + if(supervisor()) { + write(to, r.sp); + } + prefetch(); +} + +auto CPU::instructionMOVE_TO_USP(AddressRegister from) -> void { + if(supervisor()) { + r.sp = read(from); + } + prefetch(); +} + +auto CPU::instructionMULS(EffectiveAddress from, DataRegister with) -> void { + auto source = read(from); + auto target = read(with); + auto result = (int16)source * (int16)target; + //+2 cycles per 0<>1 bit transition + auto cycles = bit::count(uint16(source << 1) ^ source); + idle(34 + cycles * 2); + write(with, result); + + r.c = 0; + r.v = 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + prefetch(); +} + +auto CPU::instructionMULU(EffectiveAddress from, DataRegister with) -> void { + auto source = read(from); + auto target = read(with); + auto result = source * target; + //+2 cycles per bit set + auto cycles = bit::count(source); + idle(34 + cycles * 2); + write(with, result); + + r.c = 0; + r.v = 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + prefetch(); +} + +auto CPU::instructionNBCD(EffectiveAddress with) -> void { + if(with.mode == DataRegisterDirect || with.mode == AddressRegisterDirect) idle(2); + auto source = read(with); + auto target = 0u; + auto result = target - source - r.x; + bool c = false; + bool v = false; + + const bool adjustLo = (target ^ source ^ result) & 0x10; + const bool adjustHi = result & 0x100; + + if(adjustLo) { + auto previous = result; + result -= 0x06; + c = (~previous & 0x80) & ( result & 0x80); + v |= ( previous & 0x80) & (~result & 0x80); + } + + if(adjustHi) { + auto previous = result; + result -= 0x60; + c = true; + v |= (previous & 0x80) & (~result & 0x80); + } + + write(with, result); + + r.c = c; + r.v = v; + r.z = clip(result) ? 0 : r.z; + r.n = sign(result) < 0; + r.x = r.c; + prefetch(); +} + +template auto CPU::instructionNEG(EffectiveAddress with) -> void { + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect || with.mode == AddressRegisterDirect) idle(2); + } + auto result = SUB(read(with), 0); + prefetch(); + write(with, result); +} + +template auto CPU::instructionNEGX(EffectiveAddress with) -> void { + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect || with.mode == AddressRegisterDirect) idle(2); + } + auto result = SUB(read(with), 0); + prefetch(); + write(with, result); +} + +auto CPU::instructionNOP() -> void { + prefetch(); +} + +template auto CPU::instructionNOT(EffectiveAddress with) -> void { + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect || with.mode == AddressRegisterDirect) idle(2); + } + auto result = ~read(with); + prefetch(); + write(with, result); + + r.c = 0; + r.v = 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; +} + +template auto CPU::instructionOR(EffectiveAddress from, DataRegister with) -> void { + if constexpr(Size == Long) { + if(from.mode == DataRegisterDirect || from.mode == Immediate) { + idle(4); + } else { + idle(2); + } + } + auto source = read(from); + auto target = read(with); + auto result = OR(source, target); + prefetch(); + write(with, result); +} + +template auto CPU::instructionOR(DataRegister from, EffectiveAddress with) -> void { + auto source = read(from); + auto target = read(with); + auto result = OR(source, target); + prefetch(); + write(with, result); +} + +template auto CPU::instructionORI(EffectiveAddress with) -> void { + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(4); + } + auto source = extension(); + auto target = read(with); + auto result = OR(source, target); + prefetch(); + write(with, result); +} + +auto CPU::instructionORI_TO_CCR() -> void { + auto data = extension(); + writeCCR(readCCR() | data); + idle(8); + read(r.pc); + prefetch(); +} + +auto CPU::instructionORI_TO_SR() -> void { + if(supervisor()) { + auto data = extension(); + writeSR(readSR() | data); + idle(8); + read(r.pc); + } + prefetch(); +} + +auto CPU::instructionPEA(EffectiveAddress from) -> void { + if(from.mode == AddressRegisterIndirectWithIndex) idle(2); + auto data = fetch(from); + if(from.mode == AbsoluteShortIndirect || from.mode == AbsoluteLongIndirect) { + push(data); + prefetch(); + } else { + prefetch(); + push(data); + } +} + +auto CPU::instructionRESET() -> void { + if(supervisor()) { + r.reset = 1; + idle(128); + r.reset = 0; + } + prefetch(); +} + +template auto CPU::instructionROL(uint4 count, DataRegister with) -> void { + idle((Size != Long ? 2 : 4) + count * 2); + auto result = ROL(read(with), count); + prefetch(); + write(with, result); +} + +template auto CPU::instructionROL(DataRegister from, DataRegister with) -> void { + auto count = read(from) & 63; + idle((Size != Long ? 2 : 4) + count * 2); + auto result = ROL(read(with), count); + prefetch(); + write(with, result); +} + +auto CPU::instructionROL(EffectiveAddress with) -> void { + auto result = ROL(read(with), 1); + prefetch(); + write(with, result); +} + +template auto CPU::instructionROR(uint4 count, DataRegister with) -> void { + idle((Size != Long ? 2 : 4) + count * 2); + auto result = ROR(read(with), count); + prefetch(); + write(with, result); +} + +template auto CPU::instructionROR(DataRegister from, DataRegister with) -> void { + auto count = read(from) & 63; + idle((Size != Long ? 2 : 4) + count * 2); + auto result = ROR(read(with), count); + prefetch(); + write(with, result); +} + +auto CPU::instructionROR(EffectiveAddress with) -> void { + auto result = ROR(read(with), 1); + prefetch(); + write(with, result); +} + +template auto CPU::instructionROXL(uint4 count, DataRegister with) -> void { + idle((Size != Long ? 2 : 4) + count * 2); + auto result = ROXL(read(with), count); + prefetch(); + write(with, result); +} + +template auto CPU::instructionROXL(DataRegister from, DataRegister with) -> void { + auto count = read(from) & 63; + idle((Size != Long ? 2 : 4) + count * 2); + auto result = ROXL(read(with), count); + prefetch(); + write(with, result); +} + +auto CPU::instructionROXL(EffectiveAddress with) -> void { + auto result = ROXL(read(with), 1); + prefetch(); + write(with, result); +} + +template auto CPU::instructionROXR(uint4 count, DataRegister with) -> void { + idle((Size != Long ? 2 : 4) + count * 2); + auto result = ROXR(read(with), count); + prefetch(); + write(with, result); +} + +template auto CPU::instructionROXR(DataRegister from, DataRegister with) -> void { + auto count = read(from) & 63; + idle((Size != Long ? 2 : 4) + count * 2); + auto result = ROXR(read(with), count); + prefetch(); + write(with, result); +} + +auto CPU::instructionROXR(EffectiveAddress with) -> void { + auto result = ROXR(read(with), 1); + prefetch(); + write(with, result); +} + +auto CPU::instructionRTE() -> void { + if(supervisor()) { + auto sr = pop(); + r.pc = pop(); + writeSR(sr); + prefetch(); + } + prefetch(); + + checkForInterrupts(); +} + +auto CPU::instructionRTR() -> void { + writeCCR(pop()); + r.pc = pop(); + prefetch(); + prefetch(); + + checkForInterrupts(); +} + +auto CPU::instructionRTS() -> void { + r.pc = pop(); + prefetch(); + prefetch(); + + checkForInterrupts(); +} + +auto CPU::instructionSBCD(EffectiveAddress from, EffectiveAddress with) -> void { + if(from.mode == DataRegisterDirect) idle(2); + auto target = read(with); + auto source = read(from); + auto result = target - source - r.x; + bool c = false; + bool v = false; + + const bool adjustLo = (target ^ source ^ result) & 0x10; + const bool adjustHi = result & 0x100; + + if(adjustLo) { + auto previous = result; + result -= 0x06; + c = (~previous & 0x80) & ( result & 0x80); + v |= ( previous & 0x80) & (~result & 0x80); + } + + if(adjustHi) { + auto previous = result; + result -= 0x60; + c = true; + v |= (previous & 0x80) & (~result & 0x80); + } + + prefetch(); + write(with, result); + + r.c = c; + r.v = v; + r.z = clip(result) ? 0 : r.z; + r.n = sign(result) < 0; + r.x = r.c; +} + +auto CPU::instructionSCC(uint4 test, EffectiveAddress to) -> void { + fetch(to); + prefetch(); + if(!condition(test)) { + write(to, 0); + } else { + write(to, ~0); + if(to.mode == DataRegisterDirect) idle(2); + } +} + +auto CPU::instructionSTOP() -> void { + if(supervisor()) { + auto sr = extension(); + writeSR(sr); + r.stop = true; + } + prefetch(); +} + +template auto CPU::instructionSUB(EffectiveAddress from, DataRegister with) -> void { + if constexpr(Size == Long) { + if(from.mode == DataRegisterDirect || from.mode == AddressRegisterDirect || from.mode == Immediate) { + idle(4); + } else { + idle(2); + } + } + auto source = read(from); + auto target = read(with); + auto result = SUB(source, target); + prefetch(); + write(with, result); +} + +template auto CPU::instructionSUB(DataRegister from, EffectiveAddress with) -> void { + auto source = read(from); + auto target = read(with); + auto result = SUB(source, target); + prefetch(); + write(with, result); +} + +template auto CPU::instructionSUBA(EffectiveAddress from, AddressRegister to) -> void { + if(Size != Long || from.mode == DataRegisterDirect || from.mode == AddressRegisterDirect || from.mode == Immediate) { + idle(4); + } else { + idle(2); + } + auto source = sign(read(from)); + auto target = read(to); + prefetch(); + write(to, target - source); +} + +template auto CPU::instructionSUBI(EffectiveAddress with) -> void { + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(4); + } + auto source = extension(); + auto target = read(with); + auto result = SUB(source, target); + prefetch(); + write(with, result); +} + +template auto CPU::instructionSUBQ(uint4 immediate, EffectiveAddress with) -> void { + if constexpr(Size == Long) { + if(with.mode == DataRegisterDirect) idle(4); + } + auto source = immediate; + auto target = read(with); + auto result = SUB(source, target); + prefetch(); + write(with, result); +} + +//Size is ignored: always uses Long +template auto CPU::instructionSUBQ(uint4 immediate, AddressRegister with) -> void { + idle(4); + auto result = read(with) - immediate; + prefetch(); + write(with, result); +} + +template auto CPU::instructionSUBX(EffectiveAddress from, EffectiveAddress with) -> void { + if constexpr(Size == Long) { + if(from.mode == DataRegisterDirect) idle(4); + } + auto target = read(with); + auto source = read(from); + auto result = SUB(source, target); + prefetch(); + write(with, result); +} + +auto CPU::instructionSWAP(DataRegister with) -> void { + auto result = read(with); + result = result >> 16 | result << 16; + write(with, result); + + r.c = 0; + r.v = 0; + r.z = clip(result) == 0; + r.n = sign(result) < 0; + prefetch(); +} + +auto CPU::instructionTAS(EffectiveAddress with) -> void { + uint32 data; + + if(with.mode == DataRegisterDirect) { + data = read(with); + prefetch(); + write(with, data | 0x80); + } else { + //Mega Drive models 1&2 have a bug that prevents TAS write from taking effect + //this bugged behavior is required for certain software to function correctly + data = read(with); + prefetch(); + idle(6); + } + + r.c = 0; + r.v = 0; + r.z = clip(data) == 0; + r.n = sign(data) < 0; +} + +auto CPU::instructionTRAP(uint4 vector) -> void { + idle(6); + prefetched(); + return exception(Exception::Trap, 32 + vector, r.i); +} + +auto CPU::instructionTRAPV() -> void { + if(r.v) { + idle(6); + prefetched(); + return exception(Exception::Overflow, Vector::Overflow); + } + prefetch(); +} + +template auto CPU::instructionTST(EffectiveAddress from) -> void { + auto data = read(from); + r.c = 0; + r.v = 0; + r.z = clip(data) == 0; + r.n = sign(data) < 0; + prefetch(); +} + +auto CPU::instructionUNLK(AddressRegister with) -> void { + auto sp = AddressRegister{7}; + write(sp, read(with)); + write(with, pop()); + prefetch(); +} diff --git a/higan/md/cpu/memory.cpp b/higan/md/cpu/memory.cpp new file mode 100644 index 0000000..0bcb0fb --- /dev/null +++ b/higan/md/cpu/memory.cpp @@ -0,0 +1,114 @@ +//these functions transform internal accesses to bus accesses, and handle conversions: +//* A1-A23 are passed to the bus +//* A0 in word mode is ignored, and drives both UDS and LDS +//* A0 in byte mode drives either UDS or LDS +//* upper = /UDS (1 = selected; eg /UDS is inverted) +//* lower = /LDS (1 = selected; eg /LDS is inverted) +//* /UDS is where A0=0 and maps to D8-D15 +//* /LDS is where A0=1 and maps to D0-D7 + +template<> auto CPU::read(const uint32 address) -> uint32 { + wait(4); + if(address & 1) { + return read(0, 1, address & ~1).byte(0); /* /LDS */ + } else { + return read(1, 0, address & ~1).byte(1); /* /UDS */ + } +} + +template<> auto CPU::read(const uint32 address) -> uint32 { + wait(4); + return read(1, 1, address & ~1); +} + +template<> auto CPU::read(const uint32 address) -> uint32 { + wait(4); + uint32 data = read(1, 1, address + 0 & ~1) << 16; + wait(4); + return data | read(1, 1, address + 2 & ~1) << 0; +} + +// + +template<> auto CPU::write(const uint32 address, const uint32 data) -> void { + wait(4); + if(address & 1) { + return write(0, 1, address & ~1, data << 8 | (uint8)data << 0); /* /LDS */ + } else { + return write(1, 0, address & ~1, data << 8 | (uint8)data << 0); /* /UDS */ + } +} + +template<> auto CPU::write(const uint32 address, const uint32 data) -> void { + wait(4); + return write(1, 1, address & ~1, data); +} + +template<> auto CPU::write(const uint32 address, const uint32 data) -> void { + wait(4); + write(1, 1, address + 0 & ~1, data >> 16); + wait(4); + write(1, 1, address + 2 & ~1, data >> 0); +} + +// + +template<> auto CPU::write(const uint32 address, const uint32 data) -> void { + wait(4); + if(address & 1) { + return write(0, 1, address & ~1, data << 8 | (uint8)data << 0); /* /LDS */ + } else { + return write(1, 0, address & ~1, data << 8 | (uint8)data << 0); /* /UDS */ + } +} + +template<> auto CPU::write(const uint32 address, const uint32 data) -> void { + wait(4); + return write(1, 1, address & ~1, data); +} + +template<> auto CPU::write(const uint32 address, const uint32 data) -> void { + wait(4); + write(1, 1, address + 2 & ~1, data >> 0); + wait(4); + write(1, 1, address + 0 & ~1, data >> 16); +} + +// + +template<> auto CPU::extension() -> uint32 { + wait(4); + r.ir = r.irc; + r.irc = read(1, 1, r.pc & ~1); + r.pc += 2; + return (uint8)r.ir; +} + +template<> auto CPU::extension() -> uint32 { + wait(4); + r.ir = r.irc; + r.irc = read(1, 1, r.pc & ~1); + r.pc += 2; + return r.ir; +} + +template<> auto CPU::extension() -> uint32 { + auto hi = extension(); + auto lo = extension(); + return hi << 16 | lo << 0; +} + +// + +template auto CPU::pop() -> uint32 { + auto data = read((uint32)r.a[7]); + r.a[7] += bytes(); + return data; +} + +// + +template auto CPU::push(uint32 data) -> void { + r.a[7] -= bytes(); + return write((uint32)r.a[7], data); +} diff --git a/higan/md/cpu/registers.cpp b/higan/md/cpu/registers.cpp new file mode 100644 index 0000000..abbe1d1 --- /dev/null +++ b/higan/md/cpu/registers.cpp @@ -0,0 +1,48 @@ +template auto CPU::read(DataRegister reg) -> uint32 { + return clip(r.d[reg.number]); +} + +template auto CPU::write(DataRegister reg, uint32 data) -> void { + r.d[reg.number] = (r.d[reg.number] & ~mask()) | (data & mask()); +} + +// + +template auto CPU::read(AddressRegister reg) -> uint32 { + return sign(r.a[reg.number]); +} + +template auto CPU::write(AddressRegister reg, uint32 data) -> void { + r.a[reg.number] = sign(data); +} + +// + +//CCR,SR unused bits cannot be set; always read out as 0 + +auto CPU::readCCR() -> uint8 { + return r.c << 0 | r.v << 1 | r.z << 2 | r.n << 3 | r.x << 4; +} + +auto CPU::readSR() -> uint16 { + return readCCR() << 0 | r.i << 8 | r.s << 13 | r.t << 15; +} + +auto CPU::writeCCR(uint8 ccr) -> void { + r.c = ccr.bit(0); + r.v = ccr.bit(1); + r.z = ccr.bit(2); + r.n = ccr.bit(3); + r.x = ccr.bit(4); +} + +auto CPU::writeSR(uint16 sr) -> void { + writeCCR(sr); + + //when entering or exiting supervisor mode; swap SSP and USP into A7 + if(r.s != sr.bit(13)) swap(r.a[7], r.sp); + + r.i = sr.bit(8,10); + r.s = sr.bit(13); + r.t = sr.bit(15); +} diff --git a/higan/md/cpu/serialization.cpp b/higan/md/cpu/serialization.cpp index fc5f350..2bf6726 100644 --- a/higan/md/cpu/serialization.cpp +++ b/higan/md/cpu/serialization.cpp @@ -1,5 +1,25 @@ auto CPU::serialize(serializer& s) -> void { - M68K::serialize(s); + s.array(r.d); + s.array(r.a); + s.integer(r.sp); + s.integer(r.pc); + + s.integer(r.c); + s.integer(r.v); + s.integer(r.z); + s.integer(r.n); + s.integer(r.x); + s.integer(r.i); + s.integer(r.s); + s.integer(r.t); + + s.integer(r.irc); + s.integer(r.ir); + s.integer(r.ird); + + s.integer(r.stop); + s.integer(r.reset); + Thread::serialize(s); ram.serialize(s); diff --git a/higan/md/cpu/traits.cpp b/higan/md/cpu/traits.cpp new file mode 100644 index 0000000..e00cfe8 --- /dev/null +++ b/higan/md/cpu/traits.cpp @@ -0,0 +1,27 @@ +template<> auto CPU::bytes() -> uint { return 1; } +template<> auto CPU::bytes() -> uint { return 2; } +template<> auto CPU::bytes() -> uint { return 4; } + +template<> auto CPU::bits() -> uint { return 8; } +template<> auto CPU::bits() -> uint { return 16; } +template<> auto CPU::bits() -> uint { return 32; } + +template<> auto CPU::lsb() -> uint32 { return 1; } +template<> auto CPU::lsb() -> uint32 { return 1; } +template<> auto CPU::lsb() -> uint32 { return 1; } + +template<> auto CPU::msb() -> uint32 { return 0x80; } +template<> auto CPU::msb() -> uint32 { return 0x8000; } +template<> auto CPU::msb() -> uint32 { return 0x80000000; } + +template<> auto CPU::mask() -> uint32 { return 0xff; } +template<> auto CPU::mask() -> uint32 { return 0xffff; } +template<> auto CPU::mask() -> uint32 { return 0xffffffff; } + +template<> auto CPU::clip(const uint32 data) -> uint32 { return (uint8)data; } +template<> auto CPU::clip(const uint32 data) -> uint32 { return (uint16)data; } +template<> auto CPU::clip(const uint32 data) -> uint32 { return (uint32)data; } + +template<> auto CPU::sign(const uint32 data) -> int32 { return (int8)data; } +template<> auto CPU::sign(const uint32 data) -> int32 { return (int16)data; } +template<> auto CPU::sign(const uint32 data) -> int32 { return (int32)data; } diff --git a/higan/md/expansion/expansion.cpp b/higan/md/expansion/expansion.cpp index c779fa5..03c0dc4 100644 --- a/higan/md/expansion/expansion.cpp +++ b/higan/md/expansion/expansion.cpp @@ -34,7 +34,7 @@ auto Expansion::connect(Node::Peripheral with) -> void { information.name = document["game/label"].text(); information.regions = document["game/region"].text().split(",").strip(); - mcd.load(node, with); + //mcd.load(node, with); power(); } @@ -54,21 +54,25 @@ auto Expansion::power() -> void { /* the only existing expansion port device is the Mega CD, which is hard-coded below for now */ auto Expansion::read(uint1 upper, uint1 lower, uint22 address, uint16 data) -> uint16 { - return mcd.external_read(upper, lower, address, data); + //return mcd.external_read(upper, lower, address, data); + return data; } auto Expansion::write(uint1 upper, uint1 lower, uint22 address, uint16 data) -> void { - return mcd.external_write(upper, lower, address, data); + //return mcd.external_write(upper, lower, address, data); + return; } auto Expansion::readIO(uint1 upper, uint1 lower, uint24 address, uint16 data) -> uint16 { if(!node) return data; - return mcd.external_readIO(upper, lower, address, data); + //return mcd.external_readIO(upper, lower, address, data); + return data; } auto Expansion::writeIO(uint1 upper, uint1 lower, uint24 address, uint16 data) -> void { if(!node) return; - return mcd.external_writeIO(upper, lower, address, data); + //return mcd.external_writeIO(upper, lower, address, data); + return; } } diff --git a/higan/md/interface/interface.cpp b/higan/md/interface/interface.cpp index 72c702c..4b540c6 100644 --- a/higan/md/interface/interface.cpp +++ b/higan/md/interface/interface.cpp @@ -16,7 +16,7 @@ auto MegaDriveInterface::configure(string name, uint value) -> void { auto MegaDriveInterface::game() -> string { if(expansion.node && (!cartridge.node || !cartridge.bootable())) { - if(mcd.disc) return mcd.name(); + //if(mcd.disc) return mcd.name(); return expansion.name(); } diff --git a/higan/md/mcd/mcd.cpp b/higan/md/mcd/mcd.cpp index c019391..ed0dd3a 100644 --- a/higan/md/mcd/mcd.cpp +++ b/higan/md/mcd/mcd.cpp @@ -189,7 +189,7 @@ auto MCD::power(bool reset) -> void { for(uint address : range(bios.size())) bios.program(address, fp->readm(2)); } - M68K::power(); + CPU::power(); Thread::create(12'500'000, {&MCD::main, this}); counter = {}; if(!reset) { diff --git a/higan/md/mcd/mcd.hpp b/higan/md/mcd/mcd.hpp index 93a1bb1..af82d08 100644 --- a/higan/md/mcd/mcd.hpp +++ b/higan/md/mcd/mcd.hpp @@ -1,5 +1,7 @@ //Mega CD +#if 0 + struct MCD : M68K, Thread { Node::Component node; Node::Instruction eventInstruction; @@ -426,3 +428,5 @@ struct MCD : M68K, Thread { }; extern MCD mcd; + +#endif \ No newline at end of file diff --git a/higan/md/mcd/serialization.cpp b/higan/md/mcd/serialization.cpp index 3de12f6..44ad703 100644 --- a/higan/md/mcd/serialization.cpp +++ b/higan/md/mcd/serialization.cpp @@ -1,5 +1,5 @@ auto MCD::serialize(serializer& s) -> void { - M68K::serialize(s); + CPU::serialize(s); Thread::serialize(s); pram.serialize(s); diff --git a/higan/md/md.hpp b/higan/md/md.hpp index 0568c6c..4c5b34d 100644 --- a/higan/md/md.hpp +++ b/higan/md/md.hpp @@ -4,19 +4,11 @@ //started: 2016-07-08 #include - -#include -#include #include namespace higan::MegaDrive { #include - enum : uint { - Byte = 0, - Word = 1, - }; - struct Region { inline static auto NTSCJ() -> bool; inline static auto NTSCU() -> bool; @@ -33,7 +25,7 @@ namespace higan::MegaDrive { #include #include - #include + //#include #include #include diff --git a/higan/md/system/serialization.cpp b/higan/md/system/serialization.cpp index 8f76a9c..9869491 100644 --- a/higan/md/system/serialization.cpp +++ b/higan/md/system/serialization.cpp @@ -54,7 +54,7 @@ auto System::serializeAll(serializer& s, bool synchronize) -> void { vdp.serialize(s); psg.serialize(s); ym2612.serialize(s); - if(MegaCD()) mcd.serialize(s); + //if(MegaCD()) mcd.serialize(s); controllerPort1.serialize(s); controllerPort2.serialize(s); extensionPort.serialize(s); diff --git a/higan/md/system/system.cpp b/higan/md/system/system.cpp index 58bddf2..92ca1ee 100644 --- a/higan/md/system/system.cpp +++ b/higan/md/system/system.cpp @@ -70,7 +70,7 @@ auto System::unload() -> void { controllerPort1.unload(); controllerPort2.unload(); extensionPort.unload(); - mcd.unload(); + //mcd.unload(); node = {}; } @@ -120,7 +120,7 @@ auto System::power(bool reset) -> void { cpu.power(reset); apu.power(reset); vdp.power(reset); - if(MegaCD()) mcd.power(reset); + //if(MegaCD()) mcd.power(reset); scheduler.power(cpu); information.serializeSize[0] = serializeInit(0); diff --git a/higan/md/vdp-performance/background.cpp b/higan/md/vdp-performance/background.cpp index fd005cc..671c32c 100644 --- a/higan/md/vdp-performance/background.cpp +++ b/higan/md/vdp-performance/background.cpp @@ -1,16 +1,10 @@ -auto VDP::Background::renderScreen(uint from, uint to) -> void { - uint nametableWidth = 32 * (1 + io.nametableWidth); - uint nametableWidthMask = nametableWidth - 1; - uint nametableHeightMask = 32 * (1 + io.nametableHeight) - 1; - - static const uint mask[] = {0u, 7u, ~7u, ~0u}; - uint15 scrollAddress = io.horizontalScrollAddress; - scrollAddress += (vdp.state.vcounter & mask[io.horizontalScrollMode]) << 1; +template auto VDP::Background::renderScreen(const uint from, const uint to) -> void { + constexpr uint mask[] = {0u, 7u, ~7u, ~0u}; + const uint15 scrollAddress = io.horizontalScrollAddress + ((vdp.state.vcounter & mask[io.horizontalScrollMode]) << 1); uint x = 0 - vdp.vram.memory[scrollAddress + (id == ID::PlaneB)]; - bool interlace = vdp.io.interlaceMode == 3; - uint tileShift = interlace ? 7 : 6; + const uint tileShift = interlace ? 7 : 6; - auto vsram = &vdp.vsram.memory[id == ID::PlaneB]; + const auto vsram = &vdp.vsram.memory[id == ID::PlaneB]; uint y = vdp.state.vcounter; if(interlace) y = y << 1 | vdp.state.field; y += vdp.vsram.memory[id == ID::PlaneB]; @@ -18,7 +12,7 @@ auto VDP::Background::renderScreen(uint from, uint to) -> void { uint tileX = x >> 3 & nametableWidthMask; uint tileY = y >> 3 + interlace & nametableHeightMask; uint tileY_x_width = tileY * nametableWidth; - uint maskY = interlace ? 15 : 7; + const uint maskY = interlace ? 15 : 7; uint address = io.nametableAddress + (tileY_x_width + tileX & 0x0fff); uint tileAttributes = vdp.vram.memory[address & 0x7fff]; uint flipX = tileAttributes & 0x0800 ? 7 : 0; @@ -61,22 +55,17 @@ auto VDP::Background::renderScreen(uint from, uint to) -> void { } } -auto VDP::Background::renderWindow(uint from, uint to) -> void { - bool interlace = vdp.io.interlaceMode == 3; - uint tileShift = interlace ? 7 : 6; +template auto VDP::Background::renderWindow(const uint from, const uint to) -> void { + const uint tileShift = interlace ? 7 : 6; uint y = vdp.state.vcounter; if(interlace) y = y << 1 | vdp.state.field; - uint nametableAddress = io.nametableAddress & (vdp.io.displayWidth ? ~0x0400 : ~0); - uint widthSize = 32 << (bool)vdp.io.displayWidth; - uint widthMask = widthSize - 1; - uint x = from; uint tileX = x >> 3 & widthMask; - uint tileY = y >> 3 + interlace & 31; - uint tileY_x_width = tileY * widthSize; - uint maskY = interlace ? 15 : 7; + const uint tileY = y >> 3 + interlace & 31; + const uint tileY_x_width = tileY * widthSize; + const uint maskY = interlace ? 15 : 7; uint address = nametableAddress + (tileY_x_width + tileX & 0x0fff); uint tileAttributes = vdp.vram.memory[address & 0x7fff]; uint flipX = tileAttributes & 0x0800 ? 7 : 0; diff --git a/higan/md/vdp-performance/color.cpp b/higan/md/vdp-performance/color.cpp index c586fe7..9967ea2 100644 --- a/higan/md/vdp-performance/color.cpp +++ b/higan/md/vdp-performance/color.cpp @@ -4,15 +4,15 @@ auto VDP::color(uint32 color) -> uint64 { uint B = color.bit(6, 8); uint M = color.bit(9,10); - uint lookup[3][8] = { + constexpr uint lookup[3][8] = { { 0, 29, 52, 70, 87, 101, 116, 130}, //shadow { 0, 52, 87, 116, 144, 172, 206, 255}, //normal {130, 144, 158, 172, 187, 206, 228, 255}, //highlight }; - uint64 r = image::normalize(lookup[M][R], 8, 16); - uint64 g = image::normalize(lookup[M][G], 8, 16); - uint64 b = image::normalize(lookup[M][B], 8, 16); + const uint64 r = image::normalize(lookup[M][R], 8, 16); + const uint64 g = image::normalize(lookup[M][G], 8, 16); + const uint64 b = image::normalize(lookup[M][B], 8, 16); return r << 32 | g << 16 | b << 0; } diff --git a/higan/md/vdp-performance/dma.cpp b/higan/md/vdp-performance/dma.cpp index 811edfc..de57542 100644 --- a/higan/md/vdp-performance/dma.cpp +++ b/higan/md/vdp-performance/dma.cpp @@ -13,7 +13,7 @@ auto VDP::DMA::run() -> bool { auto VDP::DMA::load() -> void { active = 1; - auto data = cpu.read(1, 1, io.mode.bit(0) << 23 | io.source << 1); + const auto data = cpu.read(1, 1, io.mode.bit(0) << 23 | io.source << 1); vdp.writeDataPort(data); io.source.bit(0,15)++; @@ -38,7 +38,7 @@ auto VDP::DMA::fill() -> void { //note: this can only copy to VRAM auto VDP::DMA::copy() -> void { - auto data = vdp.vram.readByte(io.source); + const auto data = vdp.vram.readByte(io.source); vdp.vram.writeByte(vdp.io.address, data); io.source.bit(0,15)++; diff --git a/higan/md/vdp-performance/io.cpp b/higan/md/vdp-performance/io.cpp deleted file mode 100644 index 619d2c2..0000000 --- a/higan/md/vdp-performance/io.cpp +++ /dev/null @@ -1,334 +0,0 @@ -auto VDP::read(uint24 address, uint16) -> uint16 { - switch(address & 0xc0001e) { - - //data port - case 0xc00000: case 0xc00002: { - return readDataPort(); - } - - //control port - case 0xc00004: case 0xc00006: { - return readControlPort(); - } - - //counter - case 0xc00008: case 0xc0000a: case 0xc0000c: case 0xc0000e: { - auto vcounter = state.vcounter; - if(io.interlaceMode.bit(0)) { - if(io.interlaceMode.bit(1)) vcounter <<= 1; - vcounter.bit(0) = vcounter.bit(8); - } - return vcounter << 8 | (state.hdot >> 1) << 0; - } - - } - - return 0x0000; -} - -auto VDP::write(uint24 address, uint16 data) -> void { - switch(address & 0xc0001e) { - - //data port - case 0xc00000: case 0xc00002: { - return writeDataPort(data); - } - - //control port - case 0xc00004: case 0xc00006: { - return writeControlPort(data); - } - - } -} - -// - -auto VDP::readDataPort() -> uint16 { - io.commandPending = false; - - //VRAM read - if(io.command.bit(0,3) == 0) { - auto address = io.address.bit(1,16); - auto data = vram.read(address); - io.address += io.dataIncrement; - return data; - } - - //VSRAM read - if(io.command.bit(0,3) == 4) { - auto address = io.address.bit(1,6); - auto data = vsram.read(address); - io.address += io.dataIncrement; - return data; - } - - //CRAM read - if(io.command.bit(0,3) == 8) { - auto address = io.address.bit(1,6); - auto data = cram.read(address); - io.address += io.dataIncrement; - return data.bit(0,2) << 1 | data.bit(3,5) << 5 | data.bit(6,8) << 9; - } - - return 0x0000; -} - -auto VDP::writeDataPort(uint16 data) -> void { - io.commandPending = false; - - //DMA VRAM fill - if(dma.io.wait) { - dma.io.wait = false; - dma.io.fill = data >> 8; - //falls through to memory write - //causes extra transfer to occur on VRAM fill operations - } - - //VRAM write - if(io.command.bit(0,3) == 1) { - auto address = io.address.bit(1,16); - if(io.address.bit(0)) data = data >> 8 | data << 8; - vram.write(address, data); - io.address += io.dataIncrement; - return; - } - - //VSRAM write - if(io.command.bit(0,3) == 5) { - auto address = io.address.bit(1,6); - //data format: ---- --yy yyyy yyyy - vsram.write(address, data.bit(0,9)); - io.address += io.dataIncrement; - return; - } - - //CRAM write - if(io.command.bit(0,3) == 3) { - auto address = io.address.bit(1,6); - //data format: ---- bbb- ggg- rrr- - cram.write(address, data.bit(1,3) << 0 | data.bit(5,7) << 3 | data.bit(9,11) << 6); - io.address += io.dataIncrement; - return; - } -} - -// - -auto VDP::readControlPort() -> uint16 { - io.commandPending = false; - - uint16 result; - result.bit( 0) = Region::PAL(); - result.bit( 1) = io.command.bit(5); //DMA active - result.bit( 2) = state.hcounter >= 1280; //horizontal blank - result.bit( 3) = state.vcounter >= screenHeight(); //vertical blank - result.bit( 4) = io.interlaceMode.bit(0) && state.field; - result.bit( 5) = 0; //SCOL - result.bit( 6) = 0; //SOVR - result.bit( 7) = io.vblankIRQ; - result.bit( 8) = 0; //FIFO full - result.bit( 9) = 1; //FIFO empty - result.bit(10) = 1; //constants (bits 10-15) - result.bit(11) = 0; - result.bit(12) = 1; - result.bit(13) = 1; - result.bit(14) = 0; - result.bit(15) = 0; - return result; -} - -auto VDP::writeControlPort(uint16 data) -> void { - //command write (lo) - if(io.commandPending) { - io.commandPending = false; - - io.command.bit(2,5) = data.bit(4,7); - io.address.bit(14,16) = data.bit(0,2); - - if(!dma.io.enable) io.command.bit(5) = 0; - if(dma.io.mode == 3) dma.io.wait = false; - return; - } - - //command write (hi) - if(data.bit(14,15) != 2) { - io.commandPending = true; - - io.command.bit(0,1) = data.bit(14,15); - io.address.bit(0,13) = data.bit(0,13); - return; - } - - //register write (d13 is ignored) - if(data.bit(14,15) == 2) - switch(data.bit(8,12)) { - - //mode register 1 - case 0x00: { - io.displayOverlayEnable = data.bit(0); - io.counterLatch = data.bit(1); - io.horizontalBlankInterruptEnable = data.bit(4); - io.leftColumnBlank = data.bit(5); - return; - } - - //mode register 2 - case 0x01: { - io.videoMode = data.bit(2); - io.overscan = data.bit(3); - dma.io.enable = data.bit(4); - io.verticalBlankInterruptEnable = data.bit(5); - io.displayEnable = data.bit(6); - vram.mode = data.bit(7); - if(!dma.io.enable) io.command.bit(5) = 0; - return; - } - - //plane A name table location - case 0x02: { - planeA.io.nametableAddress.bit(12,15) = data.bit(3,6); - return; - } - - //window name table location - case 0x03: { - window.io.nametableAddress.bit(10,15) = data.bit(1,6); - return; - } - - //plane B name table location - case 0x04: { - planeB.io.nametableAddress.bit(12,15) = data.bit(0,3); - return; - } - - //sprite attribute table location - case 0x05: { - sprite.io.nametableAddress.bit(8,15) = data.bit(0,7); - return; - } - - //sprite pattern base address - case 0x06: { - sprite.io.generatorAddress.bit(15) = data.bit(5); - return; - } - - //background color - case 0x07: { - io.backgroundColor = data.bit(4,5) << 0 | data.bit(0,3) << 3; - return; - } - - //horizontal interrupt counter - case 0x0a: { - io.horizontalInterruptCounter = data.bit(0,7); - return; - } - - //mode register 3 - case 0x0b: { - planeA.io.horizontalScrollMode = data.bit(0,1); - planeB.io.horizontalScrollMode = data.bit(0,1); - planeA.io.verticalScrollMode = data.bit(2); - planeB.io.verticalScrollMode = data.bit(2); - io.externalInterruptEnable = data.bit(3); - return; - } - - //mode register 4 - case 0x0c: { - io.displayWidth = data.bit(0) | data.bit(7) << 1; - io.interlaceMode = data.bit(1,2); - io.shadowHighlightEnable = data.bit(3); - io.externalColorEnable = data.bit(4); - io.horizontalSync = data.bit(5); - io.verticalSync = data.bit(6); - return; - } - - //horizontal scroll data location - case 0x0d: { - planeA.io.horizontalScrollAddress = data.bit(0,6) << 9; - planeB.io.horizontalScrollAddress = data.bit(0,6) << 9; - return; - } - - //nametable pattern base address - case 0x0e: { - //bit(0) relocates plane A to the extended VRAM region. - //bit(4) relocates plane B, but only when bit(0) is also set. - planeA.io.generatorAddress.bit(15) = data.bit(0); - planeB.io.generatorAddress.bit(15) = data.bit(4) && data.bit(0); - return; - } - - //data port auto-increment value - case 0x0f: { - io.dataIncrement = data.bit(0,7); - return; - } - - //plane size - case 0x10: { - planeA.io.nametableWidth = data.bit(0,1); - planeB.io.nametableWidth = data.bit(0,1); - planeA.io.nametableHeight = data.bit(4,5); - planeB.io.nametableHeight = data.bit(4,5); - return; - } - - //window plane horizontal position - case 0x11: { - window.io.horizontalOffset = data.bit(0,4) << 4; - window.io.horizontalDirection = data.bit(7); - return; - } - - //window plane vertical position - case 0x12: { - window.io.verticalOffset = data.bit(0,4) << 3; - window.io.verticalDirection = data.bit(7); - return; - } - - //DMA length - case 0x13: { - dma.io.length.bit(0,7) = data.bit(0,7); - return; - } - - //DMA length - case 0x14: { - dma.io.length.bit(8,15) = data.bit(0,7); - return; - } - - //DMA source - case 0x15: { - dma.io.source.bit(0,7) = data.bit(0,7); - return; - } - - //DMA source - case 0x16: { - dma.io.source.bit(8,15) = data.bit(0,7); - return; - } - - //DMA source - case 0x17: { - dma.io.source.bit(16,21) = data.bit(0,5); - dma.io.mode = data.bit(6,7); - dma.io.wait = dma.io.mode.bit(1); - return; - } - - //unused - default: { - return; - } - - } -} diff --git a/higan/md/vdp-performance/memory.cpp b/higan/md/vdp-performance/memory.cpp index c1845b4..cf900d8 100644 --- a/higan/md/vdp-performance/memory.cpp +++ b/higan/md/vdp-performance/memory.cpp @@ -1,4 +1,4 @@ -auto VDP::VRAM::read(uint16 address) const -> uint16 { +auto VDP::VRAM::read(const uint16 address) const -> uint16 { if(mode == 0) { return memory[(uint15)address]; } else { @@ -8,7 +8,7 @@ auto VDP::VRAM::read(uint16 address) const -> uint16 { } } -auto VDP::VRAM::write(uint16 address, uint16 data) -> void { +auto VDP::VRAM::write(const uint16 address, const uint16 data) -> void { if(mode == 0) { memory[(uint15)address] = data; } else { @@ -27,35 +27,35 @@ auto VDP::VRAM::write(uint16 address, uint16 data) -> void { vdp.sprite.write(address - vdp.sprite.io.nametableAddress, data); } -auto VDP::VRAM::readByte(uint17 address) const -> uint8 { +auto VDP::VRAM::readByte(const uint17 address) const -> uint8 { return read(address >> 1).byte(!address.bit(0)); } -auto VDP::VRAM::writeByte(uint17 address, uint8 data) -> void { +auto VDP::VRAM::writeByte(const uint17 address, const uint8 data) -> void { auto word = read(address >> 1); word.byte(!address.bit(0)) = data; write(address >> 1, word); } -auto VDP::VSRAM::read(uint6 address) const -> uint10 { +auto VDP::VSRAM::read(const uint6 address) const -> uint10 { if(address >= 40) return 0x0000; return memory[address]; } -auto VDP::VSRAM::write(uint6 address, uint10 data) -> void { +auto VDP::VSRAM::write(const uint6 address, const uint10 data) -> void { if(address >= 40) return; memory[address] = data; } -auto VDP::CRAM::read(uint6 address) const -> uint9 { +auto VDP::CRAM::read(const uint6 address) const -> uint9 { return memory[address]; } -auto VDP::CRAM::write(uint6 address, uint9 data) -> void { +auto VDP::CRAM::write(const uint6 address, const uint9 data) -> void { memory[address] = data; //ppcccc -> cccc-pp - uint7 offset = (address >> 4 | address << 3) & 0x7b; + const uint7 offset = (address >> 4 | address << 3) & 0x7b; palette[0 << 7 | 0 << 2 | offset] = palette[0 << 7 | 1 << 2 | offset] = 0 << 9 | data; palette[1 << 7 | 0 << 2 | offset] = palette[1 << 7 | 1 << 2 | offset] = 1 << 9 | data; palette[2 << 7 | 0 << 2 | offset] = palette[2 << 7 | 1 << 2 | offset] = 2 << 9 | data; diff --git a/higan/md/vdp-performance/sprite.cpp b/higan/md/vdp-performance/sprite.cpp index d8e3675..99e9448 100644 --- a/higan/md/vdp-performance/sprite.cpp +++ b/higan/md/vdp-performance/sprite.cpp @@ -1,5 +1,4 @@ -auto VDP::Sprite::render() -> void { - bool interlace = vdp.io.interlaceMode == 3; +template auto VDP::Sprite::render() -> void { uint y = vdp.state.vcounter + 128; if(interlace) y = y << 1 | vdp.state.field; @@ -20,22 +19,22 @@ auto VDP::Sprite::render() -> void { } while(link && link < 80 && objectSize < 20 && tiles < 40 && ++count < 80); memory::fill(pixels, vdp.screenWidth()); - uint shiftY = interlace ? 4 : 3; - uint maskY = interlace ? 15 : 7; - uint tileShift = interlace ? 7 : 6; + const uint shiftY = interlace ? 4 : 3; + const uint maskY = interlace ? 15 : 7; + const uint tileShift = interlace ? 7 : 6; for(int index = objectSize - 1; index >= 0; index--) { - auto& object = objects[index]; + const auto& object = objects[index]; uint objectY = y - object.y; if(object.verticalFlip) objectY = (object.height() - 1) - objectY; - uint tileIncrement = (object.height() >> interlace) >> 3 << tileShift; + const uint tileIncrement = (object.height() >> interlace) >> 3 << tileShift; uint tileAddress = object.address + (objectY >> shiftY) << tileShift; tileAddress += (objectY & maskY) << 3; auto tileData = &vdp.vram.pixels[tileAddress & 0x1fff8]; uint w = !object.horizontalFlip ? object.x - 128 : (object.x + object.width() - 1) - 128; int incrementX = object.horizontalFlip ? -1 : +1; for(uint objectX = 0; objectX < object.width();) { - if(uint color = tileData[objectX & 7]) { + if(const uint color = tileData[objectX & 7]) { pixels[w & 511] = object.palette << 0 | object.priority << 2 | color; } w += incrementX; diff --git a/higan/md/vdp-performance/vdp.cpp b/higan/md/vdp-performance/vdp.cpp index cacdde3..097131a 100644 --- a/higan/md/vdp-performance/vdp.cpp +++ b/higan/md/vdp-performance/vdp.cpp @@ -4,7 +4,7 @@ namespace higan::MegaDrive { VDP vdp; #include "memory.cpp" -#include "io.cpp" + #include "dma.cpp" #include "background.cpp" #include "object.cpp" @@ -167,7 +167,7 @@ auto VDP::refresh() -> void { auto VDP::render() -> void { auto output = this->output; - uint y = state.vcounter; + const uint y = state.vcounter; if(!latch.interlace) { output += y * 320; } else { @@ -175,17 +175,31 @@ auto VDP::render() -> void { } if(!io.displayEnable) return (void)memory::fill(output, screenWidth()); - if(y < window.io.verticalOffset ^ window.io.verticalDirection) { - window.renderWindow(0, screenWidth()); - } else if(!window.io.horizontalDirection) { - window.renderWindow(0, window.io.horizontalOffset); - planeA.renderScreen(window.io.horizontalOffset, screenWidth()); + if (vdp.io.interlaceMode == 3) { + if(y < window.verticalOffsetXorDirection) { + window.renderWindow(0, screenWidth()); + } else if(!window.io.horizontalDirection) { + window.renderWindow(0, window.io.horizontalOffset); + planeA.renderScreen(window.io.horizontalOffset, screenWidth()); + } else { + planeA.renderScreen(0, window.io.horizontalOffset); + window.renderWindow(window.io.horizontalOffset, screenWidth()); + } + planeB.renderScreen(0, screenWidth()); + sprite.render(); } else { - planeA.renderScreen(0, window.io.horizontalOffset); - window.renderWindow(window.io.horizontalOffset, screenWidth()); - } - planeB.renderScreen(0, screenWidth()); - sprite.render(); + if(y < window.verticalOffsetXorDirection) { + window.renderWindow(0, screenWidth()); + } else if(!window.io.horizontalDirection) { + window.renderWindow(0, window.io.horizontalOffset); + planeA.renderScreen(window.io.horizontalOffset, screenWidth()); + } else { + planeA.renderScreen(0, window.io.horizontalOffset); + window.renderWindow(window.io.horizontalOffset, screenWidth()); + } + planeB.renderScreen(0, screenWidth()); + sprite.render(); + } auto A = &planeA.pixels[0]; auto B = &planeB.pixels[0]; diff --git a/higan/md/vdp-performance/vdp.hpp b/higan/md/vdp-performance/vdp.hpp index 540031a..3e3a61d 100644 --- a/higan/md/vdp-performance/vdp.hpp +++ b/higan/md/vdp-performance/vdp.hpp @@ -19,15 +19,357 @@ struct VDP : Thread { auto render() -> void; auto power(bool reset) -> void; - //io.cpp - auto read(uint24 address, uint16 data) -> uint16; - auto write(uint24 address, uint16 data) -> void; + inline auto readDataPort() -> uint16 { + io.commandPending = false; + + //VRAM read + if(io.command.bit(0,3) == 0) { + auto address = io.address.bit(1,16); + auto data = vram.read(address); + io.address += io.dataIncrement; + return data; + } + + //VSRAM read + if(io.command.bit(0,3) == 4) { + auto address = io.address.bit(1,6); + auto data = vsram.read(address); + io.address += io.dataIncrement; + return data; + } + + //CRAM read + if(io.command.bit(0,3) == 8) { + auto address = io.address.bit(1,6); + auto data = cram.read(address); + io.address += io.dataIncrement; + return data.bit(0,2) << 1 | data.bit(3,5) << 5 | data.bit(6,8) << 9; + } + + return 0x0000; + } + + inline auto writeDataPort(uint16 data) -> void { + io.commandPending = false; + + //DMA VRAM fill + if(dma.io.wait) { + dma.io.wait = false; + dma.io.fill = data >> 8; + //falls through to memory write + //causes extra transfer to occur on VRAM fill operations + } + + //VRAM write + if(io.command.bit(0,3) == 1) { + auto address = io.address.bit(1,16); + if(io.address.bit(0)) data = data >> 8 | data << 8; + vram.write(address, data); + io.address += io.dataIncrement; + return; + } + + //VSRAM write + if(io.command.bit(0,3) == 5) { + auto address = io.address.bit(1,6); + //data format: ---- --yy yyyy yyyy + vsram.write(address, data.bit(0,9)); + io.address += io.dataIncrement; + return; + } + + //CRAM write + if(io.command.bit(0,3) == 3) { + auto address = io.address.bit(1,6); + //data format: ---- bbb- ggg- rrr- + cram.write(address, data.bit(1,3) << 0 | data.bit(5,7) << 3 | data.bit(9,11) << 6); + io.address += io.dataIncrement; + return; + } + } + + // + + inline auto readControlPort() -> uint16 { + io.commandPending = false; + + uint16 result; + result.bit( 0) = Region::PAL(); + result.bit( 1) = io.command.bit(5); //DMA active + result.bit( 2) = state.hcounter >= 1280; //horizontal blank + result.bit( 3) = state.vcounter >= screenHeight(); //vertical blank + result.bit( 4) = io.interlaceMode.bit(0) && state.field; + result.bit( 5) = 0; //SCOL + result.bit( 6) = 0; //SOVR + result.bit( 7) = io.vblankIRQ; + result.bit( 8) = 0; //FIFO full + result.bit( 9) = 1; //FIFO empty + result.bit(10) = 1; //constants (bits 10-15) + result.bit(11) = 0; + result.bit(12) = 1; + result.bit(13) = 1; + result.bit(14) = 0; + result.bit(15) = 0; + return result; + } + + inline auto writeControlPort(uint16 data) -> void { + //command write (lo) + if(io.commandPending) { + io.commandPending = false; + + io.command.bit(2,5) = data.bit(4,7); + io.address.bit(14,16) = data.bit(0,2); + + if(!dma.io.enable) io.command.bit(5) = 0; + if(dma.io.mode == 3) dma.io.wait = false; + return; + } + + //command write (hi) + if(data.bit(14,15) != 2) { + io.commandPending = true; + + io.command.bit(0,1) = data.bit(14,15); + io.address.bit(0,13) = data.bit(0,13); + return; + } + + //register write (d13 is ignored) + if(data.bit(14,15) == 2) + switch(data.bit(8,12)) { + + //mode register 1 + case 0x00: { + io.displayOverlayEnable = data.bit(0); + io.counterLatch = data.bit(1); + io.horizontalBlankInterruptEnable = data.bit(4); + io.leftColumnBlank = data.bit(5); + return; + } + + //mode register 2 + case 0x01: { + io.videoMode = data.bit(2); + io.overscan = data.bit(3); + dma.io.enable = data.bit(4); + io.verticalBlankInterruptEnable = data.bit(5); + io.displayEnable = data.bit(6); + vram.mode = data.bit(7); + if(!dma.io.enable) io.command.bit(5) = 0; + return; + } + + //plane A name table location + case 0x02: { + planeA.io.nametableAddress.bit(12,15) = data.bit(3,6); + return; + } + + //window name table location + case 0x03: { + window.io.nametableAddress.bit(10,15) = data.bit(1,6); + window.nametableAddress = window.io.nametableAddress & (io.displayWidth ? ~0x0400 : ~0); + window.widthSize = 32 << (bool)io.displayWidth; + window.widthMask = window.widthSize - 1; + return; + } + + //plane B name table location + case 0x04: { + planeB.io.nametableAddress.bit(12,15) = data.bit(0,3); + return; + } + + //sprite attribute table location + case 0x05: { + sprite.io.nametableAddress.bit(8,15) = data.bit(0,7); + return; + } + + //sprite pattern base address + case 0x06: { + sprite.io.generatorAddress.bit(15) = data.bit(5); + return; + } + + //background color + case 0x07: { + io.backgroundColor = data.bit(4,5) << 0 | data.bit(0,3) << 3; + return; + } + + //horizontal interrupt counter + case 0x0a: { + io.horizontalInterruptCounter = data.bit(0,7); + return; + } + + //mode register 3 + case 0x0b: { + planeA.io.horizontalScrollMode = data.bit(0,1); + planeB.io.horizontalScrollMode = data.bit(0,1); + planeA.io.verticalScrollMode = data.bit(2); + planeB.io.verticalScrollMode = data.bit(2); + io.externalInterruptEnable = data.bit(3); + return; + } + + //mode register 4 + case 0x0c: { + io.displayWidth = data.bit(0) | data.bit(7) << 1; + io.interlaceMode = data.bit(1,2); + io.shadowHighlightEnable = data.bit(3); + io.externalColorEnable = data.bit(4); + io.horizontalSync = data.bit(5); + io.verticalSync = data.bit(6); + + window.nametableAddress = window.io.nametableAddress & (io.displayWidth ? ~0x0400 : ~0); + window.widthSize = 32 << (bool)io.displayWidth; + window.widthMask = window.widthSize - 1; + return; + } + + //horizontal scroll data location + case 0x0d: { + planeA.io.horizontalScrollAddress = data.bit(0,6) << 9; + planeB.io.horizontalScrollAddress = data.bit(0,6) << 9; + return; + } + + //nametable pattern base address + case 0x0e: { + //bit(0) relocates plane A to the extended VRAM region. + //bit(4) relocates plane B, but only when bit(0) is also set. + planeA.io.generatorAddress.bit(15) = data.bit(0); + planeB.io.generatorAddress.bit(15) = data.bit(4) && data.bit(0); + return; + } + + //data port auto-increment value + case 0x0f: { + io.dataIncrement = data.bit(0,7); + return; + } + + //plane size + case 0x10: { + planeA.io.nametableWidth = data.bit(0,1); + planeB.io.nametableWidth = data.bit(0,1); + planeA.io.nametableHeight = data.bit(4,5); + planeB.io.nametableHeight = data.bit(4,5); + + planeA.nametableWidth = 32 * (1 + planeA.io.nametableWidth); + planeA.nametableWidthMask = planeA.nametableWidth - 1; + planeA.nametableHeightMask = 32 * (1 + planeA.io.nametableHeight) - 1; + + planeB.nametableWidth = 32 * (1 + planeB.io.nametableWidth); + planeB.nametableWidthMask = planeB.nametableWidth - 1; + planeB.nametableHeightMask = 32 * (1 + planeB.io.nametableHeight) - 1; + + return; + } + + //window plane horizontal position + case 0x11: { + window.io.horizontalOffset = data.bit(0,4) << 4; + window.io.horizontalDirection = data.bit(7); + return; + } + + //window plane vertical position + case 0x12: { + window.io.verticalOffset = data.bit(0,4) << 3; + window.io.verticalDirection = data.bit(7); + window.verticalOffsetXorDirection = window.io.verticalOffset ^ window.io.verticalDirection; + return; + } + + //DMA length + case 0x13: { + dma.io.length.bit(0,7) = data.bit(0,7); + return; + } + + //DMA length + case 0x14: { + dma.io.length.bit(8,15) = data.bit(0,7); + return; + } + + //DMA source + case 0x15: { + dma.io.source.bit(0,7) = data.bit(0,7); + return; + } + + //DMA source + case 0x16: { + dma.io.source.bit(8,15) = data.bit(0,7); + return; + } + + //DMA source + case 0x17: { + dma.io.source.bit(16,21) = data.bit(0,5); + dma.io.mode = data.bit(6,7); + dma.io.wait = dma.io.mode.bit(1); + return; + } + + //unused + default: { + return; + } + + } + } - auto readDataPort() -> uint16; - auto writeDataPort(uint16 data) -> void; - auto readControlPort() -> uint16; - auto writeControlPort(uint16 data) -> void; + //io.cpp + inline auto read(uint24 address, uint16) -> uint16 { + switch(address & 0xc0001e) { + + //data port + case 0xc00000: case 0xc00002: { + return readDataPort(); + } + + //control port + case 0xc00004: case 0xc00006: { + return readControlPort(); + } + + //counter + case 0xc00008: case 0xc0000a: case 0xc0000c: case 0xc0000e: { + auto vcounter = state.vcounter; + if(io.interlaceMode.bit(0)) { + if(io.interlaceMode.bit(1)) vcounter <<= 1; + vcounter.bit(0) = vcounter.bit(8); + } + return vcounter << 8 | (state.hdot >> 1) << 0; + } + + } + + return 0x0000; + } + + inline auto write(uint24 address, uint16 data) -> void { + switch(address & 0xc0001e) { + + //data port + case 0xc00000: case 0xc00002: { + return writeDataPort(data); + } + + //control port + case 0xc00004: case 0xc00006: { + return writeControlPort(data); + } + + } + } //color.cpp auto color(uint32) -> uint64; @@ -36,21 +378,21 @@ struct VDP : Thread { auto serialize(serializer&) -> void; private: - auto pixelWidth() const -> uint { return latch.displayWidth ? 4 : 5; } - auto screenWidth() const -> uint { return latch.displayWidth ? 320 : 256; } - auto screenHeight() const -> uint { return latch.overscan ? 240 : 224; } - auto frameHeight() const -> uint { return Region::PAL() ? 312 : 262; } + constexpr inline auto pixelWidth() const -> uint { return latch.displayWidth ? 4 : 5; } + constexpr inline auto screenWidth() const -> uint { return latch.displayWidth ? 320 : 256; } + constexpr inline auto screenHeight() const -> uint { return latch.overscan ? 240 : 224; } + constexpr inline auto frameHeight() const -> uint { return Region::PAL() ? 312 : 262; } uint32 buffer[320 * 512]; uint32* output = nullptr; struct VRAM { //memory.cpp - auto read(uint16 address) const -> uint16; - auto write(uint16 address, uint16 data) -> void; + auto read(const uint16 address) const -> uint16; + auto write(const uint16 address, const uint16 data) -> void; - auto readByte(uint17 address) const -> uint8; - auto writeByte(uint17 address, uint8 data) -> void; + auto readByte(const uint17 address) const -> uint8; + auto writeByte(const uint17 address, const uint8 data) -> void; //serialization.cpp auto serialize(serializer&) -> void; @@ -63,8 +405,8 @@ struct VDP : Thread { struct VSRAM { //memory.cpp - auto read(uint6 address) const -> uint10; - auto write(uint6 address, uint10 data) -> void; + auto read(const uint6 address) const -> uint10; + auto write(const uint6 address, const uint10 data) -> void; //serialization.cpp auto serialize(serializer&) -> void; @@ -74,8 +416,8 @@ struct VDP : Thread { struct CRAM { //memory.cpp - auto read(uint6 address) const -> uint9; - auto write(uint6 address, uint9 data) -> void; + auto read(const uint6 address) const -> uint9; + auto write(const uint6 address, const uint9 data) -> void; //serialization.cpp auto serialize(serializer&) -> void; @@ -112,8 +454,8 @@ struct VDP : Thread { enum class ID : uint { PlaneA, Window, PlaneB } id; //background.cpp - auto renderScreen(uint from, uint to) -> void; - auto renderWindow(uint from, uint to) -> void; + template auto renderScreen(const uint from, const uint to) -> void; + template auto renderWindow(const uint from, const uint to) -> void; //serialization.cpp auto serialize(serializer&) -> void; @@ -136,6 +478,16 @@ struct VDP : Thread { uint1 verticalDirection; } io; + uint16 verticalOffsetXorDirection = 0; + + uint nametableWidth; + uint nametableWidthMask; + uint nametableHeightMask; + + uint nametableAddress; + uint widthSize; + uint widthMask; + //unserialized: uint7 pixels[320]; }; @@ -165,7 +517,7 @@ struct VDP : Thread { struct Sprite { //sprite.cpp - auto render() -> void; + template auto render() -> void; auto write(uint9 address, uint16 data) -> void; //serialization.cpp diff --git a/higan/target-web/GNUmakefile b/higan/target-web/GNUmakefile index 046d5fa..1d5d5a8 100644 --- a/higan/target-web/GNUmakefile +++ b/higan/target-web/GNUmakefile @@ -14,6 +14,7 @@ synchro := true simulated_synchro := false eventinstruction_notify := false simd := false +lto := true PYTHON=$(shell which python3 || which python) diff --git a/higan/target-web/web.cpp b/higan/target-web/web.cpp index a1c5ebc..2c014f2 100644 --- a/higan/target-web/web.cpp +++ b/higan/target-web/web.cpp @@ -3,6 +3,8 @@ WebPlatform *webplatform = new WebPlatform(); emscripten::val scheduledStateSave = emscripten::val::null(); +//#define NO_LIMIT + /* lifecycle */ bool isStarted() { return webplatform->started; } bool isRunning() { return webplatform->running; } @@ -12,9 +14,11 @@ static uint lastExecution = chrono::millisecond(); void run() { uint currentExecution = chrono::millisecond(); +#ifndef NO_LIMIT if (currentExecution - lastExecution < 16) { return; } +#endif lastExecution = currentExecution; @@ -47,6 +51,9 @@ bool start() { lastExecution = 0; webplatform->started = true; emscripten_set_main_loop(run, 0, 0); +#ifdef NO_LIMIT + emscripten_set_main_loop_timing(EM_TIMING_SETIMMEDIATE, 0); +#endif return true; } diff --git a/icarus/cartridge/mega-drive.cpp b/icarus/cartridge/mega-drive.cpp index d9435c9..ac6d672 100644 --- a/icarus/cartridge/mega-drive.cpp +++ b/icarus/cartridge/mega-drive.cpp @@ -90,7 +90,7 @@ auto MegaDrive::heuristics(vector& data, string location) -> string { s += " content: Program\n"; s += " slot\n"; s += " type: Mega Drive\n"; - } else if(domesticName == "SONIC & KNUCKLES") { + } /* else if(domesticName == "SONIC & KNUCKLES") { // Disabled to allow common (combined) roms to work s += " memory\n"; s += " type: ROM\n"; s += " size: 0x200000\n"; @@ -101,7 +101,7 @@ auto MegaDrive::heuristics(vector& data, string location) -> string { s += " content: Patch\n"; s += " slot\n"; s += " type: Mega Drive\n"; - } else { + } */ else { s += " memory\n"; s += " type: ROM\n"; s +={" size: 0x", hex(data.size()), "\n"};