mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-28 02:56:36 -04:00
e6ce5006b4
The repository root carried seven markdown files that were not onboarding
material, plus two that had been superseded by the code they described. This
sorts them: reader-facing docs to docs/, working notes to docs/internal/, and
the two superseded ones merged into the docs that replaced them.
Moved to docs/: WIDESCREEN.md, ENHANCEMENTS.md, TCP_COMMANDS.md
Moved to docs/internal/: PRINCIPLES.md, ISSUES.md, DEBUG.md, GTE_LM_FIX.md,
and from docs/: bios-hardening.md, overlay-status.md,
overlay-plan.md, overlay-recompilation-design.md,
overlay-discovery.md, TOMBA2_EXC_CONSULT_RESPONSE.md,
CASE_A_AOT_GAP.md, audit_inventory.md
Moved to docs/internal/upstream/: UPSTREAM_2026-08-20.md
The root keeps only what belongs there: README.md, CONTRIBUTING.md, LICENSE,
CLAUDE.md, THIRD_PARTY_ATTRIBUTION.md, RELEASE_NOTES.md, the dotfiles and
psxrecomp_cli.py.
MERGES
SPLITGEN_MIGRATION.md folded into docs/BUILDING.md as "Split generated C". Its
migration instructions were stale in a way that mattered: it told every game
repo to hand-edit its CMakeLists and add a file(GLOB ...) over
<serial>_full*.c, but runtime/runtime.cmake now does that itself. GEN_FULL_GLOB
is a multi-value argument of psxrecomp_add_game_runtime(); the loop at
runtime.cmake:1985 makes each pattern absolute against CMAKE_CURRENT_SOURCE_DIR,
expands it with file(GLOB), and concatenates the hits into
GAME_GENERATED_FULL_C. So a title names the pattern and gets however many shards
a regeneration produced. The rewritten section describes that, the two-step
fallback when the globs match nothing (GEN_FULL_FALLBACK, else <serial>_full.c
derived from the dispatch marker by substituting _dispatch.c), and the one
caveat the code actually has: that file(GLOB) is plain, not CONFIGURE_DEPENDS,
so a regeneration which changes the shard COUNT needs a re-configure to be seen.
psxrecomp-toml-readme.md folded into docs/GAME_PROJECT_SETUP.md as "EXE-only
autofill (psxrecomp-toml)", placed after "Disc autofill (probe_disc.py)" and
before "Project Studio" so the two autofill paths sit together. Its documented
flags were checked against the usage text in recompiler/src/main_toml.cpp:28-42
and against the argument parser at :150-165 -- all seven options match with no
drift, so the flag list is reproduced as-is. Added a pointer that probe_disc.py
is preferable when a cue exists, since that tool does the same EXE-header work
plus disc identity and netplay gates.
FUNCTIONAL FIXES
tools/gen_tcp_commands.py wrote to REPO/"TCP_COMMANDS.md". After the move that
path does not exist, so the generator would have created a new stray root file
and --check would have failed against it forever. Retargeted to
REPO/"docs"/"TCP_COMMANDS.md", along with the seven docstring and message
strings that name the path.
Running it surfaced two further problems, both fixed here:
* The doc was genuinely stale -- pre-existing drift, not introduced by this
commit (the moved file is byte-identical to origin/master, and no
debug-server command table is touched here). Four commands registered since
the last regeneration were missing from the index -- cdrom_timing_dump,
savestate_status, ws_ui_groups, xprobe_watch -- and scanline had gained a
backend. Regenerated: 307 -> 311 commands, and the change is confined to the
AUTOGEN block (the hand-written head and tail are byte-identical to master).
tools/gen_tcp_commands.py --check now exits 0.
* DOC.write_text() has no newline argument, so Python translated every newline
to os.linesep. On Windows that rewrote all 585 lines of the file to CRLF and
turned a four-line index update into a whole-file diff -- measured: 589 CRLF
/ 0 LF after a run, against 0 CRLF / 585 LF in the repo. Now written through
an explicit newline="" handle, verified to leave the file at 0 CRLF.
runtime/include/debug_server.h:9 and runtime/src/debug_server.c:8 cited "TCP.md
and DEBUG.md". TCP.md has never existed in this repo under that name; both now
name docs/TCP_COMMANDS.md and docs/internal/DEBUG.md.
LINK AND REFERENCE FIXES
Every inbound reference to a moved file was located by grepping each basename
across the whole tracked tree, not just the markdown. Markdown hrefs in
README.md, CONTRIBUTING.md, docs/ARCHITECTURE.md, docs/COMPILING_OVERLAYS.md,
docs/AOT_OVERLAY_PLAN.md, docs/RELOCATION_MANIFEST_FORMAT.md,
docs/config_schema.md, docs/OVERLAY_XREF.md, docs/TCP_COMMANDS.md and several
docs/internal/ files now point at the new locations; where two files became
siblings under docs/internal/ the path was shortened rather than lengthened
(bios-hardening.md's reference to overlay-discovery.md, for instance).
docs/COMPILING_OVERLAYS.md's "in this folder" phrasing was wrong once two of the
three files it listed moved, so it now names internal/ explicitly.
24 source-comment mentions of ENHANCEMENTS.md sections G1.x across 19 files
(recompiler, runtime, cmake) were retargeted to docs/ENHANCEMENTS.md with the
section numbers untouched. docs/SHADOW_ENHANCEMENTS.md is a DIFFERENT file and
was deliberately excluded.
20 rationale strings citing docs/CASE_A_AOT_GAP.md across the four
recompiler/seeds/*.json files were updated. Done as a byte-level substring
replacement rather than a JSON round-trip: two of those files are stored with
CRLF endings and two with LF, and re-serializing them reformatted 5,700 lines.
The byte-level version changes exactly 20 lines and each file still parses.
References that look similar but point elsewhere were deliberately left alone,
and are worth naming so a future pass does not "fix" them:
* "recomp-template PRINCIPLES.md" / "NES recomp template PRINCIPLES.md" in
CLAUDE.md:43, docs/SHADOW_ENHANCEMENTS.md:28,
docs/internal/HLE_SCHEDULER_CARVEOUT_PLAN.md:5,
runtime/src/debug_server.c:4934 and tools/parity_diff.py:8 are a different
repository's file. Only the five that mean this repo's PRINCIPLES.md were
updated.
* Every remaining bare ISSUES.md mention is a GAME repo's tracker
(MegaManX6Recomp 2/7/8/9, TombaRecomp 6), not this repo's.
* docs/disc-speed.md:4,54,56 and docs/internal/bios-hardening.md:5 name
overlay-discovery as a BRANCH, not a file, so no path applies.
INDEXES
README.md's "New here?" table gains rows for docs/WIDESCREEN.md,
docs/ENHANCEMENTS.md and docs/TCP_COMMANDS.md. docs/internal/README.md gains
sections indexing all fourteen documents that moved in, plus an "Upstream
contributor reviews" section describing upstream/ and the new sweep file, plus a
"Removed" bullet for CYCLE_TIMING_ARCH.md (deleted in the previous commit)
pointing at FAITHFUL_TIMING_PLAN.md, CYCLE_MODEL_BEETLE.md and
PRECISE_IRQ_SLICE.md -- following the SLJIT_PERSIST_CACHE.md precedent already
in that file.
docs/ENHANCEMENTS.md:7 claimed framework bugs live in "game repos' ISSUES.md
until a framework tracker exists". A framework tracker does exist; it now says
so and names the Beads epic.
VERIFICATION
A link checker was written to scan every tracked *.md for relative links,
resolving each against the tree being scanned. Baseline at origin/master: 129
files, 137 relative links, 3 broken. After both commits: 127 files, 159 relative
links, 3 broken -- the SAME three, all pre-existing and untouched here:
docs/DATA_SHARDS.md -> INSTANT_LOADS_PARKED.md (never existed)
docs/DATA_SHARDS.md -> INSTANT_LOADS_SPIKE2.md (never existed)
docs/NETPLAY.md -> ../lib/recomp-net/README.md (inside a submodule)
Zero new broken links. The checker's own coverage was checked against a raw grep
for markdown link openers: the 3-link difference is C++ lambdas inside code
blocks, correctly not treated as links.
Also verified: py_compile over all 113 tracked tools/*.py plus psxrecomp_cli.py;
all four seed JSONs still parse; the test-registration guard still reports 123
test files all registered; recompiler/tests/test_coverage_vault.py still passes;
and no file in this commit had its line endings flipped (checked mechanically
across all 50 modified files, after two files were caught and redone).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
306 lines
14 KiB
C++
306 lines
14 KiB
C++
#include "overlay_api.h"
|
|
|
|
/* ---- Overlay dispatch shim (inserted by compile_overlays.py) ----------- */
|
|
static OverlayCallbacks g_cbs;
|
|
static uint32_t s_pending_cycles;
|
|
|
|
#ifdef _WIN32
|
|
__declspec(dllexport)
|
|
#else
|
|
__attribute__((visibility("default")))
|
|
#endif
|
|
void overlay_flush_cycles(void) {
|
|
uint32_t cycles = s_pending_cycles;
|
|
s_pending_cycles = 0;
|
|
if (cycles && g_cbs.advance_cycles) g_cbs.advance_cycles(cycles);
|
|
}
|
|
|
|
/* Call-contract state (ABI v2): generated code reads/bumps the runtime's
|
|
* bail state through these pointers (cpu_state.h PSX_OVERLAY_DLL_BUILD
|
|
* branch). They start at local dummies so a pre-init call can't fault. */
|
|
static int s_bail_dummy;
|
|
static uint64_t s_ctr_dummy0, s_ctr_dummy1;
|
|
int *g_psx_call_bail_p = &s_bail_dummy;
|
|
uint64_t *g_psx_bail_first_p = &s_ctr_dummy0;
|
|
uint64_t *g_psx_bail_resolved_p = &s_ctr_dummy1;
|
|
|
|
#ifdef _WIN32
|
|
__declspec(dllexport)
|
|
#else
|
|
__attribute__((visibility("default")))
|
|
#endif
|
|
int overlay_abi(void) { return PSX_OVERLAY_ABI_VERSION | (PSX_OVERLAY_FLAVOR << 16); }
|
|
|
|
#ifdef _WIN32
|
|
__declspec(dllexport)
|
|
#else
|
|
__attribute__((visibility("default")))
|
|
#endif
|
|
void overlay_init(const OverlayCallbacks *cbs) {
|
|
g_cbs = *cbs;
|
|
if (g_cbs.call_bail_flag) g_psx_call_bail_p = g_cbs.call_bail_flag;
|
|
if (g_cbs.bail_first) g_psx_bail_first_p = g_cbs.bail_first;
|
|
if (g_cbs.bail_resolved) g_psx_bail_resolved_p = g_cbs.bail_resolved;
|
|
}
|
|
|
|
void psx_dispatch_call(CPUState *cpu, uint32_t addr, uint32_t ra) {
|
|
overlay_flush_cycles();
|
|
(void)ra;
|
|
g_cbs.dispatch_call(cpu, addr, cpu->gpr[31]);
|
|
}
|
|
void psx_check_interrupts(CPUState *cpu) {
|
|
overlay_flush_cycles();
|
|
g_cbs.check_interrupts(cpu);
|
|
}
|
|
void psx_check_interrupts_at(CPUState *cpu, uint32_t resume_pc) {
|
|
overlay_flush_cycles();
|
|
if (g_cbs.check_interrupts_at) g_cbs.check_interrupts_at(cpu, resume_pc);
|
|
else g_cbs.check_interrupts(cpu);
|
|
}
|
|
void psx_advance_cycles(uint32_t cycles) {
|
|
if (cycles > UINT32_MAX - s_pending_cycles) overlay_flush_cycles();
|
|
s_pending_cycles += cycles;
|
|
}
|
|
void gte_execute(CPUState *cpu, uint32_t cmd) {
|
|
overlay_flush_cycles();
|
|
g_cbs.gte_execute(cpu, cmd);
|
|
}
|
|
int psx_syscall(CPUState *cpu, uint32_t code) {
|
|
overlay_flush_cycles();
|
|
return g_cbs.psx_syscall(cpu, code);
|
|
}
|
|
void psx_native_bad_entry(CPUState *cpu, uint32_t owner, uint32_t pc) {
|
|
overlay_flush_cycles();
|
|
if (g_cbs.psx_native_bad_entry) g_cbs.psx_native_bad_entry(cpu, owner, pc);
|
|
}
|
|
void psx_unknown_dispatch(CPUState *cpu, uint32_t addr, uint32_t phys) {
|
|
overlay_flush_cycles();
|
|
g_cbs.psx_unknown_dispatch(cpu, addr, phys);
|
|
}
|
|
void debug_server_log_call_entry(uint32_t func_addr) {
|
|
if (g_cbs.log_call_entry) g_cbs.log_call_entry(func_addr);
|
|
}
|
|
void psx_restore_state_escape(void) {
|
|
overlay_flush_cycles();
|
|
if (g_cbs.psx_restore_state_escape) g_cbs.psx_restore_state_escape();
|
|
}
|
|
/* RFE escape marker (ABI v12): emitted at every `rfe`, including in
|
|
* overlay-resident exception-context code. Mutates the runtime's shared
|
|
* RFE-pending state, so it MUST forward (a local no-op would swallow
|
|
* exception returns). Unconditional — v12 hosts always supply it; the
|
|
* ABI gate rejects mixes that don't. */
|
|
void psx_rfe_mark_escape(void) {
|
|
overlay_flush_cycles();
|
|
g_cbs.rfe_mark_escape();
|
|
}
|
|
/* Stale-static guard (ABI v22). The emitter emits this at block entry and
|
|
* before every direct jal, but it is DEFINED IN THE GENERATED DISPATCH SHARD
|
|
* (executable side), so an overlay DLL cannot resolve it without this shim.
|
|
* Its absence is what made 107 shards fail to link on the 2026-09-02 bump.
|
|
* NULL-guarded: returning 0 sends the guest down the dispatcher path, which is
|
|
* slower but never wrong -- the opposite choice (returning 1) would let an
|
|
* overlay run native code the guard was trying to reject. */
|
|
int psx_game_text_native_ok(uint32_t addr) {
|
|
return g_cbs.game_text_native_ok ? g_cbs.game_text_native_ok(addr) : 0;
|
|
}
|
|
/* Faithful-timing functions (ABI v9): overlay code built with PSX_ENABLE_BLOCK_CYCLES
|
|
* emits these; forward to the runtime's real impls so native overlays charge cycles on
|
|
* the SAME timeline as the interp/BIOS (a local copy would diverge). NULL-guarded so a
|
|
* v9 DLL stays safe on a host that predates a given callback. */
|
|
uint32_t psx_cyc_load_word(CPUState *cpu, uint32_t addr, uint32_t rt, uint32_t reg_mask) {
|
|
overlay_flush_cycles();
|
|
return g_cbs.cyc_load_word ? g_cbs.cyc_load_word(cpu, addr, rt, reg_mask) : cpu->read_word(addr);
|
|
}
|
|
uint16_t psx_cyc_load_half(CPUState *cpu, uint32_t addr, uint32_t rt, uint32_t reg_mask) {
|
|
overlay_flush_cycles();
|
|
return g_cbs.cyc_load_half ? g_cbs.cyc_load_half(cpu, addr, rt, reg_mask) : cpu->read_half(addr);
|
|
}
|
|
uint8_t psx_cyc_load_byte(CPUState *cpu, uint32_t addr, uint32_t rt, uint32_t reg_mask) {
|
|
overlay_flush_cycles();
|
|
return g_cbs.cyc_load_byte ? g_cbs.cyc_load_byte(cpu, addr, rt, reg_mask) : cpu->read_byte(addr);
|
|
}
|
|
uint32_t psx_cyc_lwc2_read(CPUState *cpu, uint32_t addr) {
|
|
overlay_flush_cycles();
|
|
return g_cbs.cyc_lwc2_read ? g_cbs.cyc_lwc2_read(cpu, addr) : cpu->read_word(addr);
|
|
}
|
|
void psx_icache_fetch(CPUState *cpu, uint32_t addr) {
|
|
overlay_flush_cycles();
|
|
if (g_cbs.icache_fetch) g_cbs.icache_fetch(cpu, addr);
|
|
}
|
|
void psx_muldiv_set(CPUState *cpu, uint32_t latency) {
|
|
overlay_flush_cycles();
|
|
if (g_cbs.muldiv_set) g_cbs.muldiv_set(cpu, latency);
|
|
}
|
|
void psx_muldiv_stall(CPUState *cpu) {
|
|
overlay_flush_cycles();
|
|
if (g_cbs.muldiv_stall) g_cbs.muldiv_stall(cpu);
|
|
}
|
|
uint32_t psx_mult_latency_s(uint32_t rs) {
|
|
return g_cbs.mult_latency_s ? g_cbs.mult_latency_s(rs) : 0u;
|
|
}
|
|
uint32_t psx_mult_latency_u(uint32_t rs) {
|
|
return g_cbs.mult_latency_u ? g_cbs.mult_latency_u(rs) : 0u;
|
|
}
|
|
void psx_gte_stall(CPUState *cpu) {
|
|
overlay_flush_cycles();
|
|
if (g_cbs.gte_stall) g_cbs.gte_stall(cpu);
|
|
}
|
|
void psx_gte_read(CPUState *cpu, uint32_t rt) {
|
|
overlay_flush_cycles();
|
|
if (g_cbs.gte_read) g_cbs.gte_read(cpu, rt);
|
|
}
|
|
int psx_slice_block(CPUState *cpu, uint32_t block_addr, uint32_t bcyc, int side_effects) {
|
|
overlay_flush_cycles();
|
|
return g_cbs.slice_block ? g_cbs.slice_block(cpu, block_addr, bcyc, side_effects) : 0;
|
|
}
|
|
/* GTE special-register accessors (ABI v10). The emitter routes masked,
|
|
* sign-extended, aliased/FIFO, read-only, precision-sensitive, flag/IR, and
|
|
* derived registers through these as DIRECT calls, so any
|
|
* GTE-heavy overlay (Tomba2 lava / Trolley attract demos) fails to link
|
|
* without them — undefined gte_read_ctrl was the signature that silently
|
|
* blocked all new overlay coverage. GTE state is SHARED runtime state; a
|
|
* local fallback would fork that timeline, so these forward
|
|
* unconditionally (ABI v10 hosts always supply them; the v10 gate rejects
|
|
* DLL/host mixes that don't).
|
|
* Proven bit-transparent registers retain direct CPUState-array fast paths;
|
|
* the shared emitter classification header is the source of truth. */
|
|
uint32_t gte_read_data(CPUState *cpu, uint8_t reg) {
|
|
overlay_flush_cycles();
|
|
return g_cbs.gte_read_data(cpu, reg);
|
|
}
|
|
uint32_t gte_read_ctrl(CPUState *cpu, uint8_t reg) {
|
|
overlay_flush_cycles();
|
|
return g_cbs.gte_read_ctrl(cpu, reg);
|
|
}
|
|
void gte_write_data(CPUState *cpu, uint8_t reg, uint32_t value) {
|
|
overlay_flush_cycles();
|
|
g_cbs.gte_write_data(cpu, reg, value);
|
|
}
|
|
void gte_write_ctrl(CPUState *cpu, uint8_t reg, uint32_t value) {
|
|
overlay_flush_cycles();
|
|
g_cbs.gte_write_ctrl(cpu, reg, value);
|
|
}
|
|
/* g_debug_last_store_pc: a provenance breadcrumb the emitter writes before stores.
|
|
* Overlays are built PSX_NO_DEBUG_TOOLS (the runtime's only consumer is debug tooling),
|
|
* so a local definition satisfies the link; overlay-store provenance is simply not
|
|
* tracked in a no-debug-tools build (by design). */
|
|
uint32_t g_debug_last_store_pc;
|
|
/* Widescreen hooks (ABI v3): forward to the runtime's live widescreen state.
|
|
* Identity fallback if the host predates the callbacks (NULL) — so a v3 DLL
|
|
* stays correct (4:3) on an older host. */
|
|
int psx_ws_backdrop_x(int x) {
|
|
return g_cbs.ws_backdrop_x ? g_cbs.ws_backdrop_x(x) : (int)(short)x;
|
|
}
|
|
int psx_ws_x_margin(void) {
|
|
return g_cbs.ws_x_margin ? g_cbs.ws_x_margin() : 0;
|
|
}
|
|
/* Shared render-funnel screen-X cull widening ([widescreen.cull] auto_screen_x).
|
|
* The recompiler emits `rt = psx_ws_cull_sltiu(sx, imm)` at every width-compare
|
|
* cull site, INCLUDING in overlay-resident code, so the overlay DLL must define
|
|
* it or it fails to link (undefined reference) -> no native shard -> that overlay
|
|
* variant runs interpreted forever (the dwarf-village-style lag). Self-contained:
|
|
* depends only on psx_ws_x_margin() above. Identity at 4:3 (margin 0). MUST stay
|
|
* byte-for-byte identical to gpu.c psx_ws_cull_sltiu. */
|
|
int psx_ws_cull_sltiu(uint32_t sx, uint32_t imm) {
|
|
int m = psx_ws_x_margin();
|
|
return ((uint32_t)((int32_t)(int16_t)(uint16_t)sx + m)
|
|
< (uint32_t)((int32_t)imm + 2 * m)) ? 1 : 0;
|
|
}
|
|
/* Signed-funnel variants (min/max + center±halfwidth idioms). Same contract:
|
|
* self-contained, identity at margin 0, MUST stay byte-for-byte identical to
|
|
* the gpu.c implementations. */
|
|
int psx_ws_cull_slti(uint32_t sx, uint32_t imm) {
|
|
return ((int32_t)sx < (int32_t)imm + psx_ws_x_margin()) ? 1 : 0;
|
|
}
|
|
int psx_ws_cull_slti_lower(uint32_t sx, uint32_t imm) {
|
|
int32_t bound = (int32_t)(int16_t)(uint16_t)imm;
|
|
return ((int32_t)sx < bound - psx_ws_x_margin()) ? 1 : 0;
|
|
}
|
|
int psx_ws_cull_bltz(uint32_t v) {
|
|
return ((int32_t)v < -psx_ws_x_margin()) ? 1 : 0;
|
|
}
|
|
int psx_ws_cull_vxrange(uint32_t x, uint32_t imm) {
|
|
int32_t margin = psx_ws_x_margin();
|
|
uint32_t bound = (uint32_t)(int32_t)(int16_t)(uint16_t)imm;
|
|
return (((x + (uint32_t)margin) & 0xFFFFu) <
|
|
(bound + 2u * (uint32_t)margin)) ? 1 : 0;
|
|
}
|
|
void psx_ws_sprite_tag(CPUState *cpu) {
|
|
if (g_cbs.ws_sprite_tag) g_cbs.ws_sprite_tag(cpu);
|
|
}
|
|
/* Widescreen backdrop column-preload value (ABI v4). Identity fallback if the
|
|
* host predates the callback, so the DLL stays correct (4:3) on an older host. */
|
|
uint32_t psx_ws_backdrop_value(uint32_t orig, int is_end, int window_cols) {
|
|
return g_cbs.ws_backdrop_value ? g_cbs.ws_backdrop_value(orig, is_end, window_cols) : orig;
|
|
}
|
|
/* Typed native-wide signed player-X bound (ABI v13). The emitter rewrites a
|
|
* configured [widescreen] signed_x_bound LUI site into
|
|
* psx_ws_player_x_bound(vanilla) (Einhander player bounds), including in
|
|
* overlay-resident code, so the DLL must define it or the shard fails to link
|
|
* (undefined reference — the rfe/gte_read_ctrl class). Computes against the
|
|
* host's live widescreen state; identity fallback (the vanilla constant, i.e.
|
|
* 4:3 behavior) if the host predates the callback. */
|
|
int32_t psx_ws_player_x_bound(int32_t vanilla) {
|
|
return g_cbs.ws_player_x_bound ? g_cbs.ws_player_x_bound(vanilla) : vanilla;
|
|
}
|
|
int32_t psx_ws_depth_bound(int32_t imm) {
|
|
return g_cbs.ws_depth_bound ? g_cbs.ws_depth_bound(imm) : imm;
|
|
}
|
|
int32_t psx_ws_plane_nx(int32_t nx) {
|
|
return g_cbs.ws_plane_nx ? g_cbs.ws_plane_nx(nx) : nx;
|
|
}
|
|
uint32_t psx_ws_xclip_bound(uint32_t vanilla) {
|
|
return g_cbs.ws_xclip_bound ? g_cbs.ws_xclip_bound(vanilla) : vanilla;
|
|
}
|
|
uint32_t psx_ws_cull_keep_result(uint32_t vanilla, uint32_t forced) {
|
|
return g_cbs.ws_cull_keep_result
|
|
? g_cbs.ws_cull_keep_result(vanilla, forced)
|
|
: vanilla;
|
|
}
|
|
uint32_t psx_ws_aspect_cone_result(uint32_t site, uint32_t vanilla,
|
|
uint32_t object, int32_t x, int32_t z,
|
|
int32_t y) {
|
|
return g_cbs.ws_aspect_cone_result
|
|
? g_cbs.ws_aspect_cone_result(site, vanilla, object, x, z, y)
|
|
: vanilla;
|
|
}
|
|
uint32_t psx_ws_angle_widen(uint32_t vanilla) {
|
|
return g_cbs.ws_angle_widen
|
|
? g_cbs.ws_angle_widen(vanilla)
|
|
: vanilla;
|
|
}
|
|
/* Sub-pixel GTE precision-store tracker (ABI v14). The emitter emits a direct
|
|
* gte_precision_store_word() call for every swc2 (GTE store-word), including in
|
|
* overlay-resident code, so the DLL must define it or the shard fails to link
|
|
* (undefined reference — the rfe/gte_read_ctrl class). Forwards to the host's
|
|
* stateful tracker (gte.cpp); no-op if the host predates the callback
|
|
* (precision tracking is a widescreen enhancement, off by default — 4:3
|
|
* behavior is unaffected). */
|
|
void gte_precision_store_word(uint32_t addr, uint8_t reg) {
|
|
if (g_cbs.gte_precision_store_word) g_cbs.gte_precision_store_word(addr, reg);
|
|
}
|
|
#if defined(PSX_PGXP) && PSX_PGXP
|
|
/* PGXP dataflow-shadowing hooks (pgxp_hooks.h; docs/ENHANCEMENTS.md G1.10). Only a
|
|
* pgxp-flavour shard (compiled -DPSX_PGXP=1, flavor bit set) references these
|
|
* — base-flavour shards preprocess the PGXP_*() macros away, so this block is
|
|
* compiled out of them entirely. Forwards to the host engine through the
|
|
* appended-last table; every hook no-ops on a host that predates it
|
|
* (precision shadowing is visual-only, never load-bearing). */
|
|
void psx_pgxp_load(struct CPUState *cpu, uint32_t instr, uint32_t addr, uint32_t value) {
|
|
if (g_cbs.pgxp && g_cbs.pgxp->load) g_cbs.pgxp->load(cpu, instr, addr, value);
|
|
}
|
|
void psx_pgxp_store(struct CPUState *cpu, uint32_t instr, uint32_t addr, uint32_t value) {
|
|
if (g_cbs.pgxp && g_cbs.pgxp->store) g_cbs.pgxp->store(cpu, instr, addr, value);
|
|
}
|
|
void psx_pgxp_alu(struct CPUState *cpu, uint32_t instr, uint32_t result, uint32_t s1, uint32_t s2) {
|
|
if (g_cbs.pgxp && g_cbs.pgxp->alu) g_cbs.pgxp->alu(cpu, instr, result, s1, s2);
|
|
}
|
|
void psx_pgxp_muldiv(struct CPUState *cpu, uint32_t instr, uint32_t hi, uint32_t lo, uint32_t s1, uint32_t s2) {
|
|
if (g_cbs.pgxp && g_cbs.pgxp->muldiv) g_cbs.pgxp->muldiv(cpu, instr, hi, lo, s1, s2);
|
|
}
|
|
void psx_pgxp_cop2(struct CPUState *cpu, uint32_t instr, uint32_t value, uint32_t addr) {
|
|
if (g_cbs.pgxp && g_cbs.pgxp->cop2) g_cbs.pgxp->cop2(cpu, instr, value, addr);
|
|
}
|
|
#endif /* PSX_PGXP */
|
|
/* ----------------------------------------------------------------------- */
|