mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
Merge pull request #676 from zap0xfce2/feat/ws-screen-x-sltu-band
feat(widescreen): screen_x_sites band form for sltu with the width in a register
This commit is contained in:
+16
-1
@@ -299,13 +299,28 @@ are non-empty, so titles that do not use them keep their caches.
|
||||
|
||||
`screen_x_sites` is the explicit form of the `auto_screen_x` compare, for a
|
||||
title whose width is not in `screen_w_imms` or whose function has no paired
|
||||
height compare. It takes three instructions:
|
||||
height compare. It takes four instructions:
|
||||
|
||||
| Instruction at the site | While widened |
|
||||
|---|---|
|
||||
| `sltiu rt, SX, W` | `rt = -m <= SX < W + m` |
|
||||
| `lui rt, W` (a screen edge kept as `W << 16`) | `rt = (W + m) << 16`; a negative `W` moves left instead |
|
||||
| `bltz x, outside` where `x` is `SX << 16` | taken while `SX < -m` |
|
||||
| `sltu rd, SX, W` with `W` in a register | `rd = -4m <= (int16)SX < W` |
|
||||
|
||||
The `sltu` form covers a renderer that compares a 16-bit screen X
|
||||
(`andi SX, sxy, 0xffff`) against a width it keeps in a register, typically a
|
||||
clip bound loaded from RAM. The unsigned compare rejects every negative X, so
|
||||
widening the bound (`clip_edge_x_load_sites`) only reveals the right band; the
|
||||
band helper `psx_ws_cull_sltu_band` also accepts negative X. Its left reach
|
||||
is four margins: these tests check single vertices, so a large near polygon
|
||||
whose corners all lie past one margin would still drop, while an unbounded
|
||||
left side lets every off-screen polygon through and overflows a renderer's
|
||||
fixed primitive lists (Kula World's world-10 intro stalled for 5 s). Pair it with the
|
||||
load site so `W` already carries the right margin. A bound derived from the
|
||||
loaded one (`x + r < W + 2r` bounding-sphere tests) works the same way.
|
||||
Kula World (SCES-01000) is the first user: its object, level-quad and sky
|
||||
tests use this shape, and forcing them true instead overflowed its renderer.
|
||||
|
||||
The last two cover a renderer that keeps a screen X in the high half of a
|
||||
register (`sll x, sx, 16`), tests the sign of differences against a `lui`-held
|
||||
|
||||
@@ -1445,6 +1445,13 @@ std::string CodeGenerator::translate_instruction_core(uint32_t addr, uint32_t in
|
||||
return fmt::format("{} = psx_ws_cull_sltiu({}, {});"
|
||||
" /* ws explicit screen-x cull */{}",
|
||||
reg_name(rt), reg_name(rs), imm, comment);
|
||||
} else if (opcode == 0x00 && (instr & 0x3Fu) == 0x2Bu && get_rd(instr) != 0) {
|
||||
// sltu rd,SX,W: the width held in a register (often a loaded
|
||||
// clip bound); SX is a 16-bit screen X. Accept the left band too.
|
||||
return fmt::format("{} = psx_ws_cull_sltu_band({}, {});"
|
||||
" /* ws explicit screen-x band */{}",
|
||||
reg_name(get_rd(instr)), reg_name(get_rs(instr)),
|
||||
reg_name(get_rt(instr)), comment);
|
||||
} else if (opcode == 0x0F && get_rt(instr) != 0) {
|
||||
// lui rt,W: a screen edge kept in the high half of a register
|
||||
// (compared with `SX << 16`). Move it out by the live margin.
|
||||
@@ -1455,7 +1462,7 @@ std::string CodeGenerator::translate_instruction_core(uint32_t addr, uint32_t in
|
||||
reg_name(rt), imm, comment);
|
||||
} else if (!config_.overlay_mode) {
|
||||
fmt::print(stderr, "ERROR: [widescreen.cull] screen_x site 0x{:08X} "
|
||||
"is not sltiu, lui or bltz (0x{:08X})\n", addr, instr);
|
||||
"is not sltiu, sltu, lui or bltz (0x{:08X})\n", addr, instr);
|
||||
std::exit(1);
|
||||
}
|
||||
// Overlay variant: the configured main-EXE PC holds different code.
|
||||
@@ -3626,6 +3633,7 @@ void CodeGenerator::emit_runtime_externs(std::ostream& ss) const {
|
||||
if (!config_.ws_cull_screen_x_sites.empty()) {
|
||||
ss << "extern uint32_t psx_ws_cull_lui_hi(uint32_t imm); /* ws screen-x edge in the high half (gpu.c) */\n";
|
||||
ss << "extern int psx_ws_cull_bltz_hi(uint32_t v); /* ws cull left edge, X in the high half (gpu.c) */\n";
|
||||
ss << "extern int psx_ws_cull_sltu_band(uint32_t sx, uint32_t bound); /* ws screen-x band, W in a register (gpu.c) */\n";
|
||||
}
|
||||
ss << "extern int psx_ws_cull_vxrange(uint32_t x, uint32_t imm); /* ws masked-u16 X window */\n";
|
||||
ss << "extern int32_t psx_ws_depth_bound(int32_t imm); /* ws aspect-scaled far bound */\n";
|
||||
|
||||
@@ -110,6 +110,7 @@ bool exits_with_failure(F&& body) {
|
||||
}
|
||||
|
||||
constexpr uint32_t kSltiuV0 = 0x2C620200u; // sltiu v0, v1, 0x200
|
||||
constexpr uint32_t kSltuV0 = 0x0064102Bu; // sltu v0, v1, a0 (width in a register)
|
||||
constexpr uint32_t kLuiS6 = 0x3C160200u; // lui s6, 0x200
|
||||
constexpr uint32_t kLuiZero = 0x3C000200u; // lui zero, 0x200
|
||||
constexpr uint32_t kBltzT0 = 0x05000002u; // bltz t0, +2
|
||||
@@ -165,6 +166,22 @@ void listed_sites_emit_the_helpers() {
|
||||
"a screen_x site on sltiu still routes through psx_ws_cull_sltiu");
|
||||
}
|
||||
|
||||
// sltu with the width in a register: the unsigned compare rejects negative X,
|
||||
// the band helper also accepts the left reveal.
|
||||
void listed_sltu_emits_the_band_helper() {
|
||||
PSXRecomp::CodeGenConfig config{};
|
||||
config.emit_comments = true;
|
||||
check(!has(generate_first_instruction(kSltuV0, config), "psx_ws_cull_sltu_band"),
|
||||
"an unlisted sltu stays vanilla");
|
||||
config.ws_cull_screen_x_sites.insert(kBase);
|
||||
check(has(generate_first_instruction(kSltuV0, config),
|
||||
"cpu->gpr[2] = psx_ws_cull_sltu_band(cpu->gpr[3], cpu->gpr[4]); "
|
||||
"/* ws explicit screen-x band */"),
|
||||
"a screen_x site on sltu routes through psx_ws_cull_sltu_band");
|
||||
check(!exits_with_failure([&] { generate_first_instruction(kSltuV0, config); }),
|
||||
"a screen_x site on sltu generates");
|
||||
}
|
||||
|
||||
// bltz_sites is the low-half form and keeps its meaning when an address is in
|
||||
// both lists.
|
||||
void bltz_sites_wins_over_screen_x_sites() {
|
||||
@@ -283,6 +300,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
unlisted_instructions_stay_vanilla();
|
||||
listed_sites_emit_the_helpers();
|
||||
listed_sltu_emits_the_band_helper();
|
||||
bltz_sites_wins_over_screen_x_sites();
|
||||
main_exe_guard_and_overlay_variants();
|
||||
declarations_only_when_the_key_is_used();
|
||||
|
||||
@@ -426,6 +426,8 @@ int32_t psx_ws_screen_x_bound(int32_t vanilla);
|
||||
* `sltiu rt, sx, 0x140/0x141` through this one helper so every overlay execution
|
||||
* path widens identically. Returns the sltiu verdict (1 = keep). 0 at 4:3. */
|
||||
int psx_ws_cull_sltiu(uint32_t sx, uint32_t imm);
|
||||
/* screen_x_sites on `sltu rd, SX, W` (W in a register): -4m <= SX < W. */
|
||||
int psx_ws_cull_sltu_band(uint32_t sx, uint32_t bound);
|
||||
/* Signed-funnel variants (min/max + center±halfwidth idioms — see
|
||||
* ws_cull_detect.h): right-edge widen for `slti v, minSX, W` and left-edge
|
||||
* widen for the paired `bltz maxSX` reject. Identity at 4:3. */
|
||||
|
||||
@@ -203,6 +203,13 @@ int psx_ws_cull_sltiu(uint32_t sx, uint32_t imm) {
|
||||
return ((uint32_t)((int32_t)(int16_t)(uint16_t)sx + m)
|
||||
< (uint32_t)((int32_t)imm + 2 * m)) ? 1 : 0;
|
||||
}
|
||||
/* MUST stay byte-for-byte identical to gpu.c psx_ws_cull_sltu_band. */
|
||||
int psx_ws_cull_sltu_band(uint32_t sx, uint32_t bound) {
|
||||
int m = psx_ws_x_margin();
|
||||
if (m <= 0) return sx < bound ? 1 : 0;
|
||||
int32_t x = (int32_t)(int16_t)(uint16_t)sx;
|
||||
return (x >= -4 * m && x < (int32_t)bound) ? 1 : 0; /* WS_SLTU_BAND_LEFT_REACH */
|
||||
}
|
||||
/* 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. */
|
||||
|
||||
@@ -2003,6 +2003,23 @@ int psx_ws_cull_sltiu(uint32_t sx, uint32_t imm) {
|
||||
< (uint32_t)((int32_t)imm + 2 * m)) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* [widescreen.cull] screen_x_sites on `sltu rd, SX, W` with W in a register:
|
||||
* SX is a 16-bit screen X (typically `andi SX, sxy, 0xffff`), W the width the
|
||||
* guest loaded (pair with clip_edge_x_load_sites so W already carries the right
|
||||
* margin). The unsigned compare rejects negative X; while widened the verdict is
|
||||
* -k*m <= (int16)SX < W, so the left band passes too. Identity at margin 0.
|
||||
* These tests check single vertices, so a large near polygon whose corners all
|
||||
* lie past the reveal would still drop: k = 4 margins keeps them (Kula World's
|
||||
* level quads), while an unbounded left side lets every off-screen polygon
|
||||
* through and overflows renderers with fixed primitive lists. */
|
||||
#define WS_SLTU_BAND_LEFT_REACH 4
|
||||
int psx_ws_cull_sltu_band(uint32_t sx, uint32_t bound) {
|
||||
int m = psx_ws_x_margin();
|
||||
if (m <= 0) return sx < bound ? 1 : 0;
|
||||
int32_t x = (int32_t)(int16_t)(uint16_t)sx;
|
||||
return (x >= -WS_SLTU_BAND_LEFT_REACH * m && x < (int32_t)bound) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* Signed right-edge widen for the min/max funnel idiom (`slti v, minSX, W`):
|
||||
* the paired LEFT edge is a separate bltz (psx_ws_cull_bltz below), so this
|
||||
* bound moves out by ONE margin only. Operand is an already sign-extended /
|
||||
|
||||
Reference in New Issue
Block a user