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>
71 lines
3.1 KiB
C
71 lines
3.1 KiB
C
#ifndef PSXRECOMP_PSX_RUNTIME_H
|
|
#define PSXRECOMP_PSX_RUNTIME_H
|
|
|
|
#include "cpu_state.h"
|
|
#include "debug_server.h"
|
|
#include "interrupts.h"
|
|
#include "psx_cycles.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* Fix B exception-return escape state + helpers are declared in cpu_state.h
|
|
* (the lowest common header the generated BIOS/game both include). */
|
|
|
|
static inline void call_by_address(CPUState* cpu, uint32_t addr) {
|
|
psx_dispatch_call(cpu, addr, cpu->gpr[31]);
|
|
}
|
|
|
|
/* ---- Enhancement scratch RAM (generic, DEFAULT OFF; see runtime/src/memory.c)
|
|
*
|
|
* Guest-addressable memory that the game can never touch, for enhancements
|
|
* that need somewhere to park state. Mapped ABOVE the 4x main-RAM mirror
|
|
* (physical >= 0x00800000), a window that is unmapped on real hardware and
|
|
* open-bus here — so unlike a "free" hole inside main RAM it cannot be
|
|
* overwritten by the game (notably not by CD-DMA stage loads, which is exactly
|
|
* what invalidated a hand-picked reservation inside MMX6's heap->stack gap).
|
|
*
|
|
* Disabled until a game opts in, and the checks live off the main-RAM fast
|
|
* path, so a game that never calls configure() is byte-identical.
|
|
*
|
|
* Guest address = 0x80000000 | phys_base (KSEG0) for the configured window. */
|
|
int psx_enh_scratch_configure(uint32_t phys_base, uint32_t len);
|
|
void psx_enh_scratch_reset(void);
|
|
uint8_t *psx_enh_scratch_ptr(uint32_t *out_base, uint32_t *out_len);
|
|
int psx_enh_scratch_contains(uint32_t phys);
|
|
|
|
/* Host pointer for a guest DATA block in main RAM or the scratch window, or
|
|
* NULL if it does not lie wholly inside one. For bulk block moves an
|
|
* enhancement makes on its own behalf; guest-visible accesses still go through
|
|
* the psx_read / psx_write accessors, which carry the write-trace fan-out. */
|
|
uint8_t *psx_guest_block_ptr(uint32_t addr, uint32_t len);
|
|
|
|
/* ---- Simultaneous extra player actors (generic, DEFAULT OFF; coop.c) -------
|
|
*
|
|
* For an engine that runs exactly one player actor through a per-frame stage
|
|
* pipeline. Each stage declared in [coop] replay_sites is replayed once per
|
|
* extra actor, with that actor's saved state SWAPPED INTO the storage the game
|
|
* keeps its player in — so every access finds the right actor however it is
|
|
* addressed, including from streamed overlay code the recompiler never saw.
|
|
*
|
|
* Per-actor state the engine keeps outside that struct (an animation object, a
|
|
* pad-word global) is declared with psx_coop_add_swap_region() and swapped
|
|
* along with it, so extending the swap set never needs new generated code. */
|
|
int psx_coop_configure(uint32_t primary_base, uint32_t struct_len,
|
|
int companions, uint32_t scratch_phys_base);
|
|
int psx_coop_add_swap_region(uint32_t guest_base, uint32_t len);
|
|
void psx_coop_set_gate(int (*gate)(void));
|
|
void psx_coop_set_gate_state(uint32_t mode_addr, uint8_t mode_val,
|
|
const uint32_t *zero_addrs, int zero_count);
|
|
void psx_coop_set_input_layout(uint32_t cur_off, uint32_t edge_off, int enabled);
|
|
int psx_coop_spawn(int idx);
|
|
void psx_coop_despawn(int idx);
|
|
void psx_coop_reset(void);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* PSXRECOMP_PSX_RUNTIME_H */
|