From cd99b7c9bca1b3bb87fc7ff238658d49c990db30 Mon Sep 17 00:00:00 2001 From: Pegasus Date: Mon, 14 Sep 2026 13:19:21 +0300 Subject: [PATCH] Time an explicit seek from the physical read head during a read Nymashock measures a seek with CalcSeekTime(CurSector, SeekTarget). CurSector is the physical read head, and an established read keeps it two sectors ahead of the sector handed to the guest because HandlePlayRead fills the two-slot SectorPipe before the guest drains it. source_explicit_seek_cycles timed a SeekL/SeekP from the delivery cursor instead, so a seek issued mid-read travelled two sectors too far and completed 209 cycles late at double speed. Command_Reset already applied the same +2 lead. Bio Hazard return 233568 is the first place this is observable: a mid-read SeekL to LBA 141535 completed 209 cycles late, which pushed the driver's following ReadS past the frontend return boundary and left the guest one CD command behind for the rest of the ending tail. Evidence, resuming the admitted capture state at return 233500: - armed completion moves 132227220226 -> 132227220017, the value the source's own PSRCounter samples at returns 233560-233568 independently imply - all 5702 compared returns 233501-239202 match the admitted reference on RAM page hashes and return clocks - raw RAM at 233567/233568/233569/235000/239202 matches the stock capture byte for byte, closing the nine-byte terminal difference Cold full-route requalification and the Tekken 3 / Pepsiman cold-prefix regressions are still outstanding; this commit records the component result only. Co-Authored-By: Claude Opus 5 (1M context) --- runtime/src/cdrom.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/runtime/src/cdrom.c b/runtime/src/cdrom.c index 6b913f3b..335b1d12 100644 --- a/runtime/src/cdrom.c +++ b/runtime/src/cdrom.c @@ -1763,12 +1763,21 @@ static int source_toc_seek_cycles(void) { static int source_explicit_seek_cycles(uint8_t cmd) { /* The older model uses the delivery cursor; Nymashock also tracks the - * physical head while paused or in standby. Both use the source tape. */ - int origin = msf_to_lba(read_min, read_sec, read_sect); + * physical head while paused or in standby. Both use the source tape. + * + * The source measures a seek from CurSector, its physical read head. An + * established read keeps that head CDC_SECTOR_PIPE_COUNT (2) sectors + * ahead of the sector handed to the guest, because HandlePlayRead fills + * the pipe before the guest drains it. A seek issued mid-read therefore + * starts two sectors further along than the delivery cursor says, and + * timing it from the delivery cursor makes its travel two sectors too + * long. Command_Reset already accounts for the same lead below. */ + int cursor = msf_to_lba(read_min, read_sec, read_sect); + if (cursor < 0) cursor = 0; + int origin = reading ? cursor + 2 : cursor; int target = msf_to_lba(seek_min, seek_sec, seek_sect); - if (origin < 0) origin = 0; if (target < 0) target = 0; - if(s_nymashock_drive && source_drive_head_valid)origin=source_drive_head_lba; + if(s_nymashock_drive && !reading && source_drive_head_valid)origin=source_drive_head_lba; source_drive_head_valid=0; int seek = source_seek_lower_bound(origin, target, !!(stat_reg & CDSTAT_MOTOR), s_source_seek_paused, mode_reg);