diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 72bd606d..d16eb5ab 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 = "102.03"; + static const string Version = "102.04"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/gb/apu/apu.cpp b/higan/gb/apu/apu.cpp index 2f8ef868..2780df53 100644 --- a/higan/gb/apu/apu.cpp +++ b/higan/gb/apu/apu.cpp @@ -25,11 +25,11 @@ auto APU::main() -> void { hipass(sequencer.left, sequencer.leftBias); hipass(sequencer.right, sequencer.rightBias); - if(!system.sgb()) { + if(!Model::SuperGameBoy()) { stream->sample(sequencer.left / 32768.0, sequencer.right / 32768.0); } else { double samples[] = {sequencer.left / 32768.0, sequencer.right / 32768.0}; - //interface->audioSample(samples, 2); + superGameBoy->audioSample(samples, 2); } if(cycle == 0) { //512hz @@ -63,7 +63,7 @@ auto APU::hipass(int16& sample, int64& bias) -> void { auto APU::power() -> void { create(Enter, 2 * 1024 * 1024); - if(!system.sgb()) stream = Emulator::audio.createStream(2, 2 * 1024 * 1024); + if(!Model::SuperGameBoy()) stream = Emulator::audio.createStream(2, 2 * 1024 * 1024); for(uint n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this; square1.power(); @@ -91,7 +91,7 @@ auto APU::readIO(uint16 addr) -> uint8 { auto APU::writeIO(uint16 addr, uint8 data) -> void { if(!sequencer.enable) { bool valid = addr == 0xff26; //NR52 - if(!system.cgb()) { + if(!Model::GameBoyColor()) { //NRx1 length is writable only on DMG,SGB; not on CGB if(addr == 0xff11) valid = true, data &= 0x3f; //NR11; duty is not writable (remains 0) if(addr == 0xff16) valid = true, data &= 0x3f; //NR21; duty is not writable (remains 0) diff --git a/higan/gb/apu/sequencer.cpp b/higan/gb/apu/sequencer.cpp index 5e633189..4cd3cd4d 100644 --- a/higan/gb/apu/sequencer.cpp +++ b/higan/gb/apu/sequencer.cpp @@ -91,10 +91,10 @@ auto APU::Sequencer::write(uint16 addr, uint8 data) -> void { if(!enable) { //power(bool) resets length counters when true (eg for CGB only) - apu.square1.power(system.cgb()); - apu.square2.power(system.cgb()); - apu.wave.power(system.cgb()); - apu.noise.power(system.cgb()); + apu.square1.power(Model::GameBoyColor()); + apu.square2.power(Model::GameBoyColor()); + apu.wave.power(Model::GameBoyColor()); + apu.noise.power(Model::GameBoyColor()); power(); } else { apu.phase = 0; diff --git a/higan/gb/apu/wave.cpp b/higan/gb/apu/wave.cpp index af41302a..bd610cc8 100644 --- a/higan/gb/apu/wave.cpp +++ b/higan/gb/apu/wave.cpp @@ -47,7 +47,7 @@ auto APU::Wave::read(uint16 addr) -> uint8 { if(addr >= 0xff30 && addr <= 0xff3f) { if(enable) { - if(!system.cgb() && !patternHold) return 0xff; + if(!Model::GameBoyColor() && !patternHold) return 0xff; return pattern[patternOffset >> 1]; } else { return pattern[addr & 15]; @@ -84,7 +84,7 @@ auto APU::Wave::write(uint16 addr, uint8 data) -> void { frequency.bits(10,8) = data.bits(2,0); if(data.bit(7)) { - if(!system.cgb() && patternHold) { + if(!Model::GameBoyColor() && patternHold) { //DMG,SGB trigger while channel is being read corrupts wave RAM if((patternOffset >> 1) <= 3) { //if current pattern is with 0-3; only byte 0 is corrupted @@ -113,7 +113,7 @@ auto APU::Wave::write(uint16 addr, uint8 data) -> void { if(addr >= 0xff30 && addr <= 0xff3f) { if(enable) { - if(!system.cgb() && !patternHold) return; + if(!Model::GameBoyColor() && !patternHold) return; pattern[patternOffset >> 1] = data; } else { pattern[addr & 15] = data; diff --git a/higan/gb/cartridge/cartridge.cpp b/higan/gb/cartridge/cartridge.cpp index 451d3d31..53f5e9af 100644 --- a/higan/gb/cartridge/cartridge.cpp +++ b/higan/gb/cartridge/cartridge.cpp @@ -14,25 +14,25 @@ namespace GameBoy { #include "serialization.cpp" Cartridge cartridge; -auto Cartridge::load(System::Revision revision) -> bool { - information = Information(); +auto Cartridge::load() -> bool { + information = {}; - switch(revision) { - case System::Revision::GameBoy: + if(Model::GameBoy()) { if(auto pathID = platform->load(ID::GameBoy, "Game Boy", "gb")) { information.pathID = pathID(); } else return false; - break; - case System::Revision::SuperGameBoy: - if(auto pathID = platform->load(ID::SuperGameBoy, "Game Boy", "gb")) { - information.pathID = pathID(); - } else return false; - break; - case System::Revision::GameBoyColor: + } + + if(Model::GameBoyColor()) { if(auto pathID = platform->load(ID::GameBoyColor, "Game Boy Color", "gbc")) { information.pathID = pathID(); } else return false; - break; + } + + if(Model::SuperGameBoy()) { + if(auto pathID = platform->load(ID::SuperGameBoy, "Game Boy", "gb")) { + information.pathID = pathID(); + } else return false; } if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) { @@ -136,13 +136,11 @@ auto Cartridge::readIO(uint16 addr) -> uint8 { if(bootromEnable) { const uint8* data = nullptr; - switch(system.revision()) { default: - case System::Revision::GameBoy: data = system.bootROM.dmg; break; - case System::Revision::SuperGameBoy: data = system.bootROM.sgb; break; - case System::Revision::GameBoyColor: data = system.bootROM.cgb; break; - } + if(Model::GameBoy()) data = system.bootROM.dmg; + if(Model::GameBoyColor()) data = system.bootROM.cgb; + if(Model::SuperGameBoy()) data = system.bootROM.sgb; if(addr >= 0x0000 && addr <= 0x00ff) return data[addr]; - if(addr >= 0x0200 && addr <= 0x08ff && system.cgb()) return data[addr - 256]; + if(addr >= 0x0200 && addr <= 0x08ff && Model::GameBoyColor()) return data[addr - 256]; } return mapper->readIO(addr); diff --git a/higan/gb/cartridge/cartridge.hpp b/higan/gb/cartridge/cartridge.hpp index befcf9be..8c9d1ac4 100644 --- a/higan/gb/cartridge/cartridge.hpp +++ b/higan/gb/cartridge/cartridge.hpp @@ -4,7 +4,7 @@ struct Cartridge : MMIO { auto manifest() const -> string { return information.manifest; } auto title() const -> string { return information.title; } - auto load(System::Revision revision) -> bool; + auto load() -> bool; auto save() -> void; auto unload() -> void; diff --git a/higan/gb/cpu/cpu.cpp b/higan/gb/cpu/cpu.cpp index 817ff4c6..5e2b9020 100644 --- a/higan/gb/cpu/cpu.cpp +++ b/higan/gb/cpu/cpu.cpp @@ -102,7 +102,7 @@ auto CPU::power() -> void { bus.mmio[0xff0f] = this; //IF bus.mmio[0xffff] = this; //IE - if(system.cgb()) { + if(Model::GameBoyColor()) { bus.mmio[0xff4d] = this; //KEY1 bus.mmio[0xff51] = this; //HDMA1 bus.mmio[0xff52] = this; //HDMA2 diff --git a/higan/gb/cpu/io.cpp b/higan/gb/cpu/io.cpp index ba35528a..72770e87 100644 --- a/higan/gb/cpu/io.cpp +++ b/higan/gb/cpu/io.cpp @@ -6,19 +6,22 @@ auto CPU::wramAddress(uint16 addr) const -> uint { } auto CPU::joypPoll() -> void { - uint button = 0, dpad = 0; + function int16> inputPoll = {&Emulator::Platform::inputPoll, platform}; + if(Model::SuperGameBoy()) inputPoll = {&SuperGameBoyInterface::inputPoll, superGameBoy}; - button |= platform->inputPoll(0, 0, (uint)Input::Start) << 3; - button |= platform->inputPoll(0, 0, (uint)Input::Select) << 2; - button |= platform->inputPoll(0, 0, (uint)Input::B) << 1; - button |= platform->inputPoll(0, 0, (uint)Input::A) << 0; + uint button = 0; + button |= inputPoll(0, 0, (uint)Input::Start) << 3; + button |= inputPoll(0, 0, (uint)Input::Select) << 2; + button |= inputPoll(0, 0, (uint)Input::B) << 1; + button |= inputPoll(0, 0, (uint)Input::A) << 0; - dpad |= platform->inputPoll(0, 0, (uint)Input::Down) << 3; - dpad |= platform->inputPoll(0, 0, (uint)Input::Up) << 2; - dpad |= platform->inputPoll(0, 0, (uint)Input::Left) << 1; - dpad |= platform->inputPoll(0, 0, (uint)Input::Right) << 0; + uint dpad = 0; + dpad |= inputPoll(0, 0, (uint)Input::Down) << 3; + dpad |= inputPoll(0, 0, (uint)Input::Up) << 2; + dpad |= inputPoll(0, 0, (uint)Input::Left) << 1; + dpad |= inputPoll(0, 0, (uint)Input::Right) << 0; - if(system.revision() != System::Revision::SuperGameBoy) { + if(!Model::SuperGameBoy()) { //D-pad pivot makes it impossible to press opposing directions at the same time //however, Super Game Boy BIOS is able to set these bits together if(dpad & 4) dpad &= ~8; //disallow up+down @@ -145,7 +148,7 @@ auto CPU::writeIO(uint16 addr, uint8 data) -> void { if(addr == 0xff00) { //JOYP status.p15 = data & 0x20; status.p14 = data & 0x10; - //interface->joypWrite(status.p15, status.p14); + if(Model::SuperGameBoy()) superGameBoy->joypWrite(status.p15, status.p14); return; } diff --git a/higan/gb/cpu/timing.cpp b/higan/gb/cpu/timing.cpp index 1b54bb3c..afe8e29d 100644 --- a/higan/gb/cpu/timing.cpp +++ b/higan/gb/cpu/timing.cpp @@ -21,7 +21,7 @@ auto CPU::step(uint clocks) -> void { synchronize(apu); } - if(system.sgb()) { + if(Model::SuperGameBoy()) { system._clocksExecuted += clocks; scheduler.exit(Scheduler::Event::Step); } diff --git a/higan/gb/gb.hpp b/higan/gb/gb.hpp index 09d4255c..a817210e 100644 --- a/higan/gb/gb.hpp +++ b/higan/gb/gb.hpp @@ -29,6 +29,12 @@ namespace GameBoy { } }; + struct Model { + inline static auto GameBoy() -> bool; + inline static auto GameBoyColor() -> bool; + inline static auto SuperGameBoy() -> bool; + }; + #include #include #include diff --git a/higan/gb/interface/game-boy-color.cpp b/higan/gb/interface/game-boy-color.cpp index fd5c0d91..681de929 100644 --- a/higan/gb/interface/game-boy-color.cpp +++ b/higan/gb/interface/game-boy-color.cpp @@ -3,49 +3,7 @@ GameBoyColorInterface::GameBoyColorInterface() { information.name = "Game Boy Color"; information.overscan = false; - information.capability.states = true; - information.capability.cheats = true; - media.append({ID::GameBoyColor, "Game Boy Color", "gb"}); - - Port hardwarePort{ID::Port::Hardware, "Hardware"}; - - { Device device{ID::Device::Controls, "Controls"}; - device.inputs.append({0, "Up" }); - device.inputs.append({0, "Down" }); - device.inputs.append({0, "Left" }); - device.inputs.append({0, "Right" }); - device.inputs.append({0, "B" }); - device.inputs.append({0, "A" }); - device.inputs.append({0, "Select"}); - device.inputs.append({0, "Start" }); - hardwarePort.devices.append(device); - } - - ports.append(move(hardwarePort)); -} - -auto GameBoyColorInterface::manifest() -> string { - return cartridge.manifest(); -} - -auto GameBoyColorInterface::title() -> string { - return cartridge.title(); -} - -auto GameBoyColorInterface::videoSize() -> VideoSize { - return {160, 144}; -} - -auto GameBoyColorInterface::videoSize(uint width, uint height, bool arc) -> VideoSize { - uint w = 160; - uint h = 144; - uint m = min(width / w, height / h); - return {w * m, h * m}; -} - -auto GameBoyColorInterface::videoFrequency() -> double { - return 4194304.0 / (154.0 * 456.0); } auto GameBoyColorInterface::videoColors() -> uint32 { @@ -73,77 +31,7 @@ auto GameBoyColorInterface::videoColor(uint32 color) -> uint64 { return R << 32 | G << 16 | B << 0; } -auto GameBoyColorInterface::audioFrequency() -> double { - return 4194304.0 / 2.0; -} - -auto GameBoyColorInterface::loaded() -> bool { - return system.loaded(); -} - -auto GameBoyColorInterface::sha256() -> string { - return cartridge.sha256(); -} - auto GameBoyColorInterface::load(uint id) -> bool { - if(id == ID::GameBoyColor) return system.load(this, System::Revision::GameBoyColor); - return false; -} - -auto GameBoyColorInterface::save() -> void { - system.save(); -} - -auto GameBoyColorInterface::unload() -> void { - save(); - system.unload(); -} - -auto GameBoyColorInterface::power() -> void { - system.power(); -} - -auto GameBoyColorInterface::run() -> void { - system.run(); -} - -auto GameBoyColorInterface::serialize() -> serializer { - system.runToSave(); - return system.serialize(); -} - -auto GameBoyColorInterface::unserialize(serializer& s) -> bool { - return system.unserialize(s); -} - -auto GameBoyColorInterface::cheatSet(const string_vector& list) -> void { - cheat.assign(list); -} - -auto GameBoyColorInterface::cap(const string& name) -> bool { - if(name == "Blur Emulation") return true; - if(name == "Color Emulation") return true; - return false; -} - -auto GameBoyColorInterface::get(const string& name) -> any { - if(name == "Blur Emulation") return settings.blurEmulation; - if(name == "Color Emulation") return settings.colorEmulation; - return {}; -} - -auto GameBoyColorInterface::set(const string& name, const any& value) -> bool { - if(name == "Blur Emulation" && value.is()) { - settings.blurEmulation = value.get(); - system.configureVideoEffects(); - return true; - } - - if(name == "Color Emulation" && value.is()) { - settings.colorEmulation = value.get(); - system.configureVideoPalette(); - return true; - } - + if(id == ID::GameBoyColor) return system.load(this, System::Model::GameBoyColor); return false; } diff --git a/higan/gb/interface/game-boy.cpp b/higan/gb/interface/game-boy.cpp index 0bb3061b..2dd0b9d7 100644 --- a/higan/gb/interface/game-boy.cpp +++ b/higan/gb/interface/game-boy.cpp @@ -3,49 +3,7 @@ GameBoyInterface::GameBoyInterface() { information.name = "Game Boy"; information.overscan = false; - information.capability.states = true; - information.capability.cheats = true; - media.append({ID::GameBoy, "Game Boy", "gb"}); - - Port hardwarePort{ID::Port::Hardware, "Hardware"}; - - { Device device{ID::Device::Controls, "Controls"}; - device.inputs.append({0, "Up" }); - device.inputs.append({0, "Down" }); - device.inputs.append({0, "Left" }); - device.inputs.append({0, "Right" }); - device.inputs.append({0, "B" }); - device.inputs.append({0, "A" }); - device.inputs.append({0, "Select"}); - device.inputs.append({0, "Start" }); - hardwarePort.devices.append(device); - } - - ports.append(move(hardwarePort)); -} - -auto GameBoyInterface::manifest() -> string { - return cartridge.manifest(); -} - -auto GameBoyInterface::title() -> string { - return cartridge.title(); -} - -auto GameBoyInterface::videoSize() -> VideoSize { - return {160, 144}; -} - -auto GameBoyInterface::videoSize(uint width, uint height, bool arc) -> VideoSize { - uint w = 160; - uint h = 144; - uint m = min(width / w, height / h); - return {w * m, h * m}; -} - -auto GameBoyInterface::videoFrequency() -> double { - return 4194304.0 / (154.0 * 456.0); } auto GameBoyInterface::videoColors() -> uint32 { @@ -88,77 +46,7 @@ auto GameBoyInterface::videoColor(uint32 color) -> uint64 { } } -auto GameBoyInterface::audioFrequency() -> double { - return 4194304.0 / 2.0; -} - -auto GameBoyInterface::loaded() -> bool { - return system.loaded(); -} - -auto GameBoyInterface::sha256() -> string { - return cartridge.sha256(); -} - auto GameBoyInterface::load(uint id) -> bool { - if(id == ID::GameBoy) return system.load(this, System::Revision::GameBoy); - return false; -} - -auto GameBoyInterface::save() -> void { - system.save(); -} - -auto GameBoyInterface::unload() -> void { - save(); - system.unload(); -} - -auto GameBoyInterface::power() -> void { - system.power(); -} - -auto GameBoyInterface::run() -> void { - system.run(); -} - -auto GameBoyInterface::serialize() -> serializer { - system.runToSave(); - return system.serialize(); -} - -auto GameBoyInterface::unserialize(serializer& s) -> bool { - return system.unserialize(s); -} - -auto GameBoyInterface::cheatSet(const string_vector& list) -> void { - cheat.assign(list); -} - -auto GameBoyInterface::cap(const string& name) -> bool { - if(name == "Blur Emulation") return true; - if(name == "Color Emulation") return true; - return false; -} - -auto GameBoyInterface::get(const string& name) -> any { - if(name == "Blur Emulation") return settings.blurEmulation; - if(name == "Color Emulation") return settings.colorEmulation; - return {}; -} - -auto GameBoyInterface::set(const string& name, const any& value) -> bool { - if(name == "Blur Emulation" && value.is()) { - settings.blurEmulation = value.get(); - system.configureVideoEffects(); - return true; - } - - if(name == "Color Emulation" && value.is()) { - settings.colorEmulation = value.get(); - system.configureVideoPalette(); - return true; - } - + if(id == ID::GameBoy) return system.load(this, System::Model::GameBoy); return false; } diff --git a/higan/gb/interface/interface.cpp b/higan/gb/interface/interface.cpp index c9e6b156..a67ecceb 100644 --- a/higan/gb/interface/interface.cpp +++ b/higan/gb/interface/interface.cpp @@ -2,8 +2,123 @@ namespace GameBoy { +SuperGameBoyInterface* superGameBoy = nullptr; Settings settings; #include "game-boy.cpp" #include "game-boy-color.cpp" +Interface::Interface() { + information.capability.states = true; + information.capability.cheats = true; + + Port hardwarePort{ID::Port::Hardware, "Hardware"}; + + { Device device{ID::Device::Controls, "Controls"}; + device.inputs.append({0, "Up" }); + device.inputs.append({0, "Down" }); + device.inputs.append({0, "Left" }); + device.inputs.append({0, "Right" }); + device.inputs.append({0, "B" }); + device.inputs.append({0, "A" }); + device.inputs.append({0, "Select"}); + device.inputs.append({0, "Start" }); + hardwarePort.devices.append(device); + } + + ports.append(move(hardwarePort)); +} + +auto Interface::manifest() -> string { + return cartridge.manifest(); +} + +auto Interface::title() -> string { + return cartridge.title(); +} + +auto Interface::videoSize() -> VideoSize { + return {160, 144}; +} + +auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { + uint w = 160; + uint h = 144; + uint m = min(width / w, height / h); + return {w * m, h * m}; +} + +auto Interface::videoFrequency() -> double { + return 4'194'304.0 / (154.0 * 456.0); +} + +auto Interface::audioFrequency() -> double { + return 4'194'304.0 / 2.0; +} + +auto Interface::loaded() -> bool { + return system.loaded(); +} + +auto Interface::sha256() -> string { + return cartridge.sha256(); +} + +auto Interface::save() -> void { + system.save(); +} + +auto Interface::unload() -> void { + save(); + system.unload(); +} + +auto Interface::power() -> void { + system.power(); +} + +auto Interface::run() -> void { + system.run(); +} + +auto Interface::serialize() -> serializer { + system.runToSave(); + return system.serialize(); +} + +auto Interface::unserialize(serializer& s) -> bool { + return system.unserialize(s); +} + +auto Interface::cheatSet(const string_vector& list) -> void { + cheat.assign(list); +} + +auto Interface::cap(const string& name) -> bool { + if(name == "Blur Emulation") return true; + if(name == "Color Emulation") return true; + return false; +} + +auto Interface::get(const string& name) -> any { + if(name == "Blur Emulation") return settings.blurEmulation; + if(name == "Color Emulation") return settings.colorEmulation; + return {}; +} + +auto Interface::set(const string& name, const any& value) -> bool { + if(name == "Blur Emulation" && value.is()) { + settings.blurEmulation = value.get(); + system.configureVideoEffects(); + return true; + } + + if(name == "Color Emulation" && value.is()) { + settings.colorEmulation = value.get(); + system.configureVideoPalette(); + return true; + } + + return false; +} + } diff --git a/higan/gb/interface/interface.hpp b/higan/gb/interface/interface.hpp index 0e9db93b..b510fea1 100644 --- a/higan/gb/interface/interface.hpp +++ b/higan/gb/interface/interface.hpp @@ -17,97 +17,74 @@ struct ID { };}; }; -struct GameBoyInterface : Emulator::Interface { +struct Interface : Emulator::Interface { + Interface(); + + auto manifest() -> string override; + auto title() -> string override; + + auto videoSize() -> VideoSize override; + auto videoSize(uint width, uint height, bool arc) -> VideoSize override; + auto videoFrequency() -> double override; + + auto audioFrequency() -> double override; + + auto loaded() -> bool override; + auto sha256() -> string override; + + auto save() -> void override; + auto unload() -> void override; + + auto power() -> void override; + auto run() -> void override; + + auto serialize() -> serializer override; + auto unserialize(serializer&) -> bool override; + + auto cheatSet(const string_vector&) -> void override; + + auto cap(const string& name) -> bool override; + auto get(const string& name) -> any override; + auto set(const string& name, const any& value) -> bool override; +}; + +struct GameBoyInterface : Interface { using Emulator::Interface::load; GameBoyInterface(); - auto manifest() -> string override; - auto title() -> string override; - - auto videoSize() -> VideoSize override; - auto videoSize(uint width, uint height, bool arc) -> VideoSize override; - auto videoFrequency() -> double override; auto videoColors() -> uint32 override; auto videoColor(uint32 color) -> uint64 override; - auto audioFrequency() -> double override; - - auto loaded() -> bool override; - auto sha256() -> string override; auto load(uint id) -> bool override; - auto save() -> void override; - auto unload() -> void override; - - auto power() -> void override; - auto run() -> void override; - - auto serialize() -> serializer override; - auto unserialize(serializer&) -> bool override; - - auto cheatSet(const string_vector&) -> void override; - - auto cap(const string& name) -> bool override; - auto get(const string& name) -> any override; - auto set(const string& name, const any& value) -> bool override; }; -struct GameBoyColorInterface : Emulator::Interface { +struct GameBoyColorInterface : Interface { using Emulator::Interface::load; GameBoyColorInterface(); - auto manifest() -> string override; - auto title() -> string override; - - auto videoSize() -> VideoSize override; - auto videoSize(uint width, uint height, bool arc) -> VideoSize override; - auto videoFrequency() -> double override; auto videoColors() -> uint32 override; auto videoColor(uint32 color) -> uint64 override; - auto audioFrequency() -> double override; - - auto loaded() -> bool override; - auto sha256() -> string override; auto load(uint id) -> bool override; - auto save() -> void override; - auto unload() -> void override; - - auto power() -> void override; - auto run() -> void override; - - auto serialize() -> serializer override; - auto unserialize(serializer&) -> bool override; - - auto cheatSet(const string_vector&) -> void override; - - auto cap(const string& name) -> bool override; - auto get(const string& name) -> any override; - auto set(const string& name, const any& value) -> bool override; }; -/* -struct Interface : Emulator::Interface { - //Super Game Boy bindings - struct Hook { - virtual auto lcdScanline() -> void {} - virtual auto lcdOutput(uint2 color) -> void {} - virtual auto joypWrite(bool p15, bool p14) -> void {} - }; - Hook* hook = nullptr; +struct SuperGameBoyInterface { + virtual auto audioSample(const double* samples, uint channels) -> void = 0; + virtual auto inputPoll(uint port, uint device, uint id) -> int16 = 0; - auto lcdScanline() -> void; - auto lcdOutput(uint2 color) -> void; - auto joypWrite(bool p15, bool p14) -> void; + virtual auto lcdScanline() -> void = 0; + virtual auto lcdOutput(uint2 color) -> void = 0; + virtual auto joypWrite(bool p15, bool p14) -> void = 0; }; -*/ struct Settings { bool blurEmulation = true; bool colorEmulation = true; }; +extern SuperGameBoyInterface* superGameBoy; extern Settings settings; } diff --git a/higan/gb/ppu/dmg.cpp b/higan/gb/ppu/dmg.cpp index 5781033a..3619796e 100644 --- a/higan/gb/ppu/dmg.cpp +++ b/higan/gb/ppu/dmg.cpp @@ -78,7 +78,7 @@ auto PPU::runDMG() -> void { uint32* output = screen + status.ly * 160 + px++; *output = color; -//interface->lcdOutput(color); //Super Game Boy notification + if(Model::SuperGameBoy()) superGameBoy->lcdOutput(color); } auto PPU::runBackgroundDMG() -> void { diff --git a/higan/gb/ppu/io.cpp b/higan/gb/ppu/io.cpp index 0fd85715..b07617ca 100644 --- a/higan/gb/ppu/io.cpp +++ b/higan/gb/ppu/io.cpp @@ -142,7 +142,7 @@ auto PPU::writeIO(uint16 addr, uint8 data) -> void { //hardware bug: writes to STAT on DMG,SGB during vblank triggers STAT IRQ //note: this behavior isn't entirely correct; more research is needed ... - if(!system.cgb() && status.mode == 1) { + if(!Model::GameBoyColor() && status.mode == 1) { cpu.raise(CPU::Interrupt::Stat); } diff --git a/higan/gb/ppu/ppu.cpp b/higan/gb/ppu/ppu.cpp index 2c9f772d..6d6d90b3 100644 --- a/higan/gb/ppu/ppu.cpp +++ b/higan/gb/ppu/ppu.cpp @@ -16,7 +16,7 @@ auto PPU::Enter() -> void { auto PPU::main() -> void { status.lx = 0; -//interface->lcdScanline(); //Super Game Boy notification + if(Model::SuperGameBoy()) superGameBoy->lcdScanline(); if(status.ly <= 143) { mode(2); @@ -71,7 +71,7 @@ auto PPU::coincidence() -> bool { } auto PPU::refresh() -> void { - if(!system.sgb()) Emulator::video.refresh(screen, 160 * sizeof(uint32), 160, 144); + if(!Model::SuperGameBoy()) Emulator::video.refresh(screen, 160 * sizeof(uint32), 160, 144); } auto PPU::step(uint clocks) -> void { @@ -109,7 +109,7 @@ auto PPU::hflip(uint data) const -> uint { auto PPU::power() -> void { create(Enter, 4 * 1024 * 1024); - if(system.cgb()) { + if(Model::GameBoyColor()) { scanline = {&PPU::scanlineCGB, this}; run = {&PPU::runCGB, this}; } else { @@ -133,7 +133,7 @@ auto PPU::power() -> void { bus.mmio[0xff4a] = this; //WY bus.mmio[0xff4b] = this; //WX - if(system.cgb()) { + if(Model::GameBoyColor()) { bus.mmio[0xff4f] = this; //VBK bus.mmio[0xff68] = this; //BGPI bus.mmio[0xff69] = this; //BGPD diff --git a/higan/gb/system/system.cpp b/higan/gb/system/system.cpp index 71e680ad..e7405774 100644 --- a/higan/gb/system/system.cpp +++ b/higan/gb/system/system.cpp @@ -22,26 +22,49 @@ auto System::init() -> void { assert(interface != nullptr); } -auto System::load(Emulator::Interface* interface, Revision revision) -> bool { - _revision = revision; +auto System::load(Emulator::Interface* interface, Model model_, maybe systemID) -> bool { + _model = model_; - if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) { - information.manifest = fp->reads(); - } else return false; + if(model() == Model::GameBoy) { + if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) { + information.manifest = fp->reads(); + } else return false; - auto document = BML::unserialize(information.manifest); - string path = "system/cpu/rom/name"; - if(revision == Revision::SuperGameBoy) path = "board/icd2/rom/name"; - - if(auto name = document[path].text()) { - if(auto fp = platform->open(ID::System, name, File::Read, File::Required)) { - if(revision == Revision::GameBoy) fp->read(bootROM.dmg, 256); - if(revision == Revision::SuperGameBoy) fp->read(bootROM.sgb, 256); - if(revision == Revision::GameBoyColor) fp->read(bootROM.cgb, 2048); + auto document = BML::unserialize(information.manifest); + if(auto name = document["system/cpu/rom/name"].text()) { + if(auto fp = platform->open(ID::System, name, File::Read, File::Required)) { + fp->read(bootROM.dmg, 256); + } } } - if(!cartridge.load(revision)) return false; + if(model() == Model::GameBoyColor) { + if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) { + information.manifest = fp->reads(); + } else return false; + + auto document = BML::unserialize(information.manifest); + if(auto name = document["system/cpu/rom/name"].text()) { + if(auto fp = platform->open(ID::System, name, File::Read, File::Required)) { + fp->read(bootROM.cgb, 2048); + } + } + } + + if(model() == Model::SuperGameBoy) { + if(auto fp = platform->open(systemID(), "manifest.bml", File::Read, File::Required)) { + information.manifest = fp->reads(); + } else return false; + + auto document = BML::unserialize(information.manifest); + if(auto name = document["board/icd2/rom/name"].text()) { + if(auto fp = platform->open(systemID(), name, File::Read, File::Required)) { + fp->read(bootROM.sgb, 256); + } + } + } + + if(!cartridge.load()) return false; serializeInit(); this->interface = interface; return _loaded = true; @@ -59,7 +82,7 @@ auto System::unload() -> void { } auto System::power() -> void { - if(!system.sgb()) { + if(model() != Model::SuperGameBoy) { Emulator::video.reset(); Emulator::video.setInterface(interface); configureVideoPalette(); diff --git a/higan/gb/system/system.hpp b/higan/gb/system/system.hpp index 874ebecb..957a7c32 100644 --- a/higan/gb/system/system.hpp +++ b/higan/gb/system/system.hpp @@ -3,25 +3,21 @@ enum class Input : uint { }; struct System { - enum class Revision : uint { + enum class Model : uint { GameBoy, - SuperGameBoy, GameBoyColor, + SuperGameBoy, }; - auto loaded() const -> bool { return _loaded; } - auto revision() const -> Revision { return _revision; } - auto clocksExecuted() const -> uint { return _clocksExecuted; } - - inline auto dmg() const { return _revision == Revision::GameBoy; } - inline auto sgb() const { return _revision == Revision::SuperGameBoy; } - inline auto cgb() const { return _revision == Revision::GameBoyColor; } + inline auto loaded() const -> bool { return _loaded; } + inline auto model() const -> Model { return _model; } + inline auto clocksExecuted() const -> uint { return _clocksExecuted; } auto run() -> void; auto runToSave() -> void; auto init() -> void; - auto load(Emulator::Interface*, Revision) -> bool; + auto load(Emulator::Interface*, Model, maybe = nothing) -> bool; auto save() -> void; auto unload() -> void; auto power() -> void; @@ -51,7 +47,7 @@ struct System { } information; bool _loaded = false; - Revision _revision = Revision::GameBoy; + Model _model = Model::GameBoy; uint _serializeSize = 0; uint _clocksExecuted = 0; }; @@ -59,3 +55,7 @@ struct System { #include extern System system; + +auto Model::GameBoy() -> bool { return system.model() == System::Model::GameBoy; } +auto Model::GameBoyColor() -> bool { return system.model() == System::Model::GameBoyColor; } +auto Model::SuperGameBoy() -> bool { return system.model() == System::Model::SuperGameBoy; } diff --git a/higan/gb/system/video.cpp b/higan/gb/system/video.cpp index 6f27f6c8..5fd57461 100644 --- a/higan/gb/system/video.cpp +++ b/higan/gb/system/video.cpp @@ -1,9 +1,9 @@ auto System::configureVideoPalette() -> void { - if(sgb()) return; + if(model() == Model::SuperGameBoy) return; Emulator::video.setPalette(); } auto System::configureVideoEffects() -> void { - if(sgb()) return; + if(model() == Model::SuperGameBoy) return; Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation); } diff --git a/higan/md/interface/interface.cpp b/higan/md/interface/interface.cpp index b0d3559d..4bada49c 100644 --- a/higan/md/interface/interface.cpp +++ b/higan/md/interface/interface.cpp @@ -101,6 +101,7 @@ auto Interface::save() -> void { } auto Interface::unload() -> void { + save(); system.unload(); } diff --git a/higan/ms/interface/game-gear.cpp b/higan/ms/interface/game-gear.cpp index e0730d79..38d79dc0 100644 --- a/higan/ms/interface/game-gear.cpp +++ b/higan/ms/interface/game-gear.cpp @@ -3,9 +3,6 @@ GameGearInterface::GameGearInterface() { information.name = "Game Gear"; information.overscan = false; - information.capability.states = false; - information.capability.cheats = false; - media.append({ID::GameGear, "Game Gear", "gg"}); Port hardware{ID::Port::Hardware, "Hardware"}; @@ -24,14 +21,6 @@ GameGearInterface::GameGearInterface() { ports.append(move(hardware)); } -auto GameGearInterface::manifest() -> string { - return cartridge.manifest(); -} - -auto GameGearInterface::title() -> string { - return cartridge.title(); -} - auto GameGearInterface::videoSize() -> VideoSize { return {160, 144}; } @@ -63,55 +52,7 @@ auto GameGearInterface::videoColor(uint32 color) -> uint64 { return r << 32 | g << 16 | b << 0; } -auto GameGearInterface::audioFrequency() -> double { - return 44'100.0; -} - -auto GameGearInterface::loaded() -> bool { - return system.loaded(); -} - auto GameGearInterface::load(uint id) -> bool { if(id == ID::GameGear) return system.load(this, Model::GameGear); return false; } - -auto GameGearInterface::save() -> void { - system.save(); -} - -auto GameGearInterface::unload() -> void { - system.unload(); -} - -auto GameGearInterface::connect(uint port, uint device) -> void { - peripherals.connect(port, device); -} - -auto GameGearInterface::power() -> void { - system.power(); -} - -auto GameGearInterface::run() -> void { - system.run(); -} - -auto GameGearInterface::serialize() -> serializer { - return {}; -} - -auto GameGearInterface::unserialize(serializer& s) -> bool { - return false; -} - -auto GameGearInterface::cap(const string& name) -> bool { - return false; -} - -auto GameGearInterface::get(const string& name) -> any { - return {}; -} - -auto GameGearInterface::set(const string& name, const any& value) -> bool { - return false; -} diff --git a/higan/ms/interface/interface.cpp b/higan/ms/interface/interface.cpp index 28f14a83..c024e2aa 100644 --- a/higan/ms/interface/interface.cpp +++ b/higan/ms/interface/interface.cpp @@ -6,4 +6,66 @@ Settings settings; #include "master-system.cpp" #include "game-gear.cpp" +Interface::Interface() { + information.capability.states = false; + information.capability.cheats = false; +} + +auto Interface::manifest() -> string { + return cartridge.manifest(); +} + +auto Interface::title() -> string { + return cartridge.title(); +} + +auto Interface::audioFrequency() -> double { + return 44'100.0; //todo: not correct +} + +auto Interface::loaded() -> bool { + return system.loaded(); +} + +auto Interface::save() -> void { + system.save(); +} + +auto Interface::unload() -> void { + save(); + system.unload(); +} + +auto Interface::connect(uint port, uint device) -> void { + peripherals.connect(port, device); +} + +auto Interface::power() -> void { + system.power(); +} + +auto Interface::run() -> void { + system.run(); +} + +auto Interface::serialize() -> serializer { + return {}; +} + +auto Interface::unserialize(serializer& s) -> bool { + return false; +} + +auto Interface::cap(const string& name) -> bool { + return false; +} + +auto Interface::get(const string& name) -> any { + return {}; +} + +auto Interface::set(const string& name, const any& value) -> bool { + return false; +} + } diff --git a/higan/ms/interface/interface.hpp b/higan/ms/interface/interface.hpp index e1514b57..344c1751 100644 --- a/higan/ms/interface/interface.hpp +++ b/higan/ms/interface/interface.hpp @@ -21,24 +21,15 @@ struct ID { };}; }; -struct MasterSystemInterface : Emulator::Interface { - using Emulator::Interface::load; - - MasterSystemInterface(); +struct Interface : Emulator::Interface { + Interface(); auto manifest() -> string override; auto title() -> string override; - auto videoSize() -> VideoSize override; - auto videoSize(uint width, uint height, bool arc) -> VideoSize override; - auto videoFrequency() -> double override; - auto videoColors() -> uint32 override; - auto videoColor(uint32 color) -> uint64 override; - auto audioFrequency() -> double override; auto loaded() -> bool override; - auto load(uint id) -> bool override; auto save() -> void override; auto unload() -> void override; @@ -54,13 +45,10 @@ struct MasterSystemInterface : Emulator::Interface { auto set(const string& name, const any& value) -> bool override; }; -struct GameGearInterface : Emulator::Interface { +struct MasterSystemInterface : Interface { using Emulator::Interface::load; - GameGearInterface(); - - auto manifest() -> string override; - auto title() -> string override; + MasterSystemInterface(); auto videoSize() -> VideoSize override; auto videoSize(uint width, uint height, bool arc) -> VideoSize override; @@ -68,23 +56,21 @@ struct GameGearInterface : Emulator::Interface { auto videoColors() -> uint32 override; auto videoColor(uint32 color) -> uint64 override; - auto audioFrequency() -> double override; - - auto loaded() -> bool override; auto load(uint id) -> bool override; - auto save() -> void override; - auto unload() -> void override; +}; - auto connect(uint port, uint device) -> void override; - auto power() -> void override; - auto run() -> void override; +struct GameGearInterface : Interface { + using Emulator::Interface::load; - auto serialize() -> serializer override; - auto unserialize(serializer&) -> bool override; + GameGearInterface(); - auto cap(const string& name) -> bool override; - auto get(const string& name) -> any override; - auto set(const string& name, const any& value) -> bool override; + auto videoSize() -> VideoSize override; + auto videoSize(uint width, uint height, bool arc) -> VideoSize override; + auto videoFrequency() -> double override; + auto videoColors() -> uint32 override; + auto videoColor(uint32 color) -> uint64 override; + + auto load(uint id) -> bool override; }; struct Settings { diff --git a/higan/ms/interface/master-system.cpp b/higan/ms/interface/master-system.cpp index 18363eb6..7bbf3d17 100644 --- a/higan/ms/interface/master-system.cpp +++ b/higan/ms/interface/master-system.cpp @@ -3,9 +3,6 @@ MasterSystemInterface::MasterSystemInterface() { information.name = "Master System"; information.overscan = true; - information.capability.states = false; - information.capability.cheats = false; - media.append({ID::MasterSystem, "Master System", "ms"}); Port hardware{ID::Port::Hardware, "Hardware"}; @@ -39,14 +36,6 @@ MasterSystemInterface::MasterSystemInterface() { ports.append(move(controllerPort2)); } -auto MasterSystemInterface::manifest() -> string { - return cartridge.manifest(); -} - -auto MasterSystemInterface::title() -> string { - return cartridge.title(); -} - auto MasterSystemInterface::videoSize() -> VideoSize { return {256, 240}; } @@ -79,55 +68,7 @@ auto MasterSystemInterface::videoColor(uint32 color) -> uint64 { return r << 32 | g << 16 | b << 0; } -auto MasterSystemInterface::audioFrequency() -> double { - return 44'100.0; -} - -auto MasterSystemInterface::loaded() -> bool { - return system.loaded(); -} - auto MasterSystemInterface::load(uint id) -> bool { if(id == ID::MasterSystem) return system.load(this, Model::MasterSystem); return false; } - -auto MasterSystemInterface::save() -> void { - system.save(); -} - -auto MasterSystemInterface::unload() -> void { - system.unload(); -} - -auto MasterSystemInterface::connect(uint port, uint device) -> void { - peripherals.connect(port, device); -} - -auto MasterSystemInterface::power() -> void { - system.power(); -} - -auto MasterSystemInterface::run() -> void { - system.run(); -} - -auto MasterSystemInterface::serialize() -> serializer { - return {}; -} - -auto MasterSystemInterface::unserialize(serializer& s) -> bool { - return false; -} - -auto MasterSystemInterface::cap(const string& name) -> bool { - return false; -} - -auto MasterSystemInterface::get(const string& name) -> any { - return {}; -} - -auto MasterSystemInterface::set(const string& name, const any& value) -> bool { - return false; -} diff --git a/higan/pce/cartridge/cartridge.cpp b/higan/pce/cartridge/cartridge.cpp index 1f2833ab..a8183aac 100644 --- a/higan/pce/cartridge/cartridge.cpp +++ b/higan/pce/cartridge/cartridge.cpp @@ -38,6 +38,7 @@ auto Cartridge::load() -> bool { } } + information.sha256 = Hash::SHA256(rom.data, rom.size).digest(); return true; } diff --git a/higan/pce/cpu/cpu.cpp b/higan/pce/cpu/cpu.cpp index a79835b5..966992b6 100644 --- a/higan/pce/cpu/cpu.cpp +++ b/higan/pce/cpu/cpu.cpp @@ -3,6 +3,7 @@ namespace PCEngine { CPU cpu; +#include "memory.cpp" #include "io.cpp" #include "irq.cpp" #include "timer.cpp" diff --git a/higan/pce/cpu/cpu.hpp b/higan/pce/cpu/cpu.hpp index 56aa0d32..b04c4521 100644 --- a/higan/pce/cpu/cpu.hpp +++ b/higan/pce/cpu/cpu.hpp @@ -4,10 +4,13 @@ struct CPU : Processor::HuC6280, Thread { static auto Enter() -> void; auto main() -> void; auto step(uint clocks) -> void override; - auto power() -> void; auto lastCycle() -> void override; + //memory.cpp + auto load() -> void; + auto save() -> void; + //io.cpp auto read(uint8 bank, uint13 addr) -> uint8 override; auto write(uint8 bank, uint13 addr, uint8 data) -> void override; @@ -19,22 +22,15 @@ struct CPU : Processor::HuC6280, Thread { vector peripherals; struct IRQ { - enum class Line : uint { External, VDC, Timer }; - //irq.cpp auto pending() const -> bool; auto vector() const -> uint16; auto poll() -> void; - auto level(Line, bool = 1) -> void; private: - bool disableExternal; - bool disableVDC; - bool disableTimer; - - bool pendingExternal; - bool pendingVDC; - bool pendingTimer; + bool disableExternal; + bool disableVDC; + bool disableTimer; bool pendingIRQ; uint16 pendingVector; @@ -43,6 +39,8 @@ struct CPU : Processor::HuC6280, Thread { } irq; struct Timer { + inline auto irqLine() const { return line; } + //timer.cpp auto start() -> void; auto step(uint clocks) -> void; @@ -53,6 +51,8 @@ struct CPU : Processor::HuC6280, Thread { uint7 value; uint clock; + bool line; + friend class CPU; } timer; @@ -62,6 +62,7 @@ struct CPU : Processor::HuC6280, Thread { private: uint8 ram[0x8000]; //PC Engine = 8KB, SuperGrafx = 32KB + uint8 bram[0x800]; //PC Engine CD-ROM Backup RAM = 2KB }; extern CPU cpu; diff --git a/higan/pce/cpu/io.cpp b/higan/pce/cpu/io.cpp index fa2ce999..f3772bdc 100644 --- a/higan/pce/cpu/io.cpp +++ b/higan/pce/cpu/io.cpp @@ -4,6 +4,11 @@ auto CPU::read(uint8 bank, uint13 addr) -> uint8 { return cartridge.read(bank << 13 | addr); } + //$f7 BRAM + if(bank == 0xf7) { + return bram[addr.bits(0,10)]; + } + //$f8-fb RAM if(bank >= 0xf8 && bank <= 0xfb) { if(Model::PCEngine()) return ram[addr]; @@ -35,12 +40,17 @@ auto CPU::read(uint8 bank, uint13 addr) -> uint8 { //$1000-13ff I/O if((addr & 0x1c00) == 0x1000) { + //note 1: Turbografx-16 games check this bit for region protection. + //yet PC Engine games do not. since we cannot tell the games apart, + //it's more compatible to always identify as a Turbografx-16 system. + //note 2: we state that the CD-ROM drive is present. + //this is so games can use its backup RAM for save data. return ( PCEngine::peripherals.controllerPort->readData() << 0 | 1 << 4 | 1 << 5 | 0 << 6 //device (0 = Turbografx-16; 1 = PC Engine) - | 1 << 7 //add-on (0 = CD-ROM; 1 = nothing) + | 0 << 7 //add-on (0 = CD-ROM; 1 = nothing) ); } @@ -64,10 +74,13 @@ auto CPU::read(uint8 bank, uint13 addr) -> uint8 { } if(addr.bits(0,1) == 3) { + bool pendingExternal = 0; + bool pendingVDC = vdc0.irqLine() | vdc1.irqLine(); + bool pendingTimer = timer.irqLine(); return ( - irq.pendingExternal << 0 - | irq.pendingVDC << 1 - | irq.pendingTimer << 2 + pendingExternal << 0 + | pendingVDC << 1 + | pendingTimer << 2 | (io.mdr & 0xf8) ); } @@ -93,6 +106,12 @@ auto CPU::write(uint8 bank, uint13 addr, uint8 data) -> void { return cartridge.write(bank << 13 | addr, data); } + //$f7 BRAM + if(bank == 0xf7) { + bram[addr.bits(0,10)] = data; + return; + } + //$f8-fb RAM if(bank >= 0xf8 && bank <= 0xfb) { if(Model::PCEngine()) ram[addr] = data; @@ -149,7 +168,7 @@ auto CPU::write(uint8 bank, uint13 addr, uint8 data) -> void { } if(addr.bits(0,1) == 3) { - irq.level(IRQ::Line::Timer, 0); + timer.line = 0; return; } } diff --git a/higan/pce/cpu/irq.cpp b/higan/pce/cpu/irq.cpp index 04feead9..1f04d530 100644 --- a/higan/pce/cpu/irq.cpp +++ b/higan/pce/cpu/irq.cpp @@ -10,28 +10,18 @@ auto CPU::IRQ::poll() -> void { pendingIRQ = false; if(cpu.r.p.i) return; - if(!disableExternal && pendingExternal) { - pendingIRQ = true; + if(0) { //external IRQ sources + pendingIRQ = !disableExternal; pendingVector = 0xfff6; - } else if(!disableVDC && pendingVDC) { - pendingIRQ = true; + } + + if(!disableVDC && (vdc0.irqLine() | vdc1.irqLine())) { + pendingIRQ = !disableVDC; pendingVector = 0xfff8; - } else if(!disableTimer && pendingTimer) { - pendingIRQ = true; + } + + if(cpu.timer.irqLine()) { + pendingIRQ = !disableTimer; pendingVector = 0xfffa; } } - -auto CPU::IRQ::level(Line line, bool level) -> void { - if(line == Line::External) { - pendingExternal = level; - } - - if(line == Line::VDC) { - pendingVDC = level; - } - - if(line == Line::Timer) { - pendingTimer = level; - } -} diff --git a/higan/pce/cpu/memory.cpp b/higan/pce/cpu/memory.cpp new file mode 100644 index 00000000..ced825fa --- /dev/null +++ b/higan/pce/cpu/memory.cpp @@ -0,0 +1,23 @@ +//PC Engine HuCards lack save RAM on them due to the card size and cost savings. +//The PC Engine CD adds 2KB of backup RAM that most HuCard games can use for saves. +//However, all games must share this small amount of RAM. +//Since this is an emulator, we can make this process nicer by storing BRAM per-game. + +//This does hard-code the save.ram name, rather than using a manifest file name. +//It also creates a save.ram file no matter what, even for games that don't save data. +//Unfortunately, we can't know in advance if a game supports BRAM saves or not. +//So because of this, we have to always create it. +//Thankfully, the file is very small so it should not prove to be a burden in practice. + +auto CPU::load() -> void { + for(auto& byte : bram) byte = 0xff; + if(auto fp = platform->open(cartridge.pathID(), "save.ram", File::Read)) { + fp->read(bram, 0x800); + } +} + +auto CPU::save() -> void { + if(auto fp = platform->open(cartridge.pathID(), "save.ram", File::Write)) { + fp->write(bram, 0x800); + } +} diff --git a/higan/pce/cpu/timer.cpp b/higan/pce/cpu/timer.cpp index 5e0ce56f..189d2e9a 100644 --- a/higan/pce/cpu/timer.cpp +++ b/higan/pce/cpu/timer.cpp @@ -10,7 +10,7 @@ auto CPU::Timer::step(uint clocks) -> void { clock -= 1024; if(!value--) { value = latch; - cpu.irq.level(CPU::IRQ::Line::Timer, 1); + line = 1; } } } diff --git a/higan/pce/interface/interface.cpp b/higan/pce/interface/interface.cpp index 4f184aed..2cc12159 100644 --- a/higan/pce/interface/interface.cpp +++ b/higan/pce/interface/interface.cpp @@ -82,11 +82,16 @@ auto Interface::loaded() -> bool { return system.loaded(); } +auto Interface::sha256() -> string { + return cartridge.sha256(); +} + auto Interface::save() -> void { system.save(); } auto Interface::unload() -> void { + save(); system.unload(); } diff --git a/higan/pce/interface/interface.hpp b/higan/pce/interface/interface.hpp index 249abc2c..ed6000b5 100644 --- a/higan/pce/interface/interface.hpp +++ b/higan/pce/interface/interface.hpp @@ -18,8 +18,6 @@ struct ID { }; struct Interface : Emulator::Interface { - using Emulator::Interface::load; - Interface(); auto manifest() -> string override; @@ -34,6 +32,7 @@ struct Interface : Emulator::Interface { auto audioFrequency() -> double override; auto loaded() -> bool override; + auto sha256() -> string override; auto save() -> void override; auto unload() -> void override; diff --git a/higan/pce/interface/pc-engine.cpp b/higan/pce/interface/pc-engine.cpp index b653a88d..afa3d923 100644 --- a/higan/pce/interface/pc-engine.cpp +++ b/higan/pce/interface/pc-engine.cpp @@ -6,6 +6,6 @@ PCEngineInterface::PCEngineInterface() { } auto PCEngineInterface::load(uint id) -> bool { - if(id == ID::PCEngine) return system.load(this, id); + if(id == ID::PCEngine) return system.load(this, System::Model::PCEngine); return false; } diff --git a/higan/pce/interface/supergrafx.cpp b/higan/pce/interface/supergrafx.cpp index 8e6dc87a..c8024d50 100644 --- a/higan/pce/interface/supergrafx.cpp +++ b/higan/pce/interface/supergrafx.cpp @@ -6,6 +6,6 @@ SuperGrafxInterface::SuperGrafxInterface() { } auto SuperGrafxInterface::load(uint id) -> bool { - if(id == ID::SuperGrafx) return system.load(this, id); + if(id == ID::SuperGrafx) return system.load(this, System::Model::SuperGrafx); return false; } diff --git a/higan/pce/pce.hpp b/higan/pce/pce.hpp index 625fb764..257d4de0 100644 --- a/higan/pce/pce.hpp +++ b/higan/pce/pce.hpp @@ -27,9 +27,8 @@ namespace PCEngine { }; struct Model { - inline static auto PCEngine() -> bool { return id == 1; } - inline static auto SuperGrafx() -> bool { return id == 2; } - static uint id; + inline static auto PCEngine() -> bool; + inline static auto SuperGrafx() -> bool; }; #include diff --git a/higan/pce/system/system.cpp b/higan/pce/system/system.cpp index 9727c5d8..379d00a2 100644 --- a/higan/pce/system/system.cpp +++ b/higan/pce/system/system.cpp @@ -2,7 +2,6 @@ namespace PCEngine { -uint Model::id; System system; Scheduler scheduler; #include "peripherals.cpp" @@ -11,9 +10,9 @@ auto System::run() -> void { if(scheduler.enter() == Scheduler::Event::Frame) vce.refresh(); } -auto System::load(Emulator::Interface* interface, uint id) -> bool { - Model::id = id; +auto System::load(Emulator::Interface* interface, Model model) -> bool { information = {}; + information.model = model; if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) { information.manifest = fp->reads(); @@ -22,6 +21,7 @@ auto System::load(Emulator::Interface* interface, uint id) -> bool { auto document = BML::unserialize(information.manifest); if(!cartridge.load()) return false; + cpu.load(); this->interface = interface; information.colorburst = Emulator::Constants::Colorburst::NTSC; return information.loaded = true; @@ -29,6 +29,7 @@ auto System::load(Emulator::Interface* interface, uint id) -> bool { auto System::save() -> void { cartridge.save(); + cpu.save(); } auto System::unload() -> void { @@ -47,8 +48,8 @@ auto System::power() -> void { scheduler.reset(); cartridge.power(); cpu.power(); - vpc.power(); vce.power(); + vpc.power(); vdc0.power(); vdc1.power(); psg.power(); diff --git a/higan/pce/system/system.hpp b/higan/pce/system/system.hpp index dbd7988a..f7ef776a 100644 --- a/higan/pce/system/system.hpp +++ b/higan/pce/system/system.hpp @@ -1,10 +1,13 @@ struct System { - auto loaded() const -> bool { return information.loaded; } - auto colorburst() const -> double { return information.colorburst; } + enum class Model : uint { PCEngine, SuperGrafx }; + + inline auto loaded() const -> bool { return information.loaded; } + inline auto model() const -> Model { return information.model; } + inline auto colorburst() const -> double { return information.colorburst; } auto run() -> void; - auto load(Emulator::Interface*, uint) -> bool; + auto load(Emulator::Interface*, Model) -> bool; auto save() -> void; auto unload() -> void; @@ -15,6 +18,7 @@ private: struct Information { bool loaded = false; + Model model = Model::PCEngine; string manifest; double colorburst = 0.0; } information; @@ -30,3 +34,6 @@ struct Peripherals { extern System system; extern Peripherals peripherals; + +auto Model::PCEngine() -> bool { return system.model() == System::Model::PCEngine; } +auto Model::SuperGrafx() -> bool { return system.model() == System::Model::SuperGrafx; } diff --git a/higan/pce/vce/vce.cpp b/higan/pce/vce/vce.cpp index d128610f..d0d120f9 100644 --- a/higan/pce/vce/vce.cpp +++ b/higan/pce/vce/vce.cpp @@ -48,6 +48,8 @@ auto VCE::main() -> void { auto VCE::step(uint clocks) -> void { Thread::step(clocks); synchronize(cpu); + synchronize(vdc0); + synchronize(vdc1); timing.hclock += clocks; } diff --git a/higan/pce/vdc/irq.cpp b/higan/pce/vdc/irq.cpp index 6f0c2cca..dea7d7ae 100644 --- a/higan/pce/vdc/irq.cpp +++ b/higan/pce/vdc/irq.cpp @@ -6,7 +6,7 @@ auto VDC::IRQ::poll() -> void { pending |= pendingVblank; pending |= pendingTransferVRAM; pending |= pendingTransferSATB; - cpu.irq.level(CPU::IRQ::Line::VDC, pending); + line = pending; } auto VDC::IRQ::raise(Line line) -> void { @@ -44,6 +44,5 @@ auto VDC::IRQ::lower() -> void { pendingVblank = false; pendingTransferVRAM = false; pendingTransferSATB = false; - - poll(); + line = false; } diff --git a/higan/pce/vdc/vdc.cpp b/higan/pce/vdc/vdc.cpp index c0f9193b..414ea57a 100644 --- a/higan/pce/vdc/vdc.cpp +++ b/higan/pce/vdc/vdc.cpp @@ -91,6 +91,7 @@ auto VDC::frame() -> void { auto VDC::step(uint clocks) -> void { Thread::step(clocks); synchronize(cpu); + synchronize(vce); timing.hclock += clocks; dma.step(clocks); diff --git a/higan/pce/vdc/vdc.hpp b/higan/pce/vdc/vdc.hpp index 5c20cc14..25809de3 100644 --- a/higan/pce/vdc/vdc.hpp +++ b/higan/pce/vdc/vdc.hpp @@ -2,6 +2,7 @@ struct VDC : Thread { inline auto bus() const -> uint9 { return data; } + inline auto irqLine() const -> bool { return irq.line; } static auto Enter() -> void; auto main() -> void; @@ -98,6 +99,8 @@ private: bool pendingVblank; bool pendingTransferVRAM; bool pendingTransferSATB; + + bool line; } irq; struct DMA { diff --git a/higan/pce/vpc/vpc.cpp b/higan/pce/vpc/vpc.cpp index 9bf483a8..d52647a0 100644 --- a/higan/pce/vpc/vpc.cpp +++ b/higan/pce/vpc/vpc.cpp @@ -5,49 +5,48 @@ namespace PCEngine { VPC vpc; auto VPC::bus(uint hclock) -> uint9 { + //bus values are direct CRAM entry indexes: + //d0-d3 => color (0 = neither background nor sprite) + //d4-d7 => palette + //d8 => source (0 = background; 1 = sprite) auto bus0 = vdc0.bus(); auto bus1 = vdc1.bus(); - auto color0 = bus0.bits(0,3); - auto color1 = bus1.bits(0,3); + //note: timing may not be correct here; unable to test behavior + //no official SuperGrafx games ever use partial screen-width windows + bool window0 = window[0] >= 64 && (window[0] - 64) >= hclock / 2; + bool window1 = window[1] >= 64 && (window[1] - 64) >= hclock / 2; - auto palette0 = bus0.bits(4,7); - auto palette1 = bus1.bits(4,7); - - auto mode0 = bus0.bit(8); - auto mode1 = bus1.bit(8); - - //todo: I am unsure how the window coordinates relate to raw screen pixels ... - bool window0 = window[0] >= 64 && (window[0] - 64) >= hclock; - bool window1 = window[1] >= 64 && (window[1] - 64) >= hclock; - - uint2 mode; - if(!window0 && !window1) mode = 1; - if( window0 && !window1) mode = 0; - if(!window0 && window1) mode = 3; - if( window0 && window1) mode = 2; - - auto enableVDC0 = settings[mode].enableVDC0; - auto enableVDC1 = settings[mode].enableVDC1; + uint2 mode = !window0 << 0 | !window1 << 1; + auto enableVDC0 = settings[mode].enableVDC0 && bus0.bits(0,3); + auto enableVDC1 = settings[mode].enableVDC1 && bus1.bits(0,3); auto priority = settings[mode].priority; - //todo: I am unsure how this should work ... if(priority == 0 || priority == 3) { - if(color1) return bus1; - return bus0; + //SP0 > BG0 > SP1 > BG1 + if(bus0.bit(8) == 1 && enableVDC0) return bus0; + if(bus0.bit(8) == 0 && enableVDC0) return bus0; + if(bus1.bit(8) == 1 && enableVDC1) return bus1; + if(bus1.bit(8) == 0 && enableVDC1) return bus1; } if(priority == 1) { - if(color1) return bus1; - return bus0; + //SP0 > SP1 > BG0 > BG1 + if(bus0.bit(8) == 1 && enableVDC0) return bus0; + if(bus1.bit(8) == 1 && enableVDC1) return bus1; + if(bus0.bit(8) == 0 && enableVDC0) return bus0; + if(bus1.bit(8) == 0 && enableVDC1) return bus1; } if(priority == 2) { - if(color1) return bus1; - return bus0; + //BG0 > SP1 > BG1 > SP0 + if(bus0.bit(8) == 0 && enableVDC0) return bus0; + if(bus1.bit(8) == 1 && enableVDC1) return bus1; + if(bus1.bit(8) == 0 && enableVDC1) return bus1; + if(bus0.bit(8) == 1 && enableVDC0) return bus0; } - unreachable; + return 0x000; } auto VPC::power() -> void { @@ -68,7 +67,7 @@ auto VPC::power() -> void { settings[3].priority = 0; window[0] = 0; - window[1] = 1; + window[1] = 0; select = 0; } diff --git a/higan/pce/vpc/vpc.hpp b/higan/pce/vpc/vpc.hpp index 80c0ac2c..9dafbea8 100644 --- a/higan/pce/vpc/vpc.hpp +++ b/higan/pce/vpc/vpc.hpp @@ -16,7 +16,7 @@ private: } settings[4]; uint10 window[2]; - bool select; + bool select; }; extern VPC vpc; diff --git a/higan/processor/huc6280/huc6280.hpp b/higan/processor/huc6280/huc6280.hpp index 75064d52..5441d1f0 100644 --- a/higan/processor/huc6280/huc6280.hpp +++ b/higan/processor/huc6280/huc6280.hpp @@ -73,7 +73,6 @@ struct HuC6280 { auto instruction_indirectYStore(uint8) -> void; auto instruction_memory(fp) -> void; auto instruction_pull(uint8&) -> void; - auto instruction_pullP() -> void; auto instruction_push(uint8) -> void; auto instruction_set(bool&) -> void; auto instruction_swap(uint8&, uint8&) -> void; @@ -92,7 +91,7 @@ struct HuC6280 { auto instruction_JMP_indirect(uint8 = 0) -> void; auto instruction_JSR() -> void; auto instruction_NOP() -> void; - auto instruction_PHP() -> void; + auto instruction_PLP() -> void; auto instruction_RMB(uint3) -> void; auto instruction_RTI() -> void; auto instruction_RTS() -> void; diff --git a/higan/processor/huc6280/instruction.cpp b/higan/processor/huc6280/instruction.cpp index 80cdc792..44848ab9 100644 --- a/higan/processor/huc6280/instruction.cpp +++ b/higan/processor/huc6280/instruction.cpp @@ -68,7 +68,7 @@ U op(0x1b, NOP) op(0x25, zeropageLoad, fp(AND), A) op(0x26, zeropageModify, fp(ROL)) op(0x27, RMB, 2) - op(0x28, pullP) + op(0x28, PLP) op(0x29, immediate, fp(AND), A) op(0x2a, implied, fp(ROL), A) U op(0x2b, NOP) diff --git a/higan/processor/huc6280/instructions.cpp b/higan/processor/huc6280/instructions.cpp index fcc2a592..c7d907ce 100644 --- a/higan/processor/huc6280/instructions.cpp +++ b/higan/processor/huc6280/instructions.cpp @@ -314,12 +314,8 @@ auto HuC6280::instruction_pull(uint8& data) -> void { io(); io(); L data = pull(); -} - -auto HuC6280::instruction_pullP() -> void { - io(); - io(); -L P = pull(); + Z = data == 0; + N = data.bit(7); } auto HuC6280::instruction_push(uint8 data) -> void { @@ -453,6 +449,12 @@ auto HuC6280::instruction_NOP() -> void { L io(); } +auto HuC6280::instruction_PLP() -> void { + io(); + io(); +L P = pull(); +} + auto HuC6280::instruction_RMB(uint3 index) -> void { auto zeropage = operand(); io(); diff --git a/higan/sfc/cartridge/cartridge.cpp b/higan/sfc/cartridge/cartridge.cpp index eaadab74..0e4c6826 100644 --- a/higan/sfc/cartridge/cartridge.cpp +++ b/higan/sfc/cartridge/cartridge.cpp @@ -89,9 +89,9 @@ auto Cartridge::load() -> bool { auto Cartridge::loadGameBoy() -> bool { #if defined(SFC_SUPERGAMEBOY) //invoked from ICD2::load() - information.sha256 = GameBoy::interface->sha256(); - information.manifest.gameBoy = GameBoy::interface->manifest(); - information.title.gameBoy = GameBoy::interface->title(); + information.sha256 = GameBoy::cartridge.sha256(); + information.manifest.gameBoy = GameBoy::cartridge.manifest(); + information.title.gameBoy = GameBoy::cartridge.title(); loadGameBoy(BML::unserialize(information.manifest.gameBoy)); return true; #endif diff --git a/higan/sfc/coprocessor/icd2/icd2.cpp b/higan/sfc/coprocessor/icd2/icd2.cpp index 5f736b19..a40bfc44 100644 --- a/higan/sfc/coprocessor/icd2/icd2.cpp +++ b/higan/sfc/coprocessor/icd2/icd2.cpp @@ -6,6 +6,7 @@ ICD2 icd2; #if defined(SFC_SUPERGAMEBOY) +#include "platform.cpp" #include "interface.cpp" #include "io.cpp" #include "serialization.cpp" @@ -34,18 +35,14 @@ auto ICD2::init() -> void { } auto ICD2::load() -> bool { - bind = GameBoy::interface->bind; - hook = GameBoy::interface->hook; - GameBoy::interface->bind = this; - GameBoy::interface->hook = this; - GameBoy::interface->load(GameBoy::ID::SuperGameBoy); + GameBoy::superGameBoy = this; + GameBoy::system.load(&gameBoyInterface, GameBoy::System::Model::SuperGameBoy, cartridge.pathID()); return cartridge.loadGameBoy(); } auto ICD2::unload() -> void { - GameBoy::interface->unload(); - GameBoy::interface->bind = bind; - GameBoy::interface->hook = hook; + GameBoy::system.save(); + GameBoy::system.unload(); } auto ICD2::power() -> void { @@ -78,7 +75,31 @@ auto ICD2::power() -> void { } auto ICD2::reset() -> void { - //todo: same as power() but without re-creating the audio stream + auto frequency = system.colorburst() * 6.0; + create(ICD2::Enter, frequency / 5); + + r6003 = 0x00; + r6004 = 0xff; + r6005 = 0xff; + r6006 = 0xff; + r6007 = 0xff; + for(auto& r : r7000) r = 0x00; + mltReq = 0; + + for(auto& n : output) n = 0xff; + readBank = 0; + readAddress = 0; + writeBank = 0; + writeAddress = 0; + + packetSize = 0; + joypID = 3; + joyp15Lock = 0; + joyp14Lock = 0; + pulseLock = true; + + GameBoy::system.init(); + GameBoy::system.power(); } #endif diff --git a/higan/sfc/coprocessor/icd2/icd2.hpp b/higan/sfc/coprocessor/icd2/icd2.hpp index 3026d86d..faad0c37 100644 --- a/higan/sfc/coprocessor/icd2/icd2.hpp +++ b/higan/sfc/coprocessor/icd2/icd2.hpp @@ -1,6 +1,6 @@ #if defined(SFC_SUPERGAMEBOY) -struct ICD2 : Emulator::Interface::Bind, GameBoy::Interface::Hook, Thread { +struct ICD2 : Emulator::Platform, GameBoy::SuperGameBoyInterface, Thread { shared_pointer stream; static auto Enter() -> void; @@ -12,18 +12,15 @@ struct ICD2 : Emulator::Interface::Bind, GameBoy::Interface::Hook, Thread { auto power() -> void; auto reset() -> void; //software reset + //platform.cpp + auto audioSample(const double* samples, uint channels) -> void override; + auto inputPoll(uint port, uint device, uint id) -> int16 override; + //interface.cpp auto lcdScanline() -> void override; auto lcdOutput(uint2 color) -> void override; auto joypWrite(bool p15, bool p14) -> void override; - auto open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file override; - auto load(uint id, string name, string type) -> maybe override; - - auto videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void override; - auto audioSample(const double* samples, uint channels) -> void override; - auto inputPoll(uint port, uint device, uint id) -> int16 override; - //io.cpp auto readIO(uint24 addr, uint8 data) -> uint8; auto writeIO(uint24 addr, uint8 data) -> void; @@ -34,9 +31,6 @@ struct ICD2 : Emulator::Interface::Bind, GameBoy::Interface::Hook, Thread { uint revision; private: - Emulator::Interface::Bind* bind = nullptr; - GameBoy::Interface::Hook* hook = nullptr; - struct Packet { auto operator[](uint addr) -> uint8& { return data[addr & 15]; } uint8 data[16]; @@ -67,6 +61,8 @@ private: uint readAddress; uint writeBank; uint writeAddress; + + GameBoy::GameBoyInterface gameBoyInterface; }; #else diff --git a/higan/sfc/coprocessor/icd2/interface.cpp b/higan/sfc/coprocessor/icd2/interface.cpp index a4ca83c6..c453fc79 100644 --- a/higan/sfc/coprocessor/icd2/interface.cpp +++ b/higan/sfc/coprocessor/icd2/interface.cpp @@ -84,46 +84,3 @@ auto ICD2::joypWrite(bool p15, bool p14) -> void { if(++packetOffset < 16) return; packetLock = true; } - -auto ICD2::open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file { - //redirect system folder to cartridge folder: - //expects "GameBoy.sys"; but this would be "Super Famicom.sys"; redirect to "Super Game Boy.sfc/" - if(id == ID::System) id = cartridge.pathID(); - return interface->open(id, name, mode, required); -} - -auto ICD2::load(uint id, string name, string type) -> maybe { - return interface->load(id, name, type); -} - -auto ICD2::videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void { -} - -auto ICD2::audioSample(const double* samples, uint channels) -> void { - stream->write(samples); -} - -auto ICD2::inputPoll(uint port, uint device, uint id) -> int16 { - GameBoy::cpu.status.mltReq = joypID & mltReq; - - uint data = 0x00; - switch(joypID & mltReq) { - case 0: data = ~r6004; break; - case 1: data = ~r6005; break; - case 2: data = ~r6006; break; - case 3: data = ~r6007; break; - } - - switch((GameBoy::Input)id) { - case GameBoy::Input::Start: return (bool)(data & 0x80); - case GameBoy::Input::Select: return (bool)(data & 0x40); - case GameBoy::Input::B: return (bool)(data & 0x20); - case GameBoy::Input::A: return (bool)(data & 0x10); - case GameBoy::Input::Down: return (bool)(data & 0x08); - case GameBoy::Input::Up: return (bool)(data & 0x04); - case GameBoy::Input::Left: return (bool)(data & 0x02); - case GameBoy::Input::Right: return (bool)(data & 0x01); - } - - return 0; -} diff --git a/higan/sfc/coprocessor/icd2/io.cpp b/higan/sfc/coprocessor/icd2/io.cpp index 5155182d..5b4e885d 100644 --- a/higan/sfc/coprocessor/icd2/io.cpp +++ b/higan/sfc/coprocessor/icd2/io.cpp @@ -54,7 +54,7 @@ auto ICD2::writeIO(uint24 addr, uint8 data) -> void { //d1,d0: 0 = frequency divider (clock rate adjust) if(addr == 0x6003) { if((r6003 & 0x80) == 0x00 && (data & 0x80) == 0x80) { - reset(true); + reset(); } auto frequency = system.colorburst() * 6.0; switch(data & 3) { diff --git a/higan/sfc/coprocessor/icd2/platform.cpp b/higan/sfc/coprocessor/icd2/platform.cpp new file mode 100644 index 00000000..625b71d8 --- /dev/null +++ b/higan/sfc/coprocessor/icd2/platform.cpp @@ -0,0 +1,28 @@ +auto ICD2::audioSample(const double* samples, uint channels) -> void { + stream->write(samples); +} + +auto ICD2::inputPoll(uint port, uint device, uint id) -> int16 { + GameBoy::cpu.status.mltReq = joypID & mltReq; + + uint data = 0x00; + switch(joypID & mltReq) { + case 0: data = ~r6004; break; + case 1: data = ~r6005; break; + case 2: data = ~r6006; break; + case 3: data = ~r6007; break; + } + + switch((GameBoy::Input)id) { + case GameBoy::Input::Start: return (bool)(data & 0x80); + case GameBoy::Input::Select: return (bool)(data & 0x40); + case GameBoy::Input::B: return (bool)(data & 0x20); + case GameBoy::Input::A: return (bool)(data & 0x10); + case GameBoy::Input::Down: return (bool)(data & 0x08); + case GameBoy::Input::Up: return (bool)(data & 0x04); + case GameBoy::Input::Left: return (bool)(data & 0x02); + case GameBoy::Input::Right: return (bool)(data & 0x01); + } + + return 0; +} diff --git a/higan/target-tomoko/GNUmakefile b/higan/target-tomoko/GNUmakefile index 2b7dffc8..478d36f1 100644 --- a/higan/target-tomoko/GNUmakefile +++ b/higan/target-tomoko/GNUmakefile @@ -1,5 +1,5 @@ name := higan -#flags += -DSFC_SUPERGAMEBOY +flags += -DSFC_SUPERGAMEBOY include fc/GNUmakefile include sfc/GNUmakefile diff --git a/higan/ws/interface/interface.cpp b/higan/ws/interface/interface.cpp index b19f4a7b..c6ae68ec 100644 --- a/higan/ws/interface/interface.cpp +++ b/higan/ws/interface/interface.cpp @@ -6,4 +6,125 @@ Settings settings; #include "wonderswan.cpp" #include "wonderswan-color.cpp" +Interface::Interface() { + information.capability.states = true; + information.capability.cheats = true; + + Port hardwareHorizontalPort{ID::Port::HardwareHorizontal, "Hardware - Horizontal"}; + Port hardwareVerticalPort{ID::Port::HardwareVertical, "Hardware - Vertical"}; + + { Device device{ID::Device::Controls, "Controls"}; + device.inputs.append({0, "Y1"}); + device.inputs.append({0, "Y2"}); + device.inputs.append({0, "Y3"}); + device.inputs.append({0, "Y4"}); + device.inputs.append({0, "X1"}); + device.inputs.append({0, "X2"}); + device.inputs.append({0, "X3"}); + device.inputs.append({0, "X4"}); + device.inputs.append({0, "B"}); + device.inputs.append({0, "A"}); + device.inputs.append({0, "Start"}); + device.inputs.append({0, "Rotate"}); + hardwareHorizontalPort.devices.append(device); + hardwareVerticalPort.devices.append(device); + } + + ports.append(move(hardwareHorizontalPort)); + ports.append(move(hardwareVerticalPort)); +} + +auto Interface::manifest() -> string { + return cartridge.information.manifest; +} + +auto Interface::title() -> string { + return cartridge.information.title; +} + +auto Interface::videoSize() -> VideoSize { + return {224, 224}; +} + +auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { + uint w = 224; + uint h = 224; + uint m = min(width / w, height / h); + return {w * m, h * m}; +} + +auto Interface::videoFrequency() -> double { + return 3'072'000.0 / (159.0 * 256.0); //~75.47hz +} + +auto Interface::audioFrequency() -> double { + return 3'072'000.0; +} + +auto Interface::loaded() -> bool { + return system.loaded(); +} + +auto Interface::sha256() -> string { + return cartridge.information.sha256; +} + +auto Interface::save() -> void { + system.save(); +} + +auto Interface::unload() -> void { + save(); + system.unload(); +} + +auto Interface::power() -> void { + system.power(); +} + +auto Interface::run() -> void { + system.run(); +} + +auto Interface::serialize() -> serializer { + system.runToSave(); + return system.serialize(); +} + +auto Interface::unserialize(serializer& s) -> bool { + return system.unserialize(s); +} + +auto Interface::cheatSet(const string_vector& list) -> void { + cheat.assign(list); +} + +auto Interface::cap(const string& name) -> bool { + if(name == "Blur Emulation") return true; + if(name == "Color Emulation") return true; + return false; +} + +auto Interface::get(const string& name) -> any { + if(name == "Blur Emulation") return settings.blurEmulation; + if(name == "Color Emulation") return settings.colorEmulation; + return {}; +} + +auto Interface::set(const string& name, const any& value) -> bool { + if(name == "Blur Emulation" && value.is()) { + settings.blurEmulation = value.get(); + system.configureVideoEffects(); + return true; + } + + if(name == "Color Emulation" && value.is()) { + settings.colorEmulation = value.get(); + system.configureVideoPalette(); + return true; + } + + return false; +} + } diff --git a/higan/ws/interface/interface.hpp b/higan/ws/interface/interface.hpp index 0548a21d..db09511d 100644 --- a/higan/ws/interface/interface.hpp +++ b/higan/ws/interface/interface.hpp @@ -17,10 +17,8 @@ struct ID { };}; }; -struct WonderSwanInterface : Emulator::Interface { - using Emulator::Interface::load; - - WonderSwanInterface(); +struct Interface : Emulator::Interface { + Interface(); auto manifest() -> string override; auto title() -> string override; @@ -28,14 +26,11 @@ struct WonderSwanInterface : Emulator::Interface { auto videoSize() -> VideoSize override; auto videoSize(uint width, uint height, bool arc) -> VideoSize override; auto videoFrequency() -> double override; - auto videoColors() -> uint32; - auto videoColor(uint32 color) -> uint64; auto audioFrequency() -> double override; auto loaded() -> bool override; auto sha256() -> string override; - auto load(uint id) -> bool override; auto save() -> void override; auto unload() -> void override; @@ -52,39 +47,26 @@ struct WonderSwanInterface : Emulator::Interface { auto set(const string& name, const any& value) -> bool override; }; -struct WonderSwanColorInterface : Emulator::Interface { +struct WonderSwanInterface : Interface { + using Emulator::Interface::load; + + WonderSwanInterface(); + + auto videoColors() -> uint32 override; + auto videoColor(uint32 color) -> uint64 override; + + auto load(uint id) -> bool override; +}; + +struct WonderSwanColorInterface : Interface { using Emulator::Interface::load; WonderSwanColorInterface(); - auto manifest() -> string override; - auto title() -> string override; + auto videoColors() -> uint32 override; + auto videoColor(uint32 color) -> uint64 override; - auto videoSize() -> VideoSize override; - auto videoSize(uint width, uint height, bool arc) -> VideoSize override; - auto videoFrequency() -> double override; - auto videoColors() -> uint32; - auto videoColor(uint32 color) -> uint64; - - auto audioFrequency() -> double override; - - auto loaded() -> bool override; - auto sha256() -> string override; auto load(uint id) -> bool override; - auto save() -> void override; - auto unload() -> void override; - - auto power() -> void override; - auto run() -> void override; - - auto serialize() -> serializer override; - auto unserialize(serializer&) -> bool override; - - auto cheatSet(const string_vector&) -> void override; - - auto cap(const string& name) -> bool override; - auto get(const string& name) -> any override; - auto set(const string& name, const any& value) -> bool override; }; struct Settings { diff --git a/higan/ws/interface/wonderswan-color.cpp b/higan/ws/interface/wonderswan-color.cpp index c5291576..8c527c65 100644 --- a/higan/ws/interface/wonderswan-color.cpp +++ b/higan/ws/interface/wonderswan-color.cpp @@ -3,56 +3,7 @@ WonderSwanColorInterface::WonderSwanColorInterface() { information.name = "WonderSwan Color"; information.overscan = false; - information.capability.states = true; - information.capability.cheats = true; - media.append({ID::WonderSwanColor, "WonderSwan Color", "wsc"}); - - Port hardwareHorizontalPort{ID::Port::HardwareHorizontal, "Hardware - Horizontal"}; - Port hardwareVerticalPort{ID::Port::HardwareVertical, "Hardware - Vertical"}; - - { Device device{ID::Device::Controls, "Controls"}; - device.inputs.append({0, "Y1"}); - device.inputs.append({0, "Y2"}); - device.inputs.append({0, "Y3"}); - device.inputs.append({0, "Y4"}); - device.inputs.append({0, "X1"}); - device.inputs.append({0, "X2"}); - device.inputs.append({0, "X3"}); - device.inputs.append({0, "X4"}); - device.inputs.append({0, "B"}); - device.inputs.append({0, "A"}); - device.inputs.append({0, "Start"}); - device.inputs.append({0, "Rotate"}); - hardwareHorizontalPort.devices.append(device); - hardwareVerticalPort.devices.append(device); - } - - ports.append(move(hardwareHorizontalPort)); - ports.append(move(hardwareVerticalPort)); -} - -auto WonderSwanColorInterface::manifest() -> string { - return cartridge.information.manifest; -} - -auto WonderSwanColorInterface::title() -> string { - return cartridge.information.title; -} - -auto WonderSwanColorInterface::videoSize() -> VideoSize { - return {224, 224}; -} - -auto WonderSwanColorInterface::videoSize(uint width, uint height, bool arc) -> VideoSize { - uint w = 224; - uint h = 224; - uint m = min(width / w, height / h); - return {w * m, h * m}; -} - -auto WonderSwanColorInterface::videoFrequency() -> double { - return 3072000.0 / (159.0 * 256.0); //~75.47hz } auto WonderSwanColorInterface::videoColors() -> uint32 { @@ -80,77 +31,7 @@ auto WonderSwanColorInterface::videoColor(uint32 color) -> uint64 { return R << 32 | G << 16 | B << 0; } -auto WonderSwanColorInterface::audioFrequency() -> double { - return 3072000.0; -} - -auto WonderSwanColorInterface::loaded() -> bool { - return system.loaded(); -} - -auto WonderSwanColorInterface::sha256() -> string { - return cartridge.information.sha256; -} - auto WonderSwanColorInterface::load(uint id) -> bool { if(id == ID::WonderSwanColor) return system.load(this, Model::WonderSwanColor); return false; } - -auto WonderSwanColorInterface::save() -> void { - system.save(); -} - -auto WonderSwanColorInterface::unload() -> void { - save(); - system.unload(); -} - -auto WonderSwanColorInterface::power() -> void { - system.power(); -} - -auto WonderSwanColorInterface::run() -> void { - system.run(); -} - -auto WonderSwanColorInterface::serialize() -> serializer { - system.runToSave(); - return system.serialize(); -} - -auto WonderSwanColorInterface::unserialize(serializer& s) -> bool { - return system.unserialize(s); -} - -auto WonderSwanColorInterface::cheatSet(const string_vector& list) -> void { - cheat.assign(list); -} - -auto WonderSwanColorInterface::cap(const string& name) -> bool { - if(name == "Blur Emulation") return true; - if(name == "Color Emulation") return true; - return false; -} - -auto WonderSwanColorInterface::get(const string& name) -> any { - if(name == "Blur Emulation") return settings.blurEmulation; - if(name == "Color Emulation") return settings.colorEmulation; - return {}; -} - -auto WonderSwanColorInterface::set(const string& name, const any& value) -> bool { - if(name == "Blur Emulation" && value.is()) { - settings.blurEmulation = value.get(); - system.configureVideoEffects(); - return true; - } - - if(name == "Color Emulation" && value.is()) { - settings.colorEmulation = value.get(); - system.configureVideoPalette(); - return true; - } - - return false; -} diff --git a/higan/ws/interface/wonderswan.cpp b/higan/ws/interface/wonderswan.cpp index e9f78361..73c7b57d 100644 --- a/higan/ws/interface/wonderswan.cpp +++ b/higan/ws/interface/wonderswan.cpp @@ -3,57 +3,11 @@ WonderSwanInterface::WonderSwanInterface() { information.name = "WonderSwan"; information.overscan = false; - information.capability.states = true; - information.capability.cheats = true; - media.append({ID::WonderSwan, "WonderSwan", "ws"}); - - Port hardwareHorizontalPort{ID::Port::HardwareHorizontal, "Hardware - Horizontal"}; - Port hardwareVerticalPort{ID::Port::HardwareVertical, "Hardware - Vertical"}; - - { Device device{ID::Device::Controls, "Controls"}; - device.inputs.append({0, "Y1"}); - device.inputs.append({0, "Y2"}); - device.inputs.append({0, "Y3"}); - device.inputs.append({0, "Y4"}); - device.inputs.append({0, "X1"}); - device.inputs.append({0, "X2"}); - device.inputs.append({0, "X3"}); - device.inputs.append({0, "X4"}); - device.inputs.append({0, "B"}); - device.inputs.append({0, "A"}); - device.inputs.append({0, "Start"}); - device.inputs.append({0, "Rotate"}); - hardwareHorizontalPort.devices.append(device); - hardwareVerticalPort.devices.append(device); - } - - ports.append(move(hardwareHorizontalPort)); - ports.append(move(hardwareVerticalPort)); } -auto WonderSwanInterface::manifest() -> string { - return cartridge.information.manifest; -} - -auto WonderSwanInterface::title() -> string { - return cartridge.information.title; -} - -auto WonderSwanInterface::videoSize() -> VideoSize { - return {224, 224}; -} - -auto WonderSwanInterface::videoSize(uint width, uint height, bool arc) -> VideoSize { - uint w = 224; - uint h = 224; - uint m = min(width / w, height / h); - return {w * m, h * m}; -} - -auto WonderSwanInterface::videoFrequency() -> double { - return 3072000.0 / (159.0 * 256.0); //~75.47hz -} +//todo: this should be generating grayscale colors +//instead, the PPU is selecting grayscale colors from the color palette auto WonderSwanInterface::videoColors() -> uint32 { return 1 << 12; @@ -80,77 +34,7 @@ auto WonderSwanInterface::videoColor(uint32 color) -> uint64 { return R << 32 | G << 16 | B << 0; } -auto WonderSwanInterface::audioFrequency() -> double { - return 3072000.0; -} - -auto WonderSwanInterface::loaded() -> bool { - return system.loaded(); -} - -auto WonderSwanInterface::sha256() -> string { - return cartridge.information.sha256; -} - auto WonderSwanInterface::load(uint id) -> bool { if(id == ID::WonderSwan) return system.load(this, Model::WonderSwan); return false; } - -auto WonderSwanInterface::save() -> void { - system.save(); -} - -auto WonderSwanInterface::unload() -> void { - save(); - system.unload(); -} - -auto WonderSwanInterface::power() -> void { - system.power(); -} - -auto WonderSwanInterface::run() -> void { - system.run(); -} - -auto WonderSwanInterface::serialize() -> serializer { - system.runToSave(); - return system.serialize(); -} - -auto WonderSwanInterface::unserialize(serializer& s) -> bool { - return system.unserialize(s); -} - -auto WonderSwanInterface::cheatSet(const string_vector& list) -> void { - cheat.assign(list); -} - -auto WonderSwanInterface::cap(const string& name) -> bool { - if(name == "Blur Emulation") return true; - if(name == "Color Emulation") return true; - return false; -} - -auto WonderSwanInterface::get(const string& name) -> any { - if(name == "Blur Emulation") return settings.blurEmulation; - if(name == "Color Emulation") return settings.colorEmulation; - return {}; -} - -auto WonderSwanInterface::set(const string& name, const any& value) -> bool { - if(name == "Blur Emulation" && value.is()) { - settings.blurEmulation = value.get(); - system.configureVideoEffects(); - return true; - } - - if(name == "Color Emulation" && value.is()) { - settings.colorEmulation = value.get(); - system.configureVideoPalette(); - return true; - } - - return false; -}