diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp index f9b771d3..e367da1c 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.3"; + static const string Version = "113.4"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org"; diff --git a/bsnes/heuristics/super-famicom.cpp b/bsnes/heuristics/super-famicom.cpp index 7c1507eb..0101a246 100644 --- a/bsnes/heuristics/super-famicom.cpp +++ b/bsnes/heuristics/super-famicom.cpp @@ -266,6 +266,9 @@ auto SuperFamicom::board() const -> string { if(headerAddress == 0x40ffb0) mode = "EXHIROM-"; } + //this game's title ovewrites the map mode with '!' (0x21), but is a LOROM game + if(title() == "YUYU NO QUIZ DE GO!GO") mode = "LOROM-"; + if(mode == "LOROM-" && headerAddress == 0x407fb0) mode = "EXLOROM-"; bool epsonRTC = false; diff --git a/bsnes/sfc/cpu/timing.cpp b/bsnes/sfc/cpu/timing.cpp index d4f713bd..da6f668c 100644 --- a/bsnes/sfc/cpu/timing.cpp +++ b/bsnes/sfc/cpu/timing.cpp @@ -5,11 +5,7 @@ auto CPU::dmaCounter() const -> uint { //joypad auto-poll clock divider auto CPU::joypadCounter() const -> uint { - //todo: this should be &255, 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) - //Tatakae Genshijin 2: attract sequence ends early - return counter.cpu & 31; + return counter.cpu & 255; } auto CPU::stepOnce() -> void { @@ -206,6 +202,41 @@ 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) + //Tatakae Genshijin 2: attract sequence ends early + //Williams Arcade's Greatest Hits: verifies io.joy# should be set to 0 and not ~0 + //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); + + 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(); + + 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; @@ -218,11 +249,11 @@ auto CPU::joypadEdge() -> void { controllerPort1.device->latch(0); controllerPort2.device->latch(0); - //shift registers are flushed at start of auto joypad polling - io.joy1 = ~0; - io.joy2 = ~0; - io.joy3 = ~0; - io.joy4 = ~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(); diff --git a/bsnes/sfc/ppu-fast/ppu.cpp b/bsnes/sfc/ppu-fast/ppu.cpp index 474d63e4..0ebb3450 100644 --- a/bsnes/sfc/ppu-fast/ppu.cpp +++ b/bsnes/sfc/ppu-fast/ppu.cpp @@ -94,6 +94,15 @@ auto PPU::main() -> void { auto PPU::scanline() -> void { if(vcounter() == 0) { + if(latch.overscan && !io.overscan) { + //when disabling overscan, clear the overscan area that won't be rendered to: + for(uint y = 1; y <= 240; y++) { + if(y >= 8 && y <= 231) continue; + auto output = ppu.output + y * 1024; + memory::fill(output, 1024); + } + } + ppubase.display.interlace = io.interlace; ppubase.display.overscan = io.overscan; latch.overscan = io.overscan; diff --git a/bsnes/sfc/ppu/main.cpp b/bsnes/sfc/ppu/main.cpp index be5f8a43..d2330d7c 100644 --- a/bsnes/sfc/ppu/main.cpp +++ b/bsnes/sfc/ppu/main.cpp @@ -1,5 +1,13 @@ auto PPU::main() -> void { if(vcounter() == 0) { + if(display.overscan && !io.overscan) { + //when disabling overscan, clear the overscan area that won't be rendered to: + for(uint y = 1; y <= 240; y++) { + if(y >= 8 && y <= 231) continue; + auto output = ppu.output + y * 1024; + memory::fill(output, 1024); + } + } display.interlace = io.interlace; display.overscan = io.overscan; bg1.frame(); diff --git a/bsnes/target-bsnes/program/hacks.cpp b/bsnes/target-bsnes/program/hacks.cpp index 4f831525..b0e89472 100644 --- a/bsnes/target-bsnes/program/hacks.cpp +++ b/bsnes/target-bsnes/program/hacks.cpp @@ -15,6 +15,13 @@ auto Program::hackCompatibility() -> void { //stage 2 uses pseudo-hires in a way that's not compatible with the scanline-based renderer if(title == "SFC クレヨンシンチャン") fastPPU = false; + //title screen game select (after choosing a game) changes OAM tiledata address mid-frame + //this is only supported by the cycle-based PPU renderer + if(title == "Winter olympics") fastPPU = false; + + //title screen shows remnants of the flag after choosing a language with the scanline-based renderer + if(title == "WORLD CUP STRIKER") fastPPU = false; + //relies on cycle-accurate writes to the echo buffer if(title == "KOUSHIEN_2") fastDSP = false;