From 5df717ff2a42ac40214af7b85537ecb8dfb683c7 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 22 Aug 2016 08:11:24 +1000 Subject: [PATCH] Update to v101r12 release. byuu says: Changelog: - new md/bus/ module for bus reads/writes - abstracts byte/word accesses wherever possible (everything but RAM; forces all but I/O to word, I/O to byte) - holds the system RAM since that's technically not part of the CPU anyway - added md/controller and md/system/peripherals - added emulation of gamepads - added stub PSG audio output (silent) to cap the framerate at 60fps with audio sync enabled - fixed VSRAM reads for plane vertical scrolling (two bugs here: add instead of sub; interlave plane A/B) - mask nametable read offsets (can't exceed 8192-byte nametables apparently) - emulated VRAM/VSRAM/CRAM reads from VDP data port - fixed sprite width/height size calculations - added partial emulation of 40-tile per scanline limitation (enough to fix Sonic's title screen) - fixed off-by-one sprite range testing - fixed sprite tile indexing - Vblank happens at Y=224 with overscan disabled - unsure what happens when you toggle it between Y=224 and Y=240 ... probably bad things - fixed reading of address register for ADDA, CMPA, SUBA - fixed sign extension for MOVEA effect address reads - updated MOVEM to increment the read addresses (but not writeback) for (aN) mode With all of that out of the way, we finally have Sonic the Hedgehog (fully?) playable. I played to stage 1-2 and through the special stage, at least. EDIT: yeah, we probably need HIRQs for Labyrinth Zone. Not much else works, of course. Most games hang waiting on the Z80, and those that don't (like Altered Beast) are still royally screwed. Tons of features still missing; including all of the Z80/PSG/YM2612. A note on the perihperals this time around: the Mega Drive EXT port is basically identical to the regular controller ports. So unlike with the Famicom and Super Famicom, I'm inheriting the exension port from the controller class. --- higan/emulator/emulator.hpp | 2 +- higan/md/GNUmakefile | 21 +++--- higan/md/bus/bus.cpp | 77 ++++++++++++++++++++++ higan/md/bus/bus.hpp | 14 ++++ higan/md/cartridge/cartridge.cpp | 11 +--- higan/md/cartridge/cartridge.hpp | 6 +- higan/md/controller/controller.cpp | 28 ++++++++ higan/md/controller/controller.hpp | 17 +++++ higan/md/controller/gamepad/gamepad.cpp | 28 ++++++++ higan/md/controller/gamepad/gamepad.hpp | 13 ++++ higan/md/cpu/cpu.cpp | 38 ++--------- higan/md/cpu/cpu.hpp | 8 +-- higan/md/interface/interface.cpp | 12 ++++ higan/md/interface/interface.hpp | 6 ++ higan/md/md.hpp | 3 + higan/md/psg/psg.cpp | 4 +- higan/md/psg/psg.hpp | 2 + higan/md/system/peripherals.cpp | 55 ++++++++++++++++ higan/md/system/system.cpp | 4 ++ higan/md/system/system.hpp | 11 ++++ higan/md/vdp/background.cpp | 12 ++-- higan/md/vdp/dma.cpp | 2 +- higan/md/vdp/io.cpp | 39 ++++++----- higan/md/vdp/render.cpp | 2 +- higan/md/vdp/sprite.cpp | 15 +++-- higan/md/vdp/vdp.cpp | 4 +- higan/md/vdp/vdp.hpp | 7 +- higan/processor/m68k/effective-address.cpp | 16 ----- higan/processor/m68k/instructions.cpp | 22 ++++--- higan/processor/m68k/m68k.hpp | 5 +- 30 files changed, 353 insertions(+), 131 deletions(-) create mode 100644 higan/md/bus/bus.cpp create mode 100644 higan/md/bus/bus.hpp create mode 100644 higan/md/controller/controller.cpp create mode 100644 higan/md/controller/controller.hpp create mode 100644 higan/md/controller/gamepad/gamepad.cpp create mode 100644 higan/md/controller/gamepad/gamepad.hpp create mode 100644 higan/md/system/peripherals.cpp diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 4a4f4d43..b4909108 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.11"; + static const string Version = "101.12"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/md/GNUmakefile b/higan/md/GNUmakefile index 01261213..a8086bb1 100644 --- a/higan/md/GNUmakefile +++ b/higan/md/GNUmakefile @@ -2,13 +2,16 @@ processors += m68k z80 objects += md-interface objects += md-cpu md-apu md-vdp md-psg md-ym2612 -objects += md-system md-cartridge +objects += md-system md-cartridge md-bus +objects += md-controller -obj/md-interface.o: md/interface/interface.cpp $(call rwildcard,md/interface) -obj/md-cpu.o: md/cpu/cpu.cpp $(call rwildcard,md/cpu) -obj/md-apu.o: md/apu/apu.cpp $(call rwildcard,md/apu) -obj/md-vdp.o: md/vdp/vdp.cpp $(call rwildcard,md/vdp) -obj/md-psg.o: md/psg/psg.cpp $(call rwildcard,md/psg) -obj/md-ym2612.o: md/ym2612/ym2612.cpp $(call rwildcard,md/ym2612) -obj/md-system.o: md/system/system.cpp $(call rwildcard,md/system) -obj/md-cartridge.o: md/cartridge/cartridge.cpp $(call rwildcard,md/cartridge) +obj/md-interface.o: md/interface/interface.cpp $(call rwildcard,md/interface) +obj/md-cpu.o: md/cpu/cpu.cpp $(call rwildcard,md/cpu) +obj/md-apu.o: md/apu/apu.cpp $(call rwildcard,md/apu) +obj/md-vdp.o: md/vdp/vdp.cpp $(call rwildcard,md/vdp) +obj/md-psg.o: md/psg/psg.cpp $(call rwildcard,md/psg) +obj/md-ym2612.o: md/ym2612/ym2612.cpp $(call rwildcard,md/ym2612) +obj/md-system.o: md/system/system.cpp $(call rwildcard,md/system) +obj/md-cartridge.o: md/cartridge/cartridge.cpp $(call rwildcard,md/cartridge) +obj/md-bus.o: md/bus/bus.cpp $(call rwildcard,md/bus) +obj/md-controller.o: md/controller/controller.cpp $(call rwildcard,md/controller) diff --git a/higan/md/bus/bus.cpp b/higan/md/bus/bus.cpp new file mode 100644 index 00000000..7f809b66 --- /dev/null +++ b/higan/md/bus/bus.cpp @@ -0,0 +1,77 @@ +#include + +namespace MegaDrive { + +Bus bus; + +auto Bus::readByte(uint24 addr) -> uint16 { + if(addr < 0x400000) return cartridge.read(addr & ~1).byte(!addr.bit(0)); + if(addr < 0xa00000) return 0x0000; + if(addr < 0xa10000) return 0x0000; + if(addr < 0xa10020) return readIO(addr); + if(addr < 0xc00000) return 0x0000; + if(addr < 0xe00000) return vdp.read(addr & ~1).byte(!addr.bit(0)); + return ram[addr & 0xffff]; +} + +auto Bus::readWord(uint24 addr) -> uint16 { + if(addr < 0x400000) return cartridge.read(addr); + if(addr < 0xa00000) return 0x0000; + if(addr < 0xa10000) return 0x0000; + if(addr < 0xa10020) return readIO(addr); + if(addr < 0xc00000) return 0x0000; + if(addr < 0xe00000) return vdp.read(addr); + uint16 data = ram[addr + 0 & 0xffff] << 8; + return data | ram[addr + 1 & 0xffff] << 0; +} + +auto Bus::writeByte(uint24 addr, uint16 data) -> void { + if(addr < 0x400000) return cartridge.write(addr & ~1, data << 8 | data << 0); + if(addr < 0xa00000) return; + if(addr < 0xa10000) return; + if(addr < 0xa10020) return writeIO(addr, data); + if(addr < 0xc00000) return; + if(addr < 0xe00000) return vdp.write(addr & ~1, data << 8 | data << 0); + ram[addr & 0xffff] = data; +} + +auto Bus::writeWord(uint24 addr, uint16 data) -> void { + if(addr < 0x400000) return cartridge.write(addr, data); + if(addr < 0xa00000) return; + if(addr < 0xa10000) return; + if(addr < 0xa10020) return writeIO(addr, data); + if(addr < 0xc00000) return; + if(addr < 0xe00000) return vdp.write(addr, data); + ram[addr + 0 & 0xffff] = data >> 8; + ram[addr + 1 & 0xffff] = data >> 0; +} + +// + +auto Bus::readIO(uint24 addr) -> uint16 { + switch(addr & ~1) { + case 0xa10002: return peripherals.controllerPort1->readData(); + case 0xa10004: return peripherals.controllerPort2->readData(); + case 0xa10006: return peripherals.extensionPort->readData(); + + case 0xa10008: return peripherals.controllerPort1->readControl(); + case 0xa1000a: return peripherals.controllerPort2->readControl(); + case 0xa1000c: return peripherals.extensionPort->readControl(); + } + + return 0x0000; +} + +auto Bus::writeIO(uint24 addr, uint16 data) -> void { + switch(addr & ~1) { + case 0xa10002: return peripherals.controllerPort1->writeData(data); + case 0xa10004: return peripherals.controllerPort2->writeData(data); + case 0xa10006: return peripherals.extensionPort->writeData(data); + + case 0xa10008: return peripherals.controllerPort1->writeControl(data); + case 0xa1000a: return peripherals.controllerPort2->writeControl(data); + case 0xa1000c: return peripherals.extensionPort->writeControl(data); + } +} + +} diff --git a/higan/md/bus/bus.hpp b/higan/md/bus/bus.hpp new file mode 100644 index 00000000..c230b65a --- /dev/null +++ b/higan/md/bus/bus.hpp @@ -0,0 +1,14 @@ +struct Bus { + auto readByte(uint24 addr) -> uint16; + auto readWord(uint24 addr) -> uint16; + auto writeByte(uint24 addr, uint16 data) -> void; + auto writeWord(uint24 addr, uint16 data) -> void; + + auto readIO(uint24 addr) -> uint16; + auto writeIO(uint24 addr, uint16 data) -> void; + +private: + uint8 ram[64 * 1024]; +}; + +extern Bus bus; diff --git a/higan/md/cartridge/cartridge.cpp b/higan/md/cartridge/cartridge.cpp index 7c28be32..804888dc 100644 --- a/higan/md/cartridge/cartridge.cpp +++ b/higan/md/cartridge/cartridge.cpp @@ -70,19 +70,12 @@ auto Cartridge::power() -> void { auto Cartridge::reset() -> void { } -auto Cartridge::readByte(uint24 addr) -> uint8 { - return rom.data[addr & rom.mask]; -} - -auto Cartridge::readWord(uint24 addr) -> uint16 { +auto Cartridge::read(uint24 addr) -> uint16 { uint16 data = rom.data[addr + 0 & rom.mask] << 8; return data | rom.data[addr + 1 & rom.mask] << 0; } -auto Cartridge::writeByte(uint24 addr, uint8 data) -> void { -} - -auto Cartridge::writeWord(uint24 addr, uint16 data) -> void { +auto Cartridge::write(uint24 addr, uint16 data) -> void { } } diff --git a/higan/md/cartridge/cartridge.hpp b/higan/md/cartridge/cartridge.hpp index a36a3ce5..08323503 100644 --- a/higan/md/cartridge/cartridge.hpp +++ b/higan/md/cartridge/cartridge.hpp @@ -10,10 +10,8 @@ struct Cartridge { auto power() -> void; auto reset() -> void; - auto readByte(uint24 addr) -> uint8; - auto readWord(uint24 addr) -> uint16; - auto writeByte(uint24 addr, uint8 data) -> void; - auto writeWord(uint24 addr, uint16 data) -> void; + auto read(uint24 addr) -> uint16; + auto write(uint24 addr, uint16 data) -> void; struct Information { uint pathID = 0; diff --git a/higan/md/controller/controller.cpp b/higan/md/controller/controller.cpp new file mode 100644 index 00000000..96195fb6 --- /dev/null +++ b/higan/md/controller/controller.cpp @@ -0,0 +1,28 @@ +#include + +namespace MegaDrive { + +#include "gamepad/gamepad.cpp" + +Controller::Controller(uint port) : port(port) { + if(!handle()) create(Controller::Enter, 100); +} + +Controller::~Controller() { +} + +auto Controller::Enter() -> void { + while(true) { + scheduler.synchronize(); + if(peripherals.controllerPort1->active()) peripherals.controllerPort1->main(); + if(peripherals.controllerPort2->active()) peripherals.controllerPort2->main(); + if(peripherals.extensionPort->active()) peripherals.extensionPort->main(); + } +} + +auto Controller::main() -> void { + step(1); + synchronize(cpu); +} + +} diff --git a/higan/md/controller/controller.hpp b/higan/md/controller/controller.hpp new file mode 100644 index 00000000..a2f23011 --- /dev/null +++ b/higan/md/controller/controller.hpp @@ -0,0 +1,17 @@ +struct Controller : Thread { + Controller(uint port); + virtual ~Controller(); + + static auto Enter() -> void; + auto main() -> void; + + virtual auto readData() -> uint8 { return 0xff; } + virtual auto writeData(uint8 data) -> void {} + + virtual auto readControl() -> uint8 { return 0x00; } + virtual auto writeControl(uint8 data) -> void {} + + const uint port; +}; + +#include "gamepad/gamepad.hpp" diff --git a/higan/md/controller/gamepad/gamepad.cpp b/higan/md/controller/gamepad/gamepad.cpp new file mode 100644 index 00000000..7a630b42 --- /dev/null +++ b/higan/md/controller/gamepad/gamepad.cpp @@ -0,0 +1,28 @@ +Gamepad::Gamepad(uint port) : Controller(port) { +} + +auto Gamepad::readData() -> uint8 { + uint6 data; + + if(select == 0) { + data.bit(0) = interface->inputPoll(port, ID::Device::Gamepad, Up); + data.bit(1) = interface->inputPoll(port, ID::Device::Gamepad, Down); + data.bit(4) = interface->inputPoll(port, ID::Device::Gamepad, A); + data.bit(5) = interface->inputPoll(port, ID::Device::Gamepad, Start); + } else { + data.bit(0) = interface->inputPoll(port, ID::Device::Gamepad, Up); + data.bit(1) = interface->inputPoll(port, ID::Device::Gamepad, Down); + data.bit(2) = interface->inputPoll(port, ID::Device::Gamepad, Left); + data.bit(3) = interface->inputPoll(port, ID::Device::Gamepad, Right); + data.bit(4) = interface->inputPoll(port, ID::Device::Gamepad, B); + data.bit(5) = interface->inputPoll(port, ID::Device::Gamepad, C); + } + + data = ~data; + return latch << 7 | select << 6 | data; +} + +auto Gamepad::writeData(uint8 data) -> void { + select = data.bit(6); + latch = data.bit(7); +} diff --git a/higan/md/controller/gamepad/gamepad.hpp b/higan/md/controller/gamepad/gamepad.hpp new file mode 100644 index 00000000..f0e9311a --- /dev/null +++ b/higan/md/controller/gamepad/gamepad.hpp @@ -0,0 +1,13 @@ +struct Gamepad : Controller { + enum : uint { + Up, Down, Left, Right, A, B, C, X, Y, Z, Start, + }; + + Gamepad(uint port); + + auto readData() -> uint8 override; + auto writeData(uint8 data) -> void override; + + boolean select; + boolean latch; +}; diff --git a/higan/md/cpu/cpu.cpp b/higan/md/cpu/cpu.cpp index d8929774..55551f5a 100644 --- a/higan/md/cpu/cpu.cpp +++ b/higan/md/cpu/cpu.cpp @@ -55,6 +55,7 @@ auto CPU::synchronize() -> void { synchronize(vdp); synchronize(psg); synchronize(ym2612); + for(auto peripheral : peripherals) synchronize(*peripheral); } auto CPU::raise(Interrupt interrupt) -> void { @@ -71,8 +72,6 @@ auto CPU::lower(Interrupt interrupt) -> void { auto CPU::power() -> void { M68K::power(); - - for(auto& byte : ram) byte = 0x00; } auto CPU::reset() -> void { @@ -82,36 +81,9 @@ auto CPU::reset() -> void { memory::fill(&state, sizeof(State)); } -auto CPU::readByte(uint24 addr) -> uint8 { - if(addr < 0x400000) return cartridge.readByte(addr); - if(addr < 0xa00000) return 0x00; - if(addr < 0xc00000) return rand(), 0; - if(addr < 0xe00000) return vdp.readByte(addr); - return ram[addr & 0xffff]; -} - -auto CPU::readWord(uint24 addr) -> uint16 { - if(addr < 0x400000) return cartridge.readWord(addr); - if(addr < 0xa00000) return 0x0000; - if(addr < 0xc00000) return rand(), 0; - if(addr < 0xe00000) return vdp.readWord(addr); - uint16 data = ram[addr + 0 & 0xffff] << 8; - return data | ram[addr + 1 & 0xffff] << 0; -} - -auto CPU::writeByte(uint24 addr, uint8 data) -> void { - if(addr < 0x400000) return cartridge.writeByte(addr, data); - if(addr < 0xc00000) return; - if(addr < 0xe00000) return vdp.writeByte(addr, data); - ram[addr & 0xffff] = data; -} - -auto CPU::writeWord(uint24 addr, uint16 data) -> void { - if(addr < 0x400000) return cartridge.writeWord(addr, data); - if(addr < 0xc00000) return; - if(addr < 0xe00000) return vdp.writeWord(addr, data); - ram[addr + 0 & 0xffff] = data >> 8; - ram[addr + 1 & 0xffff] = data >> 0; -} +auto CPU::readByte(uint24 addr) -> uint16 { return bus.readByte(addr); } +auto CPU::readWord(uint24 addr) -> uint16 { return bus.readWord(addr); } +auto CPU::writeByte(uint24 addr, uint16 data) -> void { return bus.writeByte(addr, data); } +auto CPU::writeWord(uint24 addr, uint16 data) -> void { return bus.writeWord(addr, data); } } diff --git a/higan/md/cpu/cpu.hpp b/higan/md/cpu/cpu.hpp index d7a79232..781c2f92 100644 --- a/higan/md/cpu/cpu.hpp +++ b/higan/md/cpu/cpu.hpp @@ -20,14 +20,14 @@ struct CPU : Processor::M68K, Thread { auto power() -> void; auto reset() -> void; - auto readByte(uint24 addr) -> uint8 override; + auto readByte(uint24 addr) -> uint16 override; auto readWord(uint24 addr) -> uint16 override; - auto writeByte(uint24 addr, uint8 data) -> void override; + auto writeByte(uint24 addr, uint16 data) -> void override; auto writeWord(uint24 addr, uint16 data) -> void override; -private: - uint8 ram[64 * 1024]; + vector peripherals; +private: struct State { uint32 interruptLine; uint32 interruptPending; diff --git a/higan/md/interface/interface.cpp b/higan/md/interface/interface.cpp index 1b39979f..757d1d23 100644 --- a/higan/md/interface/interface.cpp +++ b/higan/md/interface/interface.cpp @@ -20,6 +20,13 @@ Interface::Interface() { Port controllerPort1{ID::Port::Controller1, "Controller Port 1"}; Port controllerPort2{ID::Port::Controller2, "Controller Port 2"}; + Port extensionPort{ID::Port::Extension, "Extension Port"}; + + { Device device{ID::Device::None, "None"}; + controllerPort1.devices.append(device); + controllerPort2.devices.append(device); + extensionPort.devices.append(device); + } { Device device{ID::Device::Gamepad, "Gamepad"}; device.inputs.append({0, "Up" }); @@ -39,6 +46,7 @@ Interface::Interface() { ports.append(move(controllerPort1)); ports.append(move(controllerPort2)); + ports.append(move(extensionPort)); } auto Interface::manifest() -> string { @@ -100,6 +108,10 @@ auto Interface::unload() -> void { system.unload(); } +auto Interface::connect(uint port, uint device) -> void { + MegaDrive::peripherals.connect(port, device); +} + auto Interface::power() -> void { system.power(); } diff --git a/higan/md/interface/interface.hpp b/higan/md/interface/interface.hpp index 37fa5862..2eda14c8 100644 --- a/higan/md/interface/interface.hpp +++ b/higan/md/interface/interface.hpp @@ -9,9 +9,11 @@ struct ID { struct Port { enum : uint { Controller1, Controller2, + Extension, };}; struct Device { enum : uint { + None, Gamepad, };}; }; @@ -37,6 +39,7 @@ struct Interface : Emulator::Interface { auto save() -> void override; auto unload() -> void override; + auto connect(uint port, uint device) -> void override; auto power() -> void override; auto reset() -> void override; auto run() -> void override; @@ -50,6 +53,9 @@ struct Interface : Emulator::Interface { }; struct Settings { + uint controllerPort1 = 0; + uint controllerPort2 = 0; + uint extensionPort = 0; }; extern Interface* interface; diff --git a/higan/md/md.hpp b/higan/md/md.hpp index 43590e35..65966edf 100644 --- a/higan/md/md.hpp +++ b/higan/md/md.hpp @@ -35,6 +35,8 @@ namespace MegaDrive { uint wait = 0; }; + #include + #include #include #include @@ -43,6 +45,7 @@ namespace MegaDrive { #include #include + #include } #include diff --git a/higan/md/psg/psg.cpp b/higan/md/psg/psg.cpp index 6a408180..06cf78f0 100644 --- a/higan/md/psg/psg.cpp +++ b/higan/md/psg/psg.cpp @@ -9,6 +9,7 @@ auto PSG::Enter() -> void { } auto PSG::main() -> void { + stream->sample(0.0, 0.0); step(1); } @@ -21,7 +22,8 @@ auto PSG::power() -> void { } auto PSG::reset() -> void { - create(PSG::Enter, system.colorburst()); + create(PSG::Enter, 52'000); //system.colorburst()); + stream = Emulator::audio.createStream(2, 52'000.0); } } diff --git a/higan/md/psg/psg.hpp b/higan/md/psg/psg.hpp index f9d20933..89ba7388 100644 --- a/higan/md/psg/psg.hpp +++ b/higan/md/psg/psg.hpp @@ -1,6 +1,8 @@ //TI SN76489 struct PSG : Thread { + shared_pointer stream; + static auto Enter() -> void; auto main() -> void; auto step(uint clocks) -> void; diff --git a/higan/md/system/peripherals.cpp b/higan/md/system/peripherals.cpp new file mode 100644 index 00000000..1afb1479 --- /dev/null +++ b/higan/md/system/peripherals.cpp @@ -0,0 +1,55 @@ +Peripherals peripherals; + +auto Peripherals::unload() -> void { + delete controllerPort1; + delete controllerPort2; + delete extensionPort; + controllerPort1 = nullptr; + controllerPort2 = nullptr; + extensionPort = nullptr; +} + +auto Peripherals::reset() -> void { + connect(ID::Port::Controller1, settings.controllerPort1); + connect(ID::Port::Controller2, settings.controllerPort2); + connect(ID::Port::Extension, settings.extensionPort); +} + +auto Peripherals::connect(uint port, uint device) -> void { + if(port == ID::Port::Controller1) { + settings.controllerPort1 = device; + if(!system.loaded()) return; + + delete controllerPort1; + switch(device) { default: + case ID::Device::None: controllerPort1 = new Controller(0); break; + case ID::Device::Gamepad: controllerPort1 = new Gamepad(0); break; + } + } + + if(port == ID::Port::Controller2) { + settings.controllerPort2 = device; + if(!system.loaded()) return; + + delete controllerPort2; + switch(device) { default: + case ID::Device::None: controllerPort2 = new Controller(1); break; + case ID::Device::Gamepad: controllerPort2 = new Gamepad(1); break; + } + } + + if(port == ID::Port::Extension) { + settings.extensionPort = device; + if(!system.loaded()) return; + + delete extensionPort; + switch(device) { default: + case ID::Device::None: extensionPort = new Controller(2); break; + } + } + + cpu.peripherals.reset(); + cpu.peripherals.append(controllerPort1); + cpu.peripherals.append(controllerPort2); + cpu.peripherals.append(extensionPort); +} diff --git a/higan/md/system/system.cpp b/higan/md/system/system.cpp index ad594e8e..5ad4ccb5 100644 --- a/higan/md/system/system.cpp +++ b/higan/md/system/system.cpp @@ -2,6 +2,7 @@ namespace MegaDrive { +#include "peripherals.cpp" System system; Scheduler scheduler; @@ -25,6 +26,7 @@ auto System::save() -> void { } auto System::unload() -> void { + peripherals.unload(); cartridge.unload(); } @@ -54,6 +56,8 @@ auto System::reset() -> void { psg.reset(); ym2612.reset(); scheduler.primary(cpu); + + peripherals.reset(); } } diff --git a/higan/md/system/system.hpp b/higan/md/system/system.hpp index 2a406015..ae3d905a 100644 --- a/higan/md/system/system.hpp +++ b/higan/md/system/system.hpp @@ -17,4 +17,15 @@ struct System { } information; }; +struct Peripherals { + auto unload() -> void; + auto reset() -> void; + auto connect(uint port, uint device) -> void; + + Controller* controllerPort1 = nullptr; + Controller* controllerPort2 = nullptr; + Controller* extensionPort = nullptr; +}; + extern System system; +extern Peripherals peripherals; diff --git a/higan/md/vdp/background.cpp b/higan/md/vdp/background.cpp index 5c8a04f0..42d71f95 100644 --- a/higan/md/vdp/background.cpp +++ b/higan/md/vdp/background.cpp @@ -12,16 +12,14 @@ auto VDP::Background::run(uint x, uint y) -> void { output.priority = 0; output.color = 0; - static const uint tiles[] = {32, 64, 0, 128}; - y -= vdp.vsram[(x >> 4) & (io.verticalScrollMode ? ~0u : 0u)]; - y &= (tiles[io.nametableHeight] << 3) - 1; + static const uint tiles[] = {32, 64, 96, 128}; + y += vdp.vsram[((x >> 4) & (io.verticalScrollMode ? ~0u : 0u)) * 2 + (this == &vdp.planeB)]; x -= state.horizontalScroll; - x &= (tiles[io.nametableWidth] << 3) - 1; - uint tileX = x >> 3; - uint tileY = y >> 3; + uint tileX = x >> 3 & (tiles[io.nametableWidth ] - 1); + uint tileY = y >> 3 & (tiles[io.nametableHeight] - 1); uint15 nametableAddress = io.nametableAddress; - nametableAddress += tileY * tiles[io.nametableWidth] + tileX; + nametableAddress += (tileY * tiles[io.nametableWidth] + tileX) & 0x0fff; uint16 tileAttributes = vdp.vram[nametableAddress]; uint15 tileAddress = tileAttributes.bits(0,10) << 4; diff --git a/higan/md/vdp/dma.cpp b/higan/md/vdp/dma.cpp index dd7d7e61..0c385250 100644 --- a/higan/md/vdp/dma.cpp +++ b/higan/md/vdp/dma.cpp @@ -1,6 +1,6 @@ auto VDP::dmaRun() -> void { if(!io.dmaEnable) return; - if(io.command.bits(4,5) != 2) return; + if(!io.command.bit(5)) return; if(io.dmaMode <= 1) return dmaLoad(); if(io.dmaMode == 2) return dmaFill(); diff --git a/higan/md/vdp/io.cpp b/higan/md/vdp/io.cpp index f653ee96..fd185192 100644 --- a/higan/md/vdp/io.cpp +++ b/higan/md/vdp/io.cpp @@ -1,16 +1,5 @@ -auto VDP::readByte(uint24 addr) -> uint8 { - auto data = readWord(addr & ~1); - return data.byte(!addr.bit(0)); -} - -auto VDP::writeByte(uint24 addr, uint8 data) -> void { - return writeWord(addr & ~1, data << 8 | data << 0); -} - -// - -auto VDP::readWord(uint24 addr) -> uint16 { - switch(addr & 0xc0001f) { +auto VDP::read(uint24 addr) -> uint16 { + switch(addr & 0xc0001e) { //data port case 0xc00000: case 0xc00002: { @@ -32,10 +21,8 @@ auto VDP::readWord(uint24 addr) -> uint16 { return 0x0000; } -auto VDP::writeWord(uint24 addr, uint16 data) -> void { -//print("[VDP] ", hex(addr, 6L), "=", hex(data, 4L), "\n"); - - switch(addr & 0xc0001f) { +auto VDP::write(uint24 addr, uint16 data) -> void { + switch(addr & 0xc0001e) { //data port case 0xc00000: case 0xc00002: { @@ -57,18 +44,30 @@ auto VDP::readDataPort() -> uint16 { //VRAM read if(io.command.bits(0,3) == 0) { - return 0x0000; + auto address = io.address.bits(1,15); + auto data = vram[address]; + io.address += io.dataIncrement; + return data; } //VSRAM read if(io.command.bits(0,3) == 4) { - return 0x0000; + auto address = io.address.bits(1,6); + if(address >= 40) return 0x0000; + auto data = vsram[address]; + io.address += io.dataIncrement; + return data; } //CRAM read if(io.command.bits(0,3) == 8) { - return 0x0000; + auto address = io.address.bits(1,6); + auto data = cram[address]; + io.address += io.dataIncrement; + return data.bits(0,2) << 1 | data.bits(3,5) << 2 | data.bits(6,8) << 3; } + + return 0x0000; } auto VDP::writeDataPort(uint16 data) -> void { diff --git a/higan/md/vdp/render.cpp b/higan/md/vdp/render.cpp index 56a2c2e7..761ba9ab 100644 --- a/higan/md/vdp/render.cpp +++ b/higan/md/vdp/render.cpp @@ -20,7 +20,7 @@ auto VDP::run() -> void { bool windowed = false; //todo: broken windowed &= state.x >= io.windowHorizontalLo && state.x <= io.windowHorizontalHi; - windowed &= state.y >= io.windowVerticalLo && state.y <= io.windowVerticalHi; + windowed &= state.y >= io.windowVerticalLo && state.y <= io.windowVerticalHi; auto& planeA = windowed ? this->window : this->planeA; planeA.run(state.x, state.y); diff --git a/higan/md/vdp/sprite.cpp b/higan/md/vdp/sprite.cpp index 71ac3b8a..18e364d9 100644 --- a/higan/md/vdp/sprite.cpp +++ b/higan/md/vdp/sprite.cpp @@ -11,8 +11,8 @@ auto VDP::Sprite::write(uint9 address, uint16 data) -> void { case 1: { object.link = data.bits(0,6); - object.height = data.bits(8,9) << 3; - object.width = data.bits(10,11) << 3; + object.height = 1 + data.bits(8,9) << 3; + object.width = 1 + data.bits(10,11) << 3; break; } @@ -37,16 +37,18 @@ auto VDP::Sprite::scanline(uint y) -> void { objects.reset(); uint7 link = 0; + uint tiles = 0; do { auto& object = oam[link]; link = object.link; if(128 + y < object.y) continue; - if(128 + y >= object.y + object.height - 1) continue; + if(128 + y >= object.y + object.height) continue; if(object.x == 0) break; objects.append(object); - } while(link && link < 80 && objects.size() < 20); + tiles += object.width >> 3; + } while(link && link < 80 && objects.size() < 20 && tiles < 40); } auto VDP::Sprite::run(uint x, uint y) -> void { @@ -55,7 +57,7 @@ auto VDP::Sprite::run(uint x, uint y) -> void { for(auto& o : objects) { if(128 + x < o.x) continue; - if(128 + x >= o.x + o.width - 1) continue; + if(128 + x >= o.x + o.width) continue; uint objectX = 128 + x - o.x; uint objectY = 128 + y - o.y; @@ -64,7 +66,7 @@ auto VDP::Sprite::run(uint x, uint y) -> void { uint tileX = objectX >> 3; uint tileY = objectY >> 3; - uint tileNumber = tileX * (o.width >> 3) + tileY; + uint tileNumber = tileX * (o.height >> 3) + tileY; uint15 tileAddress = o.address + (tileNumber << 4); uint pixelX = objectX & 7; uint pixelY = objectY & 7; @@ -75,6 +77,7 @@ auto VDP::Sprite::run(uint x, uint y) -> void { if(color) { output.color = o.palette << 4 | color; output.priority = o.priority; + break; } } } diff --git a/higan/md/vdp/vdp.cpp b/higan/md/vdp/vdp.cpp index 6fbe5586..5ebf7340 100644 --- a/higan/md/vdp/vdp.cpp +++ b/higan/md/vdp/vdp.cpp @@ -15,7 +15,7 @@ auto VDP::Enter() -> void { auto VDP::main() -> void { scanline(); - if(state.y < 240) { + if(state.y < screenHeight()) { if(state.y == 0) { cpu.lower(CPU::Interrupt::VerticalBlank); } @@ -29,7 +29,7 @@ auto VDP::main() -> void { } step(430); } else { - if(state.y == 240) { + if(state.y == screenHeight()) { if(io.verticalBlankInterruptEnable) { cpu.raise(CPU::Interrupt::VerticalBlank); } diff --git a/higan/md/vdp/vdp.hpp b/higan/md/vdp/vdp.hpp index c29f2112..bb9d6cfe 100644 --- a/higan/md/vdp/vdp.hpp +++ b/higan/md/vdp/vdp.hpp @@ -10,11 +10,8 @@ struct VDP : Thread { auto reset() -> void; //io.cpp - auto readByte(uint24 addr) -> uint8; - auto writeByte(uint24 addr, uint8 data) -> void; - - auto readWord(uint24 addr) -> uint16; - auto writeWord(uint24 addr, uint16 data) -> void; + auto read(uint24 addr) -> uint16; + auto write(uint24 addr, uint16 data) -> void; auto readDataPort() -> uint16; auto writeDataPort(uint16 data) -> void; diff --git a/higan/processor/m68k/effective-address.cpp b/higan/processor/m68k/effective-address.cpp index fc58d836..28ad251e 100644 --- a/higan/processor/m68k/effective-address.cpp +++ b/higan/processor/m68k/effective-address.cpp @@ -189,19 +189,3 @@ template auto M68K::write(EffectiveAddress& ea, uint32 dat } } - -template auto M68K::flush(EffectiveAddress& ea, uint32 data) -> void { - switch(ea.mode) { - - case AddressRegisterIndirectWithPostIncrement: { - write(AddressRegister{ea.reg}, data); - return; - } - - case AddressRegisterIndirectWithPreDecrement: { - write(AddressRegister{ea.reg}, data); - return; - } - - } -} diff --git a/higan/processor/m68k/instructions.cpp b/higan/processor/m68k/instructions.cpp index 81961050..a4e95ee5 100644 --- a/higan/processor/m68k/instructions.cpp +++ b/higan/processor/m68k/instructions.cpp @@ -106,7 +106,7 @@ template auto M68K::instructionADD(DataRegister from, EffectiveAddres template auto M68K::instructionADDA(AddressRegister ar, EffectiveAddress ea) -> void { auto source = sign(read(ea)); - auto target = read(ar); + auto target = read(ar); write(ar, source + target); } @@ -364,8 +364,8 @@ template auto M68K::instructionCMP(DataRegister dr, EffectiveAddress template auto M68K::instructionCMPA(AddressRegister ar, EffectiveAddress ea) -> void { auto source = sign(read(ea)); - auto target = read(ar); - CMP(source, target); + auto target = read(ar); + CMP(source, target); } template auto M68K::instructionCMPI(EffectiveAddress ea) -> void { @@ -623,7 +623,7 @@ template auto M68K::instructionMOVE(EffectiveAddress to, EffectiveAdd } template auto M68K::instructionMOVEA(AddressRegister ar, EffectiveAddress ea) -> void { - auto data = read(ea); + auto data = sign(read(ea)); write(ar, data); } @@ -639,10 +639,12 @@ template auto M68K::instructionMOVEM_TO_MEM(EffectiveAddress to) -> v if(to.mode == AddressRegisterIndirectWithPreDecrement) addr -= bytes(); auto data = index < 8 ? read(DataRegister{index}) : read(AddressRegister{index}); write(addr, data); - if(to.mode == AddressRegisterIndirectWithPostIncrement) addr += bytes(); + if(to.mode != AddressRegisterIndirectWithPreDecrement) addr += bytes(); } - flush(to, addr); + AddressRegister with{to.reg}; + if(to.mode == AddressRegisterIndirectWithPreDecrement ) write(with, addr); + if(to.mode == AddressRegisterIndirectWithPostIncrement) write(with, addr); } template auto M68K::instructionMOVEM_TO_REG(EffectiveAddress from) -> void { @@ -657,10 +659,12 @@ template auto M68K::instructionMOVEM_TO_REG(EffectiveAddress from) -> auto data = read(addr); data = sign(data); index < 8 ? write(DataRegister{index}, data) : write(AddressRegister{index}, data); - if(from.mode == AddressRegisterIndirectWithPostIncrement) addr += bytes(); + if(from.mode != AddressRegisterIndirectWithPreDecrement) addr += bytes(); } - flush(from, addr); + AddressRegister with{from.reg}; + if(from.mode == AddressRegisterIndirectWithPreDecrement ) write(with, addr); + if(from.mode == AddressRegisterIndirectWithPostIncrement) write(with, addr); } template auto M68K::instructionMOVEP(DataRegister from, EffectiveAddress to) -> void { @@ -1074,7 +1078,7 @@ template auto M68K::instructionSUB(DataRegister source_, EffectiveAdd template auto M68K::instructionSUBA(AddressRegister to, EffectiveAddress from) -> void { auto source = sign(read(from)); - auto target = read(to); + auto target = read(to); write(to, target - source); } diff --git a/higan/processor/m68k/m68k.hpp b/higan/processor/m68k/m68k.hpp index c06f222c..85ab051a 100644 --- a/higan/processor/m68k/m68k.hpp +++ b/higan/processor/m68k/m68k.hpp @@ -50,9 +50,9 @@ struct M68K { M68K(); virtual auto step(uint clocks) -> void = 0; - virtual auto readByte(uint24 addr) -> uint8 = 0; + virtual auto readByte(uint24 addr) -> uint16 = 0; virtual auto readWord(uint24 addr) -> uint16 = 0; - virtual auto writeByte(uint24 addr, uint8 data) -> void = 0; + virtual auto writeByte(uint24 addr, uint16 data) -> void = 0; virtual auto writeWord(uint24 addr, uint16 data) -> void = 0; auto power() -> void; @@ -103,7 +103,6 @@ struct M68K { template auto fetch(EffectiveAddress& ea) -> uint32; template auto read(EffectiveAddress& ea) -> uint32; template auto write(EffectiveAddress& ea, uint32 data) -> void; - template auto flush(EffectiveAddress& ea, uint32 data) -> void; //instruction.cpp auto instruction() -> void;