tengen: auto-reload + trigger-on-reach-0 for RAMBO-1 IRQ (#544)
The Tengen 800032 (RAMBO-1) IRQ counter on real hardware auto-reloads from its latch when it would otherwise underflow, and the IRQ triggers when the counter REACHES zero (going from 1 to 0) rather than on the underflow step that follows. Upstream's earlier model triggered on underflow (count 0 -> 0xFF) and then continued decrementing through 0xFE, 0xFD, ... until the game wrote a reload register ($C000/$C001/$E000/$E001 with rmode==1). For titles that don't write the reload register on every IRQ - Skull & Crossbones in 2-player mode is the documented case (libretro docs Compatibility entry), but Hard Drivin', Klax, Rolling Thunder, Road Runner, Xenophobe etc. all use the same mapper - subsequent IRQs after the first underflow are silently swallowed, displacing the status-bar split point and producing the screen-shake / glitch reported in #544. Backport reference: negativeExponent's libretro-fceumm_next commit 7f277a8 ("Cleanup mapper 64/158") plus the Mesen RAMBO-1 model. This patch is the minimal algorithmic delta against upstream: - RAMBO1IRQHook (CPU-cycle mode, IRQmode==1): trigger on IRQCount reaching 0, auto-reload from IRQLatch on the next clock after the trigger. - RAMBO1HBHook (scanline / A12-approximation mode, IRQmode==0): same change. rmode is still set on IRQ fire so existing register-reload semantics on $C000/$C001/$E000/$E001 are preserved bit-for-bit - the writes do exactly what they did before, the only behaviour change is what the IRQ hook itself does between writes. - IRQLatch == 0 edge case handled (reload yields 0, which is the reach-0 trigger condition, so IRQs fire on every clock - the documented hardware behaviour at latch=0). Crucially, this does NOT change: - SFORMAT chunk layout (REGS/CMDR/MIRR/RMOD/IRQM/IRQC/IRQA/IRQL chunks all still present, same byte sizes, same order). Old savestates continue to load correctly. - The write-side dispatch (RAMBO1_Write) for $A000, $8000, $8001, $C000, $C001, $E000, $E001 - all behave exactly as before, including the rmode==1 conditional reload. - Mapper 158 (TENGEN 800037, Alien Syndrome Unl) wiring - it shares the RAMBO1 IRQ hook and gets the same fix for free. - PRG/CHR/mirroring sync logic. - Power-on and StateRestore behaviour. Build verified: tengen.c -O2 / -O3 / aarch64 -O2 clean.
This commit is contained in:
committed by
U-DESKTOP-SPFP6AQ\twistedtechre
parent
5f8a864bf3
commit
71bbd10ad0
@@ -48,9 +48,28 @@ static void FP_FASTAPASS(1) RAMBO1IRQHook(int a) {
|
||||
smallcount += a;
|
||||
while (smallcount >= 4) {
|
||||
smallcount -= 4;
|
||||
IRQCount--;
|
||||
if (IRQCount == 0xFF)
|
||||
if (IRQa) X6502_IRQBegin(FCEU_IQEXT);
|
||||
/* Mesen / _next reference: the Tengen RAMBO-1 IRQ counter
|
||||
* auto-reloads from the latch when it would otherwise
|
||||
* underflow, and the IRQ triggers when the counter
|
||||
* REACHES zero (going from 1 to 0) rather than on the
|
||||
* underflow step that follows. Upstream's earlier model
|
||||
* triggered on underflow (count 0 -> 0xFF) and then
|
||||
* continued decrementing through 0xFE, 0xFD, ... until
|
||||
* the game wrote a reload register. Match the hardware
|
||||
* model: trigger on reach-0, and on the following clock
|
||||
* reload from latch automatically so the counter cycles
|
||||
* continuously without needing per-IRQ register writes. */
|
||||
if (IRQCount == 0) {
|
||||
IRQCount = IRQLatch;
|
||||
if (IRQCount == 0 && IRQa) {
|
||||
X6502_IRQBegin(FCEU_IQEXT);
|
||||
}
|
||||
} else {
|
||||
IRQCount--;
|
||||
if (IRQCount == 0 && IRQa) {
|
||||
X6502_IRQBegin(FCEU_IQEXT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,9 +77,20 @@ static void FP_FASTAPASS(1) RAMBO1IRQHook(int a) {
|
||||
static void RAMBO1HBHook(void) {
|
||||
if ((!IRQmode) && (scanline != 240)) {
|
||||
rmode = 0;
|
||||
IRQCount--;
|
||||
if (IRQCount == 0xFF) {
|
||||
if (IRQa) {
|
||||
/* See comment in RAMBO1IRQHook above for the auto-reload /
|
||||
* trigger-on-reach-0 model. Same change applies here for the
|
||||
* scanline-clocked (A12 approximation) IRQ path. */
|
||||
if (IRQCount == 0) {
|
||||
IRQCount = IRQLatch;
|
||||
/* Latch == 0 means fire on every clock - reload yields
|
||||
* 0 again, which is the reach-0 trigger condition. */
|
||||
if (IRQCount == 0 && IRQa) {
|
||||
rmode = 1;
|
||||
X6502_IRQBegin(FCEU_IQEXT);
|
||||
}
|
||||
} else {
|
||||
IRQCount--;
|
||||
if (IRQCount == 0 && IRQa) {
|
||||
rmode = 1;
|
||||
X6502_IRQBegin(FCEU_IQEXT);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user