From ee7662a8beca153b8f6cc50dd50da2753b27e9d9 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Thu, 26 Jan 2017 12:06:06 +1100 Subject: [PATCH] Update to v102r04 release. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit byuu says: Changelog: - Super Game Boy support is functional once again - new GameBoy::SuperGameBoyInterface class - system.(dmg,cgb,sgb) is now Model::(Super)GameBoy(Color) ala the PC Engine - merged WonderSwanInterface, WonderSwanColorInterface shared functions to WonderSwan::Interface - merged GameBoyInterface, GameBoyColorInterface shared functions to GameBoy::Interface - Interface::unload() now calls Interface::save() for Master System, Game Gear, Mega Drive, PC Engine, SuperGrafx - PCE: emulated PCE-CD backup RAM; stored per-game as save.ram (2KiB file) - this means you can now save your progress in games like Neutopia - the PCE-CD I/O registers like BRAM write protect are not emulated yet - PCE: IRQ sources now hold the IRQ line state, instead of the CPU holding it - this fixes most SuperGrafx games, which were fighting over the VDC IRQ line previously - PCE: CPU I/O $14xx should return the pending IRQ bits even if IRQs are disabled - PCE: VCE and the VDCs now synchronize to each other; fixes pixel widths in all games - PCE: greatly increased the accuracy of the VPC priority selection code (windows may be buggy still) - HuC6280: PLA, PLX, PLY should set Z, N flags; fixes many game bugs [Jonas Quinn] The big thing I wanted to do was enslave the VDC(s) to the VCE. But unfortunately, I forgot about the asynchronous DMA channels that each VDC supports, so this isn't going to be possible I'm afraid. In the most demanding case, Daimakaimura in-game, we're looking at 85fps on my Xeon E3 1276v3. So ... not great, and we don't even have sound connected yet. We are going to have to profile and optimize this code once sound emulation and save states are in. Basically, think of it like this: the VCE, VDC0, and VDC1 all have the same overhead, scheduling wise (which is the bulk of the performance loss) as the dot-renderer for the SNES core. So it's like there's three bsnes-accuracy PPU threads running just for video. ----- Oh, just a fair warning ... the hooks for the SGB are a work in progress. If anyone is working on higan or a fork and want to do something similar to it, don't use it as a template, at least not yet. Right now, higan looks like this: - Emulator::Video handles the platform→videoRefresh calls - Emulator::Audio handles the platform→audioSample calls - each core hard-codes the platform→inputPoll, inputRumble calls - each core hard-codes calls to path, open, load to process files - dipSettings and notify are specialty hacks, neither are even hooked up right now to anything With the SGB, it's an emulation core inside an emulation core, so ideally you want to hook all of those functions. Emulator::Video and Emulator::Audio aren't really abstractions over that, as the GB core calls them and we have to special case not calling them in SGB mode. The path, open, load can be implemented without hooks, thanks to the UI only using one instance of Emulator::Platform for all cores. All we have to do is override the folder path ID for the "Game Boy.sys" folder, so that it picks "Super Game Boy.sfc/" and loads its boot ROM instead. That's just a simple argument to GameBoy::System::load() and we're done. dipSettings, notify and inputRumble don't matter. But we do also have to hook inputPoll as well. The nice idea would be for SuperFamicom::ICD2 to inherit from Emulator::Platform and provide the desired functions that we need to overload. After that, we'd just need the GB core to keep an abstraction over the global Emulator::platform\* handle, to select between the UI version and the SFC::ICD2 version. However ... that doesn't work because of Emulator::Video and Emulator::Audio. They would also have to gain an abstraction over Emulator::platform\*, and even worse ... you'd have to constantly swap between the two so that the SFC core uses the UI, and the GB core uses the ICD2. And so, for right now, I'm checking Model::SuperGameBoy() -> bool everywhere, and choosing between the UI and ICD2 targets that way. And as such, the ICD2 doesn't really need Emulator::Platform inheritance, although it certainly could do that and just use the functions it needs. But the SGB is even weirder, because we need additional new signals beyond just Emulator::Platform, like joypWrite(), etc. I'd also like to work on the Emulator::Stream for the SGB core. I don't see why we can't have the GB core create its own stream, and let the ICD2 just use that instead. We just have to be careful about the ICD2's CPU soft reset function, to make sure the GB core's Stream object remains valid. What I think that needs is a way to release an Emulator::Stream individually, rather than calling Emulator::Audio::reset() to do it. They are shared\_pointer objects, so I think if I added a destructor function to remove it from Emulator::Audio::streams, then that should work. --- higan/emulator/emulator.hpp | 2 +- higan/gb/apu/apu.cpp | 8 +- higan/gb/apu/sequencer.cpp | 8 +- higan/gb/apu/wave.cpp | 6 +- higan/gb/cartridge/cartridge.cpp | 34 +++---- higan/gb/cartridge/cartridge.hpp | 2 +- higan/gb/cpu/cpu.cpp | 2 +- higan/gb/cpu/io.cpp | 25 ++--- higan/gb/cpu/timing.cpp | 2 +- higan/gb/gb.hpp | 6 ++ higan/gb/interface/game-boy-color.cpp | 114 +-------------------- higan/gb/interface/game-boy.cpp | 114 +-------------------- higan/gb/interface/interface.cpp | 115 +++++++++++++++++++++ higan/gb/interface/interface.hpp | 103 ++++++++----------- higan/gb/ppu/dmg.cpp | 2 +- higan/gb/ppu/io.cpp | 2 +- higan/gb/ppu/ppu.cpp | 8 +- higan/gb/system/system.cpp | 55 ++++++++--- higan/gb/system/system.hpp | 22 ++--- higan/gb/system/video.cpp | 4 +- higan/md/interface/interface.cpp | 1 + higan/ms/interface/game-gear.cpp | 59 ----------- higan/ms/interface/interface.cpp | 62 ++++++++++++ higan/ms/interface/interface.hpp | 44 +++------ higan/ms/interface/master-system.cpp | 59 ----------- higan/pce/cartridge/cartridge.cpp | 1 + higan/pce/cpu/cpu.cpp | 1 + higan/pce/cpu/cpu.hpp | 23 ++--- higan/pce/cpu/io.cpp | 29 +++++- higan/pce/cpu/irq.cpp | 30 ++---- higan/pce/cpu/memory.cpp | 23 +++++ higan/pce/cpu/timer.cpp | 2 +- higan/pce/interface/interface.cpp | 5 + higan/pce/interface/interface.hpp | 3 +- higan/pce/interface/pc-engine.cpp | 2 +- higan/pce/interface/supergrafx.cpp | 2 +- higan/pce/pce.hpp | 5 +- higan/pce/system/system.cpp | 9 +- higan/pce/system/system.hpp | 13 ++- higan/pce/vce/vce.cpp | 2 + higan/pce/vdc/irq.cpp | 5 +- higan/pce/vdc/vdc.cpp | 1 + higan/pce/vdc/vdc.hpp | 3 + higan/pce/vpc/vpc.cpp | 57 ++++++----- higan/pce/vpc/vpc.hpp | 2 +- higan/processor/huc6280/huc6280.hpp | 3 +- higan/processor/huc6280/instruction.cpp | 2 +- higan/processor/huc6280/instructions.cpp | 14 +-- higan/sfc/cartridge/cartridge.cpp | 6 +- higan/sfc/coprocessor/icd2/icd2.cpp | 39 ++++++-- higan/sfc/coprocessor/icd2/icd2.hpp | 18 ++-- higan/sfc/coprocessor/icd2/interface.cpp | 43 -------- higan/sfc/coprocessor/icd2/io.cpp | 2 +- higan/sfc/coprocessor/icd2/platform.cpp | 28 ++++++ higan/target-tomoko/GNUmakefile | 2 +- higan/ws/interface/interface.cpp | 121 +++++++++++++++++++++++ higan/ws/interface/interface.hpp | 50 +++------- higan/ws/interface/wonderswan-color.cpp | 119 ---------------------- higan/ws/interface/wonderswan.cpp | 120 +--------------------- 59 files changed, 697 insertions(+), 947 deletions(-) create mode 100644 higan/pce/cpu/memory.cpp create mode 100644 higan/sfc/coprocessor/icd2/platform.cpp 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; -}