mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 10:19:47 -04:00
94a1966444
The complete §25 fix: make BOTH recompiler emitters emit guest calls as TAIL-TRANSFERS driven by the unified flat trampoline + the guest's own $ra/$sp, instead of nesting one host C frame per guest call. The host stack can no longer mirror unbounded guest re-entrancy — the architectural root of the ~14-min idle freeze. Gated gen-time by PSX_CPS; legacy codegen byte-identical when unset. VALIDATED (user): full-CPS Tomba boots BIOS -> FMV -> New Game -> village idle and plays; ce_profile cur_max_kb FLAT at 5 KB from frame ~9.9k through 56,890 (turbo soak) — blew past the ~50k-frame freeze threshold with the host stack bounded (legacy climbed +1.17 KB/frame -> 48 MB -> freeze). KEY CORRECTION to the prior session's §25.1: there are TWO emitters. The BIOS (full_function_emitter.cpp) was done last session but the §8 leaking chain (func_8001A954 ...) is GAME code, emitted by code_generator.cpp (via main_psx.cpp) — never converted. Also, §25.1's "continuations not in the dispatch table" hypothesis was wrong (they were always added); the real boot regressions were found by dumping the always-on fntrace ring on the abnormal psx_dispatch return. Emitter changes (gated PSX_CPS): - full_function_emitter.cpp (BIOS): BIOS A0/B0/C0 vector runtime-fallback tail-transfers instead of nested psx_dispatch_call (was the frame-0 exit). - code_generator.cpp/.h (game): jal/jalr/jr-$ra/jr-table/branch-split/j-split -> tail-transfer (jalr captures rs before the link, alias-safe); each call's return point registered as a dispatchable continuation; entry-switch (keyed on cpu->pc) at the top of every function AND every overlapping-alias body. 0 psx_call_contract / call_by_address emitted under CPS. - main_psx.cpp: psx_dispatch_game_compiled gains continuation cases (case cont: cpu->pc=cont; func_owner(cpu)); entries clear cpu->pc. - strict_translator.cpp: syscall emits `if (psx_syscall(...)) return;` so a void syscall falls through to the inline post-syscall jr $ra (real-HW EPC+4). Runtime changes (the legacy "return via nested dispatch / cpu->pc=0" idioms that exit the flat CPS trampoline): - traps.c: psx_syscall returns int (0=void/fall-through, 1=transfer); Enter/ExitCriticalSection return 0; case-3 ChangeThread-success returns 0 — the fiber scheduler leaves cpu->pc=0 on resume, which was the frame-712 exit. - cpu_state.h / overlay_api.h: psx_syscall signature void->int; overlay callback ABI bumped 4->5 (stale overlay caches rejected). - dirty_ram_interp.c: gate interp_enter_compiled behind PSX_HAS_GAME_DISPATCH (un-breaks the BIOS-only build). - main.cpp: dump the always-on fntrace ring tail + stack to psx_cps_exit_trace.json on an abnormal psx_dispatch return (diagnostic; CLAUDE.md ring-buffer model). Docs: RECURSION_BUG.md §25.2-§25.4. Remaining: dwarf->overworld playtest; Stage 3 (sljit/overlay CPS parity); then consider making CPS default. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
138 lines
7.1 KiB
C
138 lines
7.1 KiB
C
#ifndef OVERLAY_API_H
|
|
#define OVERLAY_API_H
|
|
|
|
/* Shared ABI between psx-runtime.exe and overlay DLLs.
|
|
*
|
|
* After LoadLibrary, the runtime calls overlay_init() with this struct.
|
|
* The DLL stores the pointers and routes all cross-module calls through them.
|
|
*/
|
|
|
|
#include "cpu_state.h"
|
|
#include <stdint.h>
|
|
|
|
/* ABI version exported by every overlay DLL as `overlay_abi()`. The loader
|
|
* rejects (and deletes, so autocompile regenerates) any DLL whose version
|
|
* doesn't match — including all pre-versioning DLLs, which lack the export.
|
|
*
|
|
* v2: dispatch call-contract (Bug D family) — DLL call sites carry (ra, sp)
|
|
* contract checks and share the runtime's bail state via the appended
|
|
* callback pointers below.
|
|
* v3: widescreen backdrop screenX squash ([widescreen.backdrop] x_sites) —
|
|
* overlay backdrop handlers now emit psx_ws_backdrop_x() at their screenX
|
|
* store. This is an EMIT-content change (not a struct-ABI change), but the
|
|
* version is the cache discriminator: bumping it forces the loader to
|
|
* reject pre-backdrop DLLs so autocompile regenerates them with the squash.
|
|
* (The deferred follow-up is to auto-derive this tag from a hash of the
|
|
* widescreen config + emit headers; until then it's a manual bump.)
|
|
* v4: widescreen backdrop column PRELOAD ([widescreen.cull] auto_backdrop) —
|
|
* overlay backdrop generators now emit psx_ws_backdrop_value() at the
|
|
* window START/END finalize, and the callback struct grows a
|
|
* ws_backdrop_value pointer (appended last). Bumping rejects pre-preload
|
|
* DLLs (which lack both the emit and a host that supplies the callback). */
|
|
/* v5: psx_syscall callback return type void->int (CPS, RECURSION_BUG.md §25);
|
|
* overlays compiled under CPS emit `if (psx_syscall(...)) return;`. */
|
|
#define PSX_OVERLAY_ABI_VERSION 5
|
|
|
|
/* Codegen flavor of the recompiled output the overlays + runtime were built
|
|
* against. Overlays are keyed in the cache by guest-bytes CRC, which is
|
|
* flavor-BLIND — the same guest overlay yields the same filename whether it was
|
|
* compiled by the base recompiler or a variant (e.g. widescreen, which emits
|
|
* extra runtime symbols + GTE adjustments). Without a flavor tag a widescreen
|
|
* DLL and a base DLL for the same overlay collide. The flavor is folded into
|
|
* overlay_abi() (high 16 bits) so the loader's existing ABI gate rejects (and
|
|
* regenerates) any DLL whose flavor differs from the running build's, keeping
|
|
* caches separate even in a shared directory.
|
|
*
|
|
* Base/master MUST stay 0 so the tag == PSX_OVERLAY_ABI_VERSION (backward
|
|
* compatible with all existing base-flavor DLLs). A variant build overrides it
|
|
* (e.g. -DPSX_OVERLAY_FLAVOR=1 for widescreen) for BOTH the runtime and
|
|
* compile_overlays.py (its --flavor arg). */
|
|
#ifndef PSX_OVERLAY_FLAVOR
|
|
#define PSX_OVERLAY_FLAVOR 0
|
|
#endif
|
|
|
|
/* Combined tag exported by overlay_abi() and checked by the loader. */
|
|
#define PSX_OVERLAY_ABI_TAG \
|
|
((int)((PSX_OVERLAY_ABI_VERSION & 0xFFFF) | ((PSX_OVERLAY_FLAVOR & 0xFFFF) << 16)))
|
|
|
|
/* Codegen version — bumped whenever the recompiler emits DIFFERENT bytes for the
|
|
* same guest overlay WITHOUT an ABI change (the load-time overlay_abi() gate only
|
|
* catches ABI/flavor changes; a pure-emitter change produces a different DLL the
|
|
* gate would happily accept). The cache is namespaced by this version
|
|
* (gcc/<arch-abi>/cg<N>/), so a build with new codegen writes + reads a FRESH
|
|
* directory and never reuses a stale DLL; old versions coexist on disk (no
|
|
* auto-delete — a user who downgrades still finds their matching cache). Python
|
|
* (compile_overlays.py) parses this same constant from this header, so the two
|
|
* sides can never drift. BUMP THIS when you change code_generator.cpp emit in a
|
|
* way that alters overlay output (e.g. the auto_screen_x widescreen widening).
|
|
* 1: baseline + auto_screen_x render-funnel cull via psx_ws_cull_sltiu.
|
|
* 2: + auto_backdrop far-backdrop column preload via psx_ws_backdrop_value.
|
|
* 3: auto_backdrop switched to camera-tracked window WIDENING
|
|
* (psx_ws_backdrop_value gains a window_cols arg). */
|
|
#define PSX_OVERLAY_CODEGEN_VER 3
|
|
|
|
typedef struct {
|
|
/* Core dispatch: routes call_by_address() and out-of-overlay jal */
|
|
void (*dispatch_call)(CPUState *cpu, uint32_t addr, uint32_t ra);
|
|
/* Interrupt check: called after every function return in overlay */
|
|
void (*check_interrupts)(CPUState *cpu);
|
|
/* GTE coprocessor 2 execution */
|
|
void (*gte_execute)(CPUState *cpu, uint32_t cmd);
|
|
/* MIPS syscall (break/syscall instructions). Returns 1 if control
|
|
* transfers (cpu->pc set), 0 for a directly-handled void syscall. See
|
|
* cpu_state.h. The signature changed with CPS (RECURSION_BUG.md §25), so
|
|
* PSX_OVERLAY_ABI_VERSION was bumped to reject stale overlay caches. */
|
|
int (*psx_syscall)(CPUState *cpu, uint32_t code);
|
|
/* Unresolved dispatch target */
|
|
void (*psx_unknown_dispatch)(CPUState *cpu, uint32_t addr, uint32_t phys);
|
|
/* Debug instrumentation: called at every function entry (may be NULL) */
|
|
void (*log_call_entry)(uint32_t func_addr);
|
|
/* RestoreState/ReturnFromException longjmp escape (interrupts.c). The
|
|
* recompiler emits this at longjmp-return sites in exception-context
|
|
* kernel code (the install-slot / kernel-window class). Appended LAST:
|
|
* overlay_init copies the struct by value, so older DLLs built against
|
|
* the shorter struct simply never read this member. */
|
|
void (*psx_restore_state_escape)(void);
|
|
/* Call-contract state shared with the runtime (ABI v2; see the contract
|
|
* model in cpu_state.h). DLL code reads the bail flag and bumps the
|
|
* counters through these pointers. */
|
|
int *call_bail_flag;
|
|
uint64_t *bail_first;
|
|
uint64_t *bail_resolved;
|
|
/* Widescreen hooks (ABI v3). The recompiler can emit psx_ws_* calls into
|
|
* OVERLAY code (backdrop screenX squash; and, for other games, sprite-tag
|
|
* or cull sites that resolve into overlays). These compute against the
|
|
* host's live widescreen state (gpu.c), so the DLL forwards to the runtime
|
|
* through these. Appended LAST (struct grows back-compatibly); may be NULL
|
|
* on a host that predates them — the DLL glue falls back to identity. */
|
|
int (*ws_backdrop_x)(int x);
|
|
int (*ws_x_margin)(void);
|
|
void (*ws_sprite_tag)(CPUState *cpu);
|
|
/* Widescreen backdrop column-preload value substitution (ABI v4). Overlay
|
|
* backdrop generators call this at a detected window START/END bound; the
|
|
* runtime returns `orig` unless native-wide is engaged, in which case it
|
|
* widens the camera-tracked window by the 16:9 reveal (START -> orig-margin,
|
|
* END -> orig+margin; margin scales with window_cols). Appended LAST (struct
|
|
* grows back-compatibly); may be NULL on a host that predates it — the DLL
|
|
* glue falls back to orig. */
|
|
uint32_t (*ws_backdrop_value)(uint32_t orig, int is_end, int window_cols);
|
|
} OverlayCallbacks;
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* Exported by every overlay DLL. Call once after LoadLibrary. */
|
|
#ifdef _WIN32
|
|
__declspec(dllexport)
|
|
#else
|
|
__attribute__((visibility("default")))
|
|
#endif
|
|
void overlay_init(const OverlayCallbacks *cbs);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* OVERLAY_API_H */
|