mirror of
https://github.com/libretro/virtualjaguar-libretro.git
synced 2026-11-07 22:44:42 -10:00
fix(jerry): SCLK write no longer fires the SSI interrupt synchronously
Writing the I2S serial-clock register (SCLK, $F1A150) invoked JERRYI2SCallback() directly, asserting DSPIRQ_SSI inside the very DSP store instruction that programmed the clock. On real hardware the SSI raises its first word-strobe interrupt only after a complete I2S word has been shifted (32 bits x 2 phases x (SCLK+1) system clocks), so DSP init code that enables I2S interrupts and then programs SCLK always finishes its register setup before the first interrupt can arrive. The synchronous assert broke the Jaguar CD titles that re-upload a DSP audio module mid-boot (park DSP -> upload -> D_PC -> DSPGO): the new module's init wrote SCLK and was vectored into the new I2S handler with the OLD module's register values still live. The handler mixed a CD sample through a stale pointer (r13) straight over its own instruction stream at $F1B07C, the stomped word $FFFF later executed as ADDQMOD 31,r31 (stack pointer += $1F -> odd, past the end of DSP RAM), and every subsequent interrupt return popped wavetable garbage and jumped to the sign-extended sample value $FFFFCE03 -- the recurring dsp_pc_escape / video_stall seen on Dragon's Lair (BIOS, device) and Highlander (HLE, cd_boot_matrix frame 128). Fix: restart the I2S timer chain one full I2S frame period out (JERRYRescheduleI2S) instead of calling the callback at t=0. Steady- state period math is unchanged; slave-mode BUTCH delivery starts one 22.68us period later. Verified: - Highlander HLE: dsp_pc_escape $FFFFCE03 at frame 128 -> gone (400f) - Dragon's Lair BIOS: was video_stall f1008 baseline -> now renders 2400 frames, audio RMS 1234, no crash-detect signatures - Highlander BIOS: was video_stall f920 -> now renders 2400 frames, audio RMS 1526, no dsp escape (separate known cd_seek_wedge remains) - Primal Rage HLE/BIOS signatures identical to documented baselines - make TEST_EXPORTS=1 test exit 0; audio pair: IS1 presence RMS 1175.7 (= develop baseline), Atari Karts clipping negative control PASS Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
1 parent
874330f4ce
commit
b63fb7d903
3 files changed
+32
-1
No files matched your search
+4
-1
@@ -274,7 +274,10 @@ void DACWriteWord(uint32_t offset, uint16_t data, uint32_t who)
|
||||
DACUpdateSCLKRate();
|
||||
JERRYI2SInterruptTimer = -1;
|
||||
RemoveCallback(JERRYI2SCallback);
|
||||
JERRYI2SCallback();
|
||||
/* Restart the timer chain one I2S frame period out; a synchronous
|
||||
* JERRYI2SCallback() here would assert the SSI interrupt inside
|
||||
* the very instruction that wrote SCLK (see jerry.c). */
|
||||
JERRYRescheduleI2S();
|
||||
}
|
||||
else if (offset == SMODE + 2)
|
||||
{
|
||||
|
||||
@@ -289,6 +289,33 @@ void JERRYPIT2Callback(void)
|
||||
}
|
||||
|
||||
|
||||
/* Restart the I2S interrupt timer chain WITHOUT asserting an interrupt
|
||||
* now. Called on SCLK writes (src/jerry/dac.c). On real hardware the
|
||||
* SSI raises its first word-strobe interrupt only after a complete I2S
|
||||
* word has been shifted -- 32 bits x 2 clock phases x (SCLK+1) system
|
||||
* clocks -- never in the same instruction that programmed the clock.
|
||||
* Invoking JERRYI2SCallback() synchronously here vectored the DSP into
|
||||
* its I2S handler in the middle of the store that wrote SCLK, before
|
||||
* the DSP module's init code had set up the handler's pointer
|
||||
* registers; the handler then wrote CD samples through stale pointers
|
||||
* over its own code (Highlander / Dragon's Lair CD crash: dsp_pc
|
||||
* escape to $FFFFCE03 after a module re-upload). */
|
||||
void JERRYRescheduleI2S(void)
|
||||
{
|
||||
double usecs;
|
||||
|
||||
jerryI2SCycles = 32 * (2 * (*sclk + 1));
|
||||
|
||||
if (*smode & SMODE_INTERNAL)
|
||||
usecs = (double)jerryI2SCycles
|
||||
* (vjs.hardwareTypeNTSC ? RISC_CYCLE_IN_USEC : RISC_CYCLE_PAL_IN_USEC);
|
||||
else
|
||||
usecs = 22.675737;
|
||||
|
||||
SetCallbackTime(JERRYI2SCallback, usecs, EVENT_JERRY);
|
||||
}
|
||||
|
||||
|
||||
void JERRYI2SCallback(void)
|
||||
{
|
||||
// We don't have to divide the RISC clock rate by this--the reason is a bit
|
||||
|
||||
@@ -46,6 +46,7 @@ void JERRYSetPendingIRQ(int irq);
|
||||
// This should stay inside this file, but it's here for now...
|
||||
// Need to set up an interface function so that this can go back
|
||||
void JERRYI2SCallback(void);
|
||||
void JERRYRescheduleI2S(void);
|
||||
|
||||
// External variables
|
||||
|
||||
|
||||
Reference in new issue
Block a user