From dde9b4c2c72e84edb4329dbef2e9abf134901d91 Mon Sep 17 00:00:00 2001 From: byuu <2107894+byuu@users.noreply.github.com> Date: Tue, 31 Dec 2019 10:22:31 +0900 Subject: [PATCH] v113.5 It seems auto-joypad poll timing is needed for most games. So that's back in as before. Instead, I added an override for Taikyoku Igo - Goliath specifically, until auto-joypad emulation can be improved further. --- bsnes/emulator/emulator.hpp | 2 +- bsnes/sfc/cpu/timing.cpp | 110 +++++++++++++------------- bsnes/sfc/interface/configuration.cpp | 1 + bsnes/sfc/interface/configuration.hpp | 1 + bsnes/target-bsnes/program/hacks.cpp | 4 + 5 files changed, 63 insertions(+), 55 deletions(-) diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp index e367da1c..79499f46 100644 --- a/bsnes/emulator/emulator.hpp +++ b/bsnes/emulator/emulator.hpp @@ -29,7 +29,7 @@ using namespace nall; namespace Emulator { static const string Name = "bsnes"; - static const string Version = "113.4"; + static const string Version = "113.5"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org"; diff --git a/bsnes/sfc/cpu/timing.cpp b/bsnes/sfc/cpu/timing.cpp index da6f668c..f8e2fa10 100644 --- a/bsnes/sfc/cpu/timing.cpp +++ b/bsnes/sfc/cpu/timing.cpp @@ -202,69 +202,71 @@ auto CPU::dmaEdge() -> void { //called every 256 clocks; see CPU::step() auto CPU::joypadEdge() -> void { - //todo: auto-joypad polling should poll one bit every 256 clock cycles, - //but it causes too many issues in games, due to incomplete emulation: - //Nuke (PD): inputs do not work (unless clearing $421x to $00) - //Taikyoku Igo - Goliath: start button not acknowledged (unless clearing $421x to $ff) + //fast joypad polling is a hack to work around edge cases not currently emulated in auto-joypad polling. + //below is a list of games that have had input issues over the years. + //Nuke (PD): inputs do not work + //Super Conflict: sends random inputs even with no buttons pressed + //Super Star Wars: Start button auto-unpauses + //Taikyoku Igo - Goliath: start button not acknowledged //Tatakae Genshijin 2: attract sequence ends early - //Williams Arcade's Greatest Hits: verifies io.joy# should be set to 0 and not ~0 + //Williams Arcade's Greatest Hits: inputs fire on their own //World Masters Golf: inputs do not work at all - //immediate polling: - if(!status.autoJoypadCounter && vcounter() >= ppu.vdisp()) { - controllerPort1.device->latch(1); - controllerPort2.device->latch(1); - controllerPort1.device->latch(0); - controllerPort2.device->latch(0); + if(configuration.hacks.cpu.fastJoypadPolling) { + //Taikyoku Igo - Goliath + if(!status.autoJoypadCounter && vcounter() >= ppu.vdisp()) { + controllerPort1.device->latch(1); + controllerPort2.device->latch(1); + controllerPort1.device->latch(0); + controllerPort2.device->latch(0); - io.joy1 = 0; - io.joy2 = 0; - io.joy3 = 0; - io.joy4 = 0; + io.joy1 = 0; + io.joy2 = 0; + io.joy3 = 0; + io.joy4 = 0; - for(uint index : range(16)) { - uint2 port0 = controllerPort1.device->data(); - uint2 port1 = controllerPort2.device->data(); + for(uint index : range(16)) { + uint2 port0 = controllerPort1.device->data(); + uint2 port1 = controllerPort2.device->data(); - io.joy1 = io.joy1 << 1 | port0.bit(0); - io.joy2 = io.joy2 << 1 | port1.bit(0); - io.joy3 = io.joy3 << 1 | port0.bit(1); - io.joy4 = io.joy4 << 1 | port1.bit(1); - } - - status.autoJoypadCounter = 16; - } - return; - - //disabled cycle-timed polling: - if(vcounter() >= ppu.vdisp()) { - //cache enable state at first iteration - if(status.autoJoypadCounter == 0) status.autoJoypadLatch = io.autoJoypadPoll; - status.autoJoypadActive = status.autoJoypadCounter <= 15; - - if(status.autoJoypadActive && status.autoJoypadLatch) { - if(status.autoJoypadCounter == 0) { - controllerPort1.device->latch(1); - controllerPort2.device->latch(1); - controllerPort1.device->latch(0); - controllerPort2.device->latch(0); - - //shift registers are cleared at start of auto joypad polling - io.joy1 = 0; - io.joy2 = 0; - io.joy3 = 0; - io.joy4 = 0; + io.joy1 = io.joy1 << 1 | port0.bit(0); + io.joy2 = io.joy2 << 1 | port1.bit(0); + io.joy3 = io.joy3 << 1 | port0.bit(1); + io.joy4 = io.joy4 << 1 | port1.bit(1); } - uint2 port0 = controllerPort1.device->data(); - uint2 port1 = controllerPort2.device->data(); - - io.joy1 = io.joy1 << 1 | port0.bit(0); - io.joy2 = io.joy2 << 1 | port1.bit(0); - io.joy3 = io.joy3 << 1 | port0.bit(1); - io.joy4 = io.joy4 << 1 | port1.bit(1); + status.autoJoypadCounter = 16; } + } else { + if(vcounter() >= ppu.vdisp()) { + //cache enable state at first iteration + if(status.autoJoypadCounter == 0) status.autoJoypadLatch = io.autoJoypadPoll; + status.autoJoypadActive = status.autoJoypadCounter <= 15; - status.autoJoypadCounter++; + if(status.autoJoypadActive && status.autoJoypadLatch) { + if(status.autoJoypadCounter == 0) { + controllerPort1.device->latch(1); + controllerPort2.device->latch(1); + controllerPort1.device->latch(0); + controllerPort2.device->latch(0); + + //shift registers are cleared at start of auto joypad polling + io.joy1 = 0; + io.joy2 = 0; + io.joy3 = 0; + io.joy4 = 0; + } + + uint2 port0 = controllerPort1.device->data(); + uint2 port1 = controllerPort2.device->data(); + + io.joy1 = io.joy1 << 1 | port0.bit(0); + io.joy2 = io.joy2 << 1 | port1.bit(0); + io.joy3 = io.joy3 << 1 | port0.bit(1); + io.joy4 = io.joy4 << 1 | port1.bit(1); + } + + status.autoJoypadCounter++; + } } } diff --git a/bsnes/sfc/interface/configuration.cpp b/bsnes/sfc/interface/configuration.cpp index 769b8b0c..c689ad11 100644 --- a/bsnes/sfc/interface/configuration.cpp +++ b/bsnes/sfc/interface/configuration.cpp @@ -21,6 +21,7 @@ auto Configuration::process(Markup::Node document, bool load) -> void { bind(text, "Hacks/Entropy", hacks.entropy); bind(natural, "Hacks/CPU/Overclock", hacks.cpu.overclock); bind(boolean, "Hacks/CPU/FastMath", hacks.cpu.fastMath); + bind(boolean, "Hacks/CPU/FastJoypadPolling", hacks.cpu.fastJoypadPolling); bind(boolean, "Hacks/PPU/Fast", hacks.ppu.fast); bind(boolean, "Hacks/PPU/Deinterlace", hacks.ppu.deinterlace); bind(natural, "Hacks/PPU/RenderCycle", hacks.ppu.renderCycle); diff --git a/bsnes/sfc/interface/configuration.hpp b/bsnes/sfc/interface/configuration.hpp index 5f4bd683..bcf6345c 100644 --- a/bsnes/sfc/interface/configuration.hpp +++ b/bsnes/sfc/interface/configuration.hpp @@ -33,6 +33,7 @@ struct Configuration { struct CPU { uint overclock = 100; bool fastMath = false; + bool fastJoypadPolling = false; } cpu; struct PPU { bool fast = true; diff --git a/bsnes/target-bsnes/program/hacks.cpp b/bsnes/target-bsnes/program/hacks.cpp index b0e89472..4ce477f6 100644 --- a/bsnes/target-bsnes/program/hacks.cpp +++ b/bsnes/target-bsnes/program/hacks.cpp @@ -1,5 +1,6 @@ auto Program::hackCompatibility() -> void { string entropy = settings.emulator.hack.entropy; + bool fastJoypadPolling = false; bool fastPPU = settings.emulator.hack.ppu.fast; bool fastPPUNoSpriteLimit = settings.emulator.hack.ppu.noSpriteLimit; bool fastDSP = settings.emulator.hack.dsp.fast; @@ -9,6 +10,8 @@ auto Program::hackCompatibility() -> void { auto title = superFamicom.title; auto region = superFamicom.region; + if(title == "TAIKYOKU-IGO Goliath") fastJoypadPolling = true; + //relies on mid-scanline rendering techniques if(title == "AIR STRIKE PATROL" || title == "DESERT FIGHTER") fastPPU = false; @@ -54,6 +57,7 @@ auto Program::hackCompatibility() -> void { } emulator->configure("Hacks/Entropy", entropy); + emulator->configure("Hacks/CPU/FastJoypadPolling", fastJoypadPolling); emulator->configure("Hacks/PPU/Fast", fastPPU); emulator->configure("Hacks/PPU/NoSpriteLimit", fastPPUNoSpriteLimit); emulator->configure("Hacks/PPU/RenderCycle", renderCycle);