Files
Shokunin dd5fdec6bb Add opt-in draw-distance clamps ([[draw_distance.clamp]])
Many PS1 renderers drop a primitive whose depth index falls past the end
of their ordering table. Raising that limit can write past the table, so
a title can now list such guards in game.toml with the register that
carries the index and the last safe value. While a trusted mod has called
psx_mod_set_draw_distance_clamp(1), generated main-EXE code clamps that
register before the guard runs, so far geometry is kept in the farthest
slot instead of popping in later.

- Off by default and reset at every session start, so netplay and every
  title that lists no sites run unchanged; with no sites the generated
  code and declarations are byte-identical.
- Main executable only: the full instruction word is the guard,
  generation fails on a mismatch or a load-delay hazard, captured
  overlay code keeps its own code (the sites are not part of the overlay
  config identity), and the dirty-RAM interpreter applies the clamp
  where the game's text image holds the listed word.
- Compatibility: code_generator.cpp is a codegen-hash input, so the
  codegen hash and the overlay-cache tag change for every title, sites
  or not. Savestates from earlier builds are refused, and overlay shards
  rebuild once on first visit. Generated code and behaviour are
  unchanged for a title without sites or with the switch off.
- Tests: draw_distance_codegen_test (config rules, emission, guards,
  declarations, overlay identity) and draw_distance_interp_test (store,
  switch, interpreted guards); the session-reset guard covers the switch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:36:48 -07:00

72 lines
2.8 KiB
C

/* draw_distance.h - opt-in draw-distance clamps ([[draw_distance.clamp]]).
*
* Many PS1 renderers drop a primitive whose depth falls past the end of their
* ordering table: `sltiu t, z, N; beqz t, reject`. The limit protects the OT,
* so simply raising it can write past the table. A title lists such
* predicates in game.toml together with the register that carries the depth
* index and the last safe value:
*
* [[draw_distance.clamp]]
* address = "0x80061230"
* expected = "0x2C4A01C0" # sltiu t2, v0, 0x1C0
* reg = 2 # v0
* max = 0x1BF
*
* While a trusted mod has switched the clamps on, the listed register is
* clamped to `max` (signed) immediately before the listed instruction runs,
* so a far primitive is kept in the farthest OT slot instead of being
* dropped. Off (the default, reset at every session start) the sites run the
* original code. Main executable only: generated main-EXE code applies the
* clamp, captured overlay code at the same address keeps its own code, and
* the dirty-RAM interpreter applies it where the game's text image still
* holds the listed instruction.
*
* docs/config_schema.md "Draw-distance clamps" has the config rules.
*/
#ifndef PSX_DRAW_DISTANCE_H
#define PSX_DRAW_DISTANCE_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct PSXDrawDistanceClampSite {
uint32_t address; /* any segment; stored physical */
uint32_t expected; /* the complete instruction word at address */
uint32_t reg; /* 1..31: a source register of `expected` */
int32_t max; /* signed upper bound */
} PSXDrawDistanceClampSite;
#define PSX_DRAW_DISTANCE_CLAMP_SITES_MAX 256
/* Nonzero while the clamps are on. Generated main-EXE code reads it at every
* configured site. */
extern uint32_t g_psx_draw_distance_clamp;
/* On and at least one site configured: the interpreter's per-instruction
* gate. */
extern int g_psx_draw_distance_clamp_live;
/* The title's sites (game config). Copied, sorted, capped at
* PSX_DRAW_DISTANCE_CLAMP_SITES_MAX (a longer list is logged). Returns the
* number kept. */
int psx_draw_distance_set_clamp_sites(const PSXDrawDistanceClampSite *sites,
int count);
int psx_draw_distance_clamp_site_count(void);
/* The site at `pc` whose instruction is `insn`, or NULL. */
const PSXDrawDistanceClampSite *psx_draw_distance_clamp_find(uint32_t pc,
uint32_t insn);
/* The value the clamp leaves in the register. */
static inline uint32_t psx_draw_distance_clamp_value(uint32_t value,
int32_t max) {
return (int32_t)value > max ? (uint32_t)max : value;
}
#ifdef __cplusplus
}
#endif
#endif /* PSX_DRAW_DISTANCE_H */