Files
psxrecomp/tools/inspect_dispatch_ring.py
Matthew Stanley 87d99d4f1d seeds: 5 dispatch_miss entries for chain writer PCs (vertical slice partial)
Adds the Beetle-discovered chain writer/init PCs as dispatch_miss seeds
so the recompiler emits standalone functions for them at their RAM
destination addresses (via the existing Phase 1e ROM↔RAM alias rule
RAM = ROM - 0xBFC10000 + 0x500):

  0xBFC14A54 → 0x00004F54 (chain BUSY-writer)
  0xBFC158D0 → 0x00005DD0 (chain init slot-0 BUSY)
  0xBFC158D8 → 0x00005DD8 (chain init slot-1 BUSY)
  0xBFC159F4 → 0x00005EF4 (chain step 0x02-OPEN writer — KEY)
  0xBFC15AA8 → 0x00005FA8 (chain step 0x04-OPEN writer — KEY)

Post-regen dispatch_table contains all 5 as standalone entries. e.g.
func_00005EF4 is a clean 3-instruction leaf (sb $t3, 0($a3); v0=1; jr ra).

Validation (full output in docs/CASE_A_AOT_GAP.md "Vertical slice result"):
- ✓ dispatch_table contains all 5 entries
- ✗ none of the 5 are reached at runtime — chain still doesn't dispatch
- ✗ [0x80007510..0x7570] all-zero — the chain init function FUN_bfc158a8
  (= func_00005DA8) never runs in our recomp during this session, gates
  haven't even reached the initial 0x01 BUSY state
- ✓ shell IS on memcard screen ([0x80066948] = 0x32)

The seed addition is correct and necessary work — it removes one of two
layers of "writer is unreachable". The remaining layer is upstream:
nothing in our recomp causes FUN_bfc14b00 / FUN_bfc158a8 to run during
the memcard flow. That's a different category from "chain runs but
fails on byte N" and is not chased here per Case A directive.

Beetle wtrace also revealed the writers at 0x5EF4 / 0x5FA8 are tiny
leaf utilities called from BIOS code in BFC08{B98,C94,D08,F28} — NOT
chain-step jump-table targets as initially assumed. The chain step
tables at RAM 0x6c70/0x6c98/0x6ccc point at 0x52xx / 0x56xx-0x5Axx /
0x5Bxx-0x5Dxx ranges. Memo updated accordingly.

Includes 4 new validation scripts under tools/ used during this audit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:26:19 -07:00

41 lines
1.3 KiB
Python

"""Dump dispatch_tail and summarize."""
import socket, json
from collections import Counter
def call(d):
s = socket.create_connection(('127.0.0.1', 4370))
s.settimeout(15.0)
s.sendall((json.dumps(d) + '\n').encode())
buf = b''
while True:
try: chunk = s.recv(65536)
except socket.timeout: break
if not chunk: break
buf += chunk
depth = 0; instr = False; esc = False
for b in buf:
if esc: esc = False; continue
if b == 0x5C: esc = True; continue
if b == 0x22: instr = not instr; continue
if instr: continue
if b == 0x7B: depth += 1
elif b == 0x7D: depth -= 1
if depth == 0 and buf.strip(): break
s.close()
return json.loads(buf.decode())
r = call({'id': 1, 'cmd': 'dispatch_tail', 'count': '4096'})
addrs = r.get('addrs', [])
c = Counter(addrs)
print(f"total ring={r.get('total')} returned={len(addrs)} unique={len(c)}")
print()
print('top 30:')
for a, n in c.most_common(30):
print(f' {a} x{n}')
print()
print('Beetle writer PCs / chain coordinator presence in ring:')
for pc in ['0x00005EF4','0x00005FA8','0x00004F54','0x00005DD0','0x00005DD8',
'0x00005000','0x000051F4','0x00005688','0x00005DA8',
'0x00004D6C','0x0000445C','0x00006524']:
print(f' {pc}: x{c.get(pc, 0)}')