gpu/a0_history: capture true LoadImage source pointer (s2), not just the RECT

a0_history recorded $s1 and labeled it "source data pointer", but for the MMX
LoadImage (FUN_80069dfc) $s1 is the scratch RECT descriptor (x/y/w/h, reused
every call) — the true pixel source is $a1, preserved in $s2 (`move s2,a1` at
entry). The mislabel sent font-load investigation to the wrong buffer.

Capture s2 (gpr[18]) = true source, plus a0/a1, expose via gpu_get_a0_src, and
emit s2/a0/a1 in the a0_history JSON. This is what let us find the real MMX5
font source buffers (0x800D5208 / 0x800DD208) and prove the upload reads them
during a post-clear/pre-CD-load zero window.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Matthew Stanley
2026-07-04 12:55:21 -07:00
parent 4c7451d829
commit c7a7a67d0e
2 changed files with 23 additions and 3 deletions
+5 -2
View File
@@ -5028,6 +5028,7 @@ extern int gpu_get_a0_history(int index, int *x, int *y, int *w, int *h,
uint32_t *fw0, uint32_t *fw1, int *wcount);
extern int gpu_get_a0_extra(int index, uint32_t *func, uint32_t *sp, uint32_t *ra,
uint32_t *s1, uint32_t *stack10);
extern int gpu_get_a0_src(int index, uint32_t *s2, uint32_t *a0, uint32_t *a1);
static void handle_a0_history(int id, const char *json)
{
@@ -5041,18 +5042,20 @@ static void handle_a0_history(int id, const char *json)
for (int i = 0; i < count && pos < bufsz - 500; i++) {
int x, y, w, h, wcount;
uint32_t fw0, fw1, func, sp, ra, s1, stk[10];
uint32_t s2 = 0, a0r = 0, a1r = 0;
gpu_get_a0_history(i, &x, &y, &w, &h, &fw0, &fw1, &wcount);
gpu_get_a0_extra(i, &func, &sp, &ra, &s1, stk);
gpu_get_a0_src(i, &s2, &a0r, &a1r);
pos += snprintf(buf + pos, bufsz - pos,
"%s{\"i\":%d,\"x\":%d,\"y\":%d,\"w\":%d,\"h\":%d,"
"\"fw0\":\"0x%08X\",\"fw1\":\"0x%08X\",\"words\":%d,"
"\"func\":\"0x%08X\",\"sp\":\"0x%08X\",\"ra\":\"0x%08X\","
"\"s1\":\"0x%08X\","
"\"s1\":\"0x%08X\",\"s2\":\"0x%08X\",\"a0\":\"0x%08X\",\"a1\":\"0x%08X\","
"\"stk\":[\"0x%08X\",\"0x%08X\",\"0x%08X\",\"0x%08X\","
"\"0x%08X\",\"0x%08X\",\"0x%08X\",\"0x%08X\","
"\"0x%08X\",\"0x%08X\"]}",
i ? "," : "", i, x, y, w, h, fw0, fw1, wcount,
func, sp, ra, s1,
func, sp, ra, s1, s2, a0r, a1r,
stk[0], stk[1], stk[2], stk[3], stk[4], stk[5], stk[6], stk[7],
stk[8], stk[9]);
}
+18 -1
View File
@@ -2142,7 +2142,10 @@ typedef struct {
uint32_t func_addr;
uint32_t sp_val; /* CPU $sp at time of A0 header */
uint32_t ra_val; /* CPU $ra register at time of A0 header */
uint32_t s1_val; /* CPU $s1 = source data pointer in func_1FC38524 */
uint32_t s1_val; /* CPU $s1 = LoadImage RECT ptr (x/y/w/h) in FUN_80069dfc */
uint32_t s2_val; /* CPU $s2 = TRUE source pixel ptr (`move s2,a1` at entry) */
uint32_t a0_val; /* CPU $a0 at capture (RECT arg; may be clobbered) */
uint32_t a1_val; /* CPU $a1 at capture (source arg; may be clobbered) */
uint32_t stack[10]; /* first 10 words from sp (sp+32=saved $s1, sp+36=saved $ra) */
} A0HistEntry;
static A0HistEntry a0_history[A0_HISTORY_CAP];
@@ -2172,6 +2175,14 @@ int gpu_get_a0_extra(int index, uint32_t *func, uint32_t *sp, uint32_t *ra,
memcpy(stack10, a0_history[index].stack, 10 * sizeof(uint32_t));
return 1;
}
/* True source pointer + arg regs for the LoadImage upload (s2 = pixel source). */
int gpu_get_a0_src(int index, uint32_t *s2, uint32_t *a0, uint32_t *a1) {
if (index < 0 || index >= a0_history_count) return 0;
*s2 = a0_history[index].s2_val;
*a0 = a0_history[index].a0_val;
*a1 = a0_history[index].a1_val;
return 1;
}
static void gp0_exec_cpu_to_vram(void) {
/* 0xA0 — CPU→VRAM Copy (header: 3 words)
@@ -2203,12 +2214,18 @@ static void gp0_exec_cpu_to_vram(void) {
a0_history[slot].sp_val = 0;
a0_history[slot].ra_val = 0;
a0_history[slot].s1_val = 0;
a0_history[slot].s2_val = 0;
a0_history[slot].a0_val = 0;
a0_history[slot].a1_val = 0;
memset(a0_history[slot].stack, 0, sizeof(a0_history[slot].stack));
if (debug_cpu_ptr) {
uint32_t sp = debug_cpu_ptr->gpr[29];
a0_history[slot].sp_val = sp;
a0_history[slot].ra_val = debug_cpu_ptr->gpr[31];
a0_history[slot].s1_val = debug_cpu_ptr->gpr[17]; /* $s1 */
a0_history[slot].s2_val = debug_cpu_ptr->gpr[18]; /* $s2 = source ptr */
a0_history[slot].a0_val = debug_cpu_ptr->gpr[4]; /* $a0 */
a0_history[slot].a1_val = debug_cpu_ptr->gpr[5]; /* $a1 */
uint32_t sp_phys = sp & 0x1FFFFFu;
for (int si = 0; si < 10 && sp_phys + (si + 1) * 4 <= 0x200000u; si++)
a0_history[slot].stack[si] = psx_read_word(sp + si * 4);