Skip to content

Commit dabb293

Browse files
committed
HLE CD_read: stream continuation for repeated identical CD_reads
When a boot stub re-issues the same CD_read (same D0/D1/A0/A1) without varying parameters, real hardware is still feeding new sectors of disc data through the I2S stream — each call produces a different chunk. Without a notion of "where we left off" the HLE handed the same 5KB to the game over and over (Iron Soldier 2 has been stuck in this loop). We now remember the (D0, D1, dest, end) signature of the prior call plus the post-transfer LBA, and on a matching repeat we resume the sentinel scan from that LBA instead of the boot-stub-supplied MSF. This unblocks the multi-chunk boot pattern but does NOT fix Iron Soldier 2 by itself: its sentinel sync block sits at a single fixed LBA in the boot-stub track, so even after resuming we keep finding the same one. Iron Soldier 2 ultimately stops at \$007416 polling RAM[\$44F4] for a flag updated by an interrupt path we don't yet emulate; further progress needs real interrupt-driven streaming. No PASS regressions; all five PASSes hold (Battle Morph, Dragon's Lair, Highlander, Space Ace, Highlander). Made-with: Cursor
1 parent 9fbc681 commit dabb293

1 file changed

Lines changed: 42 additions & 2 deletions

File tree

src/jagcd_hle.c

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,18 @@ static bool hle_read_pending = false;
9090
* the transfer state structure there. */
9191
static uint32_t hle_gpu_data_base = 0;
9292

93+
/* Streaming continuation: when the boot stub re-issues the SAME
94+
* CD_read (same MSF + dest + sentinel) repeatedly, real hardware
95+
* is continuously serving the next sectors of disc data. Track
96+
* the prior call's signature and post-scan LBA so we can resume
97+
* from there instead of re-scanning the same start. */
98+
static uint32_t hle_last_d0 = 0;
99+
static uint32_t hle_last_d1 = 0;
100+
static uint32_t hle_last_dest = 0;
101+
static uint32_t hle_last_end = 0;
102+
static uint32_t hle_next_lba = 0;
103+
static bool hle_have_last = false;
104+
93105

94106
bool JaguarCDHLEActive(void)
95107
{
@@ -314,14 +326,31 @@ static void HLEHandleCDRead(void)
314326
#define MAX_PHASES 16
315327
uint32_t phase_starts[MAX_PHASES];
316328
uint32_t phase_count = 1;
317-
phase_starts[0] = lba;
329+
/* Streaming continuation: if this CD_read repeats the prior call's
330+
* (D0/D1/dest/end), advance the source LBA past the previously
331+
* transferred sectors so the boot stub sees fresh data each time
332+
* (mimics the I2S stream that real hardware would still be feeding).
333+
*
334+
* Examples: Iron Soldier 2 issues the same CD_read repeatedly to
335+
* pull successive chunks; without continuation we hand it the same
336+
* 5KB over and over. */
337+
uint32_t startLBA = lba;
338+
if (hle_have_last && d0 == hle_last_d0 && d1 == hle_last_d1
339+
&& a0 == hle_last_dest && a1 == hle_last_end
340+
&& hle_next_lba > lba)
341+
{
342+
HLE_LOG("CD_read: repeated read — resuming from LBA %u "
343+
"(would have been %u)\n", hle_next_lba, lba);
344+
startLBA = hle_next_lba;
345+
}
346+
phase_starts[0] = startLBA;
318347
if (sentinelIsAscii) {
319348
uint32_t n = CDIntfGetSession2TrackCount();
320349
uint32_t i;
321350
for (i = 0; i < n && phase_count < MAX_PHASES; i++) {
322351
uint32_t tl = CDIntfGetSession2TrackLBA(i);
323352
uint32_t k;
324-
bool dup = (tl == 0) || (tl == lba);
353+
bool dup = (tl == 0) || (tl == startLBA);
325354
for (k = 0; !dup && k < phase_count; k++)
326355
if (phase_starts[k] == tl) dup = true;
327356
if (!dup) phase_starts[phase_count++] = tl;
@@ -479,6 +508,15 @@ static void HLEHandleCDRead(void)
479508
hle_read_progress = byteCount;
480509
hle_read_pending = true;
481510

511+
/* Remember this call's signature + the LBA AFTER the data we just
512+
* transferred so a repeat call resumes from there. */
513+
hle_last_d0 = d0;
514+
hle_last_d1 = d1;
515+
hle_last_dest = a0;
516+
hle_last_end = a1;
517+
hle_next_lba = scanLBA + s;
518+
hle_have_last = true;
519+
482520
/* Write $FFFF sentinel padding after the transferred data.
483521
*
484522
* Game code (e.g. Primal Rage) scans DDL directory tables for a $FFFF
@@ -675,6 +713,8 @@ bool JaguarCDHLEBoot(void)
675713
hle_read_end_addr = 0;
676714
hle_read_dest = 0;
677715
hle_read_progress = 0;
716+
hle_have_last = false;
717+
hle_next_lba = 0;
678718

679719
if (!CDIntfIsImageLoaded())
680720
{

0 commit comments

Comments
 (0)