From 0b70a01b47b484bcbc40bae78ddbaacecbb0bdb2 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sat, 20 Aug 2016 00:11:26 +1000 Subject: [PATCH] Update to v101r10 release. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit byuu says: Changelog: - 68K: MOVEQ is 8-bit signed - 68K: disassembler was print EOR for OR instructions - 68K: address/program-counter indexed mode had the signed-word/long bit backward - 68K: ADDQ/SUBQ #n,aN always works in long mode; regardless of size - 68K→VDP DMA needs to use `mode.bit(0)<<22|dmaSource`; increment by one instead of two - Z80: added registers and initial two instructions - MS: hooked up enough to load and start running games - Sonic the Hedgehog can execute exactly one instruction... whoo. --- higan/emulator/emulator.hpp | 2 +- higan/md/apu/apu.cpp | 13 ++++ higan/md/apu/apu.hpp | 4 ++ higan/md/cpu/cpu.cpp | 6 +- higan/md/vdp/dma.cpp | 4 +- higan/ms/cartridge/cartridge.cpp | 82 ++++++++++++++++++++++ higan/ms/cartridge/cartridge.hpp | 31 ++++++++ higan/ms/cpu/cpu.cpp | 26 +++++++ higan/ms/cpu/cpu.hpp | 10 +++ higan/ms/interface/interface.cpp | 25 +++++-- higan/ms/ms.hpp | 5 ++ higan/ms/psg/psg.cpp | 9 +++ higan/ms/psg/psg.hpp | 3 + higan/ms/system/system.cpp | 36 +++++++++- higan/ms/system/system.hpp | 15 +++- higan/ms/vdp/vdp.cpp | 18 +++++ higan/ms/vdp/vdp.hpp | 9 ++- higan/processor/m68k/disassembler.cpp | 4 +- higan/processor/m68k/effective-address.cpp | 4 +- higan/processor/m68k/instructions.cpp | 10 +-- higan/processor/z80/instruction.cpp | 19 +++++ higan/processor/z80/instructions.cpp | 5 ++ higan/processor/z80/z80.cpp | 20 ++++++ higan/processor/z80/z80.hpp | 57 +++++++++++++++ 24 files changed, 390 insertions(+), 27 deletions(-) create mode 100644 higan/processor/z80/instruction.cpp create mode 100644 higan/processor/z80/instructions.cpp diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 582c2425..13fa1e00 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "101.09"; + static const string Version = "101.10"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/md/apu/apu.cpp b/higan/md/apu/apu.cpp index b8864549..36210098 100644 --- a/higan/md/apu/apu.cpp +++ b/higan/md/apu/apu.cpp @@ -17,6 +17,19 @@ auto APU::step(uint clocks) -> void { synchronize(cpu); } +auto APU::wait() -> void { + step(1); +} + +auto APU::read(uint16 addr) -> uint8 { + step(1); + return 0x00; +} + +auto APU::write(uint16 addr, uint8 data) -> void { + step(1); +} + auto APU::power() -> void { } diff --git a/higan/md/apu/apu.hpp b/higan/md/apu/apu.hpp index b9def4e3..629bde82 100644 --- a/higan/md/apu/apu.hpp +++ b/higan/md/apu/apu.hpp @@ -5,6 +5,10 @@ struct APU : Processor::Z80, Thread { auto main() -> void; auto step(uint clocks) -> void; + auto wait() -> void override; + auto read(uint16 addr) -> uint8 override; + auto write(uint16 addr, uint8 data) -> void override; + auto power() -> void; auto reset() -> void; }; diff --git a/higan/md/cpu/cpu.cpp b/higan/md/cpu/cpu.cpp index f3a67267..237d377a 100644 --- a/higan/md/cpu/cpu.cpp +++ b/higan/md/cpu/cpu.cpp @@ -17,7 +17,7 @@ auto CPU::boot() -> void { auto CPU::main() -> void { #if 0 static file fp; - if(!fp) fp.open({Path::user(), "Desktop/trace.log"}, file::mode::write); + if(!fp) fp.open({Path::user(), "Desktop/tracer.log"}, file::mode::write); fp.print(pad(disassemble(r.pc), -60, ' '), " ", disassembleRegisters().replace("\n", " "), "\n"); #endif @@ -85,7 +85,7 @@ auto CPU::reset() -> void { auto CPU::readByte(uint24 addr) -> uint8 { if(addr < 0x400000) return cartridge.readByte(addr); if(addr < 0xa00000) return 0x00; - if(addr < 0xc00000) return rand(); + if(addr < 0xc00000) return rand(), 0; if(addr < 0xe00000) return vdp.readByte(addr); return ram[addr & 0xffff]; } @@ -93,7 +93,7 @@ auto CPU::readByte(uint24 addr) -> uint8 { auto CPU::readWord(uint24 addr) -> uint16 { if(addr < 0x400000) return cartridge.readWord(addr); if(addr < 0xa00000) return 0x0000; - if(addr < 0xc00000) return rand(); + if(addr < 0xc00000) return rand(), 0; if(addr < 0xe00000) return vdp.readWord(addr); uint16 data = ram[addr + 0 & 65535] << 8; return data | ram[addr + 1 & 65535] << 0; diff --git a/higan/md/vdp/dma.cpp b/higan/md/vdp/dma.cpp index 5f478aa3..dd7d7e61 100644 --- a/higan/md/vdp/dma.cpp +++ b/higan/md/vdp/dma.cpp @@ -10,10 +10,10 @@ auto VDP::dmaRun() -> void { auto VDP::dmaLoad() -> void { cpu.wait |= Wait::VDP_DMA; - auto data = cpu.readWord(io.dmaSource); + auto data = cpu.readWord(io.dmaMode.bit(0) << 23 | io.dmaSource << 1); writeDataPort(data); - io.dmaSource.bits(0,15) += 2; + io.dmaSource.bits(0,15)++; if(--io.dmaLength == 0) { io.command.bit(5) = 0; cpu.wait &=~ Wait::VDP_DMA; diff --git a/higan/ms/cartridge/cartridge.cpp b/higan/ms/cartridge/cartridge.cpp index 15b1cb2a..2d5330dd 100644 --- a/higan/ms/cartridge/cartridge.cpp +++ b/higan/ms/cartridge/cartridge.cpp @@ -4,4 +4,86 @@ namespace MasterSystem { Cartridge cartridge; +auto Cartridge::load() -> bool { + information = {}; + + switch(system.model()) { + case Model::MasterSystem: + if(auto pathID = interface->load(ID::MasterSystem, "Master System", "ms")) { + information.pathID = pathID(); + } else return false; + break; + case Model::GameGear: + if(auto pathID = interface->load(ID::GameGear, "Game Gear", "gg")) { + information.pathID = pathID(); + } else return false; + break; + } + + if(auto fp = interface->open(pathID(), "manifest.bml", File::Read, File::Required)) { + information.manifest = fp->reads(); + } else return false; + + auto document = BML::unserialize(information.manifest); + information.title = document["information/title"].text(); + + if(auto node = document["board/rom"]) { + rom.size = node["size"].natural(); + rom.mask = bit::round(rom.size) - 1; + if(rom.size) { + rom.data = new uint8[rom.mask + 1]; + if(auto name = node["name"].text()) { + if(auto fp = interface->open(pathID(), name, File::Read, File::Required)) { + fp->read(rom.data, rom.size); + } + } + } + } + + if(auto node = document["board/ram"]) { + ram.size = node["size"].natural(); + ram.mask = bit::round(ram.size) - 1; + if(ram.size) { + ram.data = new uint8[ram.mask + 1]; + if(auto name = node["name"].text()) { + if(auto fp = interface->open(pathID(), name, File::Read)) { + fp->read(ram.data, ram.size); + } + } + } + } + + return true; +} + +auto Cartridge::save() -> void { + auto document = BML::unserialize(information.manifest); + + if(auto name = document["board/ram/name"].text()) { + if(auto fp = interface->open(pathID(), name, File::Write)) { + fp->write(ram.data, ram.size); + } + } +} + +auto Cartridge::unload() -> void { + delete[] rom.data; + delete[] ram.data; + rom = {}; + ram = {}; +} + +auto Cartridge::read(uint16 addr) -> uint8 { + return rom.data[addr & rom.mask]; +} + +auto Cartridge::write(uint16 addr, uint8 data) -> void { +} + +auto Cartridge::power() -> void { +} + +auto Cartridge::reset() -> void { +} + } diff --git a/higan/ms/cartridge/cartridge.hpp b/higan/ms/cartridge/cartridge.hpp index 26579aec..75cdc6a5 100644 --- a/higan/ms/cartridge/cartridge.hpp +++ b/higan/ms/cartridge/cartridge.hpp @@ -1,4 +1,35 @@ struct Cartridge { + auto pathID() const -> uint { return information.pathID; } + auto sha256() const -> string { return information.sha256; } + auto manifest() const -> string { return information.manifest; } + auto title() const -> string { return information.title; } + + auto load() -> bool; + auto save() -> void; + auto unload() -> void; + + auto read(uint16 addr) -> uint8; + auto write(uint16 addr, uint8 data) -> void; + + auto power() -> void; + auto reset() -> void; + +private: + struct Information { + uint pathID = 0; + string sha256; + string manifest; + string title; + } information; + + struct Memory { + uint8* data = nullptr; + uint size = 0; + uint mask = 0; + }; + + Memory rom; + Memory ram; }; extern Cartridge cartridge; diff --git a/higan/ms/cpu/cpu.cpp b/higan/ms/cpu/cpu.cpp index ffca5083..e0ecf92f 100644 --- a/higan/ms/cpu/cpu.cpp +++ b/higan/ms/cpu/cpu.cpp @@ -9,10 +9,36 @@ auto CPU::Enter() -> void { } auto CPU::main() -> void { + instruction(); } auto CPU::step(uint clocks) -> void { Thread::step(clocks); + synchronize(vdp); + synchronize(psg); +} + +auto CPU::wait() -> void { + step(1); +} + +auto CPU::read(uint16 addr) -> uint8 { + step(1); + if(addr < 0xc000) return cartridge.read(addr); + return ram[addr & 0x1fff]; +} + +auto CPU::write(uint16 addr, uint8 data) -> void { + step(1); + if(addr < 0xc000) return cartridge.write(addr, data); + ram[addr & 0x1fff] = data; +} + +auto CPU::power() -> void { +} + +auto CPU::reset() -> void { + create(CPU::Enter, system.colorburst()); } } diff --git a/higan/ms/cpu/cpu.hpp b/higan/ms/cpu/cpu.hpp index 92eb165e..bcdff0d6 100644 --- a/higan/ms/cpu/cpu.hpp +++ b/higan/ms/cpu/cpu.hpp @@ -4,6 +4,16 @@ struct CPU : Processor::Z80, Thread { static auto Enter() -> void; auto main() -> void; auto step(uint clocks) -> void; + + auto wait() -> void override; + auto read(uint16 addr) -> uint8 override; + auto write(uint16 addr, uint8 data) -> void override; + + auto power() -> void; + auto reset() -> void; + +private: + uint8 ram[0x2000]; //8KB }; extern CPU cpu; diff --git a/higan/ms/interface/interface.cpp b/higan/ms/interface/interface.cpp index 845b2213..61b27498 100644 --- a/higan/ms/interface/interface.cpp +++ b/higan/ms/interface/interface.cpp @@ -32,25 +32,29 @@ Interface::Interface() { controllerPort1.devices.append(device); controllerPort2.devices.append(device); } + + ports.append(move(controllerPort1)); + ports.append(move(controllerPort2)); } auto Interface::manifest() -> string { - return ""; + return cartridge.manifest(); } auto Interface::title() -> string { - return ""; + return cartridge.title(); } auto Interface::videoSize() -> VideoSize { - return {256, 192}; + return {256, 240}; } auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { + auto a = arc ? 8.0 / 7.0 : 1.0; uint w = 256; - uint h = 192; - uint m = min(width / w, height / h); - return {w * m, h * m}; + uint h = 240; + uint m = min(width / (w * a), height / h); + return {uint(w * a * m), uint(h * m)}; } auto Interface::videoFrequency() -> double { @@ -70,26 +74,33 @@ auto Interface::audioFrequency() -> double { } auto Interface::loaded() -> bool { - return false; + return system.loaded(); } auto Interface::load(uint id) -> bool { + if(id == ID::MasterSystem) return system.load(Model::MasterSystem); + if(id == ID::GameGear) return system.load(Model::GameGear); return false; } auto Interface::save() -> void { + system.save(); } auto Interface::unload() -> void { + system.unload(); } auto Interface::power() -> void { + system.power(); } auto Interface::reset() -> void { + system.reset(); } auto Interface::run() -> void { + system.run(); } auto Interface::serialize() -> serializer { diff --git a/higan/ms/ms.hpp b/higan/ms/ms.hpp index b688f6ea..4dc63286 100644 --- a/higan/ms/ms.hpp +++ b/higan/ms/ms.hpp @@ -14,6 +14,11 @@ namespace MasterSystem { using Scheduler = Emulator::Scheduler; extern Scheduler scheduler; + enum class Model : uint { + MasterSystem, + GameGear, + }; + struct Thread : Emulator::Thread { auto create(auto (*entrypoint)() -> void, double frequency) -> void { Emulator::Thread::create(entrypoint, frequency); diff --git a/higan/ms/psg/psg.cpp b/higan/ms/psg/psg.cpp index 60d64bf4..7d08d9e2 100644 --- a/higan/ms/psg/psg.cpp +++ b/higan/ms/psg/psg.cpp @@ -9,10 +9,19 @@ auto PSG::Enter() -> void { } auto PSG::main() -> void { + step(1); } auto PSG::step(uint clocks) -> void { Thread::step(clocks); + synchronize(cpu); +} + +auto PSG::power() -> void { +} + +auto PSG::reset() -> void { + create(PSG::Enter, system.colorburst()); } } diff --git a/higan/ms/psg/psg.hpp b/higan/ms/psg/psg.hpp index 0e30f7cc..f9d20933 100644 --- a/higan/ms/psg/psg.hpp +++ b/higan/ms/psg/psg.hpp @@ -4,6 +4,9 @@ struct PSG : Thread { static auto Enter() -> void; auto main() -> void; auto step(uint clocks) -> void; + + auto power() -> void; + auto reset() -> void; }; extern PSG psg; diff --git a/higan/ms/system/system.cpp b/higan/ms/system/system.cpp index 3376bb10..469c0f6c 100644 --- a/higan/ms/system/system.cpp +++ b/higan/ms/system/system.cpp @@ -6,22 +6,54 @@ System system; Scheduler scheduler; auto System::run() -> void { + if(scheduler.enter() == Scheduler::Event::Frame) vdp.refresh(); } -auto System::load() -> bool { - return false; +auto System::load(Model model) -> bool { + information = {}; + information.model = model; + + if(auto fp = interface->open(ID::System, "manifest.bml", File::Read, File::Required)) { + information.manifest = fp->reads(); + } else return false; + + auto document = BML::unserialize(information.manifest); + if(!cartridge.load()) return false; + + information.colorburst = Emulator::Constants::Colorburst::NTSC; + return information.loaded = true; } auto System::save() -> void { + cartridge.save(); } auto System::unload() -> void { + cartridge.unload(); } auto System::power() -> void { + cartridge.power(); + cpu.power(); + vdp.power(); + psg.power(); + reset(); } auto System::reset() -> void { + Emulator::video.reset(); + Emulator::video.setInterface(interface); + Emulator::video.setPalette(); + + Emulator::audio.reset(); + Emulator::audio.setInterface(interface); + + scheduler.reset(); + cartridge.reset(); + cpu.reset(); + vdp.reset(); + psg.reset(); + scheduler.primary(cpu); } } diff --git a/higan/ms/system/system.hpp b/higan/ms/system/system.hpp index e5ea4571..dfc6c961 100644 --- a/higan/ms/system/system.hpp +++ b/higan/ms/system/system.hpp @@ -1,15 +1,24 @@ struct System { - auto loaded() const -> bool { return false; } - auto colorburst() const -> double { return 0.0; } + auto loaded() const -> bool { return information.loaded; } + auto model() const -> Model { return information.model; } + auto colorburst() const -> double { return information.colorburst; } auto run() -> void; - auto load() -> bool; + auto load(Model model) -> bool; auto save() -> void; auto unload() -> void; auto power() -> void; auto reset() -> void; + +private: + struct Information { + bool loaded = false; + Model model = Model::MasterSystem; + string manifest; + double colorburst = 0.0; + } information; }; extern System system; diff --git a/higan/ms/vdp/vdp.cpp b/higan/ms/vdp/vdp.cpp index 196b5c46..da0f6f61 100644 --- a/higan/ms/vdp/vdp.cpp +++ b/higan/ms/vdp/vdp.cpp @@ -9,10 +9,28 @@ auto VDP::Enter() -> void { } auto VDP::main() -> void { + for(uint y : range(262)) { + for(uint x : range(342)) { + step(1); + } + if(y == 240) scheduler.exit(Scheduler::Event::Frame); + } } auto VDP::step(uint clocks) -> void { Thread::step(clocks); + synchronize(cpu); +} + +auto VDP::refresh() -> void { + Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240); +} + +auto VDP::power() -> void { +} + +auto VDP::reset() -> void { + create(VDP::Enter, system.colorburst()); } } diff --git a/higan/ms/vdp/vdp.hpp b/higan/ms/vdp/vdp.hpp index c913b45f..2a84de45 100644 --- a/higan/ms/vdp/vdp.hpp +++ b/higan/ms/vdp/vdp.hpp @@ -1,9 +1,16 @@ -//... +//TI TMS9918A (derivative) struct VDP : Thread { static auto Enter() -> void; auto main() -> void; auto step(uint clocks) -> void; + auto refresh() -> void; + + auto power() -> void; + auto reset() -> void; + +private: + uint32 buffer[256 * 240]; }; extern VDP vdp; diff --git a/higan/processor/m68k/disassembler.cpp b/higan/processor/m68k/disassembler.cpp index f5506e52..a8345afd 100644 --- a/higan/processor/m68k/disassembler.cpp +++ b/higan/processor/m68k/disassembler.cpp @@ -432,11 +432,11 @@ template auto M68K::disassembleNOT(EffectiveAddress with) -> string { } template auto M68K::disassembleOR(EffectiveAddress from, DataRegister with) -> string { - return {"eor", _suffix(), " ", _effectiveAddress(from), ",", _dataRegister(with)}; + return {"or", _suffix(), " ", _effectiveAddress(from), ",", _dataRegister(with)}; } template auto M68K::disassembleOR(DataRegister from, EffectiveAddress with) -> string { - return {"eor", _suffix(), " ", _dataRegister(from), ",", _effectiveAddress(with)}; + return {"or", _suffix(), " ", _dataRegister(from), ",", _effectiveAddress(with)}; } template auto M68K::disassembleORI(EffectiveAddress with) -> string { diff --git a/higan/processor/m68k/effective-address.cpp b/higan/processor/m68k/effective-address.cpp index bb301a42..fc58d836 100644 --- a/higan/processor/m68k/effective-address.cpp +++ b/higan/processor/m68k/effective-address.cpp @@ -32,7 +32,7 @@ template auto M68K::fetch(EffectiveAddress& ea) -> uint32 { auto index = extension & 0x8000 ? read(AddressRegister{extension >> 12}) : read(DataRegister{extension >> 12}); - if(extension & 0x800) index = (int16)index; + if(!(extension & 0x800)) index = (int16)index; return read(AddressRegister{ea.reg}) + index + (int8)extension; } @@ -55,7 +55,7 @@ template auto M68K::fetch(EffectiveAddress& ea) -> uint32 { auto index = extension & 0x8000 ? read(AddressRegister{extension >> 12}) : read(DataRegister{extension >> 12}); - if(extension & 0x800) index = (int16)index; + if(!(extension & 0x800)) index = (int16)index; return base + index + (int8)extension; } diff --git a/higan/processor/m68k/instructions.cpp b/higan/processor/m68k/instructions.cpp index 9e4575ab..64d7121c 100644 --- a/higan/processor/m68k/instructions.cpp +++ b/higan/processor/m68k/instructions.cpp @@ -124,8 +124,9 @@ template auto M68K::instructionADDQ(uint4 immediate, EffectiveAddress write(with, result); } +//Size is ignored: always uses Long template auto M68K::instructionADDQ(uint4 immediate, AddressRegister with) -> void { - auto result = read(with) + immediate; + auto result = read(with) + immediate; write(with, result); } @@ -687,7 +688,7 @@ template auto M68K::instructionMOVEP(EffectiveAddress from, DataRegis } auto M68K::instructionMOVEQ(DataRegister dr, uint8 immediate) -> void { - write(dr, immediate); + write(dr, sign(immediate)); r.c = 0; r.v = 0; @@ -1093,9 +1094,10 @@ template auto M68K::instructionSUBQ(uint4 immediate, EffectiveAddress write(with, result); } +//Size is ignored: always uses Long template auto M68K::instructionSUBQ(uint4 immediate, AddressRegister with) -> void { - auto result = read(with) - immediate; - write(with, result); + auto result = read(with) - immediate; + write(with, result); } template auto M68K::instructionSUBX(EffectiveAddress with, EffectiveAddress from) -> void { diff --git a/higan/processor/z80/instruction.cpp b/higan/processor/z80/instruction.cpp new file mode 100644 index 00000000..594bc8a7 --- /dev/null +++ b/higan/processor/z80/instruction.cpp @@ -0,0 +1,19 @@ +#define op(id, name, ...) case id: return instruction##name(__VA_ARGS__); + +auto Z80::instruction() -> void { + instructionsExecuted++; + + r.r = (r.r & 0x80) | (r.r + 1 & 0x7f); + + auto opcode = read(r.pc++); + switch(opcode) { + op(0x00, NOP) + op(0xf3, DI) + } + + print("[Z80] unimplemented instruction: ", hex(opcode, 2L), "\n"); + print("[Z80] instructions executed: ", --instructionsExecuted, "\n"); + while(true) wait(); +} + +#undef op diff --git a/higan/processor/z80/instructions.cpp b/higan/processor/z80/instructions.cpp new file mode 100644 index 00000000..7260a18a --- /dev/null +++ b/higan/processor/z80/instructions.cpp @@ -0,0 +1,5 @@ +auto Z80::instructionDI() -> void { +} + +auto Z80::instructionNOP() -> void { +} diff --git a/higan/processor/z80/z80.cpp b/higan/processor/z80/z80.cpp index a53fb3cc..32ff12be 100644 --- a/higan/processor/z80/z80.cpp +++ b/higan/processor/z80/z80.cpp @@ -1,5 +1,25 @@ #include +#include "z80.hpp" namespace Processor { +#include "instruction.cpp" +#include "instructions.cpp" + +auto Z80::power() -> void { +} + +auto Z80::reset() -> void { + r.af = 0x0000; + r.bc = 0x0000; + r.de = 0x0000; + r.hl = 0x0000; + r.ix = 0x0000; + r.iy = 0x0000; + r.sp = 0x0000; + r.pc = 0x0000; + r.i = 0x00; + r.r = 0x00; +} + } diff --git a/higan/processor/z80/z80.hpp b/higan/processor/z80/z80.hpp index 631c3480..08243506 100644 --- a/higan/processor/z80/z80.hpp +++ b/higan/processor/z80/z80.hpp @@ -5,6 +5,63 @@ namespace Processor { struct Z80 { + virtual auto wait() -> void = 0; + virtual auto read(uint16 addr) -> uint8 = 0; + virtual auto write(uint16 addr, uint8 data) -> void = 0; + + //z80.cpp + auto power() -> void; + auto reset() -> void; + + //instruction.cpp + auto instruction() -> void; + + //instructions.cpp + auto instructionDI() -> void; + auto instructionNOP() -> void; + + struct Registers { + union { + uint16_t af; + struct { uint8_t order_msb2(a, f); }; + union { + BooleanBitField c; //carry + BooleanBitField s; //subtract + BooleanBitField v; //overflow + //BooleanBitField _; //unused (copy of bit 3 of result) + BooleanBitField h; //half-carry + //BooleanBitField _; //unused (copy of bit 5 of result) + BooleanBitField z; //zero + BooleanBitField n; //negative + } p; + }; + + union { + uint16_t bc; + struct { uint8_t order_msb2(b, c); }; + }; + + union { + uint16_t de; + struct { uint8_t order_msb2(d, e); }; + }; + + union { + uint16_t hl; + struct { uint8_t order_msb2(h, l); }; + }; + + uint16_t ix; + uint16_t iy; + uint16_t sp; + uint16_t pc; + + uint8_t i; + uint8_t r; + } r; + +private: + uint64 instructionsExecuted = 0; }; }