mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
Add private dispatch metadata and bounded worker command capture
This commit is contained in:
@@ -2080,3 +2080,15 @@ Guest-frame lookup merged multiple extra presentations and could not verify
|
||||
HUD stability. Added duplicate-frame/freeze/eviction regression coverage and
|
||||
retained legacy frame lookup. Six focused framework checks and all 21 title
|
||||
checks pass on Windows and Linux, and both runtime builds link successfully.
|
||||
|
||||
|
||||
2026-10-21 BRIEF-5 continuation: private execution now owns IRQ/DMA/timer,
|
||||
I-cache and callback nesting state, with code-validation/MMIO metadata captured
|
||||
beside RAM. Generated game and built-in BIOS expose native code lookup for a
|
||||
sealed worker dispatcher; loadable BIOS ABI advances to 3. Added bounded,
|
||||
thread-local immutable render command capture, independent of the live SPSC
|
||||
producer. Concurrent memory/IRQ and capture/consumer fixtures pass on Windows
|
||||
and Mac; Mac syntax checks pass for the changed device modules, mod runtime,
|
||||
and regenerated OpenBIOS. Worker scheduling/plugin host-state/GL handoff remain
|
||||
in progress. No Stage 3 acceptance or push; ordered route/performance gates have
|
||||
not started for this implementation.
|
||||
|
||||
@@ -1 +1 @@
|
||||
74e1a7bb76ab90d30f3cf4d5627204d0184d88b9abc605b742e0f84934c4c464
|
||||
7b08de0636dd809d712346f2a0169885543dce64379d7d977320bfd2dfa59857
|
||||
|
||||
@@ -21,7 +21,7 @@ extern void psx_restore_state_escape(void);
|
||||
extern void gte_execute(CPUState* cpu, uint32_t cmd);
|
||||
extern void gte_write_data(CPUState* cpu, uint8_t reg, uint32_t val);
|
||||
extern uint32_t gte_read_data(CPUState* cpu, uint8_t reg);
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern void debug_server_trace_dispatch(uint32_t func_addr);
|
||||
/* BIOS HLE tier (CLAUDE.md §0 amendment 2026-07-02): null-by-default
|
||||
* hook consulted at the top of every dispatch iteration, BEFORE any
|
||||
@@ -9057,13 +9057,30 @@ static PsxRecompFunc psx_bios_hit_body(CPUState* cpu, uint32_t addr, uint32_t ke
|
||||
return home;
|
||||
}
|
||||
|
||||
extern int psx_kernel_bless_dispatchable(uint32_t phys);
|
||||
static int psx_bios_execution_lookup(CPUState* cpu, uint32_t addr, PsxExecutionCode* code) {
|
||||
if (psx_bios_try_native_call_stub(cpu, addr)) { code->function = 0; code->resume_pc = cpu->pc; return 2; }
|
||||
const uint32_t key = normalize(addr);
|
||||
int lo = 0, hi = (int)(sizeof(dispatch_table) / sizeof(dispatch_table[0])) - 1;
|
||||
while (lo <= hi) {
|
||||
int mid = (lo + hi) / 2;
|
||||
if (dispatch_table[mid].addr == key) {
|
||||
if (key - 0x500u < 0x46A0u && !psx_kernel_bless_dispatchable(key)) return 0;
|
||||
code->function = psx_bios_hit_body(cpu, addr, key, dispatch_table[mid].func);
|
||||
code->resume_pc = addr; return 1;
|
||||
}
|
||||
if (dispatch_table[mid].addr < key) lo = mid + 1; else hi = mid - 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int dirty_ram_dispatch(CPUState* cpu, uint32_t addr, uint32_t stop_addr);
|
||||
extern int dirty_ram_is_dirty(uint32_t phys);
|
||||
extern int psx_kernel_bless_dispatchable(uint32_t phys);
|
||||
extern void fntrace_record(CPUState* cpu, uint32_t target);
|
||||
extern uint64_t g_dispatch_static_hits;
|
||||
|
||||
extern int g_psx_dispatch_depth; /* runtime-owned: shared dispatch state, not per-image */
|
||||
extern PSX_THREAD_LOCAL int g_psx_dispatch_depth; /* execution-thread dispatch state */
|
||||
|
||||
static void psx_dispatch_check_return_boundary(CPUState* cpu, uint32_t stop_addr) {
|
||||
if (stop_addr != 0u) {
|
||||
@@ -9328,4 +9345,5 @@ const PsxBiosBackend OpenBIOS_psx_bios_backend = {
|
||||
OpenBIOS_psx_bios_kernel_body_count,
|
||||
OpenBIOS_psx_bios_kernel_patch_ranges,
|
||||
OpenBIOS_psx_bios_kernel_patch_range_count,
|
||||
psx_bios_execution_lookup,
|
||||
};
|
||||
|
||||
@@ -26,7 +26,9 @@ extern void cosim_instr(uint32_t pc);
|
||||
extern void debug_server_log_call_entry(uint32_t func_addr);
|
||||
extern void debug_server_cyc_observe(uint32_t block_leader_phys);
|
||||
#endif
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
#ifndef PSX_OVERLAY_DLL_BUILD
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
#endif
|
||||
/* Phase 1.0e-d: per-block guest cycle accounting.
|
||||
* Compile generated code with -DPSX_ENABLE_BLOCK_CYCLES=1 to
|
||||
* activate cycle advancement at every block leader. */
|
||||
|
||||
@@ -3724,7 +3724,7 @@ void CodeGenerator::emit_runtime_externs(std::ostream& ss) const {
|
||||
ss << "extern void gte_ws_set_suppress(int on); /* widescreen far-backdrop un-squash (gte.cpp) */\n";
|
||||
if (!config_.overlay_mode && !config_.draw_distance_clamp_sites.empty())
|
||||
ss << "extern uint32_t g_psx_draw_distance_clamp; /* [[draw_distance.clamp]] switch (draw_distance.c) */\n";
|
||||
ss << "extern uint32_t g_debug_last_store_pc; /* exact PC of the executing SW/SH/SB — wtrace/readtrace producer attribution (debug_server.c) */\n\n";
|
||||
ss << "#ifndef PSX_OVERLAY_DLL_BUILD\nextern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;\n#endif\n\n";
|
||||
}
|
||||
|
||||
void CodeGenerator::emit_unaligned_helpers(std::ostream& ss, bool as_inline) const {
|
||||
|
||||
@@ -1826,7 +1826,7 @@ void FullFunctionEmitter::emit_dispatch(
|
||||
out += "extern void gte_execute(CPUState* cpu, uint32_t cmd);\n";
|
||||
out += "extern void gte_write_data(CPUState* cpu, uint8_t reg, uint32_t val);\n";
|
||||
out += "extern uint32_t gte_read_data(CPUState* cpu, uint8_t reg);\n";
|
||||
out += "extern uint32_t g_debug_current_func_addr;\n";
|
||||
out += "extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;\n";
|
||||
out += "extern void debug_server_trace_dispatch(uint32_t func_addr);\n";
|
||||
out += "/* BIOS HLE tier (CLAUDE.md \xC2\xA7""0 amendment 2026-07-02): null-by-default\n";
|
||||
out += " * hook consulted at the top of every dispatch iteration, BEFORE any\n";
|
||||
@@ -2229,6 +2229,23 @@ void FullFunctionEmitter::emit_dispatch(
|
||||
out += " }\n";
|
||||
out += " return home;\n";
|
||||
out += "}\n\n";
|
||||
out += "extern int psx_kernel_bless_dispatchable(uint32_t phys);\n";
|
||||
out += "static int psx_bios_execution_lookup(CPUState* cpu, uint32_t addr, PsxExecutionCode* code) {\n";
|
||||
out += " if (psx_bios_try_native_call_stub(cpu, addr)) { code->function = 0; code->resume_pc = cpu->pc; return 2; }\n";
|
||||
out += " const uint32_t key = normalize(addr);\n";
|
||||
out += " int lo = 0, hi = (int)(sizeof(dispatch_table) / sizeof(dispatch_table[0])) - 1;\n";
|
||||
out += " while (lo <= hi) {\n";
|
||||
out += " int mid = (lo + hi) / 2;\n";
|
||||
out += " if (dispatch_table[mid].addr == key) {\n";
|
||||
if (addr_model().has_kbless()) {
|
||||
out += fmt::format(" if (key - 0x{:X}u < 0x{:X}u && !psx_kernel_bless_dispatchable(key)) return 0;\n",
|
||||
addr_model().kbless_ram_lo(), addr_model().kbless_ram_hi() - addr_model().kbless_ram_lo());
|
||||
}
|
||||
out += " code->function = psx_bios_hit_body(cpu, addr, key, dispatch_table[mid].func);\n";
|
||||
out += " code->resume_pc = addr; return 1;\n";
|
||||
out += " }\n";
|
||||
out += " if (dispatch_table[mid].addr < key) lo = mid + 1; else hi = mid - 1;\n";
|
||||
out += " }\n return 0;\n}\n\n";
|
||||
|
||||
out += "extern int dirty_ram_dispatch(CPUState* cpu, uint32_t addr, uint32_t stop_addr);\n";
|
||||
out += "extern int dirty_ram_is_dirty(uint32_t phys);\n";
|
||||
@@ -2236,7 +2253,7 @@ void FullFunctionEmitter::emit_dispatch(
|
||||
out += "extern void fntrace_record(CPUState* cpu, uint32_t target);\n";
|
||||
out += "extern uint64_t g_dispatch_static_hits;\n";
|
||||
out += "\n";
|
||||
out += "extern int g_psx_dispatch_depth; /* runtime-owned: shared dispatch state, not per-image */\n\n";
|
||||
out += "extern PSX_THREAD_LOCAL int g_psx_dispatch_depth; /* execution-thread dispatch state */\n\n";
|
||||
// Return PCs are compared in full, segment included, as psx_call_contract
|
||||
// does (docs/SEGMENT_AWARE_CODE.md §5.5): stop_addr is the link the call
|
||||
// wrote, in its body's segment.
|
||||
@@ -2536,6 +2553,7 @@ void FullFunctionEmitter::emit_dispatch(
|
||||
" {0}psx_bios_kernel_body_count," "\n"
|
||||
" {0}psx_bios_kernel_patch_ranges," "\n"
|
||||
" {0}psx_bios_kernel_patch_range_count," "\n"
|
||||
" psx_bios_execution_lookup," "\n"
|
||||
"}};" "\n",
|
||||
g_sym_prefix);
|
||||
}
|
||||
@@ -2639,7 +2657,7 @@ EmitStats FullFunctionEmitter::emit(
|
||||
full_c += "extern void debug_server_log_call_entry(uint32_t func_addr);\n";
|
||||
full_c += "extern void debug_server_cyc_observe(uint32_t block_leader_phys);\n";
|
||||
full_c += "#endif\n";
|
||||
full_c += "extern uint32_t g_debug_last_store_pc;\n";
|
||||
full_c += "#ifndef PSX_OVERLAY_DLL_BUILD\nextern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;\n#endif\n";
|
||||
full_c += "/* Phase 1.0e-d: per-block guest cycle accounting.\n";
|
||||
full_c += " * Compile generated code with -DPSX_ENABLE_BLOCK_CYCLES=1 to\n";
|
||||
full_c += " * activate cycle advancement at every block leader. */\n";
|
||||
|
||||
@@ -32,7 +32,7 @@ bool emit_game_dispatch(const std::vector<GameDispatchUnit>& units,
|
||||
const bool cps = units.front().codegen->cps_enabled();
|
||||
std::ostringstream ds;
|
||||
ds << "/* Generated by PSXRecomp - dynamic dispatch table */\n";
|
||||
ds << "#include \"psx_runtime.h\"\n\n";
|
||||
ds << "#include \"psx_runtime.h\"\n#include \"psx_execution.h\"\n\n";
|
||||
ds << "extern void psx_check_interrupts_dispatch_entry(CPUState* cpu, uint32_t resume_pc);\n\n";
|
||||
ds << "extern int dirty_ram_text_native_ok_ranges_from(const uint32_t* lo_len_pairs, uint32_t count, uint32_t exec_pc);\n\n";
|
||||
ds << "extern int dirty_ram_text_native_ok_ranges(const uint32_t* lo_len_pairs, uint32_t count);\n\n";
|
||||
@@ -289,6 +289,10 @@ bool emit_game_dispatch(const std::vector<GameDispatchUnit>& units,
|
||||
ds << "}\n\n";
|
||||
|
||||
ds << "/* Maps PS1 address to compiled game code. Returns 1 if dispatched, 0 if unknown. */\n";
|
||||
ds << "int psx_game_execution_lookup(uint32_t addr, PsxExecutionCode* code) {\n";
|
||||
ds << " const PsxGameDispatchEntry* entry = psx_game_find_entry(addr);\n";
|
||||
ds << " if (!entry || !psx_game_text_native_ok(addr)) return 0;\n";
|
||||
ds << " code->function = entry->fn; code->resume_pc = entry->resume_pc; return 1;\n}\n\n";
|
||||
ds << "int psx_dispatch_game_compiled(CPUState* cpu, uint32_t addr) {\n";
|
||||
ds << " const PsxGameDispatchEntry* entry = psx_game_find_entry(addr);\n";
|
||||
ds << " if (!entry) return 0;\n";
|
||||
|
||||
@@ -149,8 +149,8 @@ CONTRACT_HARNESS = r"""
|
||||
#include "cpu_state.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
int g_psx_call_bail;
|
||||
uint64_t g_psx_bail_first, g_psx_bail_resolved, g_psx_bail_flattened, g_psx_bail_anomaly;
|
||||
PSX_THREAD_LOCAL int g_psx_call_bail;
|
||||
PSX_THREAD_LOCAL uint64_t g_psx_bail_first, g_psx_bail_resolved, g_psx_bail_flattened, g_psx_bail_anomaly;
|
||||
void psx_bail_record(uint32_t a, uint32_t b, uint32_t c, uint32_t d) {
|
||||
(void)a; (void)b; (void)c; (void)d;
|
||||
}
|
||||
|
||||
@@ -1654,6 +1654,11 @@ if(BUILD_TESTING)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(render_thread_test PRIVATE Threads::Threads)
|
||||
add_test(NAME render_thread_test COMMAND render_thread_test)
|
||||
add_executable(render_capture_test tests/test_render_capture.c src/render_thread.c)
|
||||
target_include_directories(render_capture_test PRIVATE include)
|
||||
target_link_libraries(render_capture_test PRIVATE Threads::Threads)
|
||||
add_test(NAME render_capture_test COMMAND render_capture_test)
|
||||
|
||||
# Present thread core ([video] present_thread): submission order is
|
||||
# presentation order, slots are never composed while queued or on
|
||||
# screen, done tokens reach the next composer, stop drains.
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
# overlay-cache hazard (same class as the "ws emission not in cg hash" gap).
|
||||
set(PSXRECOMP_CODEGEN_HASH_SRCS
|
||||
${PSXRECOMP_CODEGEN_HASH_ROOT}/runtime/include/psx_thread_local.h
|
||||
${PSXRECOMP_CODEGEN_HASH_ROOT}/runtime/include/psx_execution.h
|
||||
${PSXRECOMP_CODEGEN_HASH_ROOT}/recompiler/src/code_generator.cpp
|
||||
${PSXRECOMP_CODEGEN_HASH_ROOT}/recompiler/src/mips_decoder.cpp
|
||||
${PSXRECOMP_CODEGEN_HASH_ROOT}/recompiler/src/control_flow.cpp
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "psx_thread_local.h"
|
||||
/*
|
||||
* card_data_writes.h — always-on capture of where the BIOS stores
|
||||
* incoming SIO data bytes during memory-card READ_DATA.
|
||||
@@ -33,7 +32,7 @@ extern "C" {
|
||||
|
||||
typedef struct {
|
||||
uint64_t seq; /* monotonic across all entries */
|
||||
PSX_THREAD_LOCAL uint64_t psx_cycle_count;
|
||||
uint64_t psx_cycle_count;
|
||||
uint32_t addr; /* dest physical RAM address */
|
||||
uint32_t store_pc; /* g_debug_last_store_pc when stored */
|
||||
uint32_t func_addr; /* g_debug_current_func_addr */
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "psx_thread_local.h"
|
||||
/*
|
||||
* card_read_summary.h — non-evicting evidence ring for the first 32
|
||||
* completed memory-card reads.
|
||||
@@ -27,7 +26,7 @@ extern "C" {
|
||||
|
||||
typedef struct {
|
||||
uint64_t seq; /* monotonic; first record = 0 */
|
||||
PSX_THREAD_LOCAL uint64_t psx_cycle_count; /* guest cycle clock at completion */
|
||||
uint64_t psx_cycle_count; /* guest cycle clock at completion */
|
||||
uint32_t current_func; /* g_debug_current_func_addr at MC_READ_END */
|
||||
uint32_t last_store_pc; /* g_debug_last_store_pc at MC_READ_END */
|
||||
uint16_t sector;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* ============================================================================
|
||||
#include "psx_thread_local.h"
|
||||
/* ============================================================================
|
||||
* cpu_state.h — Phase 2 runtime version
|
||||
* ----------------------------------------------------------------------------
|
||||
* This is the RUNTIME version of the CPUState header. It has the same struct
|
||||
@@ -315,11 +316,11 @@ static inline uint32_t *psx_overlay_last_store_pc(void) {
|
||||
}
|
||||
#define g_debug_last_store_pc (*psx_overlay_last_store_pc())
|
||||
#else
|
||||
extern int g_psx_call_bail;
|
||||
extern uint64_t g_psx_bail_first; /* contract violations detected */
|
||||
extern uint64_t g_psx_bail_resolved; /* unwinds resolved at a call site */
|
||||
extern uint64_t g_psx_bail_flattened; /* unwinds flattened at outermost */
|
||||
extern uint64_t g_psx_bail_anomaly; /* bail flag seen where impossible */
|
||||
extern PSX_THREAD_LOCAL int g_psx_call_bail;
|
||||
extern PSX_THREAD_LOCAL uint64_t g_psx_bail_first; /* contract violations detected */
|
||||
extern PSX_THREAD_LOCAL uint64_t g_psx_bail_resolved; /* unwinds resolved at a call site */
|
||||
extern PSX_THREAD_LOCAL uint64_t g_psx_bail_flattened; /* unwinds flattened at outermost */
|
||||
extern PSX_THREAD_LOCAL uint64_t g_psx_bail_anomaly; /* bail flag seen where impossible */
|
||||
#endif
|
||||
|
||||
/* Deduped wild-return source ledger (traps.c). Not present in overlay DLLs
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
#include "psx_thread_local.h"
|
||||
/*
|
||||
* debug_server.h -- TCP debug server for PSX recomp v4
|
||||
*
|
||||
* Non-blocking TCP server on localhost (default port 4370).
|
||||
@@ -127,7 +128,7 @@ int debug_server_is_connected(void);
|
||||
|
||||
/* Current dispatch target — set by psx_dispatch() before each call.
|
||||
* Captured by the write-trace hook for function attribution. */
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
|
||||
/* Multi-range write-trace check. Called from memory.c write paths.
|
||||
* Iterates up to 8 configurable address ranges; records matching
|
||||
@@ -157,7 +158,7 @@ void debug_server_log_call_entry_fn(uint32_t func_addr);
|
||||
* store (sb/sh/sw/swl/swr/swc2). Read by SIO/MMIO write handlers to
|
||||
* attribute the writing instruction without needing a full call-stack
|
||||
* walk. Stale across non-store instructions but every store updates it. */
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
|
||||
/* SIO write PC tracer ring — records (pc, addr, value, byte_seq) for every
|
||||
* write to SIO_DATA / SIO_CTRL / SIO_STAT. Always-on; pulled via TCP cmd
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* psx_bios_backend.h — the routing seam between the runtime and whichever
|
||||
* recompiled BIOS is active.
|
||||
*
|
||||
@@ -25,6 +26,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "psx_bios_image.h"
|
||||
#include "psx_execution.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -56,10 +58,13 @@ typedef struct PsxBiosBackend {
|
||||
* each range's hi. Null/0 for an image that declares no slots. */
|
||||
const PsxKernelPatchRange *kernel_patch_ranges;
|
||||
uint32_t kernel_patch_range_count;
|
||||
/* Side-effect-free code selection for an isolated execution context.
|
||||
* 1: function/resume_pc; 2: native vector instructions ran and set pc. */
|
||||
int (*execution_lookup)(struct CPUState *, uint32_t, PsxExecutionCode *);
|
||||
} PsxBiosBackend;
|
||||
|
||||
/* Dispatch nesting depth, shared by every backend (psx_bios_backend.c). */
|
||||
extern int g_psx_dispatch_depth;
|
||||
extern PSX_THREAD_LOCAL int g_psx_dispatch_depth;
|
||||
|
||||
/* The backend in use. Null before psx_bios_select() runs; every forwarder and
|
||||
* every consumer of psx_bios_image depends on it being set first. */
|
||||
|
||||
@@ -84,7 +84,7 @@ struct CPUState;
|
||||
struct OverlayCallbacks;
|
||||
struct PsxBiosBackend;
|
||||
|
||||
#define PSX_BIOS_MODULE_ABI_VERSION 2
|
||||
#define PSX_BIOS_MODULE_ABI_VERSION 3
|
||||
|
||||
/* Combined tag: ABI version (low 16) | codegen flavor (high 16), like
|
||||
* PSX_OVERLAY_ABI_TAG. The flavor comes from the same -DPSX_OVERLAY_FLAVOR
|
||||
|
||||
@@ -40,9 +40,9 @@ static uint32_t s_mod_guest_count_dummy;
|
||||
* mappings): `extern int g_psx_dispatch_depth;` became
|
||||
* `extern int (*g_psx_bm_dispatch_depth_p);`, and so on. Defined here. */
|
||||
int (**g_psx_bm_hle_hook_p)(CPUState *cpu, uint32_t phys) = &s_hle_hook_dummy;
|
||||
int *g_psx_bm_dispatch_depth_p = &s_depth_dummy;
|
||||
PSX_THREAD_LOCAL int *g_psx_bm_dispatch_depth_p = &s_depth_dummy;
|
||||
uint64_t *g_psx_bm_static_hits_p = &s_hits_dummy;
|
||||
uint32_t *g_psx_bm_current_func_addr_p = &s_func_addr_dummy;
|
||||
PSX_THREAD_LOCAL uint32_t *g_psx_bm_current_func_addr_p = &s_func_addr_dummy;
|
||||
uint64_t *g_psx_bm_bail_flattened_p = &s_flattened_dummy;
|
||||
uint32_t *g_psx_bm_ram_mask_p = &s_ram_mask_dummy;
|
||||
uint64_t *g_psx_bm_frame_count_p = &s_frame_count_dummy;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef PSX_EXECUTION_H
|
||||
#define PSX_EXECUTION_H
|
||||
#include <stdint.h>
|
||||
#include "psx_thread_local.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
struct CPUState;
|
||||
typedef struct PsxExecutionCode {
|
||||
void (*function)(struct CPUState *);
|
||||
uint32_t resume_pc;
|
||||
} PsxExecutionCode;
|
||||
typedef void (*PsxExecutionDispatch)(struct CPUState *, uint32_t, uint32_t);
|
||||
typedef void (*PsxExecutionAbort)(const char *, uint32_t);
|
||||
/* A private execution supplies its sealed dispatch table and abort landing.
|
||||
* NULL selects the ordinary runtime. Never replace these on another thread. */
|
||||
extern PSX_THREAD_LOCAL PsxExecutionDispatch g_psx_execution_dispatch;
|
||||
extern PSX_THREAD_LOCAL PsxExecutionAbort g_psx_execution_abort;
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
#ifndef PSXRECOMP_PSX_ICACHE_H
|
||||
#define PSXRECOMP_PSX_ICACHE_H
|
||||
|
||||
@@ -7,8 +8,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern uint32_t g_psx_icache_tv[1024];
|
||||
extern int g_psx_icache_active;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_psx_icache_tv[1024];
|
||||
extern PSX_THREAD_LOCAL int g_psx_icache_active;
|
||||
extern int g_ls_replay_active;
|
||||
void psx_icache_reset(void);
|
||||
void psx_icache_fetch(CPUState *cpu, uint32_t addr);
|
||||
|
||||
@@ -57,6 +57,15 @@ void memory_backing_destroy(PsxMemoryBacking *backing);
|
||||
void memory_backing_capture(PsxMemoryBacking *destination);
|
||||
PsxMemoryBacking *memory_backing_bind(PsxMemoryBacking *backing);
|
||||
extern PSX_THREAD_LOCAL uint8_t *g_psx_ram;
|
||||
/* Code-validity and MMIO metadata accompanies the backing snapshot. Capture
|
||||
* on the source owner; bind only on a dedicated worker after backing_bind.
|
||||
* The active code image remains immutable until that worker has stopped. */
|
||||
typedef struct PsxMemoryExecutionState PsxMemoryExecutionState;
|
||||
PsxMemoryExecutionState *memory_execution_create(void);
|
||||
void memory_execution_destroy(PsxMemoryExecutionState *state);
|
||||
void memory_execution_capture(PsxMemoryExecutionState *state);
|
||||
void memory_execution_bind(const PsxMemoryExecutionState *state, const uint32_t *status);
|
||||
|
||||
int psx_ram_8mb_active(void);
|
||||
void psx_ram_reset_size_request(void);
|
||||
/* memory_init(): latch the requested geometry for this boot. */
|
||||
|
||||
@@ -89,6 +89,21 @@ int rt_on_render_thread(void);
|
||||
void *rt_cmd_begin(uint16_t op, uint16_t flags, uint32_t payload_bytes);
|
||||
void rt_cmd_commit(void);
|
||||
|
||||
/* A dedicated guest worker records into its own bounded list. It never writes
|
||||
* the SPSC ring. Ownership transfers only after end; the consumer executes a
|
||||
* sealed list on the render thread, or drops it when its generation is late.
|
||||
* Allocation happens before publication, never while guest code is running. */
|
||||
typedef struct RtCommandList RtCommandList;
|
||||
typedef void (*RtCaptureAbort)(const char *reason);
|
||||
RtCommandList *rt_list_create(size_t capacity);
|
||||
void rt_list_destroy(RtCommandList *list);
|
||||
void rt_capture_begin(RtCommandList *list, RtCaptureAbort failure);
|
||||
void rt_capture_end(void);
|
||||
int rt_capture_active(void);
|
||||
size_t rt_list_bytes(const RtCommandList *list);
|
||||
void rt_list_execute(const RtCommandList *list, RtExecFn exec, void *user);
|
||||
|
||||
|
||||
/* Close a guest frame: enqueue a frame marker, then block while more than
|
||||
* max_frames closed frames are unconsumed. */
|
||||
void rt_frame_end(void);
|
||||
|
||||
@@ -58,8 +58,8 @@ static double boot_state_mono_ms(void) {
|
||||
extern uint8_t* memory_get_ram_ptr(void);
|
||||
extern uint32_t memory_get_ram_bytes(void);
|
||||
extern uint8_t* memory_get_scratchpad_ptr(void);
|
||||
extern uint32_t i_stat;
|
||||
extern uint32_t i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_mask;
|
||||
extern PSX_THREAD_LOCAL uint64_t psx_cycle_count;
|
||||
extern void timers_get_snapshot(uint16_t counter[3], uint32_t mode[3],
|
||||
uint16_t target[3], int32_t irq_line[3],
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* card_data_writes.c — capture BIOS RAM destination of card data bytes. */
|
||||
|
||||
#include "card_data_writes.h"
|
||||
#include "psx_cycles.h"
|
||||
#include <string.h>
|
||||
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
|
||||
/* "Armed" byte from the most recent SIO_RX_DATA read inside a card
|
||||
* data phase. The next matching RAM write captures it. */
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* card_read_summary.c — non-evicting first-32 completed card reads. */
|
||||
|
||||
#include "card_read_summary.h"
|
||||
#include "psx_cycles.h"
|
||||
#include <string.h>
|
||||
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
|
||||
static CardReadSummary s_ring[CARD_READ_SUMMARY_CAP];
|
||||
static uint32_t s_count = 0;
|
||||
|
||||
+4
-3
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/*
|
||||
* cdrom.c — PS1 CD-ROM controller (adapted from psxrecomp-v3)
|
||||
*
|
||||
@@ -53,11 +54,11 @@ extern uint32_t iso_cdda_track_pregap_lba(void* handle, int track);
|
||||
extern int iso_cdda_track_is_audio(void* handle, int track);
|
||||
|
||||
/* I_STAT owned by memory.c — set bit 2 for CDROM IRQ */
|
||||
extern uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
/* Central IRQ-raise choke point (interrupts.c) — also records the device ring. */
|
||||
extern void psx_irq_raise(uint32_t bit, uint32_t detail);
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
extern uint64_t s_frame_count;
|
||||
|
||||
/* CD-ROM state */
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ typedef int sock_t;
|
||||
#define COSIM_SLEEP(ms) usleep((ms)*1000)
|
||||
#endif
|
||||
|
||||
extern uint32_t i_stat, i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat, i_mask;
|
||||
extern PSX_THREAD_LOCAL uint64_t psx_cycle_count;
|
||||
|
||||
/* EXPERIMENTAL input-driven cosim: held pad state applied every frame by the headless
|
||||
|
||||
@@ -33,8 +33,8 @@ static inline uint64_t fnv_u64(uint64_t h, uint64_t v) { return fnv(h, &v, 8); }
|
||||
extern CPUState *debug_cpu_ptr;
|
||||
extern uint8_t *memory_get_ram_ptr(void);
|
||||
extern uint8_t *memory_get_scratchpad_ptr(void);
|
||||
extern uint32_t i_stat;
|
||||
extern uint32_t i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_mask;
|
||||
extern PSX_THREAD_LOCAL uint64_t psx_cycle_count;
|
||||
extern uint32_t dirty_ram_get_bitmap_word(uint32_t word_index);
|
||||
extern uint32_t dirty_ram_get_bitmap_word_count(void);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* crash_trace.c — unified crash diagnostic dump.
|
||||
*
|
||||
* Writes a single JSON report file (psx_last_run_report.json) on:
|
||||
@@ -94,15 +95,15 @@ extern int psx_netplay_is_resimulating(void);
|
||||
extern uint64_t s_frame_count;
|
||||
|
||||
/* Globals from debug_server.c we want to capture. */
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
|
||||
/* Native dispatch nesting depth (generated/SCPH1001_dispatch.c). Incremented per
|
||||
* nested psx_dispatch_call, decremented on return. A huge value at crash time is
|
||||
* the direct fingerprint of runaway recursion (the host C stack mirrors the guest
|
||||
* call graph, so an unbounded call chain overflows it -> STATUS_STACK_OVERFLOW
|
||||
* 0xC00000FD). Pairs with the dirty_block tail, which names the recursing PCs. */
|
||||
extern int g_psx_dispatch_depth;
|
||||
extern PSX_THREAD_LOCAL int g_psx_dispatch_depth;
|
||||
|
||||
/* Dispatch ring — accessor wrappers exported by debug_server.c. */
|
||||
#define DISPATCH_TRACE_CAP (1 << 16)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/*
|
||||
* debug_server.c -- TCP debug server for PSX recomp v4
|
||||
*
|
||||
@@ -119,8 +120,8 @@
|
||||
#include "debug_socket_io.h"
|
||||
|
||||
/* ---- Externs from runtime ---- */
|
||||
extern uint32_t i_stat;
|
||||
extern uint32_t i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_mask;
|
||||
|
||||
/* Memory access (from memory.c) */
|
||||
extern uint32_t psx_read_word(uint32_t addr);
|
||||
@@ -491,10 +492,10 @@ static struct { uint32_t lo, hi; } s_wtrace_trans_ranges[WTRACE_TRANS_MAX_RANGES
|
||||
static int s_wtrace_trans_range_count = 0;
|
||||
|
||||
/* Function attribution global — set by psx_dispatch() before each call. */
|
||||
uint32_t g_debug_current_func_addr = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr = 0;
|
||||
|
||||
/* Last store PC — set by recompiler emitter before every store instruction. */
|
||||
uint32_t g_debug_last_store_pc = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc = 0;
|
||||
|
||||
/* Static dispatch hit counter — incremented by generated dispatch code on
|
||||
* every binary-search hit (i.e. successfully dispatched to static C). */
|
||||
@@ -11391,9 +11392,9 @@ static void wtrace_fill_entry(WriteTraceEntry *e, uint64_t seq,
|
||||
uint32_t phys, uint32_t old_val,
|
||||
uint32_t new_val, uint8_t width)
|
||||
{
|
||||
extern int g_dma_exec_depth; /* >0 while a DMA moves data (dma.c) */
|
||||
extern int g_dma_cur_ch; /* in-flight DMA channel, or -1 */
|
||||
extern uint32_t g_dma_initiator_pc; /* guest PC that kicked the DMA */
|
||||
extern PSX_THREAD_LOCAL int g_dma_exec_depth; /* >0 while a DMA moves data (dma.c) */
|
||||
extern PSX_THREAD_LOCAL int g_dma_cur_ch; /* in-flight DMA channel, or -1 */
|
||||
extern PSX_THREAD_LOCAL uint32_t g_dma_initiator_pc; /* guest PC that kicked the DMA */
|
||||
uint32_t ra = debug_cpu_ptr ? debug_cpu_ptr->gpr[31] : 0;
|
||||
e->seq = seq;
|
||||
e->addr = phys;
|
||||
|
||||
@@ -283,8 +283,8 @@ static inline void exec_pc_table_record(uint32_t pc) {
|
||||
}
|
||||
|
||||
/* From debug_server.c — keep our outer-frame attribution coherent. */
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
|
||||
/* Execution-mode tag for the event-timeline ring: 1 while a dirty_ram_dispatch
|
||||
* call is on the stack. Cleared defensively at the psx_check_interrupts longjmp
|
||||
@@ -1128,8 +1128,8 @@ int dirty_ram_re954_json(char *out, int cap) {
|
||||
* so the real driver address is not pre-assumed. NB: there is no due-cycle event
|
||||
* scheduler in this build (psx_cycles.c), so "next scheduled event cycle" is N/A;
|
||||
* cycles-advanced-per-frame + I_STAT/I_MASK cover event progress instead. */
|
||||
extern uint32_t i_stat; /* owned by memory.c */
|
||||
extern uint32_t i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat; /* owned by memory.c */
|
||||
extern PSX_THREAD_LOCAL uint32_t i_mask;
|
||||
|
||||
enum { XOP_JAL = 0, XOP_JALR = 1, XOP_JR = 2, XOP_J = 3, XOP_DD = 4, XOP_BR = 5,
|
||||
XOP_RES = 6 /* watched-target call RESOLUTION: site = path code (XRES_*),
|
||||
@@ -2570,7 +2570,7 @@ static int dirty_ram_dispatch_inner(CPUState* cpu, uint32_t addr, uint32_t stop_
|
||||
* still needs the oracle; this converts a hard crash into a catchable, diagnosed
|
||||
* bail (Rule 15: make the failure observable). */
|
||||
int dirty_ram_dispatch(CPUState* cpu, uint32_t addr, uint32_t stop_addr) {
|
||||
extern int g_psx_dispatch_depth;
|
||||
extern PSX_THREAD_LOCAL int g_psx_dispatch_depth;
|
||||
extern void psx_fatal_halt(const char *reason);
|
||||
/* `addr` is architectural: the interpreter runs at exactly this PC, so a
|
||||
* retail RAM-mirror PC (0x807xxxxx) keeps its own $ra/EPC while fetch_word
|
||||
@@ -2759,7 +2759,7 @@ int dirty_ram_run_span(CPUState* cpu, uint32_t start_pc, uint32_t stop_pc,
|
||||
* boundary? Mirrors the gate in psx_check_interrupts: a pending+unmasked I_STAT
|
||||
* bit, COP0 IEc + IM2 set, and not already inside the exception handler. */
|
||||
static int precise_irq_deliverable(CPUState *cpu) {
|
||||
extern uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
uint32_t sr = cpu->cop0[12];
|
||||
/* COP0 software interrupts (CAUSE.IP0/IP1 & SR.IM0/IM1): guest-raised via
|
||||
* mtc0 to CAUSE, independent of the INTC line (see psx_check_interrupts). */
|
||||
@@ -2832,7 +2832,7 @@ static void psx_run_precise(CPUState *cpu, uint32_t bcyc, int deadline_entry) {
|
||||
for (int i = 0; i < MAX_PRECISE_INSNS; i++) {
|
||||
if (!irq_taken && precise_irq_deliverable(cpu)) {
|
||||
uint32_t committed = pc;
|
||||
extern uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
g_slice_last_committed = committed;
|
||||
g_slice_last_istat = i_stat;
|
||||
g_slice_last_imask = i_mask;
|
||||
@@ -2896,7 +2896,7 @@ static void psx_run_precise(CPUState *cpu, uint32_t bcyc, int deadline_entry) {
|
||||
* after a take re-fires an IRQ the handler has not acked yet (an
|
||||
* 8-takes-in-11-insns storm), so gate on !irq_taken. */
|
||||
if (!irq_taken && precise_irq_deliverable(cpu)) {
|
||||
extern uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
g_slice_last_committed = committed;
|
||||
g_slice_last_istat = i_stat;
|
||||
g_slice_last_imask = i_mask;
|
||||
@@ -3281,7 +3281,7 @@ static int dirty_ram_dispatch_inner(CPUState* cpu, uint32_t addr, uint32_t stop_
|
||||
s_entry_poll_enabled = (e && e[0] == '0') ? 0 : 1;
|
||||
}
|
||||
if (s_entry_poll_enabled) {
|
||||
extern uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
uint32_t sr = cpu->cop0[12];
|
||||
int deliverable =
|
||||
((i_stat & i_mask) != 0u) &&
|
||||
|
||||
+31
-30
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* dma.c — PS1 DMA controller simulation (Phase 3).
|
||||
*
|
||||
* Implements all 7 channel register reads/writes, DPCR, DICR,
|
||||
@@ -52,11 +53,11 @@ extern uint32_t psx_read_word(uint32_t addr);
|
||||
extern void psx_write_word(uint32_t addr, uint32_t val);
|
||||
|
||||
/* Interrupt status — defined in memory.c */
|
||||
extern uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
/* Central IRQ-raise choke point (interrupts.c) — also records the device ring. */
|
||||
extern void psx_irq_raise(uint32_t bit, uint32_t detail);
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
extern uint64_t s_frame_count;
|
||||
|
||||
/* ---- Per-channel registers ---- */
|
||||
@@ -67,7 +68,7 @@ typedef struct {
|
||||
uint32_t chcr; /* +0x08: Channel control */
|
||||
} DMAChannel;
|
||||
|
||||
static DMAChannel channels[7];
|
||||
static PSX_THREAD_LOCAL DMAChannel channels[7];
|
||||
|
||||
typedef struct {
|
||||
uint8_t active;
|
||||
@@ -78,12 +79,12 @@ typedef struct {
|
||||
uint32_t start_addr; /* madr at transfer start (CD overlay capture) */
|
||||
} DMAAsyncChannel;
|
||||
|
||||
static DMAAsyncChannel mdec_async[2];
|
||||
static DMAAsyncChannel cdrom_async;
|
||||
static DMAGPULinkedList gpu_linked_list;
|
||||
static DMAGpuOtStats gpu_ot_stats;
|
||||
static uint64_t gpu_ot_start_cycle;
|
||||
static uint32_t gpu_ot_polls_this_walk;
|
||||
static PSX_THREAD_LOCAL DMAAsyncChannel mdec_async[2];
|
||||
static PSX_THREAD_LOCAL DMAAsyncChannel cdrom_async;
|
||||
static PSX_THREAD_LOCAL DMAGPULinkedList gpu_linked_list;
|
||||
static PSX_THREAD_LOCAL DMAGpuOtStats gpu_ot_stats;
|
||||
static PSX_THREAD_LOCAL uint64_t gpu_ot_start_cycle;
|
||||
static PSX_THREAD_LOCAL uint32_t gpu_ot_polls_this_walk;
|
||||
|
||||
/* ---- CD DMA transfer log ---- */
|
||||
/* Every forward CH3 DMA that lands below 0x1C0000 (game data region) records
|
||||
@@ -93,9 +94,9 @@ static uint32_t gpu_ot_polls_this_walk;
|
||||
* overlay regions back to disc positions for extract_overlays.py. */
|
||||
#define CD_DMA_LOG_CAP 65536
|
||||
typedef struct { int lba; uint32_t dest; uint32_t size; } CdDmaEntry;
|
||||
static CdDmaEntry cd_dma_log[CD_DMA_LOG_CAP];
|
||||
static uint32_t cd_dma_log_head = 0;
|
||||
static uint32_t cd_dma_log_total = 0;
|
||||
static PSX_THREAD_LOCAL CdDmaEntry cd_dma_log[CD_DMA_LOG_CAP];
|
||||
static PSX_THREAD_LOCAL uint32_t cd_dma_log_head = 0;
|
||||
static PSX_THREAD_LOCAL uint32_t cd_dma_log_total = 0;
|
||||
|
||||
static void cd_dma_log_push(int lba, uint32_t dest, uint32_t size) {
|
||||
cd_dma_log[cd_dma_log_head % CD_DMA_LOG_CAP].lba = lba;
|
||||
@@ -138,18 +139,18 @@ void cd_dma_log_get_entry(uint32_t idx, int *lba, uint32_t *dest, uint32_t *
|
||||
* DMA-sourced (not a CPU/dirty-interp store, whose g_debug_last_store_pc is accurate).
|
||||
* cur_ch/cur_madr/cur_bcr name the in-flight channel + its destination, so a wrong
|
||||
* DMA destination clobbering kernel data becomes directly visible. */
|
||||
int g_dma_exec_depth = 0;
|
||||
int g_dma_cur_ch = -1;
|
||||
uint32_t g_dma_cur_madr = 0;
|
||||
uint32_t g_dma_cur_bcr = 0;
|
||||
PSX_THREAD_LOCAL int g_dma_exec_depth = 0;
|
||||
PSX_THREAD_LOCAL int g_dma_cur_ch = -1;
|
||||
PSX_THREAD_LOCAL uint32_t g_dma_cur_madr = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_dma_cur_bcr = 0;
|
||||
/* Guest PC that kicked the in-flight DMA (the CHCR store that set start/busy),
|
||||
* so write-provenance (wtrace / parity note_write) attributes DMA-sourced RAM
|
||||
* writes to the code that INITIATED the transfer, not to g_debug_last_store_pc
|
||||
* (which for an async transfer is a stale, unrelated CPU store). Captured at the
|
||||
* kick in trigger_dma_transfer and re-published for async channels whose RAM
|
||||
* writes run in a later advance_*() step. 0 = unknown. */
|
||||
uint32_t g_dma_initiator_pc = 0;
|
||||
static uint32_t s_dma_ch_initiator_pc[7] = {0};
|
||||
PSX_THREAD_LOCAL uint32_t g_dma_initiator_pc = 0;
|
||||
static PSX_THREAD_LOCAL uint32_t s_dma_ch_initiator_pc[7] = {0};
|
||||
|
||||
typedef struct {
|
||||
uint8_t active;
|
||||
@@ -157,22 +158,22 @@ typedef struct {
|
||||
uint32_t cycles_remaining;
|
||||
} DMADelayedComplete;
|
||||
|
||||
static DMADelayedComplete delayed_complete[7];
|
||||
static PSX_THREAD_LOCAL DMADelayedComplete delayed_complete[7];
|
||||
|
||||
/* ---- Global registers ---- */
|
||||
|
||||
static uint32_t dpcr; /* 0x1F8010F0: DMA control (enable bits) */
|
||||
static uint32_t dicr; /* 0x1F8010F4: DMA interrupt control */
|
||||
static PSX_THREAD_LOCAL uint32_t dpcr; /* 0x1F8010F0: DMA control (enable bits) */
|
||||
static PSX_THREAD_LOCAL uint32_t dicr; /* 0x1F8010F4: DMA interrupt control */
|
||||
|
||||
#define DICR_WRITE_MASK 0x00FF807Fu
|
||||
#define DICR_RESET_MASK 0x7F000000u
|
||||
|
||||
static DMATraceEntry dma_trace[DMA_TRACE_CAP];
|
||||
static uint64_t dma_trace_seq;
|
||||
static DMACDROMHistoryEntry cdrom_dma_history[DMA_CDROM_HISTORY_CAP];
|
||||
static uint64_t cdrom_dma_history_seq;
|
||||
static DMACDROMHistoryEntry cdrom_dma_active_entry;
|
||||
static uint8_t cdrom_dma_history_active;
|
||||
static PSX_THREAD_LOCAL DMATraceEntry dma_trace[DMA_TRACE_CAP];
|
||||
static PSX_THREAD_LOCAL uint64_t dma_trace_seq;
|
||||
static PSX_THREAD_LOCAL DMACDROMHistoryEntry cdrom_dma_history[DMA_CDROM_HISTORY_CAP];
|
||||
static PSX_THREAD_LOCAL uint64_t cdrom_dma_history_seq;
|
||||
static PSX_THREAD_LOCAL DMACDROMHistoryEntry cdrom_dma_active_entry;
|
||||
static PSX_THREAD_LOCAL uint8_t cdrom_dma_history_active;
|
||||
|
||||
static uint32_t dicr_read_value(uint32_t v);
|
||||
|
||||
@@ -823,8 +824,8 @@ static void start_async_gpu_linked_list(void) {
|
||||
* whole walk (and the GPU work it feeds) with in_exception set, which is what
|
||||
* the phase profiler's exc_share then measures. */
|
||||
extern int psx_get_in_exception(void);
|
||||
static DMAGpuHoldEntry s_hold_ring[DMA_GPU_HOLD_RING_CAP];
|
||||
static DMAGpuHoldStats s_hold_stats;
|
||||
static PSX_THREAD_LOCAL DMAGpuHoldEntry s_hold_ring[DMA_GPU_HOLD_RING_CAP];
|
||||
static PSX_THREAD_LOCAL DMAGpuHoldStats s_hold_stats;
|
||||
|
||||
static uint64_t hold_host_ticks(void) {
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/*
|
||||
* event_ring.c — always-on unified event-timeline ring. See event_ring.h.
|
||||
*
|
||||
@@ -13,10 +14,10 @@
|
||||
#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 */
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat; /* memory.c */
|
||||
extern PSX_THREAD_LOCAL uint32_t i_mask; /* memory.c */
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr; /* debug_server */
|
||||
extern PSX_THREAD_LOCAL 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 */
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* fntrace.c — runtime side of psx_dispatch call ring. See fntrace.h. */
|
||||
|
||||
#include "fntrace.h"
|
||||
@@ -192,8 +193,8 @@ void fntrace_record(CPUState* cpu, uint32_t target) {
|
||||
* bail_top_wild_pc) so the dominant wild-returning function is readable even
|
||||
* when the bail storm makes the window "Not Responding". */
|
||||
{
|
||||
extern uint64_t g_psx_bail_flattened;
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint64_t g_psx_bail_flattened;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern void psx_bail_record(uint32_t site_ra, uint32_t site_sp,
|
||||
uint32_t wild_pc, uint32_t guest_sp);
|
||||
static uint64_t s_last_flat = 0;
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
/* State accessors. All defined in other compilation units; declared here
|
||||
* to avoid pulling in heavy headers. */
|
||||
extern uint64_t s_frame_count; /* debug_server.c */
|
||||
extern uint32_t g_debug_current_func_addr; /* debug_server.c */
|
||||
extern uint32_t g_debug_last_store_pc; /* debug_server.c */
|
||||
extern uint32_t i_stat; /* memory.c */
|
||||
extern uint32_t i_mask; /* memory.c */
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr; /* debug_server.c */
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc; /* debug_server.c */
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat; /* memory.c */
|
||||
extern PSX_THREAD_LOCAL uint32_t i_mask; /* memory.c */
|
||||
|
||||
extern uint64_t psx_get_cycle_count(void);
|
||||
extern void psx_get_freeze_diag(uint64_t *total_checks,
|
||||
@@ -150,7 +150,7 @@ static int s_sym_initialized = 0;
|
||||
#define RING_CAP 64
|
||||
typedef struct {
|
||||
uint64_t frame_count;
|
||||
PSX_THREAD_LOCAL uint64_t psx_cycle_count;
|
||||
uint64_t psx_cycle_count;
|
||||
uint64_t exc_reentry;
|
||||
uint64_t dirty_ram_insns;
|
||||
uint32_t current_func;
|
||||
|
||||
+3
-3
@@ -4912,7 +4912,7 @@ static inline GpuContextType_lcf *gpu_context_lcf(void) {
|
||||
* raise the VBLANK bit when a synthetic vblank fires from a tight
|
||||
* BIOS-shell GPUSTAT poll loop, without going through the
|
||||
* vblank-callback path (which would call SDL_Delay/Sleep). */
|
||||
extern uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
/* Central IRQ-raise choke point (interrupts.c) — also records the device ring. */
|
||||
extern void psx_irq_raise(uint32_t bit, uint32_t detail);
|
||||
|
||||
@@ -8468,8 +8468,8 @@ uint32_t gpu_get_opcode_count(uint8_t op) { return (*gpu_context_gp0_opcode_coun
|
||||
|
||||
extern uint64_t s_frame_count; /* defined in debug_server.c */
|
||||
#ifndef PSX_NO_DEBUG_TOOLS
|
||||
extern uint32_t g_debug_last_store_pc; /* defined in debug_server.c */
|
||||
extern uint32_t g_debug_current_func_addr; /* defined in debug_server.c */
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc; /* defined in debug_server.c */
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr; /* defined in debug_server.c */
|
||||
uint32_t debug_guest_ra(void); /* accessor in debug_server.c (guest $ra) */
|
||||
uint32_t debug_guest_sp(void); /* accessor in debug_server.c (guest $sp) */
|
||||
extern uint8_t *memory_get_ram_ptr(void); /* raw 2MB main-RAM base (no lockstep) */
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ extern "C" int gpu_ws_precise_nclip_enabled(void);
|
||||
* plus native replay of one candidate) is running. */
|
||||
extern "C" int psx_overlay_shadow_diff_active(void);
|
||||
extern "C" uint64_t s_frame_count; /* debug_server.c */
|
||||
extern "C" uint32_t g_debug_last_store_pc; /* debug_server.c */
|
||||
extern "C" PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc; /* debug_server.c */
|
||||
extern "C" { PSX_THREAD_LOCAL int (*g_psx_projection_command)(CPUState*, uint32_t) = nullptr; }
|
||||
|
||||
namespace PSXRecomp {
|
||||
@@ -686,7 +686,7 @@ extern "C" void gte_ws_set_dome_expand(int on, int aspect_num, int aspect_den) {
|
||||
// projects FAR (SZ >= threshold) vertices, so the sky-dome draw function can be
|
||||
// identified as the top far-vertex emitter — the target for the per-function
|
||||
// dome-expand bracket (the clean alternative to the scene-dependent depth gate).
|
||||
extern "C" { extern uint32_t g_debug_current_func_addr; }
|
||||
extern "C" { extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr; }
|
||||
static PSX_THREAD_LOCAL uint32_t s_gte_caller_ra = 0; /* guest ra at gte_execute = return into the GAME fn that issued the projection (not the libgte leaf) */
|
||||
#define WS_DOME_CALL_SITES_MAX 32
|
||||
static PSX_THREAD_LOCAL uint32_t s_ws_dome_call_sites[WS_DOME_CALL_SITES_MAX];
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* guest_cycle_profile.c - always-on guest-cycle profile ring.
|
||||
*
|
||||
* Answers "where did the guest CPU spend its cycles between frame A and
|
||||
@@ -35,7 +36,7 @@
|
||||
extern uint64_t s_frame_count;
|
||||
extern volatile uint32_t g_psx_last_fn_entry;
|
||||
extern volatile uint32_t g_psx_last_block;
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern uint32_t debug_guest_ra(void);
|
||||
extern uint32_t overlay_loader_native_inprogress(void);
|
||||
extern int psx_exec_phase(void);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/*
|
||||
* interrupts.c — v4 interrupt delivery for recompiled BIOS.
|
||||
*
|
||||
@@ -124,8 +125,8 @@ static void irq_record_outcome(uint8_t kind, uint8_t detail, uint32_t aux) {
|
||||
#define COP0_EPC 14
|
||||
|
||||
/* I_STAT and I_MASK are owned by memory.c */
|
||||
extern uint32_t i_stat;
|
||||
extern uint32_t i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_mask;
|
||||
|
||||
/* Device-event cycle ring (device_trace.c): every hardware IRQ-raise edge is
|
||||
* recorded here with its guest cycle. */
|
||||
@@ -159,7 +160,7 @@ extern uint32_t i_mask;
|
||||
*
|
||||
* Derived from PR #102 by Alexandros Mandravillis; the mirror call sites and
|
||||
* the single-owner refactor are ours. */
|
||||
static uint32_t *s_cause_ptr;
|
||||
static PSX_THREAD_LOCAL uint32_t *s_cause_ptr;
|
||||
|
||||
void psx_irq_refresh_cause_ip2(void)
|
||||
{
|
||||
@@ -186,7 +187,7 @@ void psx_irq_raise(uint32_t bit, uint32_t detail)
|
||||
{
|
||||
i_stat |= (1u << bit);
|
||||
psx_irq_refresh_cause_ip2();
|
||||
device_trace_note(bit, detail);
|
||||
if (!g_psx_render_pass_active) device_trace_note(bit, detail);
|
||||
}
|
||||
|
||||
/* Dispatch counter for vblank scheduling. */
|
||||
@@ -235,7 +236,7 @@ typedef struct {
|
||||
static CosimIrqEntry s_cosim_irq_ring[COSIM_IRQ_RING_CAP];
|
||||
static uint64_t s_cosim_irq_seq;
|
||||
extern int g_cosim_dirty_pump_site;
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern uint32_t cosim_last_block(void);
|
||||
extern uint32_t overlay_loader_get_inprogress(void);
|
||||
|
||||
@@ -625,7 +626,7 @@ uint64_t psx_exception_setjmp_epoch(void) { return g_exc_setjmp_epoch; }
|
||||
* the thread scheduler is fiber-based everywhere. */
|
||||
void* g_exception_owner_fiber = NULL;
|
||||
int g_pending_exception_longjmp = 0;
|
||||
extern int g_psx_dispatch_depth;
|
||||
extern PSX_THREAD_LOCAL int g_psx_dispatch_depth;
|
||||
|
||||
/* Set by psx_check_interrupts_at while a compiled block-leader interrupt check
|
||||
* is in progress. If the delivered handler later RFEs to the sentinel outside
|
||||
@@ -676,7 +677,7 @@ int psx_irq_resume_context_snapshot_safe_at(uint32_t resume_pc)
|
||||
* dirty resume PC is the same PC the caller intends to serialize. Keep
|
||||
* call-return and precise-IRQ pump sites rejected; those are the contexts
|
||||
* that can pair a plausible resume PC with stale nested-call registers. */
|
||||
extern int g_call_unit_depth;
|
||||
extern PSX_THREAD_LOCAL int g_call_unit_depth;
|
||||
if (in_exception || g_call_unit_depth != 0 || g_psx_dispatch_depth > 1)
|
||||
return 0;
|
||||
if (resume_pc == 0u || (resume_pc & 3u) != 0u)
|
||||
@@ -1553,7 +1554,7 @@ irq_deliver_eval:
|
||||
* clobber window because the CPU isn't stalled / the event wasn't torn down. */
|
||||
{
|
||||
extern int dma_cdrom_transfer_active(void);
|
||||
extern int g_dma_exec_depth;
|
||||
extern PSX_THREAD_LOCAL int g_dma_exec_depth;
|
||||
extern uint64_t s_frame_count;
|
||||
g_irqctx_ring[g_irqctx_seq & (IRQCTX_RING_CAP - 1u)] = (IrqCtxEntry){
|
||||
.seq = g_irqctx_seq, .cycle = psx_get_cycle_count(),
|
||||
@@ -2146,7 +2147,7 @@ irq_deliver_eval:
|
||||
* live GPRs when the IRQ was taken deep inside a nested call (the
|
||||
* async-poll callback) — switching there commits a poisoned TCB context
|
||||
* and the resumed thread faults on a smeared jumptable index. */
|
||||
extern int g_call_unit_depth;
|
||||
extern PSX_THREAD_LOCAL int g_call_unit_depth;
|
||||
int at_outermost = (g_psx_dispatch_depth == 0 && g_call_unit_depth == 0);
|
||||
/* Defer only when nested AND the outgoing thread has a valid resume PC
|
||||
* of its OWN (g_exception_real_epc — the architectural EPC latched for
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* load_accel.c -- verified enhancement-phase load-time acceleration.
|
||||
*
|
||||
* PsyQ's VSync(-1) is used as a timeout clock by CD_sync/CD_ready loops. The
|
||||
@@ -23,7 +24,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
extern int g_precise_mode;
|
||||
|
||||
static int s_enabled = 1;
|
||||
|
||||
+12
-12
@@ -267,7 +267,7 @@ extern "C" {
|
||||
extern uint32_t g_slice_exit_pc, g_slice_exit_reason, g_slice_exit_iter;
|
||||
extern uint32_t g_slice_exit_dispatchable, g_slice_exit_dirty, g_slice_exit_in_text, g_slice_exit_want;
|
||||
/* memory.c */
|
||||
extern uint32_t i_stat, i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat, i_mask;
|
||||
extern uint64_t g_guest_store_count;
|
||||
extern uint64_t g_vblank_ack_count;
|
||||
/* interrupts.c */
|
||||
@@ -276,9 +276,9 @@ extern "C" {
|
||||
extern uint64_t g_dirty_ram_blocks_run;
|
||||
extern uint64_t g_dirty_pump_count;
|
||||
/* overlay_loader.c */
|
||||
extern int g_call_unit_depth;
|
||||
extern PSX_THREAD_LOCAL int g_call_unit_depth;
|
||||
/* psx_bios_backend.c */
|
||||
extern int g_psx_dispatch_depth;
|
||||
extern PSX_THREAD_LOCAL int g_psx_dispatch_depth;
|
||||
}
|
||||
|
||||
/* memory.c */
|
||||
@@ -844,7 +844,7 @@ static void post_load_probe_on_vblank(int turbo_active, int present_reached) {
|
||||
|
||||
extern uint64_t g_vblank_raise_count, g_vblank_deliver_count, g_vblank_ack_count;
|
||||
extern uint64_t g_dirty_ram_blocks_run;
|
||||
extern uint32_t i_stat, i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat, i_mask;
|
||||
extern CPUState *debug_cpu_ptr;
|
||||
const uint64_t vb_r = g_vblank_raise_count - s_post_load_probe_vb_raise0;
|
||||
const uint64_t vb_d = g_vblank_deliver_count - s_post_load_probe_vb_deliv0;
|
||||
@@ -963,9 +963,9 @@ static void post_load_probe_on_vblank(int turbo_active, int present_reached) {
|
||||
|
||||
const uint32_t irq_pc = psx_last_irq_check_pc();
|
||||
const uint32_t resume_pc = psx_compiled_irq_resume_pc();
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern int g_psx_dispatch_depth;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL int g_psx_dispatch_depth;
|
||||
const uint32_t func = g_debug_current_func_addr;
|
||||
const uint32_t store_pc = g_debug_last_store_pc;
|
||||
const uint32_t live_pc = debug_cpu_ptr ? debug_cpu_ptr->pc : 0u;
|
||||
@@ -20412,10 +20412,10 @@ session_reboot:
|
||||
psx_netplay_cold_reset();
|
||||
{
|
||||
extern uint64_t s_frame_count;
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern int g_call_unit_depth;
|
||||
extern int g_psx_dispatch_depth;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL int g_call_unit_depth;
|
||||
extern PSX_THREAD_LOCAL int g_psx_dispatch_depth;
|
||||
s_frame_count = 0;
|
||||
g_debug_current_func_addr = 0;
|
||||
g_debug_last_store_pc = 0;
|
||||
@@ -21890,7 +21890,7 @@ session_reboot:
|
||||
* longjmp fixup). */
|
||||
if (rematch_session) {
|
||||
extern PSX_THREAD_LOCAL int psx_in_device_service;
|
||||
extern int g_call_unit_depth;
|
||||
extern PSX_THREAD_LOCAL int g_call_unit_depth;
|
||||
char stall[96];
|
||||
uint32_t sim = 0;
|
||||
int lead = 0;
|
||||
|
||||
+155
-53
@@ -28,6 +28,7 @@
|
||||
#include "psx_cycles.h"
|
||||
#include "psx_icache.h"
|
||||
#include "psx_memory.h"
|
||||
#include "psx_execution.h"
|
||||
#include "pgxp.h"
|
||||
#include "render_pass.h"
|
||||
#include "render_pass_plan.h"
|
||||
@@ -279,7 +280,7 @@ void memory_clear_low_boot_scratch(void) {
|
||||
#define RAM_LIVE (g_psx_ram_size)
|
||||
#define DIRTY_RAM_LIVE_BITMAP_WORDS \
|
||||
(((RAM_LIVE >> DIRTY_RAM_PAGE_SHIFT) + 31u) / 32u)
|
||||
static uint32_t dirty_ram_bitmap[DIRTY_RAM_BITMAP_WORDS];
|
||||
static PSX_THREAD_LOCAL uint32_t dirty_ram_bitmap[DIRTY_RAM_BITMAP_WORDS];
|
||||
|
||||
/* Monotonic generation for RAM-resident CODE changes (kernel install-stub
|
||||
* writes + DMA/EXE loads that mark executable ranges). Consumers that cache
|
||||
@@ -330,24 +331,24 @@ static inline void dirty_ram_mark_page(uint32_t phys) {
|
||||
#include "psx_bios_image.h"
|
||||
#include "kernel_patch_ranges.h"
|
||||
|
||||
static uint32_t s_kb_lo = 0, s_kb_span = 0, s_kb_rom_off = 0;
|
||||
static PSX_THREAD_LOCAL uint32_t s_kb_lo = 0, s_kb_span = 0, s_kb_rom_off = 0;
|
||||
|
||||
static const PsxKernelPatchRange* s_kb_pr = 0;
|
||||
static uint32_t s_kb_pr_n = 0;
|
||||
static uint64_t kbless_patch_skips = 0; /* segments skipped, TCP counter */
|
||||
static PSX_THREAD_LOCAL const PsxKernelPatchRange* s_kb_pr = 0;
|
||||
static PSX_THREAD_LOCAL uint32_t s_kb_pr_n = 0;
|
||||
static PSX_THREAD_LOCAL uint64_t kbless_patch_skips = 0; /* segments skipped, TCP counter */
|
||||
|
||||
|
||||
#define KBLESS_UNKNOWN 0u
|
||||
#define KBLESS_CLEAN 1u
|
||||
#define KBLESS_MISMATCH 2u
|
||||
#define KBLESS_MAX_ENTRIES 4096u
|
||||
static uint8_t kbless_state[KBLESS_MAX_ENTRIES]; /* parallel to bodies[] */
|
||||
static int kbless_enabled = -1; /* env PSX_KERNEL_BLESS=0 disables */
|
||||
static PSX_THREAD_LOCAL uint8_t kbless_state[KBLESS_MAX_ENTRIES]; /* parallel to bodies[] */
|
||||
static PSX_THREAD_LOCAL int kbless_enabled = -1; /* env PSX_KERNEL_BLESS=0 disables */
|
||||
/* Always-on counters (TCP kernel_bless). */
|
||||
static uint64_t kbless_native_hits = 0;
|
||||
static uint64_t kbless_verifies = 0;
|
||||
static uint64_t kbless_mismatches = 0;
|
||||
static uint64_t kbless_invalidations = 0;
|
||||
static PSX_THREAD_LOCAL uint64_t kbless_native_hits = 0;
|
||||
static PSX_THREAD_LOCAL uint64_t kbless_verifies = 0;
|
||||
static PSX_THREAD_LOCAL uint64_t kbless_mismatches = 0;
|
||||
static PSX_THREAD_LOCAL uint64_t kbless_invalidations = 0;
|
||||
|
||||
static int kbless_on(void) {
|
||||
if (kbless_enabled < 0) {
|
||||
@@ -582,18 +583,18 @@ void dirty_ram_clear_image_baseline(void) {
|
||||
* never frees it). Intentional data patches by the translation layer are blessed
|
||||
* into it via dirty_ram_text_bless so they are not mistaken for self-modifying
|
||||
* code — see dirty_ram_text_native_ok / dirty_ram_text_bless. */
|
||||
static uint8_t *text_ref_image = NULL;
|
||||
static uint32_t text_ref_lo = 0, text_ref_hi = 0;
|
||||
static uint32_t text_modified_bitmap[DIRTY_RAM_BITMAP_WORDS];
|
||||
static uint32_t text_diverged_bitmap[DIRTY_RAM_BITMAP_WORDS];
|
||||
static uint64_t g_text_native_blocked = 0;
|
||||
static uint32_t g_text_diverged_pages = 0;
|
||||
static uint64_t g_text_exact_mismatches = 0;
|
||||
static uint32_t g_text_exact_last_range_lo = 0;
|
||||
static uint32_t g_text_exact_last_range_len = 0;
|
||||
static uint32_t g_text_exact_last_mismatch = 0;
|
||||
static uint32_t g_text_exact_last_live = 0;
|
||||
static uint32_t g_text_exact_last_ref = 0;
|
||||
static PSX_THREAD_LOCAL uint8_t *text_ref_image = NULL;
|
||||
static PSX_THREAD_LOCAL uint32_t text_ref_lo = 0, text_ref_hi = 0;
|
||||
static PSX_THREAD_LOCAL uint32_t text_modified_bitmap[DIRTY_RAM_BITMAP_WORDS];
|
||||
static PSX_THREAD_LOCAL uint32_t text_diverged_bitmap[DIRTY_RAM_BITMAP_WORDS];
|
||||
static PSX_THREAD_LOCAL uint64_t g_text_native_blocked = 0;
|
||||
static PSX_THREAD_LOCAL uint32_t g_text_diverged_pages = 0;
|
||||
static PSX_THREAD_LOCAL uint64_t g_text_exact_mismatches = 0;
|
||||
static PSX_THREAD_LOCAL uint32_t g_text_exact_last_range_lo = 0;
|
||||
static PSX_THREAD_LOCAL uint32_t g_text_exact_last_range_len = 0;
|
||||
static PSX_THREAD_LOCAL uint32_t g_text_exact_last_mismatch = 0;
|
||||
static PSX_THREAD_LOCAL uint32_t g_text_exact_last_live = 0;
|
||||
static PSX_THREAD_LOCAL uint32_t g_text_exact_last_ref = 0;
|
||||
|
||||
void dirty_ram_register_text_image(uint32_t phys_lo, const uint8_t *bytes,
|
||||
uint32_t len) {
|
||||
@@ -902,8 +903,8 @@ void dirty_ram_set_bitmap_words(const uint32_t* words, uint32_t count) {
|
||||
* The bitmap is almost always empty, so the per-store cost on the common path
|
||||
* is a single bitmap lookup.
|
||||
*/
|
||||
static uint32_t overlay_watch_bitmap[DIRTY_RAM_BITMAP_WORDS];
|
||||
static uint32_t overlay_page_gen[DIRTY_RAM_PAGE_COUNT];
|
||||
static PSX_THREAD_LOCAL uint32_t overlay_watch_bitmap[DIRTY_RAM_BITMAP_WORDS];
|
||||
static PSX_THREAD_LOCAL uint32_t overlay_page_gen[DIRTY_RAM_PAGE_COUNT];
|
||||
|
||||
void dirty_ram_reset_for_boot(void) {
|
||||
memset(dirty_ram_bitmap, 0, sizeof(dirty_ram_bitmap));
|
||||
@@ -1039,24 +1040,24 @@ static inline void overlay_watch_note_write(uint32_t phys, uint32_t size) {
|
||||
|
||||
/* Memory control registers: 0x1F801000..0x1F80103F (16 words) + 0x1F801060 (RAM size).
|
||||
* Includes expansion base/size, COM_DELAY, SPU_DELAY, CDROM_DELAY etc. */
|
||||
static uint32_t mem_ctrl[16]; /* indices 0..15 → addresses 0x1F801000..0x1F80103C */
|
||||
static uint32_t ram_size_reg; /* 0x1F801060 */
|
||||
static PSX_THREAD_LOCAL uint32_t mem_ctrl[16]; /* indices 0..15 → addresses 0x1F801000..0x1F80103C */
|
||||
static PSX_THREAD_LOCAL uint32_t ram_size_reg; /* 0x1F801060 */
|
||||
|
||||
/* KSEG2 no-op access telemetry (see the guards in the accessors). */
|
||||
uint64_t g_kseg2_ignored_reads;
|
||||
uint64_t g_kseg2_ignored_writes;
|
||||
|
||||
/* Cache control register (KSEG2: 0xFFFE0130). */
|
||||
static uint32_t cache_ctrl;
|
||||
static PSX_THREAD_LOCAL uint32_t cache_ctrl;
|
||||
|
||||
/* Pointer to cpu->cop0[12] (SR). Set once at init.
|
||||
* Used by write functions to check the IsC (Isolate Cache) bit: while it is
|
||||
* set, a CPU store goes to the caches and never reaches the bus (isc_store). */
|
||||
static const uint32_t *sr_ptr;
|
||||
static PSX_THREAD_LOCAL const uint32_t *sr_ptr;
|
||||
|
||||
/* Interrupt controller — non-static so hardware subsystems can set I_STAT bits. */
|
||||
uint32_t i_stat; /* 0x1F801070 — interrupt status (AND-acknowledge semantics) */
|
||||
uint32_t i_mask; /* 0x1F801074 — interrupt enable mask */
|
||||
PSX_THREAD_LOCAL uint32_t i_stat; /* 0x1F801070 — interrupt status (AND-acknowledge semantics) */
|
||||
PSX_THREAD_LOCAL uint32_t i_mask; /* 0x1F801074 — interrupt enable mask */
|
||||
|
||||
/* Shadow-diff device-access detector. run_shadow_diff arms g_shadow_mmio_watch
|
||||
* around its single authoritative (interpreter) probe pass; ANY MMIO touch bumps
|
||||
@@ -1072,8 +1073,8 @@ uint64_t g_shadow_mmio_hits = 0;
|
||||
* time-varying or side-effecting values (timer counters, CD response FIFO),
|
||||
* so any touch disqualifies the window. One increment per access. */
|
||||
uint64_t g_mmio_access_count = 0;
|
||||
#define SHADOW_NOTE_MMIO() do { g_mmio_access_count++; \
|
||||
g_shadow_mmio_hits += (uint64_t)g_shadow_mmio_watch; } while (0)
|
||||
#define SHADOW_NOTE_MMIO() do { if (s_backing == &s_live_backing) { \
|
||||
g_mmio_access_count++; g_shadow_mmio_hits += (uint64_t)g_shadow_mmio_watch; } } while (0)
|
||||
|
||||
/* Always-on guest store counter, bumped at the top of every raw store
|
||||
* (RAM, scratchpad, MMIO, even discarded KSEG2/IsC stores — over-counting
|
||||
@@ -1108,8 +1109,8 @@ static int imask_bit7_set_count = 0;
|
||||
static int imask_bit7_clear_count = 0;
|
||||
|
||||
static void imask_trace_record(uint32_t old_val, uint32_t new_val, uint8_t width) {
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
extern int psx_get_in_exception(void);
|
||||
ImaskTraceEntry *e = &imask_trace[imask_trace_idx];
|
||||
e->old_mask = old_val;
|
||||
@@ -1177,7 +1178,7 @@ extern void debug_server_trace_ram_read_watch(uint32_t phys, uint32_t val);
|
||||
extern void debug_server_trace_entryint_write(uint32_t phys, uint32_t old_val,
|
||||
uint32_t new_val, uint8_t width);
|
||||
extern CPUState *debug_cpu_ptr;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
|
||||
/* Parity last-writer provenance (parity_trace.c): note every main-RAM write so
|
||||
* the watch-word last-writer table tracks the exact producing store. No-op
|
||||
@@ -1187,8 +1188,8 @@ extern void parity_trace_note_write(uint32_t addr, uint32_t width, uint32_t writ
|
||||
/* Effective writer PC for provenance: during a DMA transfer the last CPU-store
|
||||
* PC is stale/unrelated, so attribute DMA-sourced RAM writes to the PC that
|
||||
* kicked the DMA (dma.c). Matches the wtrace recorder's DMA attribution. */
|
||||
extern int g_dma_exec_depth;
|
||||
extern uint32_t g_dma_initiator_pc;
|
||||
extern PSX_THREAD_LOCAL int g_dma_exec_depth;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_dma_initiator_pc;
|
||||
static inline uint32_t effective_store_pc(void) {
|
||||
return (g_dma_exec_depth > 0 && g_dma_initiator_pc) ? g_dma_initiator_pc
|
||||
: g_debug_last_store_pc;
|
||||
@@ -1317,6 +1318,18 @@ static void unmapped_fatal(uint32_t vaddr, uint32_t phys, const char* op) {
|
||||
|
||||
/* --- MMIO read/write helpers --- */
|
||||
|
||||
static int private_mmio_read_allowed(uint32_t addr) {
|
||||
if (s_backing == &s_live_backing) return 1;
|
||||
if ((addr >= 0x1F801040u && addr <= 0x1F80105Fu) ||
|
||||
(addr >= 0x1F801800u && addr <= 0x1F801803u) ||
|
||||
(addr >= 0x1F801820u && addr <= 0x1F801827u) ||
|
||||
(addr >= 0x1F801C00u && addr <= 0x1F801FFFu)) {
|
||||
if (g_psx_execution_abort) g_psx_execution_abort("device_read", addr);
|
||||
abort(); /* A private execution must install a non-returning landing. */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t mmio_read32_impl(uint32_t addr) {
|
||||
SHADOW_NOTE_MMIO();
|
||||
/* Memory control: 0x1F801000..0x1F801020 */
|
||||
@@ -1370,6 +1383,10 @@ static uint32_t mmio_read32_impl(uint32_t addr) {
|
||||
* single (side-effecting) read. Callers use mmio_read32; the body is _impl, so
|
||||
* the device read executes exactly once. */
|
||||
static uint32_t mmio_read32(uint32_t addr) {
|
||||
if (s_backing != &s_live_backing) {
|
||||
private_mmio_read_allowed(addr);
|
||||
return mmio_read32_impl(addr);
|
||||
}
|
||||
if (g_psx_render_pass_active && !render_pass_mmio_read_allowed(addr, 4)) return 0;
|
||||
psx_devices_mmio_sync();
|
||||
uint32_t v = mmio_read32_impl(addr);
|
||||
@@ -1380,7 +1397,7 @@ static uint32_t mmio_read32(uint32_t addr) {
|
||||
static void mmio_write32(uint32_t addr, uint32_t val) {
|
||||
psx_devices_mmio_sync();
|
||||
SHADOW_NOTE_MMIO();
|
||||
debug_server_trace_mmio_write(addr, val, 4);
|
||||
if (s_backing == &s_live_backing) debug_server_trace_mmio_write(addr, val, 4);
|
||||
/* Memory control: 0x1F801000..0x1F801020 */
|
||||
if (addr >= 0x1F801000u && addr <= 0x1F80103Cu) {
|
||||
mem_ctrl[(addr - 0x1F801000u) >> 2] = val;
|
||||
@@ -1493,6 +1510,10 @@ static uint16_t mmio_read16_impl(uint32_t addr) {
|
||||
}
|
||||
|
||||
static uint16_t mmio_read16(uint32_t addr) {
|
||||
if (s_backing != &s_live_backing) {
|
||||
private_mmio_read_allowed(addr);
|
||||
return mmio_read16_impl(addr);
|
||||
}
|
||||
if (g_psx_render_pass_active && !render_pass_mmio_read_allowed(addr, 2)) return 0;
|
||||
psx_devices_mmio_sync();
|
||||
uint16_t v = mmio_read16_impl(addr);
|
||||
@@ -1503,7 +1524,7 @@ static uint16_t mmio_read16(uint32_t addr) {
|
||||
static void mmio_write16(uint32_t addr, uint16_t val) {
|
||||
psx_devices_mmio_sync();
|
||||
SHADOW_NOTE_MMIO();
|
||||
debug_server_trace_mmio_write(addr, (uint32_t)val, 2);
|
||||
if (s_backing == &s_live_backing) debug_server_trace_mmio_write(addr, (uint32_t)val, 2);
|
||||
/* Memory control: 0x1F801000..0x1F80103C — halfword lane RMW. */
|
||||
if (addr >= 0x1F801000u && addr <= 0x1F80103Fu) {
|
||||
uint32_t idx = (addr - 0x1F801000u) >> 2;
|
||||
@@ -1613,6 +1634,10 @@ static uint8_t mmio_read8_impl(uint32_t addr) {
|
||||
}
|
||||
|
||||
static uint8_t mmio_read8(uint32_t addr) {
|
||||
if (s_backing != &s_live_backing) {
|
||||
private_mmio_read_allowed(addr);
|
||||
return mmio_read8_impl(addr);
|
||||
}
|
||||
if (g_psx_render_pass_active && !render_pass_mmio_read_allowed(addr, 1)) return 0;
|
||||
psx_devices_mmio_sync();
|
||||
uint8_t v = mmio_read8_impl(addr);
|
||||
@@ -1623,7 +1648,7 @@ static uint8_t mmio_read8(uint32_t addr) {
|
||||
static void mmio_write8(uint32_t addr, uint8_t val) {
|
||||
psx_devices_mmio_sync();
|
||||
SHADOW_NOTE_MMIO();
|
||||
debug_server_trace_mmio_write(addr, (uint32_t)val, 1);
|
||||
if (s_backing == &s_live_backing) debug_server_trace_mmio_write(addr, (uint32_t)val, 1);
|
||||
/* Memory control: 0x1F801000..0x1F80103C — byte lane RMW. Tomba2's late
|
||||
* attract byte-writes the EXP1 base register at 0x1F801000; only the
|
||||
* 32-bit path covered the block and the byte path fatal'd. */
|
||||
@@ -1708,7 +1733,7 @@ static void mmio_write8(uint32_t addr, uint8_t val) {
|
||||
* (psx_get_in_exception): the replay runs only the block, never the handler. */
|
||||
static int s_ls_op_active = 0;
|
||||
extern int g_ls_suppress_record;
|
||||
extern int g_dma_exec_depth;
|
||||
extern PSX_THREAD_LOCAL int g_dma_exec_depth;
|
||||
extern int psx_get_in_exception(void);
|
||||
static uint32_t psx_read_word_raw(uint32_t addr);
|
||||
/* data-shard capture feed (data_shards.c): record guest reads/writes while a
|
||||
@@ -1716,6 +1741,7 @@ static uint32_t psx_read_word_raw(uint32_t addr);
|
||||
* exception context (ISR work replays live), never under DMA (DMA writes
|
||||
* poison the window via ds_note_dma_write instead). */
|
||||
uint32_t psx_read_word(uint32_t addr) {
|
||||
if (s_backing != &s_live_backing) return psx_read_word_raw(addr);
|
||||
if (g_ls_mode == 2) return ls_read_hook(addr, 4, 0u);
|
||||
if (g_ds_recording && g_dma_exec_depth == 0 && !psx_get_in_exception())
|
||||
ds_note_read(addr, 4);
|
||||
@@ -1758,7 +1784,7 @@ static uint32_t psx_read_word_raw(uint32_t addr) {
|
||||
memcpy(&v, &ram[phys], sizeof(v));
|
||||
/* Targeted main-RAM read watch (debug). Flag is 0 in normal runs, so the
|
||||
* hot read path pays only a predictable branch. */
|
||||
if (g_ram_read_watch_active) debug_server_trace_ram_read_watch(phys, v);
|
||||
if (s_backing == &s_live_backing && g_ram_read_watch_active) debug_server_trace_ram_read_watch(phys, v);
|
||||
return v;
|
||||
}
|
||||
{
|
||||
@@ -1820,11 +1846,11 @@ typedef struct {
|
||||
} D44Entry;
|
||||
D44Entry g_d44_ring[D44_RING_CAP];
|
||||
uint64_t g_d44_seq = 0;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern int g_dma_exec_depth; /* >0 while a DMA is moving data (dma.c) */
|
||||
extern int g_dma_cur_ch; /* channel of the in-flight DMA, else -1 */
|
||||
extern uint32_t g_dma_cur_madr; /* current MADR of the in-flight DMA */
|
||||
extern uint32_t g_dma_cur_bcr; /* BCR of the in-flight DMA */
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL int g_dma_exec_depth; /* >0 while a DMA is moving data (dma.c) */
|
||||
extern PSX_THREAD_LOCAL int g_dma_cur_ch; /* channel of the in-flight DMA, else -1 */
|
||||
extern PSX_THREAD_LOCAL uint32_t g_dma_cur_madr; /* current MADR of the in-flight DMA */
|
||||
extern PSX_THREAD_LOCAL uint32_t g_dma_cur_bcr; /* BCR of the in-flight DMA */
|
||||
extern uint64_t s_frame_count;
|
||||
static inline void d44_note(uint32_t phys, uint32_t old, uint32_t val) {
|
||||
if (phys != D44_PHYS) return;
|
||||
@@ -1866,7 +1892,7 @@ uint64_t g_render_pass_dropped_writes[RENDER_PASS_DROP_CLASSES];
|
||||
* writes RAM directly, so IsC does not apply while a transfer moves data.
|
||||
* Neither is a host store (psx_host_write_*: mods, FMV skip, debug pokes,
|
||||
* enhancement fills); like DMA it reaches memory whatever SR says. */
|
||||
int g_host_store_depth; /* >0 inside psx_host_write_* */
|
||||
PSX_THREAD_LOCAL int g_host_store_depth; /* >0 inside psx_host_write_* */
|
||||
|
||||
/* DMA and host stores have no instruction hook to carry or reset a PGXP
|
||||
* shadow. Validation on read cannot catch one that rewrites the identical
|
||||
@@ -1925,6 +1951,7 @@ uint64_t g_render_pass_mod_pages = 0; /* pages journaled, total */
|
||||
|
||||
static int rp_mod_journal(uint8_t *base, uint32_t page_base, uint32_t off,
|
||||
uint32_t width) {
|
||||
if (s_backing != &s_live_backing) return 1;
|
||||
for (uint32_t pg = off / RP_MOD_PAGE; pg <= (off + width - 1u) / RP_MOD_PAGE; pg++) {
|
||||
const uint32_t index = page_base + pg;
|
||||
const uint8_t bit = (uint8_t)(1u << (index & 7u));
|
||||
@@ -2001,12 +2028,12 @@ static void render_pass_store(uint32_t addr, uint32_t val, uint32_t width) {
|
||||
if (t.isolate_cache) isc_store(addr, val, width);
|
||||
t.mmio_write = render_pass_mmio_write;
|
||||
cls = render_pass_store_to(&t, addr, val, width);
|
||||
if (cls >= 0) g_render_pass_dropped_writes[cls]++;
|
||||
if (cls >= 0 && s_backing == &s_live_backing) g_render_pass_dropped_writes[cls]++;
|
||||
}
|
||||
|
||||
static void psx_write_word_raw(uint32_t addr, uint32_t val);
|
||||
void psx_write_word(uint32_t addr, uint32_t val) {
|
||||
extern void (*g_overlay_flush_pending_cycles)(void);
|
||||
extern PSX_THREAD_LOCAL void (*g_overlay_flush_pending_cycles)(void);
|
||||
if (g_overlay_flush_pending_cycles) g_overlay_flush_pending_cycles();
|
||||
if (g_psx_render_pass_active) { render_pass_store(addr, val, 4); return; }
|
||||
if (g_ls_mode == 2) { ls_write_hook(addr, 4, val); return; }
|
||||
@@ -2173,6 +2200,7 @@ static void psx_write_word_raw(uint32_t addr, uint32_t val) {
|
||||
|
||||
static uint16_t psx_read_half_raw(uint32_t addr);
|
||||
uint16_t psx_read_half(uint32_t addr) {
|
||||
if (s_backing != &s_live_backing) return psx_read_half_raw(addr);
|
||||
if (g_ls_mode == 2) return (uint16_t)ls_read_hook(addr, 2, 0u);
|
||||
if (g_ds_recording && g_dma_exec_depth == 0 && !psx_get_in_exception())
|
||||
ds_note_read(addr, 2);
|
||||
@@ -2221,7 +2249,7 @@ static uint16_t psx_read_half_raw(uint32_t addr) {
|
||||
|
||||
static void psx_write_half_raw(uint32_t addr, uint16_t val);
|
||||
void psx_write_half(uint32_t addr, uint16_t val) {
|
||||
extern void (*g_overlay_flush_pending_cycles)(void);
|
||||
extern PSX_THREAD_LOCAL void (*g_overlay_flush_pending_cycles)(void);
|
||||
if (g_overlay_flush_pending_cycles) g_overlay_flush_pending_cycles();
|
||||
if (g_psx_render_pass_active) { render_pass_store(addr, val, 2); return; }
|
||||
if (g_ls_mode == 2) { ls_write_hook(addr, 2, val); return; }
|
||||
@@ -2302,6 +2330,7 @@ static void psx_write_half_raw(uint32_t addr, uint16_t val) {
|
||||
|
||||
static uint8_t psx_read_byte_raw(uint32_t addr);
|
||||
uint8_t psx_read_byte(uint32_t addr) {
|
||||
if (s_backing != &s_live_backing) return psx_read_byte_raw(addr);
|
||||
if (g_ls_mode == 2) return (uint8_t)ls_read_hook(addr, 1, 0u);
|
||||
if (g_ls_mode != 1 || s_ls_op_active || g_ls_suppress_record || g_dma_exec_depth > 0) return psx_read_byte_raw(addr);
|
||||
s_ls_op_active = 1;
|
||||
@@ -2576,7 +2605,7 @@ uint8_t psx_guest_read_byte(uint32_t addr) { return psx_read_byte(addr); }
|
||||
|
||||
static void psx_write_byte_raw(uint32_t addr, uint8_t val);
|
||||
void psx_write_byte(uint32_t addr, uint8_t val) {
|
||||
extern void (*g_overlay_flush_pending_cycles)(void);
|
||||
extern PSX_THREAD_LOCAL void (*g_overlay_flush_pending_cycles)(void);
|
||||
if (g_overlay_flush_pending_cycles) g_overlay_flush_pending_cycles();
|
||||
if (g_psx_render_pass_active) { render_pass_store(addr, val, 1); return; }
|
||||
if (g_ls_mode == 2) { ls_write_hook(addr, 1, val); return; }
|
||||
@@ -2663,3 +2692,76 @@ static void psx_write_byte_raw(uint32_t addr, uint8_t val) {
|
||||
}
|
||||
unmapped_fatal(addr, phys, "WRITE");
|
||||
}
|
||||
|
||||
struct PsxMemoryExecutionState { size_t bytes; unsigned char data[]; };
|
||||
typedef struct { void *address; size_t bytes; } MemoryExecutionRegion;
|
||||
static size_t memory_execution_regions(MemoryExecutionRegion *regions) {
|
||||
size_t n = 0;
|
||||
#define MEMORY_REGION(name) do { regions[n].address = &(name); regions[n++].bytes = sizeof(name); } while (0)
|
||||
MEMORY_REGION(dirty_ram_bitmap);
|
||||
MEMORY_REGION(s_kb_lo);
|
||||
MEMORY_REGION(s_kb_span);
|
||||
MEMORY_REGION(s_kb_rom_off);
|
||||
MEMORY_REGION(s_kb_pr);
|
||||
MEMORY_REGION(s_kb_pr_n);
|
||||
MEMORY_REGION(kbless_patch_skips);
|
||||
MEMORY_REGION(kbless_state);
|
||||
MEMORY_REGION(kbless_enabled);
|
||||
MEMORY_REGION(kbless_native_hits);
|
||||
MEMORY_REGION(kbless_verifies);
|
||||
MEMORY_REGION(kbless_mismatches);
|
||||
MEMORY_REGION(kbless_invalidations);
|
||||
MEMORY_REGION(text_ref_image);
|
||||
MEMORY_REGION(text_ref_lo);
|
||||
MEMORY_REGION(text_ref_hi);
|
||||
MEMORY_REGION(text_modified_bitmap);
|
||||
MEMORY_REGION(text_diverged_bitmap);
|
||||
MEMORY_REGION(g_text_native_blocked);
|
||||
MEMORY_REGION(g_text_diverged_pages);
|
||||
MEMORY_REGION(g_text_exact_mismatches);
|
||||
MEMORY_REGION(g_text_exact_last_range_lo);
|
||||
MEMORY_REGION(g_text_exact_last_range_len);
|
||||
MEMORY_REGION(g_text_exact_last_mismatch);
|
||||
MEMORY_REGION(g_text_exact_last_live);
|
||||
MEMORY_REGION(g_text_exact_last_ref);
|
||||
MEMORY_REGION(overlay_watch_bitmap);
|
||||
MEMORY_REGION(overlay_page_gen);
|
||||
MEMORY_REGION(mem_ctrl);
|
||||
MEMORY_REGION(ram_size_reg);
|
||||
MEMORY_REGION(cache_ctrl);
|
||||
MEMORY_REGION(sr_ptr);
|
||||
MEMORY_REGION(i_stat);
|
||||
MEMORY_REGION(i_mask);
|
||||
#undef MEMORY_REGION
|
||||
return n;
|
||||
}
|
||||
PsxMemoryExecutionState *memory_execution_create(void) {
|
||||
MemoryExecutionRegion regions[64];
|
||||
size_t n = memory_execution_regions(regions), bytes = 0;
|
||||
for (size_t i = 0; i < n; ++i) bytes += regions[i].bytes;
|
||||
PsxMemoryExecutionState *state = (PsxMemoryExecutionState *)malloc(sizeof(*state) + bytes);
|
||||
if (state) state->bytes = bytes;
|
||||
return state;
|
||||
}
|
||||
void memory_execution_destroy(PsxMemoryExecutionState *state) { free(state); }
|
||||
void memory_execution_capture(PsxMemoryExecutionState *state) {
|
||||
MemoryExecutionRegion regions[64];
|
||||
size_t n = memory_execution_regions(regions), offset = 0;
|
||||
if (!state || s_backing != &s_live_backing) abort();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (offset + regions[i].bytes > state->bytes) abort();
|
||||
memcpy(state->data + offset, regions[i].address, regions[i].bytes);
|
||||
offset += regions[i].bytes;
|
||||
}
|
||||
}
|
||||
void memory_execution_bind(const PsxMemoryExecutionState *state, const uint32_t *status) {
|
||||
MemoryExecutionRegion regions[64];
|
||||
size_t n = memory_execution_regions(regions), offset = 0;
|
||||
if (!state || s_backing == &s_live_backing) abort();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (offset + regions[i].bytes > state->bytes) abort();
|
||||
memcpy(regions[i].address, state->data + offset, regions[i].bytes);
|
||||
offset += regions[i].bytes;
|
||||
}
|
||||
sr_ptr = status; /* Never retain the source CPU's status pointer. */
|
||||
}
|
||||
|
||||
+41
-33
@@ -142,10 +142,19 @@ struct RuntimeMods {
|
||||
/* The plan is a netplay match's own-view set: its hooks run only inside
|
||||
* psx_mod_render_local_view, its vblank/savestate callbacks not at all. */
|
||||
bool netplay_view_plan = false;
|
||||
};
|
||||
|
||||
/* Callback ownership belongs to the guest execution thread. The active
|
||||
* plan and its hook tables remain immutable until all executions stop. */
|
||||
struct ModExecutionContext {
|
||||
const ModResolution::Plugin* current_plugin = nullptr;
|
||||
CPUState* current_function_cpu = nullptr;
|
||||
bool current_function_finished = false;
|
||||
};
|
||||
ModExecutionContext& execution() {
|
||||
static thread_local ModExecutionContext value;
|
||||
return value;
|
||||
}
|
||||
|
||||
RuntimeMods& state() {
|
||||
static RuntimeMods value;
|
||||
@@ -253,23 +262,22 @@ public:
|
||||
* only entry hooks would let VBlank erase the interrupted plugin or complete
|
||||
* a function that belongs to another callback. */
|
||||
class PluginCallbackScope {
|
||||
RuntimeMods& runtime;
|
||||
const ModResolution::Plugin* previous_plugin;
|
||||
CPUState* previous_cpu;
|
||||
bool previous_finished;
|
||||
public:
|
||||
PluginCallbackScope(RuntimeMods& s, const ModResolution::Plugin* plugin,
|
||||
CPUState* cpu = nullptr)
|
||||
: runtime(s), previous_plugin(s.current_plugin),
|
||||
previous_cpu(s.current_function_cpu), previous_finished(s.current_function_finished) {
|
||||
s.current_plugin = plugin;
|
||||
s.current_function_cpu = cpu;
|
||||
s.current_function_finished = false;
|
||||
: previous_plugin(execution().current_plugin),
|
||||
previous_cpu(execution().current_function_cpu), previous_finished(execution().current_function_finished) {
|
||||
execution().current_plugin = plugin;
|
||||
execution().current_function_cpu = cpu;
|
||||
execution().current_function_finished = false;
|
||||
}
|
||||
~PluginCallbackScope() {
|
||||
runtime.current_plugin = previous_plugin;
|
||||
runtime.current_function_cpu = previous_cpu;
|
||||
runtime.current_function_finished = previous_finished;
|
||||
execution().current_plugin = previous_plugin;
|
||||
execution().current_function_cpu = previous_cpu;
|
||||
execution().current_function_finished = previous_finished;
|
||||
}
|
||||
PluginCallbackScope(const PluginCallbackScope&) = delete;
|
||||
PluginCallbackScope& operator=(const PluginCallbackScope&) = delete;
|
||||
@@ -293,7 +301,7 @@ std::vector<ActiveFunctionEntryHook>& active_function_entry_hooks() {
|
||||
static std::vector<ActiveFunctionEntryHook> value;
|
||||
return value;
|
||||
}
|
||||
unsigned function_entry_depth;
|
||||
thread_local unsigned function_entry_depth;
|
||||
|
||||
std::vector<ActiveFunctionEntryHook>& active_guest_functions() {
|
||||
static std::vector<ActiveFunctionEntryHook> value;
|
||||
@@ -2603,9 +2611,9 @@ extern "C" void mod_runtime_on_savestate_loaded(void) {
|
||||
}
|
||||
s.main_applied = true;
|
||||
for (const ModResolution::Plugin& plugin : s.plan.plugins) {
|
||||
s.current_plugin = &plugin;
|
||||
execution().current_plugin = &plugin;
|
||||
mod_invoke_savestate_plugin(plugin.id);
|
||||
s.current_plugin = nullptr;
|
||||
execution().current_plugin = nullptr;
|
||||
}
|
||||
if (applied)
|
||||
std::fprintf(stdout,
|
||||
@@ -2998,10 +3006,10 @@ extern "C" void psx_mod_set_disc_request_provider(PSXModDiscRequestProvider prov
|
||||
return;
|
||||
}
|
||||
RuntimeMods& s = state();
|
||||
if (!s.initialized || !s.plan.ok || !s.activating || !s.current_plugin ||
|
||||
if (!s.initialized || !s.plan.ok || !s.activating || !execution().current_plugin ||
|
||||
s.netplay_view_plan || psx_netplay_active()) return;
|
||||
g_disc_request_provider = provider;
|
||||
g_disc_request_owner = s.current_plugin;
|
||||
g_disc_request_owner = execution().current_plugin;
|
||||
}
|
||||
|
||||
extern "C" int mod_runtime_disc_request(int *ready) {
|
||||
@@ -3150,10 +3158,10 @@ extern "C" int psx_mod_current_resource_path(const char* resource_id,
|
||||
if (!resource_id || !resource_id[0] || !out || out_size == 0)
|
||||
return 0;
|
||||
RuntimeMods& s = state();
|
||||
if (!s.initialized || !s.plan.ok || !s.current_plugin) return 0;
|
||||
if (!s.initialized || !s.plan.ok || !execution().current_plugin) return 0;
|
||||
for (const ModResolution::Resource& resource : s.plan.resources) {
|
||||
if (resource.package_id != s.current_plugin->package_id ||
|
||||
resource.feature_id != s.current_plugin->feature_id ||
|
||||
if (resource.package_id != execution().current_plugin->package_id ||
|
||||
resource.feature_id != execution().current_plugin->feature_id ||
|
||||
resource.id != resource_id)
|
||||
continue;
|
||||
const std::string text = resource.path.u8string();
|
||||
@@ -3168,7 +3176,7 @@ extern "C" int psx_mod_current_option_value(const char* option_id,
|
||||
char* out, uint32_t out_size) {
|
||||
using namespace PSXRecompV4;
|
||||
if (out && out_size) out[0] = '\0';
|
||||
const auto* plugin = state().current_plugin;
|
||||
const auto* plugin = execution().current_plugin;
|
||||
if (!plugin) return 0;
|
||||
return psx_mod_option_value(plugin->package_id.c_str(), plugin->feature_id.c_str(),
|
||||
option_id, out, out_size);
|
||||
@@ -3239,10 +3247,10 @@ extern "C" int psx_mod_current_resource_bytes(const char* resource_id,
|
||||
if (size) *size = 0;
|
||||
if (!resource_id || !*resource_id || !bytes || !size) return 0;
|
||||
const auto& s = state();
|
||||
if (!s.initialized || !s.plan.ok || !s.current_plugin) return 0;
|
||||
if (!s.initialized || !s.plan.ok || !execution().current_plugin) return 0;
|
||||
for (const auto& resource : s.plan.resources) {
|
||||
if (resource.package_id == s.current_plugin->package_id &&
|
||||
resource.feature_id == s.current_plugin->feature_id &&
|
||||
if (resource.package_id == execution().current_plugin->package_id &&
|
||||
resource.feature_id == execution().current_plugin->feature_id &&
|
||||
resource.id == resource_id && resource.bytes) {
|
||||
*bytes = resource.bytes->data();
|
||||
*size = resource.bytes->size();
|
||||
@@ -3416,8 +3424,8 @@ extern "C" int psx_mod_finish_function(CPUState* cpu) {
|
||||
using namespace PSXRecompV4;
|
||||
RuntimeMods& s = state();
|
||||
if (psx_mod_host_output_scope()) return 0;
|
||||
if (!cpu || s.current_function_cpu != cpu || !s.current_plugin) return 0;
|
||||
s.current_function_finished = true;
|
||||
if (!cpu || execution().current_function_cpu != cpu || !execution().current_plugin) return 0;
|
||||
execution().current_function_finished = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -3535,7 +3543,7 @@ static void run_host_output_hooks(CPUState* cpu, uint32_t address) {
|
||||
++s_host_output_depth;
|
||||
it->callback(&view, address);
|
||||
--s_host_output_depth;
|
||||
s.current_function_finished = false;
|
||||
execution().current_function_finished = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3544,8 +3552,8 @@ static void run_host_output_hooks(CPUState* cpu, uint32_t address) {
|
||||
static bool mod_host_output_write_blocked(void) {
|
||||
const auto& s = PSXRecompV4::state();
|
||||
return psx_mod_host_output_scope() ||
|
||||
(s.netplay_view_plan && s.current_plugin &&
|
||||
s.current_plugin->netplay_host_output);
|
||||
(s.netplay_view_plan && PSXRecompV4::execution().current_plugin &&
|
||||
PSXRecompV4::execution().current_plugin->netplay_host_output);
|
||||
}
|
||||
|
||||
extern "C" int psx_mod_function_entry(CPUState* cpu, uint32_t address) {
|
||||
@@ -3567,7 +3575,7 @@ extern "C" int psx_mod_function_entry(CPUState* cpu, uint32_t address) {
|
||||
if (it->callback) it->callback(cpu, address);
|
||||
/* Either completion form skips the body: an entry callback that
|
||||
* called psx_mod_finish_function(), or a filter returning nonzero. */
|
||||
const bool handled = s.current_function_finished ||
|
||||
const bool handled = execution().current_function_finished ||
|
||||
(it->filter && it->filter(cpu, address));
|
||||
--function_entry_depth;
|
||||
if (handled) {
|
||||
@@ -3640,17 +3648,17 @@ extern "C" int psx_mod_function_entry_active(void) {
|
||||
|
||||
extern "C" void mod_runtime_function_entry_context_save(ModFunctionEntryContext *out) {
|
||||
out->depth = PSXRecompV4::function_entry_depth;
|
||||
out->plugin = PSXRecompV4::state().current_plugin;
|
||||
out->cpu = PSXRecompV4::state().current_function_cpu;
|
||||
out->finished = PSXRecompV4::state().current_function_finished;
|
||||
out->plugin = PSXRecompV4::execution().current_plugin;
|
||||
out->cpu = PSXRecompV4::execution().current_function_cpu;
|
||||
out->finished = PSXRecompV4::execution().current_function_finished;
|
||||
}
|
||||
|
||||
extern "C" void mod_runtime_function_entry_context_restore(const ModFunctionEntryContext *in) {
|
||||
PSXRecompV4::function_entry_depth = in->depth;
|
||||
PSXRecompV4::state().current_plugin =
|
||||
PSXRecompV4::execution().current_plugin =
|
||||
static_cast<const PSXRecompV4::ModResolution::Plugin *>(in->plugin);
|
||||
PSXRecompV4::state().current_function_cpu = in->cpu;
|
||||
PSXRecompV4::state().current_function_finished = in->finished != 0;
|
||||
PSXRecompV4::execution().current_function_cpu = in->cpu;
|
||||
PSXRecompV4::execution().current_function_finished = in->finished != 0;
|
||||
}
|
||||
|
||||
/* Apply the committed plan's disc writes and overlays to one sector. The
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
#include "netplay_state_digest.h"
|
||||
#include "mod_netplay.h"
|
||||
#include "cdrom.h"
|
||||
@@ -14,8 +15,8 @@
|
||||
|
||||
extern uint8_t* memory_get_ram_ptr(void);
|
||||
extern uint32_t memory_get_ram_bytes(void);
|
||||
extern uint32_t i_stat;
|
||||
extern uint32_t i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_mask;
|
||||
extern void timers_get_snapshot(uint16_t counter[3], uint32_t mode[3],
|
||||
uint16_t target[3], int32_t irq_line[3],
|
||||
uint32_t frac[3]);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
#include "overlay_loader.h"
|
||||
#include "overlay_api.h"
|
||||
#include "overlay_path_canon.h"
|
||||
@@ -543,7 +544,7 @@ static uint32_t s_last_write_size = 0;
|
||||
* before the guest-visible write; block/device boundaries flush through the
|
||||
* DLL glue directly. */
|
||||
static OverlayFlushFn s_dll_flush[4096];
|
||||
OverlayFlushFn g_overlay_flush_pending_cycles = NULL;
|
||||
PSX_THREAD_LOCAL OverlayFlushFn g_overlay_flush_pending_cycles = NULL;
|
||||
|
||||
static OverlayFlushFn overlay_flush_enter(const Candidate *c) {
|
||||
OverlayFlushFn prev = g_overlay_flush_pending_cycles;
|
||||
@@ -2268,8 +2269,8 @@ static int s_irq_suppress_cdrom_only = 0;
|
||||
static int s_irq_post_dispatch_pump = 0;
|
||||
static int s_irq_defer_cdrom = 0;
|
||||
|
||||
extern uint32_t i_stat;
|
||||
extern uint32_t i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_mask;
|
||||
|
||||
/* Nested call-unit depth (Ape memcard native<->interp resume-desync fix).
|
||||
*
|
||||
@@ -2296,7 +2297,7 @@ extern uint32_t i_mask;
|
||||
* inside units; the blanket skip survives only behind
|
||||
* PSX_OVERLAY_UNIT_IRQ_DEFER=1 for A/B bisection (overlay_unit_defer.h).
|
||||
* Incremented only when the A/B toggle is on (below). */
|
||||
int g_call_unit_depth = 0;
|
||||
PSX_THREAD_LOCAL int g_call_unit_depth = 0;
|
||||
|
||||
/* A/B toggle for the nested-unit IRQ deferral (PSX_OVERLAY_UNIT_DEFER, default
|
||||
* ON). Gates the depth INCREMENTS (so =0 leaves g_call_unit_depth at 0 and
|
||||
@@ -2553,7 +2554,7 @@ static void init_callbacks(void) {
|
||||
* static and interpreted stores do, in every build, because memory.c's
|
||||
* store filters read it as well as the debug tooling. */
|
||||
{
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
s_callbacks.last_store_pc = &g_debug_last_store_pc;
|
||||
}
|
||||
/* Widescreen hooks (ABI v3): overlay-emitted psx_ws_* calls forward to
|
||||
@@ -4271,7 +4272,7 @@ retry_candidates:
|
||||
* the current activation, so we permanently demote that entry to interp. Called
|
||||
* from memory.c only when the written page is watched. */
|
||||
void overlay_loader_active_write_check(uint32_t phys, uint32_t size) {
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
uint32_t p = phys & 0x1FFFFFFFu;
|
||||
for (int d = 0; d < s_active_depth; d++) {
|
||||
Candidate *c = &s_cand[s_active_stack[d]];
|
||||
@@ -4713,7 +4714,7 @@ static void run_shadow_diff(CPUState *cpu, Candidate *c, uint32_t addr) {
|
||||
extern void psx_dispatch_call(CPUState *cpu, uint32_t addr, uint32_t return_addr);
|
||||
extern int gte_replay_side_effects_begin(void);
|
||||
extern void gte_replay_side_effects_end(void);
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern uint64_t psx_exception_setjmp_epoch(void);
|
||||
extern int g_shadow_mmio_watch;
|
||||
|
||||
@@ -4912,7 +4913,7 @@ static void shadow_escape_cleanup(void) {
|
||||
s_suppress_irq = s_shadow_saved_supp;
|
||||
g_exec_phase = s_shadow_saved_exec_phase;
|
||||
g_psx_call_bail = s_shadow_saved_call_bail;
|
||||
{ extern uint32_t g_debug_current_func_addr;
|
||||
{ extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
g_debug_current_func_addr = s_shadow_saved_debug_func; }
|
||||
s_irq_callcount = s_shadow_saved_irq_calls;
|
||||
s_irq_suppressed = s_shadow_saved_irq_supp;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* psx_bios_backend.c — routing between the runtime and the active BIOS.
|
||||
*
|
||||
* A build links every recompiled BIOS it ships (bundled OpenBIOS plus a retail
|
||||
@@ -19,6 +20,7 @@
|
||||
#include "bios_hle.h"
|
||||
#include "cpu_state.h"
|
||||
#include "psx_bios_backend.h"
|
||||
#include "psx_execution.h"
|
||||
|
||||
/* ── State published to the rest of the runtime ─────────────────────────── */
|
||||
|
||||
@@ -37,7 +39,7 @@ uint32_t psx_bios_kernel_patch_range_count = 0;
|
||||
/* Dispatch nesting depth. Shared dispatch state, not per-image: each generated
|
||||
* dispatch used to define its own copy, which is precisely why two of them
|
||||
* could not be linked together. */
|
||||
int g_psx_dispatch_depth = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_dispatch_depth = 0;
|
||||
|
||||
/* ── Registry ───────────────────────────────────────────────────────────────
|
||||
*
|
||||
@@ -99,14 +101,19 @@ __attribute__((constructor)) static void psx_bios_registry_ctor(void) { registry
|
||||
* The generated BIOS C defines <STEM>_psx_dispatch; these keep the unprefixed
|
||||
* names that ~66 runtime call sites and the game's generated C already use. */
|
||||
|
||||
PSX_THREAD_LOCAL PsxExecutionDispatch g_psx_execution_dispatch;
|
||||
PSX_THREAD_LOCAL PsxExecutionAbort g_psx_execution_abort;
|
||||
|
||||
void psx_dispatch(CPUState *cpu, uint32_t addr)
|
||||
{
|
||||
if (g_psx_execution_dispatch) { g_psx_execution_dispatch(cpu, addr, 0); return; }
|
||||
if (!psx_bios_active || !psx_bios_active->dispatch) return;
|
||||
psx_bios_active->dispatch(cpu, addr);
|
||||
}
|
||||
|
||||
void psx_dispatch_call(CPUState *cpu, uint32_t addr, uint32_t return_addr)
|
||||
{
|
||||
if (g_psx_execution_dispatch) { g_psx_execution_dispatch(cpu, addr, return_addr); return; }
|
||||
if (!psx_bios_active || !psx_bios_active->dispatch_call) return;
|
||||
psx_bios_active->dispatch_call(cpu, addr, return_addr);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* psx_bios_module.c — host side of loadable BIOS backends (psx_bios_module.h).
|
||||
*
|
||||
* A bundled release links OpenBIOS only. When the player picks a retail dump
|
||||
@@ -203,7 +204,7 @@ int psx_bios_module_cache_path(const char *exe_dir, const char *stem,
|
||||
void psx_bios_module_fill_callbacks(PsxBiosModuleCallbacks *out) {
|
||||
extern int (*g_psx_bios_hle_hook)(struct CPUState *cpu, uint32_t phys);
|
||||
extern uint64_t g_dispatch_static_hits;
|
||||
extern uint64_t g_psx_bail_flattened;
|
||||
extern PSX_THREAD_LOCAL uint64_t g_psx_bail_flattened;
|
||||
extern int psx_game_address_in_text(uint32_t addr);
|
||||
extern uint64_t s_frame_count;
|
||||
extern uint32_t g_psx_mod_guest_functions;
|
||||
|
||||
@@ -320,7 +320,7 @@ static uint32_t devices_cycles_to_next_internal_event(void) {
|
||||
* tens of thousands of host-side re-detections per second. VBlank remains an
|
||||
* unconditional ceiling so frame pacing is never crossed in one jump. */
|
||||
static uint32_t devices_cycles_to_next_idle_event(void) {
|
||||
extern uint32_t i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_mask;
|
||||
uint32_t best = interrupts_cycles_to_vblank();
|
||||
uint32_t t = timers_cycles_to_irq(i_mask); if (t < best) best = t;
|
||||
uint32_t c = cdrom_cycles_to_irq(i_mask); if (c < best) best = c;
|
||||
@@ -723,7 +723,8 @@ void psx_idle_note_check(CPUState *cpu, uint32_t check_pc) {
|
||||
(void)cpu; (void)check_pc;
|
||||
return;
|
||||
#else
|
||||
extern int g_psx_call_bail, g_precise_mode, g_ls_mode, g_ls_replay_active;
|
||||
extern PSX_THREAD_LOCAL int g_psx_call_bail;
|
||||
extern int g_precise_mode, g_ls_mode, g_ls_replay_active;
|
||||
extern int psx_get_in_exception(void);
|
||||
extern uint64_t g_guest_store_count, g_mmio_access_count;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* psx_icache.c — R3000A instruction-cache FETCH cost model (faithful).
|
||||
*
|
||||
* FAITHFUL_TIMING_PLAN.md axis-2 (I-cache). Transcribed from the in-tree Beetle
|
||||
@@ -14,7 +15,7 @@
|
||||
#include <string.h>
|
||||
|
||||
int psx_icache_enabled(void) {
|
||||
static int s = -1;
|
||||
static PSX_THREAD_LOCAL int s = -1;
|
||||
if (s < 0) {
|
||||
const char* e = getenv("PSX_ICACHE");
|
||||
s = (e == NULL || e[0] == '\0') ? 1 : (e[0] != '0');
|
||||
@@ -25,16 +26,16 @@ int psx_icache_enabled(void) {
|
||||
/* Per-word tag+validity, mirroring Beetle ICache[idx].TV. Init to a value that can
|
||||
* never equal an aligned fetch address (aligned addrs have bits 0-1 = 0), so every
|
||||
* line starts cold (miss). */
|
||||
uint32_t g_psx_icache_tv[1024];
|
||||
int g_psx_icache_active = -1;
|
||||
PSX_THREAD_LOCAL uint32_t g_psx_icache_tv[1024];
|
||||
PSX_THREAD_LOCAL int g_psx_icache_active = -1;
|
||||
|
||||
/* Whole-call shadow: a transactional view of the cache tags so an AOT/diff
|
||||
* shadow replay can evolve the cache from the recorded entry state and then
|
||||
* restore the live view (master's shadow machinery, retargeted at the global
|
||||
* tag array the interpreter fast path exports). */
|
||||
static uint32_t s_icache_shadow_entry[1024];
|
||||
static uint32_t s_icache_shadow_live[1024];
|
||||
static int s_icache_shadow_state = 0; /* 0 none, 1 recorded, 2 replay */
|
||||
static PSX_THREAD_LOCAL uint32_t s_icache_shadow_entry[1024];
|
||||
static PSX_THREAD_LOCAL uint32_t s_icache_shadow_live[1024];
|
||||
static PSX_THREAD_LOCAL int s_icache_shadow_state = 0; /* 0 none, 1 recorded, 2 replay */
|
||||
|
||||
int psx_icache_shadow_record_begin(void) {
|
||||
if (s_icache_shadow_state != 0) return 0;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* psx_interpreter.c — Minimal R3000A interpreter for oracle builds.
|
||||
*
|
||||
* Implements the full MIPS I instruction set used by the PSX BIOS:
|
||||
@@ -22,11 +23,11 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
/* I_STAT and I_MASK are owned by memory.c */
|
||||
extern uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
/* Central IRQ-raise choke point (interrupts.c) — also records the device ring. */
|
||||
extern void psx_irq_raise(uint32_t bit, uint32_t detail);
|
||||
extern void psx_irq_refresh_cause_ip2(void); /* interrupts.c — CAUSE.IP2 mirror */
|
||||
extern uint32_t i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_mask;
|
||||
|
||||
#define COP0_SR 12
|
||||
#define COP0_CAUSE 13
|
||||
|
||||
+15
-14
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* render_pass.c - host-timed render passes (psx_mod_render_pass_plan /
|
||||
* psx_mod_render_pass, mod_plugins.h). See docs/RENDER_PASSES.md.
|
||||
*
|
||||
@@ -44,8 +45,8 @@ extern uint16_t *gpu_get_vram_ptr(void);
|
||||
* the opt-in 8 MB RAM mod. Latched by memory_init() once per boot, so it
|
||||
* cannot change inside a pass, but it can between sessions. */
|
||||
extern uint32_t memory_get_ram_bytes(void);
|
||||
extern uint32_t i_stat;
|
||||
extern uint32_t i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_mask;
|
||||
/* gte.cpp: precision-tracking shadows (PGXP + the position cache) roll back
|
||||
* with the machine, or they describe the words a pass wrote. */
|
||||
extern void gte_precision_checkpoint_begin(void);
|
||||
@@ -70,22 +71,22 @@ extern int psx_rewind_is_open(void);
|
||||
extern int psx_presentation_fast_forward(void);
|
||||
extern uint64_t g_guest_store_count;
|
||||
extern uint64_t g_mmio_access_count;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern int g_psx_dispatch_depth;
|
||||
extern int g_psx_call_bail;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL int g_psx_dispatch_depth;
|
||||
extern PSX_THREAD_LOCAL int g_psx_call_bail;
|
||||
extern int g_ls_mode;
|
||||
extern int g_ls_replay_active;
|
||||
static int s_netplay_allowed;
|
||||
extern void (*g_overlay_flush_pending_cycles)(void);
|
||||
extern PSX_THREAD_LOCAL void (*g_overlay_flush_pending_cycles)(void);
|
||||
/* Host nesting that runtime frames set on entry and put back on exit. */
|
||||
extern int g_call_unit_depth; /* overlay_loader_call_native */
|
||||
extern int g_dma_exec_depth; /* dma.c kick / async writes */
|
||||
extern int g_host_store_depth; /* memory.c psx_host_write_* */
|
||||
extern int g_dma_cur_ch;
|
||||
extern uint32_t g_dma_cur_madr;
|
||||
extern uint32_t g_dma_cur_bcr;
|
||||
extern uint32_t g_dma_initiator_pc;
|
||||
extern PSX_THREAD_LOCAL int g_call_unit_depth; /* overlay_loader_call_native */
|
||||
extern PSX_THREAD_LOCAL int g_dma_exec_depth; /* dma.c kick / async writes */
|
||||
extern PSX_THREAD_LOCAL int g_host_store_depth; /* memory.c psx_host_write_* */
|
||||
extern PSX_THREAD_LOCAL int g_dma_cur_ch;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_dma_cur_madr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_dma_cur_bcr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_dma_initiator_pc;
|
||||
extern int g_dirty_interp_active; /* dirty-RAM interpreter */
|
||||
extern int g_exec_phase;
|
||||
extern int g_precise_mode;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
* can never fall between a check and a wait. */
|
||||
|
||||
#include "render_thread.h"
|
||||
#include "psx_thread_local.h"
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <stdlib.h>
|
||||
@@ -79,6 +80,70 @@ static void cpu_relax(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/* List capture is independent of the live producer and every ring counter. */
|
||||
struct RtCommandList {
|
||||
unsigned char *data;
|
||||
size_t capacity, used, pending;
|
||||
int sealed;
|
||||
};
|
||||
static PSX_THREAD_LOCAL RtCommandList *t_capture;
|
||||
static PSX_THREAD_LOCAL RtCaptureAbort t_capture_abort;
|
||||
static void capture_fail(const char *reason) {
|
||||
if (t_capture_abort) t_capture_abort(reason);
|
||||
abort();
|
||||
}
|
||||
RtCommandList *rt_list_create(size_t capacity) {
|
||||
if (capacity < RT_ALIGN || capacity > UINT32_MAX) return NULL;
|
||||
RtCommandList *list = (RtCommandList *)calloc(1, sizeof(*list));
|
||||
if (!list) return NULL;
|
||||
list->data = (unsigned char *)malloc(capacity);
|
||||
if (!list->data) { free(list); return NULL; }
|
||||
list->capacity = capacity;
|
||||
return list;
|
||||
}
|
||||
void rt_list_destroy(RtCommandList *list) {
|
||||
if (!list) return;
|
||||
if (list == t_capture) abort();
|
||||
free(list->data); free(list);
|
||||
}
|
||||
void rt_capture_begin(RtCommandList *list, RtCaptureAbort failure) {
|
||||
if (!list || t_capture) abort();
|
||||
list->used = list->pending = 0;
|
||||
list->sealed = 0;
|
||||
t_capture = list; t_capture_abort = failure;
|
||||
}
|
||||
void rt_capture_end(void) {
|
||||
if (!t_capture) return;
|
||||
/* An abort can abandon the last uncommitted record. It is never visible. */
|
||||
t_capture->pending = 0;
|
||||
t_capture->sealed = 1;
|
||||
t_capture = NULL; t_capture_abort = NULL;
|
||||
}
|
||||
int rt_capture_active(void) { return t_capture != NULL; }
|
||||
size_t rt_list_bytes(const RtCommandList *list) {
|
||||
return list && list->sealed ? list->used : 0;
|
||||
}
|
||||
static void *capture_record(uint16_t op, uint16_t flags, uint32_t payload) {
|
||||
RtCommandList *list = t_capture;
|
||||
size_t size = (sizeof(RtCmd) + (uint64_t)payload + RT_ALIGN - 1u) & ~(uint64_t)(RT_ALIGN - 1u);
|
||||
if (!op || list->pending) capture_fail("command_order");
|
||||
if (size > list->capacity - list->used) capture_fail("command_capacity");
|
||||
RtCmd *cmd = (RtCmd *)(list->data + list->used);
|
||||
*cmd = (RtCmd){(uint32_t)size, op, flags, payload, 0};
|
||||
list->pending = size;
|
||||
return cmd + 1;
|
||||
}
|
||||
void rt_list_execute(const RtCommandList *list, RtExecFn exec, void *user) {
|
||||
if (!list || !list->sealed || !exec || t_capture) abort();
|
||||
for (size_t offset = 0; offset < list->used;) {
|
||||
const RtCmd *cmd = (const RtCmd *)(list->data + offset);
|
||||
if (cmd->size < sizeof(*cmd) || cmd->size % RT_ALIGN ||
|
||||
cmd->size > list->used - offset || cmd->payload > cmd->size - sizeof(*cmd)) abort();
|
||||
exec(user, cmd, cmd + 1);
|
||||
offset += cmd->size;
|
||||
}
|
||||
}
|
||||
|
||||
#define OP_PAD 0u /* filler to the ring end; consumer skips it */
|
||||
#define F_FRAME 0x8000u /* op 0 with this flag: frame marker */
|
||||
|
||||
@@ -306,6 +371,7 @@ static int pred_frames(uint64_t limit) {
|
||||
static int pred_released(uint64_t unused) { (void)unused; return atomic_load(&R.released); }
|
||||
|
||||
void *rt_cmd_begin(uint16_t op, uint16_t flags, uint32_t payload_bytes) {
|
||||
if (t_capture) return capture_record(op, flags, payload_bytes);
|
||||
if (!rt_running() || R.pending || op == OP_PAD) return NULL;
|
||||
uint64_t size = align_up(sizeof(RtCmd) + (uint64_t)payload_bytes);
|
||||
if (size > R.cap / 2) { R.oversize++; return NULL; }
|
||||
@@ -338,6 +404,10 @@ void *rt_cmd_begin(uint16_t op, uint16_t flags, uint32_t payload_bytes) {
|
||||
}
|
||||
|
||||
void rt_cmd_commit(void) {
|
||||
if (t_capture) {
|
||||
if (!t_capture->pending) capture_fail("command_commit");
|
||||
t_capture->used += t_capture->pending; t_capture->pending = 0; return;
|
||||
}
|
||||
if (!R.pending) return;
|
||||
R.pending = 0;
|
||||
uint64_t end = R.pend_start + R.pend_size;
|
||||
@@ -362,6 +432,7 @@ static void commit_marker(uint16_t flags) {
|
||||
}
|
||||
|
||||
void rt_frame_end(void) {
|
||||
if (t_capture) capture_fail("live_render_sync");
|
||||
if (!rt_running()) return;
|
||||
if (R.held) {
|
||||
/* Nothing is queued while the context is held; count the frame as
|
||||
@@ -389,12 +460,14 @@ int rt_frames_ahead(void) {
|
||||
}
|
||||
|
||||
void rt_drain(void) {
|
||||
if (t_capture) capture_fail("live_render_sync");
|
||||
if (!rt_running() || R.held) return;
|
||||
uint64_t w = atomic_load(&R.wpos);
|
||||
emu_wait(pred_drained, w);
|
||||
}
|
||||
|
||||
void rt_acquire(const char *reason) {
|
||||
if (t_capture) capture_fail("live_render_sync");
|
||||
if (!rt_running() || R.held) return;
|
||||
uint64_t t0 = now_ns();
|
||||
rt_drain();
|
||||
@@ -415,6 +488,7 @@ void rt_acquire(const char *reason) {
|
||||
}
|
||||
|
||||
void rt_release(void) {
|
||||
if (t_capture) capture_fail("live_render_sync");
|
||||
if (!rt_running() || !R.held) return;
|
||||
R.ctx(R.user, 0);
|
||||
mtx_lock(&R.mtx);
|
||||
@@ -476,6 +550,7 @@ int rt_start(const RtConfig *cfg) {
|
||||
}
|
||||
|
||||
void rt_stop(void) {
|
||||
if (t_capture) capture_fail("live_render_sync");
|
||||
if (!rt_running()) return;
|
||||
int was_held = R.held;
|
||||
if (!was_held) rt_drain();
|
||||
|
||||
+10
-9
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/*
|
||||
* sio.c -- PS1 Serial I/O (SIO0) controller
|
||||
*
|
||||
@@ -21,7 +22,7 @@
|
||||
#include <string.h>
|
||||
|
||||
/* I_STAT is owned by memory.c */
|
||||
extern uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
/* Central IRQ-raise choke point (interrupts.c) — also records the device ring. */
|
||||
extern void psx_irq_raise(uint32_t bit, uint32_t detail);
|
||||
|
||||
@@ -492,9 +493,9 @@ static int s_card_handoff_count = 0;
|
||||
static int s_card_handoff_armed = 0; /* set after LOAD-style probe abort */
|
||||
|
||||
static void card_handoff_push(uint8_t kind, uint8_t byte) {
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern uint32_t i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_mask;
|
||||
extern uint64_t psx_get_cycle_count(void);
|
||||
SioCardHandoffEntry *e = &s_card_handoff[s_card_handoff_idx];
|
||||
e->kind = kind;
|
||||
@@ -1959,7 +1960,7 @@ static void mc_process_byte(uint8_t tx_byte) {
|
||||
|
||||
static void sio_process_byte(uint8_t tx_byte) {
|
||||
/* ---- Trace: capture pre-state ---- */
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
uint8_t trace_mc_pre = (uint8_t)mc_state;
|
||||
uint8_t trace_dev_pre = (uint8_t)active_device;
|
||||
uint8_t trace_irq_cd = (uint8_t)(sio_irq_countdown > 255 ? 255 : sio_irq_countdown);
|
||||
@@ -2398,7 +2399,7 @@ void sio_write(uint32_t addr, uint32_t value) {
|
||||
sio_mc_abort_ctrl = (uint16_t)value;
|
||||
}
|
||||
if (sio_txn_open) {
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
txn_close(SIO_TXN_END_ABORT_RESET, mc_state, g_debug_current_func_addr);
|
||||
}
|
||||
sio_stat = SIO_STAT_TX_RDY | SIO_STAT_TX_EMPTY;
|
||||
@@ -2479,7 +2480,7 @@ void sio_write(uint32_t addr, uint32_t value) {
|
||||
/* SELECT deassertion cancels the device's pending DSR pulse.
|
||||
* It cannot create an ACK or preserve one for later delivery. */
|
||||
if (active_device == DEV_MEMCARD && mc_state != MC_IDLE && sio_txn_open) {
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
txn_close(SIO_TXN_END_ABORT_OTHER, mc_state, g_debug_current_func_addr);
|
||||
}
|
||||
/* Persist mc_flag back to the active slot before resetting. mc_flag
|
||||
@@ -2707,7 +2708,7 @@ static void sio_fire_ack_irq(void) {
|
||||
card_handoff_push(5, 0);
|
||||
}
|
||||
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern uint8_t psx_read_byte(uint32_t addr);
|
||||
SioIrqEntry *e = &sio_irq_buf[sio_irq_idx];
|
||||
e->seq = sio_irq_seq;
|
||||
@@ -2876,7 +2877,7 @@ void sio_tick(int cycles) {
|
||||
event_ring_record_aux(EV_DEQ, (uint8_t)SRC_SIO, 1u); /* SIO shift IRQ fired */
|
||||
|
||||
/* SIO IRQ ring capture. */
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern uint8_t psx_read_byte(uint32_t addr);
|
||||
SioIrqEntry *e = &sio_irq_buf[sio_irq_idx];
|
||||
e->seq = sio_irq_seq;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* starvation_ring.c — diagnostic-only.
|
||||
*
|
||||
* Always-on ring of the last 16K SIO/MMIO events with full SIO state
|
||||
@@ -38,10 +39,10 @@ static uint64_t host_us_now(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern uint32_t i_stat;
|
||||
extern uint32_t i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_mask;
|
||||
extern int psx_get_in_exception(void);
|
||||
|
||||
#if STARVATION_RING_ENABLED
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/*
|
||||
* timers.c — PS1 root counters (3 timers), v4 clean port.
|
||||
*
|
||||
@@ -47,8 +48,8 @@ typedef struct {
|
||||
int irq_line; /* toggle mode state */
|
||||
} Timer;
|
||||
|
||||
static Timer timers[3];
|
||||
static uint32_t timer_frac[3];
|
||||
static PSX_THREAD_LOCAL Timer timers[3];
|
||||
static PSX_THREAD_LOCAL uint32_t timer_frac[3];
|
||||
|
||||
/* Always-on RootCounter/Timer IRQ-fire telemetry (Tomba 2 RCnt-wait diag):
|
||||
* counts how many times each timer raised its IRQ. If a game spins on an
|
||||
@@ -67,7 +68,7 @@ void timers_get_debug(int t, uint16_t *counter, uint16_t *target,
|
||||
}
|
||||
|
||||
/* I_STAT is owned by memory.c — we poke it directly via this extern. */
|
||||
extern uint32_t i_stat;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
/* Central IRQ-raise choke point (interrupts.c) — also records the device ring. */
|
||||
extern void psx_irq_raise(uint32_t bit, uint32_t detail);
|
||||
|
||||
|
||||
+7
-7
@@ -100,11 +100,11 @@ int psx_is_dispatchable(uint32_t pc)
|
||||
}
|
||||
|
||||
/* Dispatch call contract state (see cpu_state.h for the model). */
|
||||
int g_psx_call_bail = 0;
|
||||
uint64_t g_psx_bail_first = 0;
|
||||
uint64_t g_psx_bail_resolved = 0;
|
||||
uint64_t g_psx_bail_flattened = 0;
|
||||
uint64_t g_psx_bail_anomaly = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_call_bail = 0;
|
||||
PSX_THREAD_LOCAL uint64_t g_psx_bail_first = 0;
|
||||
PSX_THREAD_LOCAL uint64_t g_psx_bail_resolved = 0;
|
||||
PSX_THREAD_LOCAL uint64_t g_psx_bail_flattened = 0;
|
||||
PSX_THREAD_LOCAL uint64_t g_psx_bail_anomaly = 0;
|
||||
|
||||
/* ---- Deduped bail-source ledger (Tomba 2 wild-return / splash-spin diag) ----
|
||||
* psx_call_contract (cpu_state.h) calls psx_bail_record() at each FIRST-detected
|
||||
@@ -818,7 +818,7 @@ void psx_request_return_to_lobby(void)
|
||||
|
||||
void psx_scheduler_run(CPUState* cpu)
|
||||
{
|
||||
extern int g_psx_dispatch_depth;
|
||||
extern PSX_THREAD_LOCAL int g_psx_dispatch_depth;
|
||||
g_in_scheduler_run = 1;
|
||||
g_sched_escape.reason = PSX_RUN_CONTINUE;
|
||||
for (;;) {
|
||||
@@ -844,7 +844,7 @@ void psx_scheduler_run(CPUState* cpu)
|
||||
* or dispatch_nonlocal_call) would skip its depth restore; clear the
|
||||
* nested-unit gate so IRQ checks are not wedged-off after the escape
|
||||
* (backstop for the Ape memcard native<->interp fix). */
|
||||
{ extern int g_call_unit_depth; g_call_unit_depth = 0; }
|
||||
{ extern PSX_THREAD_LOCAL int g_call_unit_depth; g_call_unit_depth = 0; }
|
||||
/* flush_resume / savestate longjmp skips generated bb_defer cleanup. */
|
||||
{
|
||||
extern PSX_THREAD_LOCAL int g_psx_cyc_bb_defer;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* Shared link stubs for tests that compile runtime/src/gpu.c directly and
|
||||
* execute GP0 packets without a GL context, mod bank or emulator core.
|
||||
* Include right after "../src/gpu.c". Define GPU_EXEC_REAL_UI_GROUP before
|
||||
@@ -7,8 +8,8 @@
|
||||
uint64_t s_frame_count;
|
||||
PSX_THREAD_LOCAL uint64_t psx_cycle_count; /* gpu_timeline.c stamps notes with it */
|
||||
int g_exec_phase;
|
||||
uint32_t g_debug_current_func_addr;
|
||||
uint32_t g_debug_last_store_pc;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
CPUState *debug_cpu_ptr;
|
||||
int g_psx_vram_dirty_tracking;
|
||||
uint32_t g_psx_ram_size = 0x00200000u;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
#define PSX_OVERLAY_DLL_BUILD 1
|
||||
#include "overlay_loader.h"
|
||||
#undef PSX_OVERLAY_DLL_BUILD
|
||||
@@ -27,15 +28,15 @@ static uint8_t s_scratch[1024];
|
||||
uint32_t g_psx_ram_size = RAM_SIZE;
|
||||
uint32_t g_psx_ram_mask = RAM_SIZE - 1u;
|
||||
|
||||
uint32_t g_debug_current_func_addr;
|
||||
uint32_t g_debug_last_store_pc;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
uint32_t g_overlay_region_floor;
|
||||
uint32_t i_stat, i_mask;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat, i_mask;
|
||||
int g_exec_phase;
|
||||
int g_idle_note_suppress;
|
||||
int g_psx_call_bail;
|
||||
PSX_THREAD_LOCAL int g_psx_call_bail;
|
||||
uint32_t g_dirty_ram_code_gen;
|
||||
uint64_t g_psx_bail_first, g_psx_bail_resolved;
|
||||
PSX_THREAD_LOCAL uint64_t g_psx_bail_first, g_psx_bail_resolved;
|
||||
uint64_t s_frame_count;
|
||||
int g_shadow_mmio_watch;
|
||||
uint64_t g_shadow_mmio_hits;
|
||||
|
||||
@@ -15,15 +15,15 @@ int g_stub_device_restores;
|
||||
|
||||
/* ---- data ---------------------------------------------------------------- */
|
||||
CPUState *debug_cpu_ptr;
|
||||
uint32_t g_debug_current_func_addr;
|
||||
uint32_t g_debug_last_store_pc;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
volatile int g_ds_recording;
|
||||
int g_event_step_conservative;
|
||||
int g_ls_mode;
|
||||
int g_ls_replay_active;
|
||||
int g_ls_suppress_record;
|
||||
int g_ram_read_watch_active;
|
||||
void (*g_overlay_flush_pending_cycles)(void);
|
||||
PSX_THREAD_LOCAL void (*g_overlay_flush_pending_cycles)(void);
|
||||
PSX_THREAD_LOCAL uint32_t g_psx_cyc_batch;
|
||||
PSX_THREAD_LOCAL uint32_t g_psx_cyc_batch_limit;
|
||||
PSX_THREAD_LOCAL uint32_t *g_psx_cyc_local_acc;
|
||||
@@ -34,7 +34,7 @@ void pgxp_invalidate_word(uint32_t addr) { (void)addr; }
|
||||
#endif
|
||||
#ifndef PSX_STUBS_REAL_ICACHE
|
||||
/* The I-cache model (psx_icache.c). isc_store_test links the real one. */
|
||||
uint32_t g_psx_icache_tv[1024];
|
||||
PSX_THREAD_LOCAL uint32_t g_psx_icache_tv[1024];
|
||||
void psx_icache_isc_store(uint32_t biu, uint32_t addr, uint32_t value) {
|
||||
(void)biu; (void)addr; (void)value; __builtin_trap(); /* SR.IsC is never set here */
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ struct CPUState;
|
||||
uint8_t g_test_ram[0x200000];
|
||||
PSX_THREAD_LOCAL uint64_t psx_cycle_count = 0;
|
||||
PSX_THREAD_LOCAL uint64_t psx_next_service_cycle = 0;
|
||||
uint32_t i_stat = 0;
|
||||
uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
uint32_t g_psx_ram_size = 0x200000u;
|
||||
uint32_t g_psx_vblank_cycles = 564480u;
|
||||
|
||||
@@ -46,11 +46,11 @@ int psx_netplay_rb_recover_null_pc(struct CPUState *c, uint32_t *out_pc)
|
||||
/* ---- Runtime globals the two units reference ---- */
|
||||
uint64_t g_async_rfe_fire_count = 0;
|
||||
uint32_t g_async_rfe_resume_pc = 0;
|
||||
int g_call_unit_depth = 0;
|
||||
PSX_THREAD_LOCAL int g_call_unit_depth = 0;
|
||||
int g_cosim_dirty_pump_site = 0;
|
||||
int g_dirty_interp_active = 0;
|
||||
uint32_t g_dirty_safe_resume_pc = 0;
|
||||
int g_dma_exec_depth = 0;
|
||||
PSX_THREAD_LOCAL int g_dma_exec_depth = 0;
|
||||
int g_event_step_conservative = 0;
|
||||
int g_exec_phase = 0;
|
||||
int g_idle_skip_enabled = 0;
|
||||
@@ -60,7 +60,7 @@ PSX_THREAD_LOCAL uint32_t g_psx_cyc_batch = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_psx_cyc_batch_limit = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_cyc_bb_defer = 0;
|
||||
PSX_THREAD_LOCAL uint32_t *g_psx_cyc_local_acc = NULL;
|
||||
int g_psx_dispatch_depth = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_dispatch_depth = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_guest_time_frozen = 0;
|
||||
uint32_t g_sentinel_reach_async = 0;
|
||||
uint64_t g_sentinel_reach_traps = 0;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* test_bios_module_host.c — the host side of loadable BIOS backends
|
||||
* (runtime/src/psx_bios_module.c) against fake modules built by
|
||||
* runtime/CMakeLists.txt from tests/bios_module_fake.c.
|
||||
@@ -47,8 +48,8 @@ int psx_mod_dispatch_guest_function(struct CPUState *cpu, uint32_t address) {
|
||||
}
|
||||
int (*g_psx_bios_hle_hook)(struct CPUState *cpu, uint32_t phys) = 0;
|
||||
uint64_t g_dispatch_static_hits = 0;
|
||||
uint32_t g_debug_current_func_addr = 0;
|
||||
uint64_t g_psx_bail_flattened = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr = 0;
|
||||
PSX_THREAD_LOCAL uint64_t g_psx_bail_flattened = 0;
|
||||
uint32_t g_psx_ram_mask = 0x1FFFFFu;
|
||||
void psx_bios_hle_configure(uint32_t a, uint32_t b) { (void)a; (void)b; }
|
||||
int autocompile_toolchain_available(void) { return 0; }
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
static uint8_t ram[2*1024*1024], spad[1024], spuram[512*1024];
|
||||
static uint16_t vram[1024*512];
|
||||
static int boot_clears, cd_handoffs;
|
||||
uint32_t i_stat, i_mask, g_psx_icache_tv[1024];
|
||||
PSX_THREAD_LOCAL uint32_t i_stat, i_mask, g_psx_icache_tv[1024];
|
||||
PSX_THREAD_LOCAL uint64_t psx_cycle_count;
|
||||
PSX_THREAD_LOCAL uint32_t g_psx_gcs_frac;
|
||||
uint32_t psx_guest_cycle_scale_config(void) { return 1; }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/*
|
||||
* Validate that COP0.CAUSE bit 10 (IP2) behaves as a COMBINATIONAL mirror of
|
||||
* the interrupt-controller line, not as a latch.
|
||||
@@ -34,8 +35,8 @@
|
||||
/* ---- The unit under test -------------------------------------------------
|
||||
* Mirrors runtime/src/interrupts.c. Kept in lockstep with it; if the two ever
|
||||
* disagree this test is the thing that should be updated last, not first. */
|
||||
uint32_t i_stat;
|
||||
uint32_t i_mask;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask;
|
||||
|
||||
static uint32_t *s_cause_ptr;
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* Synthetic-sector integration tests for the actual CD controller. */
|
||||
#include "../src/cdrom.c"
|
||||
|
||||
#define CHECK(x) do { if (!(x)) { fprintf(stderr, "FAIL line %d: %s\n", __LINE__, #x); exit(1); } } while (0)
|
||||
uint32_t i_stat, g_debug_current_func_addr, g_debug_last_store_pc;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat, g_debug_current_func_addr, g_debug_last_store_pc;
|
||||
uint64_t s_frame_count, psx_cycle_count;
|
||||
static int test_dma_active;
|
||||
int dma_cdrom_transfer_active(void) { return test_dma_active; }
|
||||
|
||||
@@ -81,9 +81,9 @@ void gpu_ws_validate_linked_list_header(uint32_t addr, uint32_t h) { (void)addr;
|
||||
void gpu_ws_validate_linked_list_node(uint32_t addr, uint32_t n) { (void)addr; (void)n; }
|
||||
|
||||
/* ---- observers and IRQ (inert) ----------------------------------------- */
|
||||
uint32_t i_stat;
|
||||
uint32_t g_debug_current_func_addr;
|
||||
uint32_t g_debug_last_store_pc;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
uint64_t s_frame_count;
|
||||
uint64_t g_io_openbus_reads;
|
||||
uint64_t g_io_openbus_writes;
|
||||
|
||||
@@ -39,17 +39,17 @@
|
||||
|
||||
/* ---- link stubs for code reachable from exec_one_fetched ------------------ */
|
||||
uint64_t s_frame_count;
|
||||
uint32_t g_debug_current_func_addr;
|
||||
uint32_t g_debug_last_store_pc;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
uint32_t g_dirty_ram_code_gen;
|
||||
uint64_t g_dispatch_static_hits;
|
||||
int g_rfe_escape_pending;
|
||||
int g_exc_escape_reason;
|
||||
int g_psx_call_bail;
|
||||
uint64_t g_psx_bail_first;
|
||||
uint64_t g_psx_bail_resolved;
|
||||
uint32_t i_stat;
|
||||
uint32_t i_mask;
|
||||
PSX_THREAD_LOCAL int g_psx_call_bail;
|
||||
PSX_THREAD_LOCAL uint64_t g_psx_bail_first;
|
||||
PSX_THREAD_LOCAL uint64_t g_psx_bail_resolved;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask;
|
||||
PSX_THREAD_LOCAL uint64_t psx_cycle_count;
|
||||
uint32_t g_psx_mod_function_entry_hooks;
|
||||
uint32_t g_psx_mod_instruction_hooks;
|
||||
|
||||
@@ -229,7 +229,7 @@ STUBS_C = r"""
|
||||
|
||||
uint64_t s_frame_count = 0;
|
||||
PSX_THREAD_LOCAL uint64_t psx_cycle_count = 0;
|
||||
uint32_t g_debug_last_store_pc = 0x80012340u;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc = 0x80012340u;
|
||||
uint32_t g_psx_ram_size = 0x00200000u;
|
||||
uint32_t g_psx_ram_mask = 0x001FFFFFu;
|
||||
void psx_video_timing_set_pal(int pal) { (void)pal; }
|
||||
@@ -256,10 +256,10 @@ uint16_t mod_texture_packet_bank(uint32_t p, const uint32_t *words, uint32_t n)
|
||||
int mod_texture_packet_precision(uint32_t p, float q[3], float xy[6])
|
||||
{ (void)p; (void)q; (void)xy; return 0; }
|
||||
void psx_host_write_half(uint32_t addr, uint16_t val) { (void)addr; (void)val; }
|
||||
uint32_t g_debug_current_func_addr = 0x80045670u;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr = 0x80045670u;
|
||||
CPUState *debug_cpu_ptr = 0;
|
||||
uint32_t i_stat = 0;
|
||||
uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
static uint16_t *g_vram;
|
||||
|
||||
uint32_t debug_guest_ra(void) { return 0x8000AAAAu; }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* Production GTE deadline regressions. No BIOS, disc or copied guest code. */
|
||||
#include "psx_cyc.h"
|
||||
#include <assert.h>
|
||||
@@ -5,10 +6,10 @@
|
||||
int g_ls_replay_active = 0;
|
||||
int g_ls_mode = 0;
|
||||
int g_precise_mode = 0;
|
||||
int g_psx_call_bail = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_call_bail = 0;
|
||||
int g_boot_state_cpu_timing_restored = 0; /* no restored boot state in this fixture */
|
||||
int psx_mod_host_output_scope(void) { return 0; } /* no host-output mod callback */
|
||||
uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
uint64_t g_guest_store_count = 0;
|
||||
uint64_t g_mmio_access_count = 0;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
#include "cpu_state.h"
|
||||
#include "gte.h"
|
||||
#include "hle_gte.h"
|
||||
@@ -76,7 +77,7 @@ static int g_test_netplay_active = 0;
|
||||
extern "C" int psx_netplay_active(void) { return g_test_netplay_active; }
|
||||
extern "C" {
|
||||
uint64_t s_frame_count = 0;
|
||||
uint32_t g_debug_last_store_pc = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc = 0;
|
||||
}
|
||||
|
||||
uint64_t g_test_cycle = 0;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#include "psx_thread_local.h"
|
||||
#include "interrupts.c"
|
||||
|
||||
int psx_mod_function_entry_active(void) { return 0; }
|
||||
int g_cosim_dirty_pump_site;
|
||||
int g_call_unit_depth;
|
||||
int g_psx_dispatch_depth;
|
||||
PSX_THREAD_LOCAL int g_call_unit_depth;
|
||||
PSX_THREAD_LOCAL int g_psx_dispatch_depth;
|
||||
uint32_t g_dirty_safe_resume_pc;
|
||||
|
||||
static void check(int ok, const char* message) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/*
|
||||
* Pin the idle-loop detector's store-counter extension
|
||||
* ([runtime] idle_skip_store_counters, psx_cycles.c).
|
||||
@@ -25,10 +26,10 @@
|
||||
int g_ls_replay_active = 0;
|
||||
int g_ls_mode = 0;
|
||||
int g_precise_mode = 0;
|
||||
int g_psx_call_bail = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_call_bail = 0;
|
||||
int g_boot_state_cpu_timing_restored = 0; /* boot_state.c, read by psx_cycles.c */
|
||||
int psx_mod_host_output_scope(void) { return 0; } /* mod_runtime.cpp */
|
||||
uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
uint64_t g_guest_store_count = 0;
|
||||
uint64_t g_mmio_access_count = 0;
|
||||
static uint32_t g_guest_last_store_addr = 0xFFFFFFFFu, g_guest_last_store_val;
|
||||
|
||||
@@ -36,8 +36,8 @@ PSX_THREAD_LOCAL uint32_t g_psx_cyc_batch;
|
||||
PSX_THREAD_LOCAL uint32_t g_psx_cyc_batch_limit;
|
||||
PSX_THREAD_LOCAL int g_psx_cyc_bb_defer;
|
||||
PSX_THREAD_LOCAL uint32_t *g_psx_cyc_local_acc;
|
||||
int g_psx_icache_active; /* 0: no fetch cost */
|
||||
uint32_t g_psx_icache_tv[1024];
|
||||
PSX_THREAD_LOCAL int g_psx_icache_active; /* 0: no fetch cost */
|
||||
PSX_THREAD_LOCAL uint32_t g_psx_icache_tv[1024];
|
||||
|
||||
static CPUState *s_device_cpu;
|
||||
static unsigned s_services;
|
||||
|
||||
@@ -38,12 +38,12 @@
|
||||
extern void psx_write_word(uint32_t addr, uint32_t val);
|
||||
extern void psx_write_half(uint32_t addr, uint16_t val);
|
||||
extern void psx_write_byte(uint32_t addr, uint8_t val);
|
||||
extern int g_host_store_depth;
|
||||
extern PSX_THREAD_LOCAL int g_host_store_depth;
|
||||
extern uint32_t psx_read_word(uint32_t addr);
|
||||
extern uint8_t *memory_get_ram_ptr(void);
|
||||
extern uint8_t *memory_get_scratchpad_ptr(void);
|
||||
extern void memory_set_sr_ptr(const uint32_t *p);
|
||||
extern int g_dma_exec_depth;
|
||||
extern PSX_THREAD_LOCAL int g_dma_exec_depth;
|
||||
extern PSX_THREAD_LOCAL uint64_t psx_cycle_count;
|
||||
|
||||
/* BIU values the kernel and the tests use (Beetle cpu.cpp BIU_* bits). */
|
||||
|
||||
@@ -17,10 +17,19 @@ extern uint8_t *memory_get_ram_ptr(void);
|
||||
extern uint8_t *memory_get_scratchpad_ptr(void);
|
||||
static atomic_int phase;
|
||||
static PsxMemoryBacking *copy;
|
||||
static PsxMemoryExecutionState *metadata;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat, i_mask;
|
||||
extern uint32_t psx_read_word(uint32_t);
|
||||
static uint32_t worker_status;
|
||||
static uint8_t *live;
|
||||
static void replay(void) {
|
||||
CHECK(memory_get_ram_ptr() == live);
|
||||
CHECK(memory_backing_bind(copy) == NULL);
|
||||
memory_execution_bind(metadata, &worker_status);
|
||||
g_psx_render_pass_active = 1;
|
||||
CHECK(psx_read_word(0x1F801070u) == 0x35);
|
||||
CHECK(psx_read_word(0x1F801074u) == 0x17);
|
||||
i_stat = 0x62;
|
||||
CHECK(g_psx_ram != live && g_psx_ram == memory_get_ram_ptr());
|
||||
CHECK(g_psx_ram[0x700000] == 37);
|
||||
CHECK(memory_get_scratchpad_ptr()[17] == 19);
|
||||
@@ -29,6 +38,7 @@ static void replay(void) {
|
||||
atomic_store_explicit(&phase, 1, memory_order_release);
|
||||
while (atomic_load_explicit(&phase, memory_order_acquire) != 2) { }
|
||||
CHECK(g_psx_ram[0x700000] == 71);
|
||||
CHECK(i_stat == 0x62 && i_mask == 0x17);
|
||||
CHECK(memory_backing_bind(NULL) == copy);
|
||||
CHECK(g_psx_ram == live && live[0x700000] == 41);
|
||||
}
|
||||
@@ -44,6 +54,10 @@ int main(void) {
|
||||
copy = memory_backing_create();
|
||||
CHECK(copy);
|
||||
memory_backing_capture(copy);
|
||||
metadata = memory_execution_create();
|
||||
CHECK(metadata);
|
||||
i_stat = 0x35; i_mask = 0x17;
|
||||
memory_execution_capture(metadata);
|
||||
#ifdef _WIN32
|
||||
HANDLE thread = CreateThread(NULL, 0, entry, NULL, 0, NULL);
|
||||
CHECK(thread);
|
||||
@@ -55,6 +69,8 @@ int main(void) {
|
||||
CHECK(g_psx_ram == live && live[0x700000] == 37);
|
||||
CHECK(memory_get_scratchpad_ptr()[17] == 19);
|
||||
live[0x700000] = 41;
|
||||
CHECK(i_stat == 0x35 && i_mask == 0x17);
|
||||
i_stat = 0x43; i_mask = 0x29;
|
||||
atomic_store_explicit(&phase, 2, memory_order_release);
|
||||
#ifdef _WIN32
|
||||
CHECK(WaitForSingleObject(thread, 10000) == WAIT_OBJECT_0);
|
||||
@@ -67,6 +83,8 @@ int main(void) {
|
||||
CHECK(psx_peek_word(0x80700000, &word) && (word & 255) == 71);
|
||||
CHECK(memory_get_scratchpad_ptr()[17] == 91);
|
||||
CHECK(memory_backing_bind(NULL) == copy);
|
||||
CHECK(i_stat == 0x43 && i_mask == 0x29);
|
||||
memory_execution_destroy(metadata);
|
||||
memory_backing_destroy(copy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -29,13 +29,13 @@ HARNESS_COMMON_H = r"""
|
||||
extern int g_ls_mode;
|
||||
extern int g_ls_suppress_record;
|
||||
extern volatile int g_ds_recording;
|
||||
extern int g_dma_exec_depth;
|
||||
extern int g_dma_cur_ch;
|
||||
extern uint32_t g_dma_cur_madr;
|
||||
extern uint32_t g_dma_cur_bcr;
|
||||
extern uint32_t g_dma_initiator_pc;
|
||||
extern uint32_t g_debug_last_store_pc;
|
||||
extern uint32_t g_debug_current_func_addr;
|
||||
extern PSX_THREAD_LOCAL int g_dma_exec_depth;
|
||||
extern PSX_THREAD_LOCAL int g_dma_cur_ch;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_dma_cur_madr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_dma_cur_bcr;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_dma_initiator_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
extern uint64_t s_frame_count;
|
||||
extern uint32_t g_overlay_region_floor;
|
||||
extern uint32_t g_text_image_lo;
|
||||
@@ -45,7 +45,7 @@ extern PSX_THREAD_LOCAL uint64_t g_psx_cycle_fast_limit;
|
||||
extern int g_event_step_conservative;
|
||||
extern int g_ls_replay_active;
|
||||
extern CPUState *debug_cpu_ptr;
|
||||
extern void (*g_overlay_flush_pending_cycles)(void);
|
||||
extern PSX_THREAD_LOCAL void (*g_overlay_flush_pending_cycles)(void);
|
||||
extern uint32_t g_dirty_ram_exec_page_bitmap[DIRTY_RAM_EXEC_PAGE_BITMAP_WORDS];
|
||||
extern uint32_t g_dirty_ram_exec_pc_bitmap[DIRTY_RAM_EXEC_BITMAP_WORDS];
|
||||
extern uint32_t g_dirty_ram_dispatch_pc_bitmap[DIRTY_RAM_EXEC_BITMAP_WORDS];
|
||||
@@ -116,13 +116,13 @@ PsxBiosImageInfo psx_bios_image = {0};
|
||||
int g_ls_mode = 0;
|
||||
int g_ls_suppress_record = 0;
|
||||
volatile int g_ds_recording = 0;
|
||||
int g_dma_exec_depth = 0;
|
||||
int g_dma_cur_ch = -1;
|
||||
uint32_t g_dma_cur_madr = 0;
|
||||
uint32_t g_dma_cur_bcr = 0;
|
||||
uint32_t g_dma_initiator_pc = 0;
|
||||
uint32_t g_debug_last_store_pc = 0;
|
||||
uint32_t g_debug_current_func_addr = 0;
|
||||
PSX_THREAD_LOCAL int g_dma_exec_depth = 0;
|
||||
PSX_THREAD_LOCAL int g_dma_cur_ch = -1;
|
||||
PSX_THREAD_LOCAL uint32_t g_dma_cur_madr = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_dma_cur_bcr = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_dma_initiator_pc = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr = 0;
|
||||
uint64_t s_frame_count = 0;
|
||||
uint32_t g_overlay_region_floor = 0x200000u;
|
||||
uint32_t g_text_image_lo = 0x10000u;
|
||||
@@ -132,7 +132,7 @@ PSX_THREAD_LOCAL uint64_t g_psx_cycle_fast_limit = 0;
|
||||
int g_event_step_conservative = 0;
|
||||
int g_ls_replay_active = 0;
|
||||
CPUState *debug_cpu_ptr = 0;
|
||||
void (*g_overlay_flush_pending_cycles)(void) = 0;
|
||||
PSX_THREAD_LOCAL void (*g_overlay_flush_pending_cycles)(void) = 0;
|
||||
uint32_t g_dirty_ram_exec_page_bitmap[DIRTY_RAM_EXEC_PAGE_BITMAP_WORDS] = {0};
|
||||
uint32_t g_dirty_ram_exec_pc_bitmap[DIRTY_RAM_EXEC_BITMAP_WORDS] = {0};
|
||||
uint32_t g_dirty_ram_dispatch_pc_bitmap[DIRTY_RAM_EXEC_BITMAP_WORDS] = {0};
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* the trusted plugin's host-state partition, without creating a game/window. */
|
||||
static uint8_t ram[2048], scratchpad[1024], spu_ram[16];
|
||||
static uint16_t vram[1024 * 512];
|
||||
uint32_t i_stat, i_mask, g_psx_icache_tv[1024];
|
||||
PSX_THREAD_LOCAL uint32_t i_stat, i_mask, g_psx_icache_tv[1024];
|
||||
PSX_THREAD_LOCAL uint64_t psx_cycle_count;
|
||||
uint8_t* memory_get_ram_ptr(void) { return ram; }
|
||||
uint32_t memory_get_ram_bytes(void) { return sizeof(ram); }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* Production mult/div deadline regressions. No BIOS, disc or copied guest code.
|
||||
* Companion of test_gte_deferred.c: the MULT/DIV completion deadline and the
|
||||
* MFLO/MFHI stall must see CPU work still pending in the batch or a local
|
||||
@@ -9,10 +10,10 @@
|
||||
int g_ls_replay_active = 0;
|
||||
int g_ls_mode = 0;
|
||||
int g_precise_mode = 0;
|
||||
int g_psx_call_bail = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_call_bail = 0;
|
||||
int g_boot_state_cpu_timing_restored = 0; /* no restored boot state in this fixture */
|
||||
int psx_mod_host_output_scope(void) { return 0; } /* no host-output mod callback */
|
||||
uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
uint64_t g_guest_store_count = 0;
|
||||
uint64_t g_mmio_access_count = 0;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
#include "mod_runtime.h"
|
||||
#include "mod_packages.h"
|
||||
#include "mod_plugins.h"
|
||||
@@ -42,8 +43,8 @@ extern "C" void gpu_hd_textures_set_dump_enabled(int) {}
|
||||
extern "C" int gpu_hd_textures_reload(char*, size_t) { return 0; }
|
||||
/* sio.c's environment (as in test_sio_jogcon_protocol.c). */
|
||||
extern "C" {
|
||||
uint32_t i_stat = 0, i_mask = 0;
|
||||
uint32_t g_debug_current_func_addr = 0, g_debug_last_store_pc = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat = 0, i_mask = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr = 0, g_debug_last_store_pc = 0;
|
||||
int psx_get_in_exception(void) { return 0; }
|
||||
uint64_t psx_get_cycle_count(void) { static uint64_t t; return t += 64; }
|
||||
uint32_t memory_get_sr(void) { return 0; }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* Overlay DLLs must update the runtime's store-PC breadcrumb (ABI v24).
|
||||
*
|
||||
* This TU is an overlay DLL in miniature: it is built with the same defines
|
||||
@@ -14,7 +15,7 @@
|
||||
|
||||
/* Verbatim from code_generator.cpp's emitted extern block. Must still compile
|
||||
* once cpu_state.h has mapped the name onto the host pointer. */
|
||||
extern uint32_t g_debug_last_store_pc; /* exact PC of the executing SW/SH/SB — wtrace/readtrace producer attribution (debug_server.c) */
|
||||
extern PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc; /* exact PC of the executing SW/SH/SB — wtrace/readtrace producer attribution (debug_server.c) */
|
||||
|
||||
static uint32_t host_last_store_pc;
|
||||
static uint32_t host_ram_word;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* DMA and host stores against the PGXP shadow, end to end through the REAL
|
||||
* memory.c store paths and the REAL pgxp.cpp engine.
|
||||
*
|
||||
@@ -22,8 +23,8 @@
|
||||
extern void psx_write_word(uint32_t addr, uint32_t val);
|
||||
extern void psx_write_half(uint32_t addr, uint16_t val);
|
||||
extern void psx_write_byte(uint32_t addr, uint8_t val);
|
||||
extern int g_dma_exec_depth;
|
||||
extern int g_host_store_depth;
|
||||
extern PSX_THREAD_LOCAL int g_dma_exec_depth;
|
||||
extern PSX_THREAD_LOCAL int g_host_store_depth;
|
||||
extern void memory_set_sr_ptr(const uint32_t *p);
|
||||
|
||||
/* gte.cpp position cache: off here, so only the dataflow shadow can answer. */
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/*
|
||||
* Pin cross-device causality at a scheduler deadline.
|
||||
*
|
||||
@@ -12,8 +13,8 @@
|
||||
int g_ls_replay_active = 0;
|
||||
int g_ls_mode = 0;
|
||||
int g_precise_mode = 0;
|
||||
int g_psx_call_bail = 0;
|
||||
uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_call_bail = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
uint64_t g_guest_store_count = 0;
|
||||
uint64_t g_mmio_access_count = 0;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* [timing] guest_cycle_scale against the real psx_cycles.c:
|
||||
* - identical accounting across tiers: native per-instruction CPU charges,
|
||||
* the batched psx_cyc_charge hot path and an overlay DLL's raw batch
|
||||
@@ -33,8 +34,8 @@ int g_psx_load_delay = 1;
|
||||
int g_ls_replay_active = 0;
|
||||
int g_ls_mode = 0;
|
||||
int g_precise_mode = 0;
|
||||
int g_psx_call_bail = 0;
|
||||
uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_call_bail = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
uint64_t g_guest_store_count = 0;
|
||||
uint64_t g_mmio_access_count = 0;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* Shared PsxNetPad -> SIO apply (offline, netplay, selfcheck) and pad
|
||||
* normalization, checked on the real SIO wire. */
|
||||
|
||||
@@ -11,10 +12,10 @@ uint32_t sio_snapshot_bytes(void);
|
||||
void sio_snapshot_write(uint8_t *p);
|
||||
int sio_snapshot_read(const uint8_t *p, uint32_t len);
|
||||
|
||||
uint32_t i_stat = 0;
|
||||
uint32_t i_mask = 0;
|
||||
uint32_t g_debug_current_func_addr = 0;
|
||||
uint32_t g_debug_last_store_pc = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc = 0;
|
||||
int psx_get_in_exception(void) { return 0; }
|
||||
uint8_t psx_read_byte(uint32_t addr) { (void)addr; return 0; }
|
||||
uint32_t psx_read_word(uint32_t addr) { (void)addr; return 0; }
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/* Concurrent worker capture never becomes a second ring producer. */
|
||||
#include "render_thread.h"
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
#define CHECK(c) do { if (!(c)) abort(); } while (0)
|
||||
static RtCommandList *list;
|
||||
static atomic_int phase, executed;
|
||||
static unsigned live_next, private_next;
|
||||
static jmp_buf overflow;
|
||||
static void fail_capture(const char *reason) {
|
||||
CHECK(!strcmp(reason, "command_capacity")); longjmp(overflow, 1);
|
||||
}
|
||||
static void consume_private(void *user, const RtCmd *cmd, const void *payload) {
|
||||
(void)user;
|
||||
CHECK(rt_on_render_thread());
|
||||
CHECK(cmd->op == 7 && cmd->payload == sizeof(uint32_t));
|
||||
CHECK(*(const uint32_t *)payload == private_next++);
|
||||
}
|
||||
static void consume(void *user, const RtCmd *cmd, const void *payload) {
|
||||
(void)user;
|
||||
if (cmd->op == 1) {
|
||||
CHECK(*(const uint32_t *)payload == live_next++);
|
||||
} else {
|
||||
CHECK(cmd->op == 2 && atomic_load_explicit(&phase, memory_order_acquire) == 2);
|
||||
rt_list_execute(list, consume_private, NULL);
|
||||
CHECK(private_next == 4096);
|
||||
atomic_store_explicit(&executed, 1, memory_order_release);
|
||||
}
|
||||
}
|
||||
static void context(void *user, int current) { (void)user; (void)current; }
|
||||
static void *worker(void *user) {
|
||||
(void)user;
|
||||
rt_capture_begin(list, fail_capture);
|
||||
CHECK(rt_capture_active());
|
||||
atomic_store_explicit(&phase, 1, memory_order_release);
|
||||
for (uint32_t i = 0; i < 4096; ++i) {
|
||||
uint32_t *p = rt_cmd_begin(7, 0, sizeof(i));
|
||||
CHECK(p); *p = i; rt_cmd_commit();
|
||||
}
|
||||
rt_capture_end();
|
||||
CHECK(!rt_capture_active() && rt_list_bytes(list) == 4096 * 32u);
|
||||
atomic_store_explicit(&phase, 2, memory_order_release);
|
||||
return NULL;
|
||||
}
|
||||
int main(void) {
|
||||
RtConfig cfg = {0};
|
||||
cfg.ring_bytes = 1u << 20; cfg.max_frames = 2;
|
||||
cfg.exec = consume; cfg.ctx = context;
|
||||
CHECK(rt_start(&cfg));
|
||||
list = rt_list_create(1u << 20); CHECK(list);
|
||||
pthread_t thread; CHECK(!pthread_create(&thread, NULL, worker, NULL));
|
||||
while (!atomic_load_explicit(&phase, memory_order_acquire)) { }
|
||||
for (uint32_t i = 0; i < 16000; ++i) {
|
||||
uint32_t *p = rt_cmd_begin(1, 0, sizeof(i));
|
||||
CHECK(p); *p = i; rt_cmd_commit();
|
||||
}
|
||||
CHECK(!pthread_join(thread, NULL));
|
||||
CHECK(rt_cmd_begin(2, 0, 0)); rt_cmd_commit();
|
||||
rt_drain();
|
||||
CHECK(live_next == 16000 && atomic_load_explicit(&executed, memory_order_acquire));
|
||||
RtStats stats; rt_get_stats(&stats); CHECK(stats.records == 16001);
|
||||
rt_stop();
|
||||
/* Overflow abandons the extra; it cannot acquire or block the live ring. */
|
||||
RtCommandList *small = rt_list_create(32); CHECK(small);
|
||||
rt_capture_begin(small, fail_capture);
|
||||
if (!setjmp(overflow)) {
|
||||
CHECK(rt_cmd_begin(1, 0, 4)); rt_cmd_commit();
|
||||
rt_cmd_begin(1, 0, 4); abort();
|
||||
}
|
||||
rt_capture_end(); CHECK(rt_list_bytes(small) == 32);
|
||||
rt_list_destroy(small); rt_list_destroy(list);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* Render-pass watchdog abort (render_pass.c psx_mod_render_pass).
|
||||
*
|
||||
* A pass that runs away is cut off by a longjmp from the frozen clock's
|
||||
@@ -43,21 +44,21 @@ int g_boot_state_cpu_timing_restored = 0;
|
||||
int psx_mod_host_output_scope(void) { return 0; }
|
||||
int g_ls_mode = 0;
|
||||
int g_precise_mode = 0;
|
||||
int g_psx_call_bail = 0;
|
||||
int g_psx_dispatch_depth = 0;
|
||||
uint32_t i_stat = 0, i_mask = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_call_bail = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_dispatch_depth = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat = 0, i_mask = 0;
|
||||
uint64_t g_guest_store_count = 0;
|
||||
uint64_t g_mmio_access_count = 0;
|
||||
uint32_t g_debug_last_store_pc = 0;
|
||||
uint32_t g_debug_current_func_addr = 0;
|
||||
uint32_t g_psx_icache_tv[1024];
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_psx_icache_tv[1024];
|
||||
uint64_t g_render_pass_dropped_writes[RENDER_PASS_DROP_CLASSES];
|
||||
void (*g_overlay_flush_pending_cycles)(void) = NULL;
|
||||
int g_call_unit_depth = 0;
|
||||
int g_dma_exec_depth = 0;
|
||||
int g_host_store_depth = 0;
|
||||
int g_dma_cur_ch = -1;
|
||||
uint32_t g_dma_cur_madr = 0, g_dma_cur_bcr = 0, g_dma_initiator_pc = 0;
|
||||
PSX_THREAD_LOCAL void (*g_overlay_flush_pending_cycles)(void) = NULL;
|
||||
PSX_THREAD_LOCAL int g_call_unit_depth = 0;
|
||||
PSX_THREAD_LOCAL int g_dma_exec_depth = 0;
|
||||
PSX_THREAD_LOCAL int g_host_store_depth = 0;
|
||||
PSX_THREAD_LOCAL int g_dma_cur_ch = -1;
|
||||
PSX_THREAD_LOCAL uint32_t g_dma_cur_madr = 0, g_dma_cur_bcr = 0, g_dma_initiator_pc = 0;
|
||||
int g_dirty_interp_active = 0;
|
||||
int g_exec_phase = 0;
|
||||
uint32_t g_dirty_safe_resume_pc = 0;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* Render-pass time freeze (psx_cycle_freeze.h psx_cycle_freeze_begin/end).
|
||||
* A pass counts guest cycles but never services a device, and afterwards
|
||||
* every clock value is exactly as before. Build/run:
|
||||
@@ -14,8 +15,8 @@
|
||||
int g_ls_replay_active = 0;
|
||||
int g_ls_mode = 0;
|
||||
int g_precise_mode = 0;
|
||||
int g_psx_call_bail = 0;
|
||||
uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_call_bail = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
uint64_t g_guest_store_count = 0;
|
||||
uint64_t g_mmio_access_count = 0;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* Render-pass sandbox: what a pass may write and what it gets back.
|
||||
*
|
||||
* 1. Store policy (render_pass_plan.c render_pass_store_to, the function
|
||||
@@ -57,21 +58,21 @@ int g_boot_state_cpu_timing_restored = 0;
|
||||
int psx_mod_host_output_scope(void) { return 0; }
|
||||
int g_ls_mode = 0;
|
||||
int g_precise_mode = 0;
|
||||
int g_psx_call_bail = 0;
|
||||
int g_psx_dispatch_depth = 0;
|
||||
uint32_t i_stat = 0, i_mask = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_call_bail = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_dispatch_depth = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat = 0, i_mask = 0;
|
||||
uint64_t g_guest_store_count = 0;
|
||||
uint64_t g_mmio_access_count = 0;
|
||||
uint32_t g_debug_last_store_pc = 0;
|
||||
uint32_t g_debug_current_func_addr = 0;
|
||||
uint32_t g_psx_icache_tv[1024];
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_psx_icache_tv[1024];
|
||||
uint64_t g_render_pass_dropped_writes[RENDER_PASS_DROP_CLASSES];
|
||||
void (*g_overlay_flush_pending_cycles)(void) = NULL;
|
||||
int g_call_unit_depth = 0;
|
||||
int g_dma_exec_depth = 0;
|
||||
int g_host_store_depth = 0;
|
||||
int g_dma_cur_ch = -1;
|
||||
uint32_t g_dma_cur_madr = 0, g_dma_cur_bcr = 0, g_dma_initiator_pc = 0;
|
||||
PSX_THREAD_LOCAL void (*g_overlay_flush_pending_cycles)(void) = NULL;
|
||||
PSX_THREAD_LOCAL int g_call_unit_depth = 0;
|
||||
PSX_THREAD_LOCAL int g_dma_exec_depth = 0;
|
||||
PSX_THREAD_LOCAL int g_host_store_depth = 0;
|
||||
PSX_THREAD_LOCAL int g_dma_cur_ch = -1;
|
||||
PSX_THREAD_LOCAL uint32_t g_dma_cur_madr = 0, g_dma_cur_bcr = 0, g_dma_initiator_pc = 0;
|
||||
int g_dirty_interp_active = 0;
|
||||
int g_exec_phase = 0;
|
||||
uint32_t g_dirty_safe_resume_pc = 0;
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
extern uint8_t g_test_ram[0x200000];
|
||||
extern PSX_THREAD_LOCAL uint64_t psx_cycle_count;
|
||||
extern uint32_t i_stat, i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat, i_mask;
|
||||
extern uint32_t g_dirty_safe_resume_pc;
|
||||
extern int (*g_test_recover_null_pc)(CPUState *, uint32_t *);
|
||||
extern uint32_t psx_read_word(uint32_t addr);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <string.h>
|
||||
extern uint8_t g_test_ram[0x200000];
|
||||
extern PSX_THREAD_LOCAL uint64_t psx_cycle_count;
|
||||
extern uint32_t i_stat, i_mask;
|
||||
extern PSX_THREAD_LOCAL uint32_t i_stat, i_mask;
|
||||
extern uint32_t psx_read_word(uint32_t);
|
||||
extern void psx_host_write_word(uint32_t, uint32_t);
|
||||
#define HANDLER 0x80001000u
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* Device-level regression: guest RAM is not SIO hardware state.
|
||||
* Uses production SIO with a synthetic clock/card, no BIOS or retail data.
|
||||
* Independent references: Beetle frontio.cpp DoDSRIRQ/Write and irq.cpp
|
||||
@@ -7,7 +8,7 @@
|
||||
#include "../src/sio.c"
|
||||
#include <stdio.h>
|
||||
|
||||
uint32_t i_stat, i_mask, g_debug_current_func_addr, g_debug_last_store_pc;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat, i_mask, g_debug_current_func_addr, g_debug_last_store_pc;
|
||||
static uint64_t clock_now;
|
||||
static int card_present;
|
||||
static int checks, failures;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/*
|
||||
* test_sio_card_protocol.c — pin SIO memory card protocol against no$psx spec.
|
||||
*
|
||||
@@ -43,10 +44,10 @@
|
||||
#endif
|
||||
|
||||
/* ---- Stubs for sio.c's external dependencies ---- */
|
||||
uint32_t i_stat = 0;
|
||||
uint32_t i_mask = 0;
|
||||
uint32_t g_debug_current_func_addr = 0;
|
||||
uint32_t g_debug_last_store_pc = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc = 0;
|
||||
int psx_get_in_exception(void) { return 0; }
|
||||
uint8_t psx_read_byte(uint32_t addr) { (void)addr; return 0; }
|
||||
uint32_t psx_read_word(uint32_t addr) { (void)addr; return 0; }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* Plain digital pads shift their ID during every command byte.
|
||||
* Synthetic bus transactions over the real sio.c; no BIOS/disc/core fixture. */
|
||||
#include "sio.h"
|
||||
@@ -10,10 +11,10 @@ uint32_t sio_snapshot_bytes(void);
|
||||
void sio_snapshot_write(uint8_t *p);
|
||||
int sio_snapshot_read(const uint8_t *p, uint32_t len);
|
||||
|
||||
uint32_t i_stat = 0;
|
||||
uint32_t i_mask = 0;
|
||||
uint32_t g_debug_current_func_addr = 0;
|
||||
uint32_t g_debug_last_store_pc = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc = 0;
|
||||
int psx_get_in_exception(void) { return 0; }
|
||||
uint8_t psx_read_byte(uint32_t addr) { (void)addr; return 0; }
|
||||
uint32_t psx_read_word(uint32_t addr) { (void)addr; return 0; }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* DualShock rumble protocol regression: 0x4D map negotiation must feed the
|
||||
* small/large motor bytes from later 0x42 polls to the frontend-facing state. */
|
||||
|
||||
@@ -11,10 +12,10 @@ uint32_t sio_snapshot_bytes(void);
|
||||
void sio_snapshot_write(uint8_t *p);
|
||||
int sio_snapshot_read(const uint8_t *p, uint32_t len);
|
||||
|
||||
uint32_t i_stat = 0;
|
||||
uint32_t i_mask = 0;
|
||||
uint32_t g_debug_current_func_addr = 0;
|
||||
uint32_t g_debug_last_store_pc = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc = 0;
|
||||
int psx_get_in_exception(void) { return 0; }
|
||||
uint8_t psx_read_byte(uint32_t addr) { (void)addr; return 0; }
|
||||
uint32_t psx_read_word(uint32_t addr) { (void)addr; return 0; }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* JogCon wire protocol regression: ID, buttons, signed steering, direction,
|
||||
* command response, and rollback state. */
|
||||
|
||||
@@ -11,10 +12,10 @@ uint32_t sio_snapshot_bytes(void);
|
||||
void sio_snapshot_write(uint8_t *p);
|
||||
int sio_snapshot_read(const uint8_t *p, uint32_t len);
|
||||
|
||||
uint32_t i_stat = 0;
|
||||
uint32_t i_mask = 0;
|
||||
uint32_t g_debug_current_func_addr = 0;
|
||||
uint32_t g_debug_last_store_pc = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc = 0;
|
||||
int psx_get_in_exception(void) { return 0; }
|
||||
uint8_t psx_read_byte(uint32_t addr) { (void)addr; return 0; }
|
||||
uint32_t psx_read_word(uint32_t addr) { (void)addr; return 0; }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* NeGcon wire protocol regression: ID 0x23 poll layout (twist, I, II, L),
|
||||
* no config mode, device-swap edges against the 0x44 mode lock, deferred type
|
||||
* requests during config, 0x44 keeping the device, snapshot of the pad mode,
|
||||
@@ -13,10 +14,10 @@ uint32_t sio_snapshot_bytes(void);
|
||||
void sio_snapshot_write(uint8_t *p);
|
||||
int sio_snapshot_read(const uint8_t *p, uint32_t len);
|
||||
|
||||
uint32_t i_stat = 0;
|
||||
uint32_t i_mask = 0;
|
||||
uint32_t g_debug_current_func_addr = 0;
|
||||
uint32_t g_debug_last_store_pc = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc = 0;
|
||||
int psx_get_in_exception(void) { return 0; }
|
||||
uint8_t psx_read_byte(uint32_t addr) { (void)addr; return 0; }
|
||||
uint32_t psx_read_word(uint32_t addr) { (void)addr; return 0; }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/*
|
||||
* test_sio_spu_istat_ownership.c — cross-device I_STAT ownership.
|
||||
*
|
||||
@@ -19,10 +20,10 @@
|
||||
#include <string.h>
|
||||
|
||||
/* ---- Stubs for sio.c's external dependencies ---- */
|
||||
uint32_t i_stat = 0;
|
||||
uint32_t i_mask = 0;
|
||||
uint32_t g_debug_current_func_addr = 0;
|
||||
uint32_t g_debug_last_store_pc = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc = 0;
|
||||
int psx_get_in_exception(void) { return 0; }
|
||||
uint8_t psx_read_byte(uint32_t addr) { (void)addr; return 0; }
|
||||
uint32_t psx_read_word(uint32_t addr) { (void)addr; return 0; }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "psx_thread_local.h"
|
||||
/* Uncharged guest-time spans (psx_cycle_freeze.h psx_cycle_uncharged_*),
|
||||
* the time layer of mod_plugins.h psx_mod_call_guest_uncharged. Code in a
|
||||
* span keeps its effects but consumes no guest time; a span over budget
|
||||
@@ -12,8 +13,8 @@
|
||||
int g_ls_replay_active = 0;
|
||||
int g_ls_mode = 0;
|
||||
int g_precise_mode = 0;
|
||||
int g_psx_call_bail = 0;
|
||||
uint32_t i_mask = 0;
|
||||
PSX_THREAD_LOCAL int g_psx_call_bail = 0;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask = 0;
|
||||
uint64_t g_guest_store_count = 0;
|
||||
uint64_t g_mmio_access_count = 0;
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
|
||||
/* ---- link stubs for code reachable from exec_one_fetched ------------------ */
|
||||
uint64_t s_frame_count;
|
||||
uint32_t g_debug_current_func_addr;
|
||||
uint32_t g_debug_last_store_pc;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc;
|
||||
uint32_t g_dirty_ram_code_gen;
|
||||
uint64_t g_dispatch_static_hits;
|
||||
uint32_t g_psx_mod_instruction_hooks;
|
||||
@@ -50,11 +50,11 @@ void psx_mod_instruction(CPUState *cpu, uint32_t pc, uint32_t instruction) {
|
||||
}
|
||||
int g_rfe_escape_pending;
|
||||
int g_exc_escape_reason;
|
||||
int g_psx_call_bail;
|
||||
uint64_t g_psx_bail_first;
|
||||
uint64_t g_psx_bail_resolved;
|
||||
uint32_t i_stat;
|
||||
uint32_t i_mask;
|
||||
PSX_THREAD_LOCAL int g_psx_call_bail;
|
||||
PSX_THREAD_LOCAL uint64_t g_psx_bail_first;
|
||||
PSX_THREAD_LOCAL uint64_t g_psx_bail_resolved;
|
||||
PSX_THREAD_LOCAL uint32_t i_stat;
|
||||
PSX_THREAD_LOCAL uint32_t i_mask;
|
||||
PSX_THREAD_LOCAL uint64_t psx_cycle_count;
|
||||
/* Live RAM geometry (memory.c): retail 2 MiB, as the stub RAM below. */
|
||||
uint32_t g_psx_ram_size = PSX_MAIN_RAM_RETAIL_BYTES;
|
||||
|
||||
@@ -80,10 +80,10 @@ static uint32_t s_wtrace_head=2;
|
||||
typedef struct { uint32_t pc; uint32_t gpr[32]; } CPUState;
|
||||
static CPUState *debug_cpu_ptr = NULL;
|
||||
static uint32_t s_frame_count = 0;
|
||||
uint32_t g_debug_current_func_addr = 0;
|
||||
uint32_t g_debug_last_store_pc = 0;
|
||||
int g_dma_exec_depth = 0, g_dma_cur_ch = -1;
|
||||
uint32_t g_dma_initiator_pc = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_current_func_addr = 0;
|
||||
PSX_THREAD_LOCAL uint32_t g_debug_last_store_pc = 0;
|
||||
PSX_THREAD_LOCAL int g_dma_exec_depth = 0, g_dma_cur_ch = -1;
|
||||
PSX_THREAD_LOCAL uint32_t g_dma_initiator_pc = 0;
|
||||
static void debug_server_send_line(const char *line) { puts(line); }
|
||||
static void send_err(int id, const char *error) { (void)id; (void)error; abort(); }
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user