mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 18:25:35 -04:00
pgxp: emit hook macros + dual build targets + flavor-namespaced caches (phase 2)
Emitter side of the value-propagation engine (ENHANCEMENTS.md G1.10):
- recompiler/src/pgxp_hook_emitter.{h,cpp} (new, in the codegen-hash
surface): ONE hook grammar shared by BOTH emitters, wrapping loads/
stores/COP2 transfers and precision-carrying ALU ops (moves, add/sub,
or-merge, shifts, LUI, HI/LO, muldiv) with PGXP_*() macros. Clobberable
sources are captured into locals before the statement. Precision-
destroying ops (AND/XOR/SLT/...) are deliberately unhooked - validate-
on-read drops their stale shadows for free.
- code_generator.cpp: central append in translate_instruction; LWC2/SWC2
emissions capture the RAW loaded/stored word (a masked GTE register
write must validate against the word as loaded).
- strict_translator.cpp: translate() wraps translate_impl()'s c_code with
the shared grammar; deferred load-delay variants hook the psx_ldd temp.
- BIOS regenerated, fingerprints verified CHANGED (SCPH1001 + OpenBIOS;
42.6k/12.2k macro sites). Base runtime + 33/33 tests green against it.
- Gating: macros expand only under -DPSX_PGXP=1. runtime.cmake grows a
PGXP option on psxrecomp_add_runtime_target (defines PSX_PGXP=1 +
PSX_OVERLAY_FLAVOR=2, _pgxp exe suffix) and PSX_PGXP_VARIANT to build
the sibling target from the same generated tree (framework runtime +
game wrapper). No ABI version bump needed: the appended PGXPHooks
table is NULL-tolerant and the flavor half of the ABI tag rejects any
host/DLL flavor mix.
- Shard caches: flavor folded into the cache tag (cg<V>_<hash>_gc<hash>
_f<flavor>) in overlay_loader.c + compile_overlays.py so base and pgxp
caches never share a directory; compile_overlays.py passes
-DPSX_PGXP=1 when flavor bit 2 is set; overlay preamble forwards
psx_pgxp_* through the table. PSX_OVERLAY_CODEGEN_VER 9 -> 10.
- Env overrides PSX_GEOMETRY_CORRECTION / PSX_PERSPECTIVE_TEXTURING /
PSX_PGXP_CPU_MODE (validation path: free-running headless boots arm
from the first vertex); census grew produced/swc2_stores and the pgxp
verb reports suppress/active gate state.
Validated: PSXRecomp_pgxp.exe boots the recompiled BIOS with hooks armed
(engine active, per-vertex lookups flowing; the BIOS shell has no RTPS
content so produced=0 there is correct - coverage measurement moves to a
3D title in phase 3).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#include "code_generator.h"
|
||||
#include "pgxp_hook_emitter.h"
|
||||
#include "control_flow.h"
|
||||
#include "gte_register_classification.h"
|
||||
#include "../src/bios_address_model.h"
|
||||
@@ -1572,12 +1573,19 @@ std::string CodeGenerator::translate_instruction(uint32_t addr, uint32_t instr)
|
||||
? reg_name(rs)
|
||||
: fmt::format("{} + {}", reg_name(rs), offset);
|
||||
bool special = PSXRecompGTERegisters::data_write_needs_helper(static_cast<uint8_t>(rt));
|
||||
/* The raw loaded word is captured for the PGXP hook: the
|
||||
* register may hold a MASKED value (write helper), and
|
||||
* the shadow must validate against the word as loaded. */
|
||||
if (special) {
|
||||
code = gte_stall + fmt::format("gte_write_data(cpu, {}, psx_cyc_lwc2_read(cpu, {})); /* lwc2 gte[{}] */",
|
||||
rt, addr, rt);
|
||||
code = gte_stall + fmt::format(
|
||||
"{{ uint32_t _pgxa = {}; uint32_t _pgxv = psx_cyc_lwc2_read(cpu, _pgxa); "
|
||||
"gte_write_data(cpu, {}, _pgxv); PGXP_COP2(0x{:08X}u, _pgxv, _pgxa); }} /* lwc2 gte[{}] */",
|
||||
addr, rt, instr, rt);
|
||||
} else {
|
||||
code = gte_stall + fmt::format("cpu->gte_data[{}] = psx_cyc_lwc2_read(cpu, {}); /* lwc2 gte[{}], ({}) */",
|
||||
rt, addr, rt, addr);
|
||||
code = gte_stall + fmt::format(
|
||||
"{{ uint32_t _pgxa = {}; uint32_t _pgxv = psx_cyc_lwc2_read(cpu, _pgxa); "
|
||||
"cpu->gte_data[{}] = _pgxv; PGXP_COP2(0x{:08X}u, _pgxv, _pgxa); }} /* lwc2 gte[{}], ({}) */",
|
||||
addr, rt, instr, rt, addr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1593,16 +1601,15 @@ std::string CodeGenerator::translate_instruction(uint32_t addr, uint32_t instr)
|
||||
? fmt::format("gte_read_data(cpu, {})", rt)
|
||||
: fmt::format("cpu->gte_data[{}]", rt);
|
||||
std::string swc2_store_pc = fmt::format("g_debug_last_store_pc = 0x{:08X}u; ", addr);
|
||||
if (offset == 0) {
|
||||
code = gte_stall + swc2_store_pc + fmt::format(
|
||||
"psx_store_cycle_barrier(); cpu->write_word({}, {}); gte_precision_store_word({}, {}); /* swc2 gte[{}], ({}) */",
|
||||
reg_name(rs), value, reg_name(rs), rt, rt, reg_name(rs));
|
||||
} else {
|
||||
code = gte_stall + swc2_store_pc + fmt::format(
|
||||
"psx_store_cycle_barrier(); cpu->write_word({} + {}, {}); gte_precision_store_word({} + {}, {}); /* swc2 gte[{}], {}({}) */",
|
||||
reg_name(rs), offset, value, reg_name(rs), offset, rt,
|
||||
rt, offset, reg_name(rs));
|
||||
}
|
||||
std::string swc2_addr = (offset == 0)
|
||||
? reg_name(rs)
|
||||
: fmt::format("{} + {}", reg_name(rs), offset);
|
||||
code = gte_stall + swc2_store_pc + fmt::format(
|
||||
"{{ uint32_t _pgxa = {}; uint32_t _pgxv = {}; "
|
||||
"psx_store_cycle_barrier(); cpu->write_word(_pgxa, _pgxv); "
|
||||
"gte_precision_store_word(_pgxa, {}); "
|
||||
"PGXP_COP2(0x{:08X}u, _pgxv, _pgxa); }} /* swc2 gte[{}], {}({}) */",
|
||||
swc2_addr, value, rt, instr, rt, offset, reg_name(rs));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -1610,6 +1617,7 @@ std::string CodeGenerator::translate_instruction(uint32_t addr, uint32_t instr)
|
||||
}
|
||||
}
|
||||
|
||||
PSXRecomp::append_pgxp_hooks(instr, code);
|
||||
return config_.indent + code + comment;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user