Fixes Ape Escape terrain seams: CPU-built subdivision midpoints and lwl/lwr-copied
grid corners drew native next to precise neighbours. Adds pgxp_tri_ring/pgxp_store_ring.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
MFC0 and CFC2 overwrite a GPR, but the recompiled code did not hook them
(and neither interpreter hooked MFC0/CFC0), so the register kept the
shadow of whatever vertex it held before. Validation by value cannot
prove provenance when the new word equals the old one. Emit PGXP_ALU
after MFC0 and PGXP_COP2 after CFC2, matching the interpreters' CFC2
hook, and hook MFC0/CFC0 in both interpreters. OpenBIOS regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR #171 fixed the PGXP hooks, which were space-joined onto emissions that
end on a bare #endif (the PSX_ENABLE_BLOCK_CYCLES muldiv stall/latency
blocks) and were therefore swallowed as "extra tokens" and silently
discarded -- 3,187 hooks per title in Ape Escape alone, plus 1,024 in the
BIOS. It fixed the instance. Two same-line appenders of the same class
were left behind:
1. strict_translator.cpp -- the deferred-load PGXP_LOAD hook is still
space-joined onto c_code_deferred. All five c_code_deferred producers
currently end in plain statements, so it is benign TODAY, but the
moment a block-cycles annotation lands on deferred loads it breaks
exactly the way #171 just did, and just as silently.
2. code_generator.cpp -- the trailing disassembly comment is appended to
the emission unconditionally. Reachable whenever append_pgxp_hooks
returned early and left the emission ending on a directive, e.g.
`mfhi $zero` / `mflo $zero` (rd == 0 takes an early return). The
comment is then dropped and -Wendif-labels fires.
Both now route through one predicate,
emission_ends_on_preprocessor_directive(), so the hazard is closed by
construction rather than one site at a time. Site 1 takes an unconditional
newline (always valid where the space was); site 2 only breaks the line
when the emission actually ends on a directive, so ordinary instructions
keep their inline comment and the generated diff stays minimal.
Measured effect on emitted code, against merged master:
- SCPH1001 BIOS: byte-identical
- OpenBIOS: 2 sites, both the deferred PGXP_LOAD moving to its own line,
identical token sequence
- Ape / Tomba 1 / Tomba 2 / MMX6: every function token-identical
(3095/6891/2726/4023 functions each, none added, none removed, zero
body changes)
Adds emitter_directive_line_test (10 checks, registered with add_test) so
the predicate cannot regress: it pins both real emission shapes that end on
#endif and, just as importantly, the negative cases -- a false positive
would inject stray newlines into every instruction.
ctest before and after this change fails the identical 6 pre-existing tests
(aot_overlay_discovery, launcher_vulkan_option, mod_load_acceleration,
release_zip, sdl3_main_single_include, vk_present_wait_stage); no new
failures. full_function_emitter_test and l2_structural_test both pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>