mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
cycles: bound idle skip by the SPU sample deadline when IRQ9 is unmasked
The SPU sample event was part of the internal device slicer but not of the idle-skip observable-event distance, so a poll loop waiting on SPU IRQ9 could be skipped across several 768-cycle sample boundaries before it could acknowledge and re-arm. Include the sample deadline in devices_cycles_to_next_idle_event() when IRQ_SPU is unmasked (mask-gated like the other sources), expose the distance through psx_idle_cycles_to_next_observable_event() for tests, and extend psx_cycle_event_boundaries_test: masked IRQ9 does not bound the skip; unmasked IRQ9 stops it at the first sample boundary, never across later ones.
This commit is contained in:
@@ -164,6 +164,8 @@ uint64_t psx_get_cycle_count(void);
|
||||
struct CPUState;
|
||||
void psx_idle_note_check(struct CPUState *cpu, uint32_t check_pc);
|
||||
int psx_idle_skip_is_enabled(void);
|
||||
/* Cycles until the nearest IRQ-observable device event (mask-aware); the bound an idle skip may not cross. */
|
||||
uint32_t psx_idle_cycles_to_next_observable_event(void);
|
||||
extern int g_idle_skip_enabled;
|
||||
extern uint64_t g_idle_skip_count;
|
||||
extern uint64_t g_idle_skip_cycles;
|
||||
|
||||
@@ -158,10 +158,22 @@ static uint32_t devices_cycles_to_next_idle_event(void) {
|
||||
uint32_t c = cdrom_cycles_to_irq(i_mask); if (c < best) best = c;
|
||||
uint32_t d = dma_cycles_to_deliverable_irq(i_mask); if (d < best) best = d;
|
||||
uint32_t s = sio_cycles_to_irq(i_mask); if (s < best) best = s;
|
||||
/* SPU IRQ9 is raised by the guest-clock sample scheduler, not by a device
|
||||
* *_advance(); a poll loop waiting on it must not be skipped across several
|
||||
* 768-cycle sample boundaries before it can acknowledge and re-arm. Only an
|
||||
* unmasked IRQ9 is observable, like the other sources above. */
|
||||
if (i_mask & (1u << IRQ_SPU)) {
|
||||
uint32_t a = psx_spu_sample_event_cycles_to_next(); if (a < best) best = a;
|
||||
}
|
||||
if (best == 0) best = 1;
|
||||
return best;
|
||||
}
|
||||
|
||||
/* Test/diagnostic accessor for the idle-skip observation boundary above. */
|
||||
uint32_t psx_idle_cycles_to_next_observable_event(void) {
|
||||
return devices_cycles_to_next_idle_event();
|
||||
}
|
||||
|
||||
static void psx_devices_recompute_deadline(void) {
|
||||
uint32_t next = devices_cycles_to_next_internal_event();
|
||||
if (next > PSX_DEADLINE_HARD_CAP) next = PSX_DEADLINE_HARD_CAP;
|
||||
|
||||
@@ -54,7 +54,8 @@ uint32_t dma_cycles_to_deliverable_irq(uint32_t mask) {
|
||||
}
|
||||
uint32_t sio_cycles_to_irq(uint32_t mask) { (void)mask; return UINT32_MAX; }
|
||||
/* SPU sample-event scheduler (golden 1a973806): psx_cycles.c consults it; stub here. */
|
||||
uint32_t psx_spu_sample_event_cycles_to_next(void) { return UINT32_MAX; }
|
||||
static uint32_t s_spu_next_sample = UINT32_MAX;
|
||||
uint32_t psx_spu_sample_event_cycles_to_next(void) { return s_spu_next_sample; }
|
||||
void psx_spu_sample_event_service(void) {}
|
||||
int psx_get_in_exception(void) { return 0; }
|
||||
|
||||
@@ -88,6 +89,31 @@ int main(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "PASS cross-device deadline preserves D-1 + 1 causality\n");
|
||||
/* Idle-skip observation boundary vs the SPU sample scheduler (mstan/psxrecomp#239
|
||||
* review): with IRQ9 unmasked, a wait loop must stop at the FIRST 768-cycle
|
||||
* sample boundary, not be skipped across several; with IRQ9 masked the
|
||||
* sample deadline is not observable and must not shorten the skip. */
|
||||
s_cd_ready = 1; /* no CD event ahead */
|
||||
s_spu_next_sample = 300; /* next sample boundary in 300 cycles */
|
||||
i_mask = 0;
|
||||
if (psx_idle_cycles_to_next_observable_event() == 300) {
|
||||
fprintf(stderr, "FAIL masked SPU IRQ9 must not bound the idle skip\n");
|
||||
return 1;
|
||||
}
|
||||
i_mask = 1u << 9; /* IRQ_SPU unmasked */
|
||||
if (psx_idle_cycles_to_next_observable_event() != 300) {
|
||||
fprintf(stderr, "FAIL idle skip must stop at the first SPU sample boundary (300), got %u\n",
|
||||
psx_idle_cycles_to_next_observable_event());
|
||||
return 1;
|
||||
}
|
||||
s_spu_next_sample = 768 * 4; /* several boundaries away: still the nearest event */
|
||||
if (psx_idle_cycles_to_next_observable_event() != 768 * 4) {
|
||||
fprintf(stderr, "FAIL idle skip must not cross a later SPU sample boundary either\n");
|
||||
return 1;
|
||||
}
|
||||
s_spu_next_sample = UINT32_MAX; i_mask = 0;
|
||||
|
||||
fprintf(stderr, "PASS cross-device deadline preserves D-1 + 1 causality; "
|
||||
"idle skip bounded by the SPU sample deadline\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user