Files
psxrecomp/runtime/include/overlay_api.h
T
Matthew Stanley 0c1f005c35 feat(widescreen,sljit): shared cull helper across backends, cache versioning, sljit default-on
Three coupled landings on top of the sljit merge:

1. Shared widescreen render-cull helper. auto_screen_x now emits a call to one
   runtime helper psx_ws_cull_sltiu(sx,imm) (gpu.c) instead of an inline expr, and
   the sljit JIT (overlay_sljit.c) + dirty-RAM interpreter (dirty_ram_interp.c)
   route flagged render-funnel sltiu 0x140/0x141 through the SAME helper. So an
   overlay's GTE render funnel widens identically whether it runs as a gcc DLL, a
   sljit shard, or interpreted. Helper is byte-identical to vanilla at 4:3
   (margin 0), so widescreen-off stays bit-exact. psx_ws_func_has_screen_cull
   gates the widen to real render funnels (sltiu 0x140 + sltiu 0xE0 signature).

2. Versioned overlay cache path. gcc cache moves to <game_id>/gcc/<arch-abi>/cg<N>/
   where N = PSX_OVERLAY_CODEGEN_VER (overlay_api.h), parsed identically by the
   runtime (overlay_loader.c) and compile_overlays.py so they can't drift. A build
   whose emitter output changed (e.g. this auto_screen_x change) reads+writes a
   FRESH dir and never reuses a stale DLL; old versions coexist (no auto-delete).
   Pre-1.0: legacy flat/unversioned fallbacks dropped.

3. sljit default-on (validated). gcc and sljit are complementary, not exclusive:
   gcc fills the cache via prebuilt DLLs + async batch (compile_fragment NULL),
   sljit JITs gcc-absent regions synchronously on-miss. New code_provider_sljit()
   exposes sljit for the JIT-on-miss gap-fill independent of the "active" backend;
   the dispatch gate fires whenever sljit is available (head<0 == gcc-absent), and
   apply_live_policy defaults live ON when sljit is available. Priority gcc > sljit
   > interp; shards still route through the same-state diff gate (device-touch +
   diverging shards pinned to interp) so default-on does NOT reintroduce the
   save-load wedge; a declined/failing shard falls to interp (never a crash).
   Verified live: live=1, 62 shards compiled, 2 declines->interp, native:interp
   ratio ~31%->~87%, gcc still primary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 19:36:43 -07:00

117 lines
5.6 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.) */
#define PSX_OVERLAY_ABI_VERSION 3
/* 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. */
#define PSX_OVERLAY_CODEGEN_VER 1
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) */
void (*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);
} 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 */