mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
feat: add masked-u16 widescreen cull sites
Add a shared native/interpreter helper for sltiu classifiers operating on ANDI-masked 16-bit screen X values, preserving MIPS immediate sign extension and overlay linkage. Co-authored-by: Opok <novelasevilliousesp@gmail.com>
This commit is contained in:
@@ -100,6 +100,11 @@ struct CodeGenConfig {
|
||||
// Identity at 4:3; empty by default.
|
||||
std::set<uint32_t> ws_cull_negsub_sites;
|
||||
|
||||
// Explicit masked-16-bit screen-X window sites ([widescreen.cull]
|
||||
// vxrange_sites). The configured sltiu is routed through the shared
|
||||
// runtime helper so native and interpreted overlays use identical math.
|
||||
std::set<uint32_t> ws_cull_vxrange_sites;
|
||||
|
||||
// Screen-extent signature immediates ([widescreen.cull] screen_w_imms /
|
||||
// screen_h_imms) — per-game display-width-derived bounds. Defaults are the
|
||||
// Tomba signature; Ape Escape uses 0x181 (+ 0xF1 height).
|
||||
|
||||
@@ -834,6 +834,20 @@ std::string CodeGenerator::translate_instruction(uint32_t addr, uint32_t instr)
|
||||
}
|
||||
// overlay variant: addr is different code here — fall through to vanilla.
|
||||
}
|
||||
if (config_.ws_cull_vxrange_sites.count(addr)) {
|
||||
if (opcode == 0x0B) { // sltiu
|
||||
uint32_t rs = get_rs(instr), rt = get_rt(instr);
|
||||
uint16_t imm = get_imm16_u(instr);
|
||||
return fmt::format("{} = psx_ws_cull_vxrange({}, {});"
|
||||
" /* ws cull masked-u16 X window */{}",
|
||||
reg_name(rt), reg_name(rs), (unsigned)imm, comment);
|
||||
} else if (!config_.overlay_mode) {
|
||||
fmt::print(stderr, "ERROR: [widescreen.cull] vxrange site 0x{:08X} is not "
|
||||
"sltiu (opcode 0x{:02X})\n", addr, opcode);
|
||||
std::exit(1);
|
||||
}
|
||||
// Overlay variant at the same address: leave nonmatching code unchanged.
|
||||
}
|
||||
if (config_.ws_cull_range_sites.count(addr)) {
|
||||
if (opcode == 0x0B) { // sltiu
|
||||
uint32_t rs = get_rs(instr), rt = get_rt(instr);
|
||||
@@ -2603,6 +2617,7 @@ void CodeGenerator::emit_runtime_externs(std::ostream& ss) const {
|
||||
ss << "extern int psx_ws_cull_sltiu(uint32_t sx, uint32_t imm); /* ws auto screen-x cull (gpu.c) */\n";
|
||||
ss << "extern int psx_ws_cull_slti(uint32_t sx, uint32_t imm); /* ws cull signed right edge (gpu.c) */\n";
|
||||
ss << "extern int psx_ws_cull_bltz(uint32_t v); /* ws cull signed left edge (gpu.c) */\n";
|
||||
ss << "extern int psx_ws_cull_vxrange(uint32_t x, uint32_t imm); /* ws masked-u16 X window */\n";
|
||||
ss << "extern int psx_ws_backdrop_x(int x); /* widescreen backdrop screenX squash (gpu.c) */\n";
|
||||
ss << "extern int psx_ws_bg2d_cols(int base); /* ws 2D bg tile-loop widen: col count (gpu.c) */\n";
|
||||
ss << "extern int psx_ws_bg2d_startcol(int col, unsigned mask); /* ws 2D bg tile-loop widen: start tile col (gpu.c) */\n";
|
||||
|
||||
@@ -1048,6 +1048,7 @@ GameConfig load_game_config(const fs::path& config_path_in) {
|
||||
std::vector<uint32_t> ws_cull_screen_x_sites;
|
||||
std::vector<uint32_t> ws_cull_slti_sites;
|
||||
std::vector<uint32_t> ws_cull_negsub_sites;
|
||||
std::vector<uint32_t> ws_cull_vxrange_sites;
|
||||
int ws_cull_guard_pixels = 0;
|
||||
// Cull-signature immediates (screen_w_imms / screen_h_imms). Defaults are
|
||||
// the original Tomba signature (320-display: 0x140/0x141 + 0xE0/0xF1); a
|
||||
@@ -1071,6 +1072,7 @@ GameConfig load_game_config(const fs::path& config_path_in) {
|
||||
load_sites("screen_x_sites", ws_cull_screen_x_sites);
|
||||
load_sites("slti_sites", ws_cull_slti_sites);
|
||||
load_sites("negsub_sites", ws_cull_negsub_sites);
|
||||
load_sites("vxrange_sites", ws_cull_vxrange_sites);
|
||||
if (cull.contains("guard_pixels")) {
|
||||
ws_cull_guard_pixels = toml::find<int>(cull, "guard_pixels");
|
||||
if (ws_cull_guard_pixels < 0 || ws_cull_guard_pixels > 256)
|
||||
@@ -1222,6 +1224,7 @@ GameConfig load_game_config(const fs::path& config_path_in) {
|
||||
/*ws_cull_screen_x_sites*/ ws_cull_screen_x_sites,
|
||||
/*ws_cull_slti_sites*/ ws_cull_slti_sites,
|
||||
/*ws_cull_negsub_sites*/ ws_cull_negsub_sites,
|
||||
/*ws_cull_vxrange_sites*/ ws_cull_vxrange_sites,
|
||||
/*ws_cull_guard_pixels*/ ws_cull_guard_pixels,
|
||||
/*ws_cull_w_imms*/ ws_cull_w_imms,
|
||||
/*ws_cull_h_imms*/ ws_cull_h_imms,
|
||||
|
||||
@@ -516,6 +516,9 @@ struct GameConfig {
|
||||
// Horizontal low-edge form `subu rd,zero,rs` -> `-rs-x_margin`.
|
||||
// Empty by default; configured sites require regenerated native code.
|
||||
std::vector<uint32_t> ws_cull_negsub_sites;
|
||||
// `sltiu rt,rs,imm` where rs is an ANDI-masked 16-bit screen X.
|
||||
// Widens both edges in 16-bit space; empty by default; regen required.
|
||||
std::vector<uint32_t> ws_cull_vxrange_sites;
|
||||
// Extra per-side actor overdraw beyond the visible widescreen edge.
|
||||
int ws_cull_guard_pixels = 0;
|
||||
|
||||
|
||||
@@ -155,6 +155,7 @@ int main(int argc, char** argv) {
|
||||
std::set<uint32_t> ws_cull_screen_x; // [widescreen.cull] screen_x_sites
|
||||
std::set<uint32_t> ws_cull_slti; // [widescreen.cull] slti_sites
|
||||
std::set<uint32_t> ws_cull_negsub; // [widescreen.cull] negsub_sites
|
||||
std::set<uint32_t> ws_cull_vxrange; // [widescreen.cull] vxrange_sites
|
||||
std::vector<uint32_t> ws_cull_w_imms = { 0x140, 0x141 }; // [widescreen.cull] screen_w_imms
|
||||
std::vector<uint32_t> ws_cull_h_imms = { 0xE0, 0xF1 }; // [widescreen.cull] screen_h_imms
|
||||
std::set<uint32_t> ws_backdrop_x; // [widescreen.backdrop] x_sites
|
||||
@@ -196,6 +197,7 @@ int main(int argc, char** argv) {
|
||||
ws_cull_screen_x.insert(cfg.ws_cull_screen_x_sites.begin(), cfg.ws_cull_screen_x_sites.end());
|
||||
ws_cull_slti.insert(cfg.ws_cull_slti_sites.begin(), cfg.ws_cull_slti_sites.end());
|
||||
ws_cull_negsub.insert(cfg.ws_cull_negsub_sites.begin(), cfg.ws_cull_negsub_sites.end());
|
||||
ws_cull_vxrange.insert(cfg.ws_cull_vxrange_sites.begin(), cfg.ws_cull_vxrange_sites.end());
|
||||
ws_cull_w_imms = cfg.ws_cull_w_imms;
|
||||
ws_cull_h_imms = cfg.ws_cull_h_imms;
|
||||
ws_backdrop_x.insert(cfg.ws_backdrop_x_sites.begin(), cfg.ws_backdrop_x_sites.end());
|
||||
@@ -270,6 +272,7 @@ int main(int argc, char** argv) {
|
||||
ws_cull_screen_x.insert(wscfg.ws_cull_screen_x_sites.begin(), wscfg.ws_cull_screen_x_sites.end());
|
||||
ws_cull_slti.insert(wscfg.ws_cull_slti_sites.begin(), wscfg.ws_cull_slti_sites.end());
|
||||
ws_cull_negsub.insert(wscfg.ws_cull_negsub_sites.begin(), wscfg.ws_cull_negsub_sites.end());
|
||||
ws_cull_vxrange.insert(wscfg.ws_cull_vxrange_sites.begin(), wscfg.ws_cull_vxrange_sites.end());
|
||||
ws_cull_w_imms = wscfg.ws_cull_w_imms;
|
||||
ws_cull_h_imms = wscfg.ws_cull_h_imms;
|
||||
ws_backdrop_x.insert(wscfg.ws_backdrop_x_sites.begin(), wscfg.ws_backdrop_x_sites.end());
|
||||
@@ -927,6 +930,7 @@ int main(int argc, char** argv) {
|
||||
codegen_config.ws_cull_screen_x_sites = ws_cull_screen_x;
|
||||
codegen_config.ws_cull_slti_sites = ws_cull_slti;
|
||||
codegen_config.ws_cull_negsub_sites = ws_cull_negsub;
|
||||
codegen_config.ws_cull_vxrange_sites = ws_cull_vxrange;
|
||||
codegen_config.ws_cull_w_imms = ws_cull_w_imms;
|
||||
codegen_config.ws_cull_h_imms = ws_cull_h_imms;
|
||||
codegen_config.ws_backdrop_x_sites = ws_backdrop_x;
|
||||
|
||||
@@ -182,6 +182,15 @@ negsub_sites = ["0x80012340"]
|
||||
check(negsub_config.ws_cull_negsub_sites ==
|
||||
std::vector<uint32_t>{0x80012340u},
|
||||
"parser preserves negsub cull sites");
|
||||
|
||||
const auto vxrange = write_config(root, "vxrange", R"toml(
|
||||
[widescreen.cull]
|
||||
vxrange_sites = ["0x80012340"]
|
||||
)toml");
|
||||
const auto vxrange_config = PSXRecompV4::load_game_config(vxrange);
|
||||
check(vxrange_config.ws_cull_vxrange_sites ==
|
||||
std::vector<uint32_t>{0x80012340u},
|
||||
"parser preserves masked-u16 X-window sites");
|
||||
}
|
||||
|
||||
void capture_history_config_tests(const fs::path& root) {
|
||||
@@ -274,6 +283,19 @@ void codegen_tests() {
|
||||
0x00041021u, {}, true, negsub_config); // addu v0,zero,a0
|
||||
check(overlay_mismatch.find("ws cull negsub") == std::string::npos,
|
||||
"overlay nonmatching negsub variant remains unchanged");
|
||||
|
||||
PSXRecomp::CodeGenConfig vxrange_config;
|
||||
vxrange_config.ws_cull_vxrange_sites.insert(0x80010000u);
|
||||
const std::string vxrange = generate_first_instruction(
|
||||
0x2C820140u, {}, false, vxrange_config); // sltiu v0,a0,0x140
|
||||
check(vxrange.find("cpu->gpr[2] = psx_ws_cull_vxrange(cpu->gpr[4], 320)") !=
|
||||
std::string::npos,
|
||||
"codegen routes masked-u16 X-window sites through shared helper");
|
||||
|
||||
const std::string vxrange_overlay_mismatch = generate_first_instruction(
|
||||
0x24820140u, {}, true, vxrange_config); // addiu v0,a0,0x140
|
||||
check(vxrange_overlay_mismatch.find("ws cull masked-u16") == std::string::npos,
|
||||
"overlay nonmatching masked-u16 variant remains unchanged");
|
||||
}
|
||||
|
||||
void jump_table_producer_codegen_test() {
|
||||
|
||||
@@ -144,9 +144,11 @@ void gpu_ws_set_cull_guard_pixels(int pixels);
|
||||
void gpu_ws_set_explicit_cull_sites(const uint32_t *bias, int nbias,
|
||||
const uint32_t *slti, int nslti);
|
||||
void gpu_ws_set_negsub_cull_sites(const uint32_t *sites, int nsites);
|
||||
void gpu_ws_set_vxrange_cull_sites(const uint32_t *sites, int nsites);
|
||||
int psx_ws_is_cull_bias_site(uint32_t pc);
|
||||
int psx_ws_is_cull_slti_site(uint32_t pc);
|
||||
int psx_ws_is_cull_negsub_site(uint32_t pc);
|
||||
int psx_ws_is_cull_vxrange_site(uint32_t pc);
|
||||
/* Scale a signed Q16 horizontal gameplay limit into the active native-wide
|
||||
* game field. Identity at 4:3 / menus / FMV. */
|
||||
int32_t psx_ws_player_x_bound(int32_t vanilla);
|
||||
@@ -164,6 +166,7 @@ int psx_ws_cull_sltiu(uint32_t sx, uint32_t imm);
|
||||
* widen for the paired `bltz maxSX` reject. Identity at 4:3. */
|
||||
int psx_ws_cull_slti(uint32_t sx, uint32_t imm);
|
||||
int psx_ws_cull_bltz(uint32_t v);
|
||||
int psx_ws_cull_vxrange(uint32_t x, uint32_t imm);
|
||||
/* True if a run of instruction words carries the screen-extent reject signature
|
||||
* (a width compare AND a height compare from the configured immediate sets).
|
||||
* Used by the interp to gate the widening to real render funnels. */
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
* 4: continuation-passing (PSX_CPS, RECURSION_BUG.md §25) — overlay funcs
|
||||
* tail-transfer + carry an entry-switch; the loader routes continuations.
|
||||
* Fresh namespace so CPS DLLs never reuse a legacy (unit-model) cg3 DLL.
|
||||
* 6: + configured negsub horizontal low-edge cull emit. */
|
||||
* 6: + configured negsub and masked-16-bit screen-X cull emits. */
|
||||
#define PSX_OVERLAY_CODEGEN_VER 6
|
||||
|
||||
/* Auto codegen hash (generated by hash_codegen.cmake from the recompiler codegen
|
||||
|
||||
@@ -205,6 +205,12 @@ int psx_ws_cull_slti(uint32_t sx, uint32_t imm) {
|
||||
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);
|
||||
}
|
||||
@@ -234,4 +240,3 @@ 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);
|
||||
}
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
|
||||
@@ -1559,7 +1559,9 @@ static int exec_one(CPUState *cpu, uint32_t pc, uint32_t *next_pc_out) {
|
||||
* shared helper for a flagged render-cull site — it is byte-identical
|
||||
* to the vanilla compare at 4:3 (margin 0) and widens at 16:9, so the one
|
||||
* code path serves both aspects (no widescreen-specific caching). */
|
||||
if (psx_ws_auto_cull_on() && psx_ws_is_cull_w_imm(imm) && ws_cull_site(pc))
|
||||
if (psx_ws_is_cull_vxrange_site(pc))
|
||||
cpu->gpr[rt] = (uint32_t)psx_ws_cull_vxrange(cpu->gpr[rs], imm);
|
||||
else if (psx_ws_auto_cull_on() && psx_ws_is_cull_w_imm(imm) && ws_cull_site(pc))
|
||||
cpu->gpr[rt] = (uint32_t)psx_ws_cull_sltiu(cpu->gpr[rs], imm);
|
||||
else
|
||||
cpu->gpr[rt] = (cpu->gpr[rs] < (uint32_t)simm) ? 1u : 0u;
|
||||
|
||||
@@ -346,6 +346,18 @@ void gpu_ws_set_negsub_cull_sites(const uint32_t *sites, int nsites) {
|
||||
int psx_ws_is_cull_negsub_site(uint32_t pc) {
|
||||
return ws_explicit_site(ws_explicit_negsub_sites, ws_explicit_negsub_n, pc);
|
||||
}
|
||||
static uint32_t ws_explicit_vxrange_sites[WS_EXPLICIT_CULL_SITES_MAX];
|
||||
static int ws_explicit_vxrange_n = 0;
|
||||
void gpu_ws_set_vxrange_cull_sites(const uint32_t *sites, int nsites) {
|
||||
if (nsites < 0) nsites = 0;
|
||||
if (nsites > WS_EXPLICIT_CULL_SITES_MAX) nsites = WS_EXPLICIT_CULL_SITES_MAX;
|
||||
ws_explicit_vxrange_n = nsites;
|
||||
for (int i = 0; i < nsites; i++)
|
||||
ws_explicit_vxrange_sites[i] = sites[i] & 0x1FFFFFFFu;
|
||||
}
|
||||
int psx_ws_is_cull_vxrange_site(uint32_t pc) {
|
||||
return ws_explicit_site(ws_explicit_vxrange_sites, ws_explicit_vxrange_n, pc);
|
||||
}
|
||||
|
||||
int psx_ws_x_margin(void) {
|
||||
if (ws_margin_override >= 0) return ws_margin_override;
|
||||
@@ -921,6 +933,12 @@ int psx_ws_cull_slti(uint32_t sx, uint32_t imm) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* ---- Cull signature configuration ([widescreen.cull] screen_w_imms /
|
||||
* screen_h_imms). The width/height immediates are per-game (Tomba: 0x140/0x141
|
||||
|
||||
@@ -3163,6 +3163,8 @@ int main(int argc, char** argv) {
|
||||
gc.ws_cull_slti_sites.data(), (int)gc.ws_cull_slti_sites.size());
|
||||
gpu_ws_set_negsub_cull_sites(
|
||||
gc.ws_cull_negsub_sites.data(), (int)gc.ws_cull_negsub_sites.size());
|
||||
gpu_ws_set_vxrange_cull_sites(
|
||||
gc.ws_cull_vxrange_sites.data(), (int)gc.ws_cull_vxrange_sites.size());
|
||||
gte_ws_configure_dome_sites(
|
||||
gc.ws_dome_call_sites.data(), (int)gc.ws_dome_call_sites.size());
|
||||
/* [widescreen.cull] per-game gates + signature immediates for the
|
||||
|
||||
Reference in New Issue
Block a user