recompiler: [widescreen.cull] bltz_sites (explicit left-edge funnel widen)

Left-edge counterpart to slti_sites: emit psx_ws_cull_bltz at configured
bltz PCs. detect_cull_bltz_sites only classifies left-edge bltz for
auto_screen_x-qualified functions, so an X-only funnel wired via explicit
slti_sites had no left-edge widen. bltz_sites closes that gap. Identity at
4:3; regen-class.
This commit is contained in:
Claudio Varandas
2026-07-22 20:07:21 +01:00
parent ed5b1838b8
commit 64ca4c3dba
5 changed files with 28 additions and 4 deletions
+5
View File
@@ -99,6 +99,11 @@ struct CodeGenConfig {
// the auto-detector cannot qualify (X-only test, no height compare).
std::set<uint32_t> ws_cull_slti_sites;
// `bltz rs, reject` emitted through psx_ws_cull_bltz — the explicit
// LEFT-edge counterpart to ws_cull_slti_sites ([widescreen.cull]
// bltz_sites), for X-only funnels the auto-detector cannot qualify.
std::set<uint32_t> ws_cull_bltz_sites;
// Explicit horizontal low-edge widen sites ([widescreen.cull]
// negsub_sites): `subu rd, zero, rs` becomes `-rs - x_margin`.
// Identity at 4:3; empty by default.
+9 -4
View File
@@ -721,10 +721,15 @@ std::string CodeGenerator::generate_branch_condition(uint32_t instr, uint32_t ad
if (opcode == 0x01) {
uint32_t regimm_op = (instr >> 16) & 0x1F;
if ((regimm_op & 0x01u) == 0x00u) { // bltz family (incl. bltzal + undefined mirrors)
// Classified LEFT-edge funnel bltz (auto_screen_x, signed idioms):
// reject only past the revealed margin. Identity at 4:3 (margin 0).
if (regimm_op == 0x00 && ws_cull_bltz_pcs_.count(addr))
return fmt::format("psx_ws_cull_bltz({}) /* ws auto screen-x cull (left edge) */",
// LEFT-edge funnel bltz: reject only past the revealed margin.
// Identity at 4:3 (margin 0). Two sources: (a) auto_screen_x's
// detect_cull_bltz_sites classification (ws_cull_bltz_pcs_), and
// (b) explicit [widescreen.cull] bltz_sites — the left-edge
// counterpart to slti_sites, for X-only funnels auto_screen_x can't
// qualify (whose bltz would otherwise never be widened).
if (regimm_op == 0x00 &&
(ws_cull_bltz_pcs_.count(addr) || config_.ws_cull_bltz_sites.count(addr)))
return fmt::format("psx_ws_cull_bltz({}) /* ws cull (left edge) */",
reg_name(rs));
return fmt::format("(int32_t){} < 0", reg_name(rs));
} else { // bgez family (incl. bgezal + undefined mirrors)
+3
View File
@@ -1066,6 +1066,7 @@ GameConfig load_game_config(const fs::path& config_path_in) {
std::vector<uint32_t> ws_cull_bias_sites, ws_cull_range_sites, ws_cull_a1_sites;
std::vector<uint32_t> ws_cull_screen_x_sites;
std::vector<uint32_t> ws_cull_slti_sites;
std::vector<uint32_t> ws_cull_bltz_sites;
std::vector<uint32_t> ws_cull_negsub_sites;
std::vector<uint32_t> ws_cull_vxrange_sites;
std::vector<uint32_t> ws_cull_depth_sites;
@@ -1093,6 +1094,7 @@ GameConfig load_game_config(const fs::path& config_path_in) {
load_sites("a1_sites", ws_cull_a1_sites);
load_sites("screen_x_sites", ws_cull_screen_x_sites);
load_sites("slti_sites", ws_cull_slti_sites);
load_sites("bltz_sites", ws_cull_bltz_sites);
load_sites("negsub_sites", ws_cull_negsub_sites);
load_sites("vxrange_sites", ws_cull_vxrange_sites);
load_sites("depth_sites", ws_cull_depth_sites);
@@ -1250,6 +1252,7 @@ GameConfig load_game_config(const fs::path& config_path_in) {
/*ws_cull_a1_sites*/ ws_cull_a1_sites,
/*ws_cull_screen_x_sites*/ ws_cull_screen_x_sites,
/*ws_cull_slti_sites*/ ws_cull_slti_sites,
/*ws_cull_bltz_sites*/ ws_cull_bltz_sites,
/*ws_cull_negsub_sites*/ ws_cull_negsub_sites,
/*ws_cull_vxrange_sites*/ ws_cull_vxrange_sites,
/*ws_cull_depth_sites*/ ws_cull_depth_sites,
+7
View File
@@ -534,6 +534,13 @@ struct GameConfig {
// auto-detector cannot qualify (e.g. an X-only test with no height compare
// in the same function — Ape Escape 0x8004AB64). Empty by default; regen.
std::vector<uint32_t> ws_cull_slti_sites;
// [widescreen.cull] bltz_sites — explicit signed LEFT-edge widen sites
// (`bltz rs, reject` -> psx_ws_cull_bltz), the counterpart to slti_sites.
// detect_cull_bltz_sites only classifies left-edge bltz for functions
// auto_screen_x qualified, so an X-only funnel wired through explicit
// slti_sites has no left-edge widen without this. Empty by default;
// identity at 4:3; regen required.
std::vector<uint32_t> ws_cull_bltz_sites;
// 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;
+4
View File
@@ -156,6 +156,7 @@ int main(int argc, char** argv) {
std::set<uint32_t> ws_cull_bias, ws_cull_range, ws_cull_a1; // [widescreen.cull]
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_bltz; // [widescreen.cull] bltz_sites
std::set<uint32_t> ws_cull_negsub; // [widescreen.cull] negsub_sites
std::set<uint32_t> ws_cull_vxrange; // [widescreen.cull] vxrange_sites
std::set<uint32_t> ws_cull_depth; // [widescreen.cull] depth_sites
@@ -202,6 +203,7 @@ int main(int argc, char** argv) {
ws_cull_a1.insert(cfg.ws_cull_a1_sites.begin(), cfg.ws_cull_a1_sites.end());
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_bltz.insert(cfg.ws_cull_bltz_sites.begin(), cfg.ws_cull_bltz_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_depth.insert(cfg.ws_cull_depth_sites.begin(), cfg.ws_cull_depth_sites.end());
@@ -280,6 +282,7 @@ int main(int argc, char** argv) {
ws_cull_a1.insert(wscfg.ws_cull_a1_sites.begin(), wscfg.ws_cull_a1_sites.end());
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_bltz.insert(wscfg.ws_cull_bltz_sites.begin(), wscfg.ws_cull_bltz_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_depth.insert(wscfg.ws_cull_depth_sites.begin(), wscfg.ws_cull_depth_sites.end());
@@ -1053,6 +1056,7 @@ int main(int argc, char** argv) {
codegen_config.ws_cull_a1_sites = ws_cull_a1;
codegen_config.ws_cull_screen_x_sites = ws_cull_screen_x;
codegen_config.ws_cull_slti_sites = ws_cull_slti;
codegen_config.ws_cull_bltz_sites = ws_cull_bltz;
codegen_config.ws_cull_negsub_sites = ws_cull_negsub;
codegen_config.ws_cull_vxrange_sites = ws_cull_vxrange;
codegen_config.ws_cull_depth_sites = ws_cull_depth;