diff --git a/Assets/dll/bsnes.wbx.zst b/Assets/dll/bsnes.wbx.zst index b7f9a2da65..a43f9bba32 100644 Binary files a/Assets/dll/bsnes.wbx.zst and b/Assets/dll/bsnes.wbx.zst differ diff --git a/waterbox/bsnescore/bsnes/emulator/emulator.hpp b/waterbox/bsnescore/bsnes/emulator/emulator.hpp index 71cd809a5f..1ea50f351b 100644 --- a/waterbox/bsnescore/bsnes/emulator/emulator.hpp +++ b/waterbox/bsnescore/bsnes/emulator/emulator.hpp @@ -35,7 +35,7 @@ namespace Emulator { static const string Website = "https://bsnes.dev"; //incremented only when serialization format changes - static const string SerializerVersion = "115"; + static const string SerializerVersion = "115.1"; namespace Constants { namespace Colorburst { diff --git a/waterbox/bsnescore/bsnes/sfc/cpu/cpu.hpp b/waterbox/bsnescore/bsnes/sfc/cpu/cpu.hpp index e636f8e8e5..878a094173 100644 --- a/waterbox/bsnescore/bsnes/sfc/cpu/cpu.hpp +++ b/waterbox/bsnescore/bsnes/sfc/cpu/cpu.hpp @@ -121,6 +121,12 @@ private: bool hdmaMode = 0; //0 = init, 1 = run uint autoJoypadCounter = 33; //state machine; 4224 / 128 = 33 (inactive) + + uint2 autoJoypadPort1 = 0; + uint2 autoJoypadPort2 = 0; + + bool cpuLatch = false; + bool autoJoypadLatch = false; } status; struct IO { diff --git a/waterbox/bsnescore/bsnes/sfc/cpu/io.cpp b/waterbox/bsnescore/bsnes/sfc/cpu/io.cpp index 4fd0901cbe..2b168c9357 100644 --- a/waterbox/bsnescore/bsnes/sfc/cpu/io.cpp +++ b/waterbox/bsnescore/bsnes/sfc/cpu/io.cpp @@ -132,13 +132,22 @@ auto CPU::writeCPU(uint addr, uint8 data) -> void { //bit 0 is shared between JOYSER0 and JOYSER1: //strobing $4016.d0 affects both controller port latches. //$4017 bit 0 writes are ignored. - controllerPort1.device->latch(data & 1); - controllerPort2.device->latch(data & 1); + status.cpuLatch = data & 1; + controllerPort1.device->latch(status.autoJoypadLatch | status.cpuLatch); + controllerPort2.device->latch(status.autoJoypadLatch | status.cpuLatch); return; case 0x4200: //NMITIMEN io.autoJoypadPoll = data & 1; - if(!io.autoJoypadPoll) status.autoJoypadCounter = 33; // Disable auto-joypad read + if(status.autoJoypadCounter == 0) { + // allow controller latches during this time + status.autoJoypadLatch = io.autoJoypadPoll; + controllerPort1.device->latch(status.autoJoypadLatch | status.cpuLatch); + controllerPort2.device->latch(status.autoJoypadLatch | status.cpuLatch); + } else if (!io.autoJoypadPoll && status.autoJoypadCounter >= 2) { + status.autoJoypadCounter = 33; + } + nmitimenUpdate(data); return; diff --git a/waterbox/bsnescore/bsnes/sfc/cpu/serialization.cpp b/waterbox/bsnescore/bsnes/sfc/cpu/serialization.cpp index 7a1c240100..ec6f2e399c 100644 --- a/waterbox/bsnescore/bsnes/sfc/cpu/serialization.cpp +++ b/waterbox/bsnescore/bsnes/sfc/cpu/serialization.cpp @@ -45,6 +45,12 @@ auto CPU::serialize(serializer& s) -> void { s.integer(status.autoJoypadCounter); + s.integer(status.autoJoypadPort1); + s.integer(status.autoJoypadPort2); + + s.boolean(status.cpuLatch); + s.boolean(status.autoJoypadLatch); + s.integer(io.wramAddress); s.boolean(io.hirqEnable); diff --git a/waterbox/bsnescore/bsnes/sfc/cpu/timing.cpp b/waterbox/bsnescore/bsnes/sfc/cpu/timing.cpp index d0e64022f6..333f427803 100644 --- a/waterbox/bsnescore/bsnes/sfc/cpu/timing.cpp +++ b/waterbox/bsnescore/bsnes/sfc/cpu/timing.cpp @@ -211,45 +211,54 @@ auto CPU::dmaEdge() -> void { //called every 128 clocks from inside the CPU::stepOnce() function auto CPU::joypadEdge() -> void { - //it is not yet confirmed if polling can be stopped early and/or (re)started later - if(!io.autoJoypadPoll) return; - - if(vcounter() == ppu.vdisp() && hcounter() >= 130 && hcounter() <= 256) { + if(vcounter() == ppu.vdisp() && (counter.cpu & 255) == 0 && hcounter() >= 130 && hcounter() <= 384) { //begin new polling sequence status.autoJoypadCounter = 0; - } + } else { + //stop after polling has been completed for this frame + if(status.autoJoypadCounter >= 33) return; - //stop after polling has been completed for this frame - if(status.autoJoypadCounter >= 33) return; + status.autoJoypadCounter++; + } if(status.autoJoypadCounter == 0) { //latch controller states on the first polling cycle - controllerPort1.device->latch(1); - controllerPort2.device->latch(1); + status.autoJoypadLatch = io.autoJoypadPoll; + controllerPort1.device->latch(status.autoJoypadLatch | status.cpuLatch); + controllerPort2.device->latch(status.autoJoypadLatch | status.cpuLatch); + if(io.autoJoypadPoll) { + //shift registers are cleared to zero at start of auto-joypad polling + io.joy1 = 0; + io.joy2 = 0; + io.joy3 = 0; + io.joy4 = 0; + } } if(status.autoJoypadCounter == 1) { //release latch and begin reading on the second cycle - controllerPort1.device->latch(0); - controllerPort2.device->latch(0); - - //shift registers are cleared to zero at start of auto-joypad polling - io.joy1 = 0; - io.joy2 = 0; - io.joy3 = 0; - io.joy4 = 0; + status.autoJoypadLatch = 0; + controllerPort1.device->latch(status.autoJoypadLatch | status.cpuLatch); + controllerPort2.device->latch(status.autoJoypadLatch | status.cpuLatch); } - if(status.autoJoypadCounter >= 2 && !(status.autoJoypadCounter & 1)) { + if(status.autoJoypadCounter != 1 && !io.autoJoypadPoll) { + // if auto-joypad polling is disabled at this point skip the rest of the polling + status.autoJoypadCounter = 33; + return; + } + + if(status.autoJoypadCounter >= 2) { //sixteen bits are shifted into joy{1-4}, one bit per 256 clocks - 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); + //the bits are read on one 128-clock cycle and written on the next + if ((status.autoJoypadCounter & 1) == 0) { + status.autoJoypadPort1 = controllerPort1.device->data(); + status.autoJoypadPort2 = controllerPort2.device->data(); + } else { + io.joy1 = io.joy1 << 1 | status.autoJoypadPort1.bit(0); + io.joy2 = io.joy2 << 1 | status.autoJoypadPort2.bit(0); + io.joy3 = io.joy3 << 1 | status.autoJoypadPort1.bit(1); + io.joy4 = io.joy4 << 1 | status.autoJoypadPort2.bit(1); + } } - - status.autoJoypadCounter++; } diff --git a/waterbox/bsnescore/bsnes/sfc/ppu/background.cpp b/waterbox/bsnescore/bsnes/sfc/ppu/background.cpp index e9d3dcb3b9..7f04726c34 100644 --- a/waterbox/bsnescore/bsnes/sfc/ppu/background.cpp +++ b/waterbox/bsnescore/bsnes/sfc/ppu/background.cpp @@ -61,7 +61,7 @@ auto PPU::Background::fetchNameTable() -> void { if(!(hlookup & 0x8000)) { hoffset = hpixel + (hlookup & ~7) + (hscroll & 7); } else { - voffset = vpixel + (vlookup); + voffset = vpixel + (hlookup); } } } else {