mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 18:25:35 -04:00
5041712a05
Accumulated working-tree state for the overlay-JIT-cache phase, committed as a
checkpoint before the Bug A fix (next commit):
- event_ring.{c,h}: always-on unified event-timeline ring (IRQ deliver/gate/
raise, I_STAT changes, DMA kick/sched/done, ENQ/DEQ per source, exec-mode
tagged). Edge-recorded, eviction-bounded, survives a blue screen; dump via
event_ring_dump/_tail/_clear.
- cdrom.c: disc_speed="instant" no longer collapses every sector read to the
500cy floor (which flooded ~1100 DMA3 completions/frame and starved main
code). CDROM_INSTANT_PERIOD bounds sector IRQs to CDROM_INSTANT_MAX_PER_FRAME
(32)/frame. Known caveat: a transient ~1-2s reentry storm at boot frame ~210
still trips the watchdog once, then recovers.
- interrupts.c: claimed-IRQ cooldown is now per-source — SIO stays 0 (card
reads need back-to-back IRQs); DMA/VBLANK/timer get CLAIMED_PROGRESS_QUANTUM
(8) blocks of guaranteed main-code progress between deliveries. Root cause of
the instant-disc freeze: cooldown=0 let the block leader re-take the
exception before its body ran (pinned at the 0x8005A1D8 field loop).
- gpu/dma/memory/sio/timers/psx_cycles: event-ring instrumentation at raise/
kick/complete/schedule sites; g_event_step_conservative (single-cycle event
stepping A/B used to refute the event-granularity hypothesis for Bug A).
- overlay_capture.c, overlay_loader.h, crc32: capture/manifest plumbing for
per-entry validity (Inc3).
- recompiler: overlay-mode emission updates (code_generator, main_psx).
- tools: compile_overlays.py audit/patch updates; tomba_loadtest.py.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
134 lines
4.8 KiB
C
134 lines
4.8 KiB
C
/*
|
|
* event_ring.c — always-on unified event-timeline ring. See event_ring.h.
|
|
*
|
|
* OBSERVABILITY ONLY. Records edges/events from the IRQ-decision path, I_STAT
|
|
* raise sites, and DMA kick/complete, each tagged with cycle/pc/func/mode/
|
|
* overlay-fn so two runs of the same route can be diffed for the first
|
|
* interrupt/DMA ordering divergence.
|
|
*/
|
|
#include "event_ring.h"
|
|
#include "psx_cycles.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
/* ---- runtime globals captured at record time ---- */
|
|
extern uint32_t i_stat; /* memory.c */
|
|
extern uint32_t i_mask; /* memory.c */
|
|
extern uint32_t g_debug_current_func_addr; /* debug_server */
|
|
extern uint32_t g_debug_last_store_pc; /* debug_server */
|
|
|
|
/* Execution-mode sources (see overlay_loader.c / dirty_ram_interp.c). */
|
|
extern uint32_t overlay_loader_get_inprogress(void); /* in-progress native ovl fn, 0 if none */
|
|
extern int dirty_ram_interp_is_active(void); /* 1 while interpreting a dirty block */
|
|
|
|
static EventEntry s_ring[EVENT_RING_CAP];
|
|
static uint64_t s_seq; /* total events ever recorded (monotonic) */
|
|
|
|
static uint8_t current_mode(void) {
|
|
if (overlay_loader_get_inprogress() != 0) return MODE_NATIVE_OVERLAY;
|
|
if (dirty_ram_interp_is_active()) return MODE_INTERP;
|
|
return MODE_STATIC;
|
|
}
|
|
|
|
void event_ring_record_aux(uint16_t kind, uint8_t detail, uint32_t aux) {
|
|
#if EVENT_RING_ENABLED
|
|
EventEntry *e = &s_ring[s_seq & (EVENT_RING_CAP - 1u)];
|
|
e->seq = s_seq++;
|
|
e->cycle = psx_get_cycle_count();
|
|
e->pc = g_debug_last_store_pc;
|
|
e->func = g_debug_current_func_addr;
|
|
e->overlay_fn = overlay_loader_get_inprogress();
|
|
e->i_stat = i_stat;
|
|
e->i_mask = i_mask;
|
|
e->aux = aux;
|
|
e->kind = kind;
|
|
e->mode = current_mode();
|
|
e->detail = detail;
|
|
#else
|
|
(void)kind; (void)detail; (void)aux;
|
|
#endif
|
|
}
|
|
|
|
void event_ring_record(uint16_t kind, uint8_t detail) {
|
|
event_ring_record_aux(kind, detail, 0u);
|
|
}
|
|
|
|
void event_ring_clear(void) {
|
|
s_seq = 0;
|
|
memset(s_ring, 0, sizeof(s_ring));
|
|
}
|
|
|
|
static const char *kind_name(uint16_t k) {
|
|
switch (k) {
|
|
case EV_IRQ_DELIVER: return "IRQ_DELIVER";
|
|
case EV_IRQ_GATE: return "IRQ_GATE";
|
|
case EV_ISTAT_RAISE: return "ISTAT_RAISE";
|
|
case EV_ISTAT_CHANGE: return "ISTAT_CHANGE";
|
|
case EV_DMA_KICK: return "DMA_KICK";
|
|
case EV_DMA_DONE: return "DMA_DONE";
|
|
case EV_DMA_SCHED: return "DMA_SCHED";
|
|
case EV_ENQ: return "ENQ";
|
|
case EV_DEQ: return "DEQ";
|
|
case EV_MODE: return "MODE";
|
|
default: return "NONE";
|
|
}
|
|
}
|
|
static const char *mode_name(uint8_t m) {
|
|
switch (m) {
|
|
case MODE_STATIC: return "STATIC";
|
|
case MODE_NATIVE_OVERLAY: return "NATIVE_OVERLAY";
|
|
case MODE_INTERP: return "INTERP";
|
|
default: return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
static int format_entry(char *out, int cap, const EventEntry *e) {
|
|
return snprintf(out, cap,
|
|
"{\"seq\":%llu,\"cycle\":%llu,\"kind\":\"%s\",\"mode\":\"%s\","
|
|
"\"pc\":\"0x%08X\",\"func\":\"0x%08X\",\"ovl\":\"0x%08X\","
|
|
"\"istat\":\"0x%08X\",\"imask\":\"0x%08X\",\"detail\":%u,\"aux\":\"0x%08X\"}",
|
|
(unsigned long long)e->seq, (unsigned long long)e->cycle,
|
|
kind_name(e->kind), mode_name(e->mode),
|
|
e->pc, e->func, e->overlay_fn, e->i_stat, e->i_mask,
|
|
(unsigned)e->detail, e->aux);
|
|
}
|
|
|
|
int event_ring_dump_file(const char *path) {
|
|
FILE *f = fopen(path ? path : "event_ring.json", "w");
|
|
if (!f) return -1;
|
|
uint64_t total = s_seq;
|
|
uint64_t start = (total > EVENT_RING_CAP) ? (total - EVENT_RING_CAP) : 0;
|
|
fputc('[', f);
|
|
char buf[512];
|
|
int first = 1, count = 0;
|
|
for (uint64_t s = start; s < total; s++) {
|
|
const EventEntry *e = &s_ring[s & (EVENT_RING_CAP - 1u)];
|
|
format_entry(buf, (int)sizeof(buf), e);
|
|
fprintf(f, "%s%s", first ? "" : ",\n", buf);
|
|
first = 0; count++;
|
|
}
|
|
fputs("]\n", f);
|
|
fclose(f);
|
|
return count;
|
|
}
|
|
|
|
int event_ring_dump_json(char *out, int cap, int max_entries) {
|
|
uint64_t total = s_seq;
|
|
uint64_t want = (max_entries > 0) ? (uint64_t)max_entries : 256;
|
|
uint64_t start = (total > want) ? (total - want) : 0;
|
|
int n = 0;
|
|
n += snprintf(out + n, cap - n,
|
|
"{\"total\":%llu,\"events\":[", (unsigned long long)total);
|
|
int first = 1;
|
|
char buf[512];
|
|
for (uint64_t s = start; s < total && n < cap - 600; s++) {
|
|
const EventEntry *e = &s_ring[s & (EVENT_RING_CAP - 1u)];
|
|
format_entry(buf, (int)sizeof(buf), e);
|
|
n += snprintf(out + n, cap - n, "%s%s", first ? "" : ",", buf);
|
|
first = 0;
|
|
}
|
|
n += snprintf(out + n, cap - n, "]}");
|
|
return n;
|
|
}
|