cdrom: cancel stale INT2 on new command (PSX-CD-007)

Cancel the previous command's outstanding second-response interrupt when
a new CD command is accepted (psx-spx command flow; DuckStation
BeginCommand semantics). Without this, a Pause(0x09) INT2 survives into
the guest's next Setmode/Setloc/ReadN sequence and fires mid-read as a
stale COMPLETE — LIBDS drivers treat it as a read failure, corrupting
the streamed resource chain.

Cross-validated: C-12 Final Resistance mission 1 PC=0 null-callback
termination disappeared after absorbing this fix (DEVLOG 2026-08-05).
MOHU Mission 1 freeze in a DsRead FATAL/retry loop on single-sector
marker reads.

Also guard beetle SIGSTOP trap behind #ifndef _WIN32 (Windows has no
SIGSTOP).

(cherry picked from commit 2c43fc69c66242a86183156a6f1e60b2f81b1e47)
(cherry picked from commit c03fe5ba3d49241dfaedb344db6ad9c977070722)
This commit is contained in:
Alexandros Mandravillis
2026-08-05 04:30:59 +03:00
parent 2795b5a01a
commit 2d67bb9a79
2 changed files with 25 additions and 1 deletions
+4 -1
View File
@@ -75,7 +75,9 @@ static void cdcmd_trace_callback(uint8_t cmd, uint8_t nargs,
uint32_t pc) {
/* Symmetric self-stop trap (mirror of the runtime's PSX_CD_TRAP_CMD):
* SIGSTOP on the Nth matching CD command so a debugger can freeze the
* oracle at the exact boot point and diff its state against ours. */
* oracle at the exact boot point and diff its state against ours.
* POSIX-only, same guard as debug_server.c — Windows has no SIGSTOP. */
#ifndef _WIN32
{
static long trap_cmd = -2, trap_nth = 1, hits = 0;
if (trap_cmd == -2) {
@@ -86,6 +88,7 @@ static void cdcmd_trace_callback(uint8_t cmd, uint8_t nargs,
}
if ((long)cmd == trap_cmd && ++hits == trap_nth) raise(SIGSTOP);
}
#endif
BeetleCdcmdEntry *e = &s_cdcmd_trace[s_cdcmd_idx];
e->seq = s_cdcmd_seq++; e->pc = pc; e->cmd = cmd; e->nargs = nargs;
e->a0 = a0; e->a1 = a1; e->a2 = a2;
+21
View File
@@ -264,6 +264,7 @@ static uint8_t cd_muted;
#define XA_SUBMODE_REALTIME 0x40
#define CDROM_SECTOR_MODE2 0x02
#define CDROM_SKIP_NONE 0
#define CDROM_SKIP_XA_AUDIO_REALTIME 1
#define CDROM_REQUEST_BFRD 0x80u
@@ -2004,6 +2005,26 @@ static void exec_command(uint8_t cmd) {
trace_cdrom('C', 0, cmd, 0);
/* ENQUEUE: a CD command was issued (aux = command byte). */
event_ring_record_aux(EV_ENQ, (uint8_t)SRC_CD_CMD, (uint32_t)cmd);
/* A new command CANCELS the previous command's outstanding second
* response (psx-spx command flow; DuckStation BeginCommand "command
* cancellation"). The sub-CPU runs one transaction at a time: once a
* new command is accepted, the superseded command's INT2 is never
* delivered. Two-phase handlers below already overwrite `pending`;
* this clears it for single-phase commands (GetStat/Setmode/Setloc/…)
* and for ReadN/ReadS, which do not use `pending` at all.
*
* Without this, a Pause(0x09) INT2 scheduled ~1.1M cycles out (see
* pause_complete_delay_cycles) survives into the guest's next
* Setmode/Setloc/ReadN sequence and fires MID-READ as a stale
* COMPLETE. LIBDS drivers treat that as a read failure: MOHU Mission 1
* wedged in a phase-locked DsRead FATAL/retry loop on its single-sector
* marker reads, corrupting the streamed resource chain
* (medal-of-honor-underground DEVLOG 2026-08-05). GT1 race loading
* shows the same stale-Pause overlap during ReadS streaming. */
if (pending.pending) {
trace_cdrom('X', 0, pending.cmd, 0);
pending.pending = 0;
}
/* Snapshot the param FIFO before handlers consume it, so the
* command-history record at the end sees the original params. */
uint8_t cmd_params[PARAM_FIFO_SIZE];