Resume CD reads at the paused drive position

Preserve new Setloc targets and explicit seeks. Serialize the pending-location flag in a validated CDF3 extension while retaining CDF2 and legacy state readers. Covers ReadN/ReadS pause, repeated resume, seeks and state restores. Beads: beads-cgvd.
This commit is contained in:
Matthew Stanley
2026-10-17 09:18:17 -07:00
parent 7cddab8237
commit 8bee2b14f2
2 changed files with 93 additions and 7 deletions
+29 -7
View File
@@ -1636,10 +1636,16 @@ static void start_read_stream(uint8_t cmd) {
xa_reset_decode();
spu_cd_audio_reset();
}
/* Setloc names a new destination only until it is consumed. Pause keeps
* read_* at the next sector; a later ReadN/ReadS without Setloc resumes
* there. Reusing seek_* unconditionally restarts XA dialogue at its first
* sector while the game's subtitle clock resumes where it paused. */
if (setloc_pending) {
read_min = seek_min;
read_sec = seek_sec;
read_sect = seek_sect;
}
setloc_pending = 0;
read_min = seek_min;
read_sec = seek_sec;
read_sect = seek_sect;
read_cmd = cmd;
read_delay = initial_read_delay_cycles();
s_cd_probe_read_start_count++;
@@ -3383,6 +3389,10 @@ static int cdrom_snap_emit(PstW *w, int extended) {
WB(s_sector_ring[slot].data);
WI(s_sector_ring[slot].pos); WI(s_sector_ring[slot].size);
}
if (extended >= 2) {
WU(0x33464443u); /* CDF3: unconsumed Setloc, distinct from drive head */
W8(setloc_pending);
}
}
#undef W8
@@ -3452,6 +3462,10 @@ static int cdrom_snap_parse(PstR *r, int extended) {
RB(s_sector_ring[slot].data);
RI(s_sector_ring[slot].pos); RI(s_sector_ring[slot].size);
}
if (extended >= 2) {
RU(u); /* validated CDF3 marker */
R8(setloc_pending);
}
}
#undef R8
@@ -3476,9 +3490,10 @@ static uint32_t cdrom_snapshot_legacy_bytes(void) {
int cdrom_snapshot_validate(const uint8_t *p, uint32_t len) {
const uint32_t legacy = cdrom_snapshot_legacy_bytes();
const uint32_t current = cdrom_snapshot_bytes();
if (!p) return 0;
if (len == legacy) return 1;
if (len != cdrom_snapshot_bytes()) return 0;
if (len != current && len != current - 5u) return 0; /* CDF3 or CDF2 */
PstR r;
uint32_t magic;
int32_t index, pos, size, delay;
@@ -3496,26 +3511,33 @@ int cdrom_snapshot_validate(const uint8_t *p, uint32_t len) {
size < 0 || size > SECTOR_BUFFER_SIZE || pos < 0 || pos > size)
return 0;
}
if (len == current) {
uint8_t pending_loc;
if (!pst_r_u32(&r, &magic) || magic != 0x33464443u ||
!pst_r_u8(&r, &pending_loc) || pending_loc > 1) return 0;
}
return r.p == r.end;
}
uint32_t cdrom_snapshot_bytes(void) {
PstW w;
pst_w_init(&w, NULL, 0);
(void)cdrom_snap_emit(&w, 1);
(void)cdrom_snap_emit(&w, 2);
return (uint32_t)w.written;
}
void cdrom_snapshot_write(uint8_t *p) {
PstW w;
uint32_t n = cdrom_snapshot_bytes();
pst_w_init(&w, p, n);
(void)cdrom_snap_emit(&w, 1);
(void)cdrom_snap_emit(&w, 2);
}
int cdrom_snapshot_read(const uint8_t *p, uint32_t len) {
PstR r;
if (!cdrom_snapshot_validate(p, len)) return 0;
pst_r_init(&r, p, len);
if (!cdrom_snap_parse(&r, len != cdrom_snapshot_legacy_bytes()))
const int extended = len == cdrom_snapshot_legacy_bytes() ? 0 :
len == cdrom_snapshot_bytes() ? 2 : 1;
if (!cdrom_snap_parse(&r, extended))
return 0;
/* Absolute host deadlines are not on the wire — rebuild from restored
* relative read_delay (psx_cycle_count is resynced by the load caller). */
+64
View File
@@ -168,7 +168,71 @@ static void invalid_setloc_preserves_target(void) {
CHECK(setloc_pending==pending && setloc_seek_far==seek_far);
}
}
static void pause_read_resumes_position(void) {
const uint8_t reads[] = {0x06, 0x1B};
for (unsigned n = 0; n < sizeof reads; ++n) {
setup();
seek_min = 0; seek_sec = 2; seek_sect = 0; /* stale target, LBA 0 */
sector(); ack(); /* delivered 100; drive will read 101 next */
exec_command(0x09); ack();
CHECK(!reading && msf_to_lba(read_min, read_sec, read_sect) == 101);
exec_command(reads[n]);
CHECK(reading && read_cmd == reads[n] && !setloc_pending);
CHECK(msf_to_lba(read_min, read_sec, read_sect) == 101);
ack(); sector(); request(); drain(101, 512);
ack();
/* Another pause cannot regress to the original target either. */
exec_command(0x09); ack(); exec_command(reads[n]);
CHECK(msf_to_lba(read_min, read_sec, read_sect) == 102);
ack();
/* An explicit new Setloc still takes precedence over the drive. */
exec_command(0x09); ack();
param_fifo[0] = 0; param_fifo[1] = 4; param_fifo[2] = 0; param_count = 3;
exec_command(0x02); ack(); exec_command(reads[n]);
CHECK(msf_to_lba(read_min, read_sec, read_sect) == 150);
CHECK(!setloc_pending);
ack(); sector(); request(); drain(150, 512);
/* A completed explicit seek retains its own destination too. */
ack(); exec_command(0x09); ack();
param_fifo[0] = 0; param_fifo[1] = 5; param_fifo[2] = 0; param_count = 3;
exec_command(0x02); ack(); exec_command(0x15); ack();
psx_cycle_count += 100000000; process_pending(0); ack();
exec_command(reads[n]);
CHECK(msf_to_lba(read_min, read_sec, read_sect) == 225);
}
}
static void snapshot_setloc_and_paused_head(void) {
setup(); sector(); ack(); exec_command(0x09); ack();
param_fifo[0]=0;param_fifo[1]=4;param_fifo[2]=0;param_count=3;
exec_command(0x02);ack();
const uint32_t size=cdrom_snapshot_bytes();
uint8_t* wire=malloc(size);CHECK(wire);
cdrom_snapshot_write(wire);
setloc_pending=0;read_sec=9;
CHECK(cdrom_snapshot_read(wire,size) && setloc_pending);
exec_command(0x1B);ack();
CHECK(msf_to_lba(read_min,read_sec,read_sect)==150);
sector();ack();exec_command(0x09);ack();
cdrom_snapshot_write(wire);
CHECK(!setloc_pending);
setloc_pending=1;read_sec=9;
CHECK(cdrom_snapshot_read(wire,size) && !setloc_pending);
exec_command(0x1B);ack();
CHECK(msf_to_lba(read_min,read_sec,read_sect)==151);
wire[size-1]=2;CHECK(!cdrom_snapshot_validate(wire,size));
/* CDF2 states remain readable and retain the paused drive position. */
exec_command(0x09);ack();
PstW writer;pst_w_init(&writer,wire,size);
CHECK(cdrom_snap_emit(&writer,1) && writer.written==size-5);
read_sec=9;
CHECK(cdrom_snapshot_read(wire,size-5));
exec_command(0x06);
CHECK(msf_to_lba(read_min,read_sec,read_sect)==151);
free(wire);
}
int main(void) {
pause_read_resumes_position();
snapshot_setloc_and_paused_head();
invalid_setloc_preserves_target();
delayed_notification();
dma_continuation();