mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-27 10:38:46 -04:00
e21fe2d59b
An extra actor's saved bytes are now copied INTO the storage the game keeps its player in for the duration of that actor's pass, then copied back out. Every access then finds the right actor however it is addressed: through a pointer, absolute, $gp, a pointer some global cached frames ago, or from streamed overlay code the recompiler never saw. That deletes [coop] actor_base_sites entirely. Enumerating every instruction that forms the actor pointer needed per-game knowledge the list could never be proven complete against, and silently missed whatever it did not cover: on MMX6 damage landed on player 1 until one more site was found by watching a companion get chewed on off-camera. config_loader now THROWS on the retired key rather than ignoring it, so a config written against the old model cannot quietly stop working. Per-actor state the engine keeps outside the struct is declared with psx_coop_add_swap_region() / TCP coop_region, so extending the swap set never needs new generated code. Which regions those are is a measurement, not a guess: every recorded write now carries the actor context that made it. Two bugs fixed along the way: - The replay hook returned to the jal address (delay slot - 4). Only BLOCK LEADERS are dispatch entries, and the jal is one only when it happens to sit at a previous call's return address. MMX6's sprite-submission stage has its jal at 0x80020D00 inside block_80020CF8, so that stage's replay had never actually been happening. The generator now emits the enclosing leader. - Input delivery no longer writes a shared input global behind player 1's back (which left player 1 reading zero while a direction was held). Instead psx_coop_set_raw_pad() puts each actor's RAW pad where the game's controller poll leaves it, inside a declared swap region (enforced), and the engine's own replayed pad stage decodes it. That matters because engines do not store the pad in SIO bit order: MMX6 remaps every button, so an actor fed SIO-order bits reads LEFT as JUMP. Wire order (byteswap of the inverted SIO word) is the console's pad transfer format, not a per-game table. Observability, all found by a measurement lying rather than by an audit: - Enhancement-scratch stores were outside every write hook, so a RAM-claim report said "nothing written here" about a region written every frame. - The RAM read watch saw only 32-bit reads, so a 16-bit field read with LHU produced zero hits, and it carried no PC at all. Block leaders now stamp g_debug_last_block, which the .ranges manifest resolves to a function. - The read watch could only be armed from a boot-time environment variable, i.e. decide-what-to-watch-before-the-run. Added a live read_watch command. - read_ram/write_ram raced the swap: the debug thread landed on whichever actor was momentarily resident, which manufactured false conclusions rather than noise. psx_coop_observe_ptr resolves an address to its owner's bytes. Co-op off remains byte-identical: omit the [coop] block and nothing is generated; leave the runtime unconfigured and every hook is inert. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>