Skip to content

Commit 8c3a1f9

Browse files
committed
HLE CD_read: honour D0 bit 31 (re-seek only) + mirror data into cart space
Two related fixes for boot stubs that issue multiple CD_read calls: 1. Re-seek (D0 bit 31 set) is now a no-op transfer. Per docs/cd-bios-calling-convention.md, bit 31 means "skip hardware init, just re-seek; the GPU data area is already configured by the prior call." We were treating these as full reads, computing byteCount from A0/A1 (which hold stale or garbage values in re-seek mode) and falling back to a default \$5BC00 transfer that stomped the boot stub's just-loaded code/data with raw audio sectors. Hover Strike previously crashed to PC=\$FFFFFFFF at frame 86 because its 4th and 5th CD_reads (D0=\$80657374, \$80F652B9) overwrote 750KB of memory; with this fix it now runs to a clean wait loop at \$065B36 with 19 unique PCs. 2. Loaded data is now mirrored into cart space at the same offset. On real Jaguar CD hardware the CD cart's onboard buffer maps into cart space (\$800000-\$DFFFFF); some boot stubs scan cart-space addresses (e.g. BrainDead 13 reads A0=\$00851644) for the universal "ATRI" header. Cart space is otherwise empty in HLE mode, so the mirror is harmless when not needed. Test diagnostic upgrade: when the run barely moves we also dump 32 bytes of the current memory at A0/A1 (using the libretro core's jagMemSpace[] symbol so cart space is visible), so the wait loop's read target shows up alongside the PC bytes. Result: 4 PASS / 5 FAIL. Hover Strike no longer crashes (now a wait-loop FAIL); other failures unchanged for now. Made-with: Cursor
1 parent 6e423f0 commit 8c3a1f9

2 files changed

Lines changed: 60 additions & 0 deletions

File tree

src/jagcd_hle.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,11 +214,30 @@ static void HLEHandleCDRead(void)
214214
uint8_t pat[4];
215215
uint32_t scanLBA, scanOff;
216216
bool foundSentinel;
217+
bool reseekOnly = (d0 & 0x80000000u) != 0;
217218

218219
lba = ((uint32_t)min * 60 + sec) * 75 + frm;
219220
if (lba >= 150)
220221
lba -= 150;
221222

223+
/* Per docs/cd-bios-calling-convention.md:
224+
* "Bit 31: if set, skip hardware init, just re-seek (GPU data area
225+
* already configured by prior call)."
226+
*
227+
* Real BIOS treats bit-31 calls as DSA seek-only — the destination,
228+
* end address, and sentinel are already in place from the prior
229+
* non-bit-31 CD_read. We have no continuous streaming, so the prior
230+
* call already wrote all data; a re-seek is a no-op for HLE. The
231+
* critical thing is to NOT compute byteCount from A0/A1 (which hold
232+
* stale or garbage values in re-seek mode) and stomp memory. */
233+
if (reseekOnly)
234+
{
235+
HLE_LOG("CD_read: re-seek only (D0 bit31 set, D0=$%08X) — "
236+
"skipping data transfer\n", d0);
237+
hle_read_pending = false;
238+
return;
239+
}
240+
222241
destAddr = a0;
223242
byteCount = (a1 > a0 && a1 < 0x200000) ? (a1 - a0) : 0;
224243

@@ -438,6 +457,19 @@ static void HLEHandleCDRead(void)
438457
for (i = 0; i < copyLen && (dst + i) < 0x200000; i++)
439458
jaguarMainRAM[dst + i] = sectorBuf[copyStart + i];
440459

460+
/* Mirror the same data into cart space at the same offset.
461+
* Some boot stubs (e.g. BrainDead 13) scan cart-space addresses
462+
* like $00851644 looking for the universal "ATRI" header. On real
463+
* Jaguar CD hardware, the CD cart's onboard buffer is mapped into
464+
* cart space; in HLE we mirror the loaded data so direct cart-space
465+
* scans hit the same payload. Cart space is otherwise empty in HLE
466+
* mode, so this overlay is harmless. */
467+
{
468+
uint32_t cartDst = dst + 0x800000;
469+
for (i = 0; i < copyLen && (cartDst + i) < 0xE00000; i++)
470+
jaguarMainROM[cartDst - 0x800000 + i] = sectorBuf[copyStart + i];
471+
}
472+
441473
bytesWritten += copyLen;
442474
s++;
443475
}

test/test_cd_hle_boot.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,34 @@ static void cd_run_one_disc(const char *path, unsigned frames,
251251
fprintf(stderr, " %s=$%08X", regs[i].name,
252252
C.m68k_get_reg(NULL, regs[i].id));
253253
fprintf(stderr, "\n");
254+
255+
/* Dump 64 bytes at the current A0 (and A1) target. Use the
256+
* core's jagMemSpace[] symbol so we can see cart space
257+
* ($800000+) and not just main RAM. */
258+
uint8_t *space = (uint8_t *)dlsym(C.handle, "jagMemSpace");
259+
uint32_t a0 = C.m68k_get_reg(NULL, 8);
260+
uint32_t a1 = C.m68k_get_reg(NULL, 9);
261+
if (space) {
262+
if (a0 < 0xE00000) {
263+
fprintf(stderr, " [A0-MEM $%06X]", a0);
264+
for (uint32_t k = 0; k < 32 && a0 + k < 0xE00000; k++)
265+
fprintf(stderr, " %02X", space[a0 + k]);
266+
fprintf(stderr, "\n");
267+
}
268+
if (a1 < 0xE00000) {
269+
fprintf(stderr, " [A1-MEM $%06X]", a1);
270+
for (uint32_t k = 0; k < 32 && a1 + k < 0xE00000; k++)
271+
fprintf(stderr, " %02X", space[a1 + k]);
272+
fprintf(stderr, "\n");
273+
}
274+
} else if (ram) {
275+
if (a0 < 0x200000) {
276+
fprintf(stderr, " [A0-RAM $%06X]", a0);
277+
for (uint32_t k = 0; k < 32 && a0 + k < 0x200000; k++)
278+
fprintf(stderr, " %02X", ram[a0 + k]);
279+
fprintf(stderr, "\n");
280+
}
281+
}
254282
}
255283
}
256284

0 commit comments

Comments
 (0)