From 4cfbbeadc3abe3e3911f7f59ce57b715edc76563 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Wed, 25 Oct 2017 14:18:34 +0300 Subject: [PATCH] Bus fixes: Do not update MDR on read from CPU MMIO space Also, updates the controller read timings to be more accurate. --- snes/config/config.cpp | 1 + snes/config/config.hpp | 1 + snes/cpu/cpu.cpp | 2 + snes/cpu/memory/memory.cpp | 26 +++++++- snes/cpu/mmio/mmio.cpp | 14 +++- snes/cpu/timing/joypad.cpp | 132 ++++++++++++++++++++++++++++++++----- snes/cpu/timing/timing.cpp | 11 ++-- snes/cpu/timing/timing.hpp | 3 +- snes/snes.hpp | 1 + 9 files changed, 166 insertions(+), 25 deletions(-) diff --git a/snes/config/config.cpp b/snes/config/config.cpp index 19831370..8dcfd7e8 100755 --- a/snes/config/config.cpp +++ b/snes/config/config.cpp @@ -15,6 +15,7 @@ Configuration::Configuration() { cpu.pal_frequency = 21281370; cpu.wram_init_value = 0x55; cpu.alt_poll_timings = false; + cpu.bus_fixes = false; smp.ntsc_frequency = 24607104; //32040.5 * 768 smp.pal_frequency = 24607104; diff --git a/snes/config/config.hpp b/snes/config/config.hpp index 68fe0bde..d8577e39 100755 --- a/snes/config/config.hpp +++ b/snes/config/config.hpp @@ -14,6 +14,7 @@ struct Configuration { unsigned pal_frequency; unsigned wram_init_value; bool alt_poll_timings; + bool bus_fixes; } cpu; struct SMP { diff --git a/snes/cpu/cpu.cpp b/snes/cpu/cpu.cpp index e11fc882..5e8e3137 100755 --- a/snes/cpu/cpu.cpp +++ b/snes/cpu/cpu.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #define CPU_CPP namespace SNES { diff --git a/snes/cpu/memory/memory.cpp b/snes/cpu/memory/memory.cpp index 31f82c31..df439c22 100755 --- a/snes/cpu/memory/memory.cpp +++ b/snes/cpu/memory/memory.cpp @@ -14,10 +14,32 @@ uint8 CPU::op_read(uint32 addr, bool exec) { status.clock_count = speed(addr); dma_edge(); add_clocks(status.clock_count - 4); - regs.mdr = bus.read(addr, exec); + //MDR presents the state held by parasitic capacitance of the external bus. + //This bus is not affected by reads from CPU-internal registers, only if + //some external device responds. SDD1 does hook some of these addresses, but + //passes read straight through, as expected (as the CPU probably won't + //monitor if external device responds, even if it broadcasts a read). + // + //We use 4000-43FF as CPU register range, and not 4000-437F it likely is + //for quickness of checking. This will only affect things if some device + //tries to map the 4380-43FF range (that device will still work correctly, + //but openbus in that range won't). + // + //This was discovered while investigating why one Super Metroid glitch + //worked on emulator but crashed on real console. + // + //a word fetch from 2f4017 AND 0xfffc results in 2f3c and a word fetch from + //2f4210 AND 0x7f7f results in 2f22. This also extends to long fetches + //by arguments. E.g. long argument fetch from 94420F with 2F already on + //the bus AND 0x7f7fff results in 2f222f. + // + //The reason for masking some bits in above explanation was to ignore some + //known bits in those registers (bits 7 of 4210 and 4211, bits 0&1 of 4017). + uint8_t tmp = bus.read(addr, exec); + if(!config.cpu.bus_fixes || (addr & 0x40FC00) != 0x004000) regs.mdr = tmp; add_clocks(4); alu_edge(); - return regs.mdr; + return tmp; } void CPU::op_write(uint32 addr, uint8 data) { diff --git a/snes/cpu/mmio/mmio.cpp b/snes/cpu/mmio/mmio.cpp index 30048c19..be2990a3 100755 --- a/snes/cpu/mmio/mmio.cpp +++ b/snes/cpu/mmio/mmio.cpp @@ -33,9 +33,17 @@ void CPU::mmio_w2183(uint8 data) { //strobing $4016.d0 affects both controller port latches. //$4017 bit 0 writes are ignored. void CPU::mmio_w4016(uint8 data) { - if(data&1) interface->notifyLatched(); - input.port1->latch(data & 1); - input.port2->latch(data & 1); + //Only consider autoassert if both busfix and auto flags are set. + auto auto_asserted = (status.auto_joypad_counter & 384) == 384; + //Bit 6 of status.auto_joypad_counter follows "manual" latch. + auto oldstatus = auto_asserted || (status.auto_joypad_counter & 64) != 0; + status.auto_joypad_counter &= ~64; + status.auto_joypad_counter |= (data & 1) << 6; + auto newstatus = auto_asserted || (status.auto_joypad_counter & 64) != 0; + //If !oldstatus and newstatus, signal latch. + if(!oldstatus && newstatus) interface->notifyLatched(); + input.port1->latch(newstatus); + input.port2->latch(newstatus); } //JOYSER0 diff --git a/snes/cpu/timing/joypad.cpp b/snes/cpu/timing/joypad.cpp index afca7504..b60be020 100755 --- a/snes/cpu/timing/joypad.cpp +++ b/snes/cpu/timing/joypad.cpp @@ -3,11 +3,14 @@ //called every 256 clocks; see CPU::add_clocks() void CPU::step_auto_joypad_poll() { if(vcounter() >= (ppu.overscan() == false ? 225 : 240)) { + auto cycle = status.auto_joypad_counter & 63; //cache enable state at first iteration - if(status.auto_joypad_counter == 0) status.auto_joypad_latch = status.auto_joypad_poll; - status.auto_joypad_active = status.auto_joypad_counter <= 15; + if(cycle == 0) status.auto_joypad_latch = status.auto_joypad_poll; + status.auto_joypad_active = cycle <= 15; if(status.auto_joypad_active && status.auto_joypad_latch) { - if(status.auto_joypad_counter == 0) { + if(cycle == 0) { + if(status.auto_joypad_counter & 128) + std::cerr << "step_auto_joypad_poll(): bus fixes set (counter=" << status.auto_joypad_counter << ")???" << std::endl; if(dma_trace_fn) dma_trace_fn("-- Start automatic polling --"); interface->notifyLatched(); input.port1->latch(1); @@ -23,7 +26,7 @@ void CPU::step_auto_joypad_poll() { status.joy2 = (status.joy2 << 1) | (bool)(port1 & 1); status.joy3 = (status.joy3 << 1) | (bool)(port0 & 2); status.joy4 = (status.joy4 << 1) | (bool)(port1 & 2); - if(status.auto_joypad_counter == 15) { + if(cycle == 15) { char buf[512]; sprintf(buf, "-- End automatic polling [%04x %04x %04x %04x] --", status.joy1, status.joy2, status.joy3, status.joy4); @@ -31,32 +34,129 @@ void CPU::step_auto_joypad_poll() { } } - status.auto_joypad_counter++; + //Only bits 0-5 are supposed to increment. + if(cycle < 60) + status.auto_joypad_counter++; } } //called every 128 clocks; see CPU::add_clocks() -void CPU::step_auto_joypad_poll_NEW(bool polarity) { - if(status.auto_joypad_counter > 0 && status.auto_joypad_counter <= 34) { +void CPU::step_auto_joypad_poll_NEW2(bool polarity) { + //Poll starts on multiple of 128 mod 256 clocks (polarity=false) on first + //vblank scanline. If autopoller is off, mark as done for the frame. + if(vcounter() >= (ppu.overscan() == false ? 225 : 240) && !polarity && + (status.auto_joypad_counter & 63) == 0) { + if(!(status.auto_joypad_counter & 128)) + std::cerr << "step_auto_joypad_poll_NEW2(): bus fixes clear???" << std::endl; + //Preserve high bits of autopoll counter. + auto x = status.auto_joypad_counter & ~63; + status.auto_joypad_counter = x | (status.auto_joypad_poll ? 1 : 36); + status.auto_joypad_latch = status.auto_joypad_poll; + } + //Abuse bit 6 of counter for "manual" poll flag. Bit 7 is supposed to be + //always set. + auto cycle = status.auto_joypad_counter & 63; + auto old_latchstate = (status.auto_joypad_counter & 320) != 0; + //If not enabled... This is not latched, as autopoll can be aborted. + if(!status.auto_joypad_poll && cycle > 0 && cycle < 36) { + if(dma_trace_fn) dma_trace_fn("-- Automatic polling ABORTED --"); + status.auto_joypad_counter += (36 - cycle); + status.auto_joypad_active = false; + status.auto_joypad_latch = false; + //Release autopoll latch. + status.auto_joypad_counter &= ~256; //Autopoll clears latch. + auto new_latchstate = (status.auto_joypad_counter & 320) != 0; + if(old_latchstate && !new_latchstate) { + input.port1->latch(0); + input.port2->latch(0); + } + return; + } + //On cycle #1, latch is asserted (unless latch is already high, in this + //case the autopoller is supposed to force latch high too). + if(cycle == 1) { + if(dma_trace_fn) dma_trace_fn("-- Start automatic polling --"); + //Assert autopoll latch. + status.auto_joypad_counter |= 256; + auto new_latchstate = (status.auto_joypad_counter & 320) != 0; + if(!old_latchstate && new_latchstate) { + interface->notifyLatched(); + input.port1->latch(1); + input.port2->latch(1); + } + } + //On cycle #2, busy is asserted and controllers are cleared. + if(cycle == 2) { + status.joy1 = 0; + status.joy2 = 0; + status.joy3 = 0; + status.joy4 = 0; + status.auto_joypad_active = true; + } + //Then, on cycle #3, latch is deasserted, unless "manual" latch forces + //real latch high. + if(cycle == 3) { + //Release autopoll latch. + status.auto_joypad_counter &= ~256; + auto new_latchstate = (status.auto_joypad_counter & 320) != 0; + if(old_latchstate && !new_latchstate) { + input.port1->latch(0); + input.port2->latch(0); + } + } + //Then on cycles #4, #6, #8, ..., #34, a bit is shifted. Also, clock would + //go low, but we can not emulate that. + if(cycle >= 4 && cycle <= 34 && cycle % 2 == 0) { + uint2 port0 = input.port1->data(); + uint2 port1 = input.port2->data(); + status.joy1 = (status.joy1 << 1) | (bool)(port0 & 1); + status.joy2 = (status.joy2 << 1) | (bool)(port1 & 1); + status.joy3 = (status.joy3 << 1) | (bool)(port0 & 2); + status.joy4 = (status.joy4 << 1) | (bool)(port1 & 2); + } + //Then on cycles #5, #7, #9, ..., #35, clock drops high, But we can not + //emulate that. + //Then on cycle #35, busy flag is deasserted and poll is complete. + if(cycle == 35) { + status.auto_joypad_active = false; + char buf[512]; + sprintf(buf, "-- End automatic polling [%04x %04x %04x %04x] --", + status.joy1, status.joy2, status.joy3, status.joy4); + if(dma_trace_fn) dma_trace_fn(buf); + } + //The entiere train is 35 cycles. + if(cycle > 0 && cycle < 36) { + status.auto_joypad_counter++; + } +} + + +//called every 128 clocks; see CPU::add_clocks() +void CPU::step_auto_joypad_poll_NEW(bool polarity, bool new2) { + if(new2) return step_auto_joypad_poll_NEW2(polarity); + auto cycle = status.auto_joypad_counter & 63; + if(cycle > 0 && cycle <= 34) { if(!status.auto_joypad_latch) { //FIXME: Is this right, busy flag goes on even if not enabled??? - if(status.auto_joypad_counter == 1) + if(cycle == 1) status.auto_joypad_active = true; - if(status.auto_joypad_counter == 34) + if(cycle == 34) status.auto_joypad_active = false; } else { - if(status.auto_joypad_counter == 1) { + if(cycle == 1) { + if(status.auto_joypad_counter & 128) + std::cerr << "step_auto_joypad_poll_NEW(): bus fixes set???" << std::endl; if(dma_trace_fn) dma_trace_fn("-- Start automatic polling --"); status.auto_joypad_active = true; interface->notifyLatched(); input.port1->latch(1); input.port2->latch(1); } - if(status.auto_joypad_counter == 3) { + if(cycle == 3) { input.port1->latch(0); input.port2->latch(0); } - if((status.auto_joypad_counter & 1) != 0 && status.auto_joypad_counter != 1) { + if((cycle & 1) != 0 && cycle != 1) { uint2 port0 = input.port1->data(); uint2 port1 = input.port2->data(); @@ -65,7 +165,7 @@ void CPU::step_auto_joypad_poll_NEW(bool polarity) { status.joy3 = (status.joy3 << 1) | (bool)(port0 & 2); status.joy4 = (status.joy4 << 1) | (bool)(port1 & 2); } - if(status.auto_joypad_counter == 34) { + if(cycle == 34) { status.auto_joypad_active = false; char buf[512]; sprintf(buf, "-- End automatic polling [%04x %04x %04x %04x] --", @@ -75,9 +175,11 @@ void CPU::step_auto_joypad_poll_NEW(bool polarity) { } status.auto_joypad_counter++; } - if(vcounter() >= (ppu.overscan() == false ? 225 : 240) && status.auto_joypad_counter == 0 && !polarity) { + if(vcounter() >= (ppu.overscan() == false ? 225 : 240) && cycle == 0 && !polarity) { + //Preserve high bits of autopoller counter. + auto x = status.auto_joypad_counter & ~63; status.auto_joypad_latch = status.auto_joypad_poll; - status.auto_joypad_counter = 1; + status.auto_joypad_counter = x | 1; } } diff --git a/snes/cpu/timing/timing.cpp b/snes/cpu/timing/timing.cpp index d7cf24f3..ef81d891 100755 --- a/snes/cpu/timing/timing.cpp +++ b/snes/cpu/timing/timing.cpp @@ -17,12 +17,12 @@ void CPU::add_clocks(unsigned clocks) { step(clocks); - if(config.cpu.alt_poll_timings) { + if(config.cpu.alt_poll_timings || config.cpu.bus_fixes) { bool opolarity = (status.auto_joypad_clock & 128); status.auto_joypad_clock = (status.auto_joypad_clock + clocks) & 0xFF; bool npolarity = (status.auto_joypad_clock & 128); if(opolarity != npolarity) - step_auto_joypad_poll_NEW(opolarity); + step_auto_joypad_poll_NEW(opolarity, config.cpu.bus_fixes); } else { status.auto_joypad_clock += clocks; if(status.auto_joypad_clock >= 256) { @@ -53,7 +53,8 @@ void CPU::scanline() { status.hdma_init_position = (cpu_version == 1 ? 12 + 8 - dma_counter() : 12 + dma_counter()); status.hdma_init_triggered = false; - status.auto_joypad_counter = 0; + //Only clear the low 6 bits (counter). + status.auto_joypad_counter &= ~63; } //DRAM refresh occurs once every scanline @@ -200,7 +201,9 @@ void CPU::timing_reset() { status.auto_joypad_active = false; status.auto_joypad_latch = false; - status.auto_joypad_counter = 0; + //Set bit 7 of joypad counter if bus fixes are active (for combined + //latch behavior). + status.auto_joypad_counter = config.cpu.bus_fixes ? 128 : 0; status.auto_joypad_clock = 0; } diff --git a/snes/cpu/timing/timing.hpp b/snes/cpu/timing/timing.hpp index bf15a727..8be2b830 100755 --- a/snes/cpu/timing/timing.hpp +++ b/snes/cpu/timing/timing.hpp @@ -22,4 +22,5 @@ alwaysinline bool irq_test(); //joypad.cpp void step_auto_joypad_poll(); -void step_auto_joypad_poll_NEW(bool polarity); +void step_auto_joypad_poll_NEW(bool polarity, bool new2); +void step_auto_joypad_poll_NEW2(bool polarity); diff --git a/snes/snes.hpp b/snes/snes.hpp index 3a65e360..961842b3 100755 --- a/snes/snes.hpp +++ b/snes/snes.hpp @@ -3,6 +3,7 @@ #define BSNES_SUPPORTS_ADV_BREAKPOINTS #define BSNES_SUPPORTS_ADV_BREAKPOINTS_PPU #define BSNES_SUPPORTS_ALT_TIMINGS +#define BSNES_SUPPORTS_BUS_FIXES #define BSNES_SUPPORTS_TRACE_SA1 #define BSNES_SUPPORTS_DMA_TRACE