mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
Poll staged user snapshots before interrupt maintenance fast returns
This commit is contained in:
@@ -114,6 +114,10 @@ int savestate_request_load_blob_protocol(const void* data, size_t size);
|
||||
/* 1 while a staged save/load has not yet been consumed by savestate_poll. */
|
||||
int savestate_pending(void);
|
||||
|
||||
/* Read-only hot-path hint for interrupt maintenance. Updated with staged
|
||||
* requests; actual execution still passes every savestate_poll safety gate. */
|
||||
extern int g_psx_savestate_pending;
|
||||
|
||||
/* Machine-readable lifecycle receipt for deterministic debug harnesses. */
|
||||
void savestate_status_json(char* buf, size_t cap);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
*/
|
||||
|
||||
#include "interrupts.h"
|
||||
#include "savestate.h"
|
||||
#include "psx_memory.h"
|
||||
#include "sio.h"
|
||||
#include "timers.h"
|
||||
@@ -1116,6 +1117,15 @@ int psx_interrupt_delivery_needed(const CPUState* cpu) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void poll_staged_user_snapshot(CPUState* cpu) {
|
||||
extern uint32_t g_dirty_safe_resume_pc;
|
||||
if (!in_exception && g_psx_savestate_pending) {
|
||||
uint32_t pc = g_dirty_safe_resume_pc ? g_dirty_safe_resume_pc
|
||||
: s_compiled_interrupt_resume_pc;
|
||||
savestate_poll(cpu, pc);
|
||||
}
|
||||
}
|
||||
|
||||
void psx_check_interrupts(CPUState* cpu) {
|
||||
/* Frozen guest time (psx_cycle_freeze.h): no delivery, and no
|
||||
* check-boundary bookkeeping that the stock timeline would not do. */
|
||||
@@ -1195,6 +1205,12 @@ void psx_check_interrupts(CPUState* cpu) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Quiet native/HLE gameplay may execute far fewer than 0x4000 BB checks
|
||||
* in a frame. A staged user snapshot must not wait for that amortized
|
||||
* maintenance counter: short actions/voices can finish before capture.
|
||||
* Keep exception, host-adapter and resume-PC gates in savestate_poll, and
|
||||
* retain the canonical netplay edge gate above. Idle work stays amortized. */
|
||||
poll_staged_user_snapshot(cpu);
|
||||
s_irq_path_entry++;
|
||||
|
||||
/* MotK VLC / FMV hot edge: sticky unmasked I_STAT (CD/VBlank) while
|
||||
|
||||
+19
-3
@@ -53,10 +53,13 @@ static uint32_t s_entry_pc;
|
||||
static int s_configured = 0;
|
||||
static int s_save_pending = -1; /* slot, or -1 */
|
||||
static int s_load_pending = -1;
|
||||
int g_psx_savestate_pending = 0;
|
||||
static int s_load_completed = 0;
|
||||
static int s_load_failed = 0;
|
||||
static uint32_t s_status_generation = 0;
|
||||
static int s_status_pending = 0;
|
||||
static uint32_t s_status_polls;
|
||||
static const char* s_status_wait = "";
|
||||
static int s_status_last_ok = 0;
|
||||
static int s_status_last_load = 0;
|
||||
static int s_status_last_slot = -1;
|
||||
@@ -659,6 +662,9 @@ static int request_save_inner(int slot) {
|
||||
s_last_save_pc = 0; /* block netplay transfer until this write stamps a PC */
|
||||
s_save_defer_slot = -1;
|
||||
s_save_pending = slot;
|
||||
s_status_polls = 0;
|
||||
s_status_wait = "boundary";
|
||||
g_psx_savestate_pending = 1;
|
||||
s_status_pending = 1;
|
||||
s_status_last_load = 0;
|
||||
s_status_last_slot = slot;
|
||||
@@ -684,6 +690,9 @@ static int request_load_inner(int slot) {
|
||||
s_load_failed = 0;
|
||||
s_load_completed = 0;
|
||||
s_load_pending = slot;
|
||||
s_status_polls = 0;
|
||||
s_status_wait = "boundary";
|
||||
g_psx_savestate_pending = 1;
|
||||
s_status_pending = 1;
|
||||
s_status_last_load = 1;
|
||||
s_status_last_slot = slot;
|
||||
@@ -735,6 +744,7 @@ int savestate_request_load_blob_protocol(const void* data, size_t size) {
|
||||
s_load_failed = 0;
|
||||
s_load_completed = 0;
|
||||
s_load_pending = 0; /* non-negative: poll will prefer the blob */
|
||||
g_psx_savestate_pending = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -746,10 +756,11 @@ void savestate_status_json(char* buf, size_t cap) {
|
||||
if (!buf || cap == 0) return;
|
||||
snprintf(buf, cap,
|
||||
"\"generation\":%u,\"pending\":%d,\"last_ok\":%d,"
|
||||
"\"last_op\":\"%s\",\"last_slot\":%d",
|
||||
"\"last_op\":\"%s\",\"last_slot\":%d,"
|
||||
"\"polls\":%u,\"wait\":\"%s\"",
|
||||
(unsigned)s_status_generation, s_status_pending,
|
||||
s_status_last_ok, s_status_last_load ? "load" : "save",
|
||||
s_status_last_slot);
|
||||
s_status_last_slot, s_status_polls, s_status_pending ? s_status_wait : "");
|
||||
}
|
||||
|
||||
int savestate_take_load_completed(void) {
|
||||
@@ -777,7 +788,9 @@ uint32_t savestate_last_save_pc(void) {
|
||||
void savestate_poll(CPUState* cpu, uint32_t resume_pc) {
|
||||
if (s_save_pending < 0 && s_load_pending < 0) return; /* hot path: nothing staged */
|
||||
extern int psx_mod_function_entry_active(void);
|
||||
if (psx_mod_function_entry_active()) return;
|
||||
++s_status_polls;
|
||||
if (psx_mod_function_entry_active()) { s_status_wait = "mod_callback"; return; }
|
||||
s_status_wait = "resume_pc";
|
||||
|
||||
if (s_save_pending >= 0) {
|
||||
int slot = s_save_pending;
|
||||
@@ -810,6 +823,7 @@ void savestate_poll(CPUState* cpu, uint32_t resume_pc) {
|
||||
if (now - s_save_defer_t0 < 2000.0)
|
||||
return;
|
||||
s_save_pending = -1;
|
||||
g_psx_savestate_pending = s_load_pending >= 0;
|
||||
s_save_defer_slot = -1;
|
||||
s_last_save_pc = 0;
|
||||
s_save_failed = 1;
|
||||
@@ -830,6 +844,7 @@ void savestate_poll(CPUState* cpu, uint32_t resume_pc) {
|
||||
psx_frontend_on_savestate_notify(0, slot, 0);
|
||||
} else {
|
||||
s_save_pending = -1;
|
||||
g_psx_savestate_pending = s_load_pending >= 0;
|
||||
s_save_defer_slot = -1;
|
||||
if (pc != resume_pc && resume_pc == 0u) {
|
||||
fprintf(stderr,
|
||||
@@ -872,6 +887,7 @@ void savestate_poll(CPUState* cpu, uint32_t resume_pc) {
|
||||
int slot = s_load_pending;
|
||||
int loaded = 0;
|
||||
s_load_pending = -1;
|
||||
g_psx_savestate_pending = s_save_pending >= 0;
|
||||
char path[600];
|
||||
const double t_load0 = savestate_mono_ms();
|
||||
double t_after_boot = t_load0;
|
||||
|
||||
@@ -5,6 +5,12 @@ int g_cosim_dirty_pump_site;
|
||||
int g_call_unit_depth;
|
||||
int g_psx_dispatch_depth;
|
||||
uint32_t g_dirty_safe_resume_pc;
|
||||
int g_psx_savestate_pending;
|
||||
static unsigned snapshot_polls;
|
||||
static uint32_t snapshot_pc;
|
||||
void savestate_poll(CPUState* cpu, uint32_t pc) {
|
||||
(void)cpu; ++snapshot_polls; snapshot_pc = pc;
|
||||
}
|
||||
|
||||
static void check(int ok, const char* message) {
|
||||
if (!ok) { fprintf(stderr, "FAIL: %s\n", message); exit(1); }
|
||||
@@ -12,6 +18,26 @@ static void check(int ok, const char* message) {
|
||||
|
||||
int main(void) {
|
||||
uint32_t pc = 0x80025070u;
|
||||
CPUState cpu = {0};
|
||||
s_compiled_interrupt_resume_pc = pc;
|
||||
poll_staged_user_snapshot(&cpu);
|
||||
check(snapshot_polls == 0, "idle snapshot work stays amortized");
|
||||
g_psx_savestate_pending = 1;
|
||||
poll_staged_user_snapshot(&cpu);
|
||||
check(snapshot_polls == 1 && snapshot_pc == pc,
|
||||
"staged request reaches the next native boundary");
|
||||
in_exception = 1;
|
||||
poll_staged_user_snapshot(&cpu);
|
||||
check(snapshot_polls == 1, "exception cannot service a user snapshot");
|
||||
in_exception = 0;
|
||||
g_dirty_safe_resume_pc = pc + 4;
|
||||
poll_staged_user_snapshot(&cpu);
|
||||
check(snapshot_polls == 2 && snapshot_pc == pc + 4,
|
||||
"staged dirty-code request uses its own resume boundary");
|
||||
g_psx_savestate_pending = 0;
|
||||
poll_staged_user_snapshot(&cpu);
|
||||
check(snapshot_polls == 2, "completed request stops urgent polling");
|
||||
g_dirty_safe_resume_pc = 0;
|
||||
check(psx_irq_resume_context_snapshot_safe_at(pc), "native boundary admitted");
|
||||
psx_snapshot_host_call_begin();
|
||||
psx_snapshot_host_call_begin();
|
||||
|
||||
Reference in New Issue
Block a user