Files
psxrecomp/tools/check_postfix_state.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

50 lines
1.8 KiB
Python

"""Check current state: gate writes, interp histogram, recent dispatch."""
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: c = s.recv(65536)
except socket.timeout: break
if not c: break
buf += c
if buf.count(b'{') == buf.count(b'}') and buf.strip(): break
s.close()
return json.loads(buf.decode())
print('=== ping ===')
print(call({'id': 0, 'cmd': 'ping'}))
print()
print('=== gate writes (wtrace) ===')
r = call({'id': 1, 'cmd': 'wtrace_dump'})
entries = r.get('entries', [])
gate = [e for e in entries if int(e.get('addr', '0x0'), 16) in (0x7568, 0x7569, 0x756A, 0x756B)]
print(f'total wtrace entries={len(entries)} gate hits={len(gate)}')
for e in gate[:30]:
print(f" seq={e.get('seq')} addr={e.get('addr')} new={e.get('new_val')} "
f"ra={e.get('ra')} func={e.get('func_addr')} frame={e.get('frame')}")
print()
print('=== dirty_ram_stats (post-CROSS interp histogram) ===')
r = call({'id': 2, 'cmd': 'dirty_ram_stats'})
print(f"blocks_run={r['blocks_run']} insns_run={r['insns_run']} "
f"aborts={r['aborts']} dirty_bitmap={r['dirty_bitmap']}")
for p in sorted(r.get('per_pc', []), key=lambda e: int(e['pc'], 16)):
print(f" pc={p['pc']:>10} hits={p['hits']:>10} insns={p['insns']:>10}")
print()
print('=== shell state machine ([0x80066940..0x80066954) ===')
r = call({'id': 3, 'cmd': 'read_ram', 'addr': '0x80066940', 'len': 20})
print(r)
print()
print('=== FUN_bfc14b00 chain-coord dispatch_check ===')
for pc in [0x4D6C, 0x5000, 0x51F4, 0x5688, 0x5DA8, 0x5EF4, 0x5FA8, 0x4F54]:
r = call({'id': 100, 'cmd': 'dispatch_check', 'addr': f'0x{pc:08X}'})
print(f" PC=0x{pc:08X}: found={r.get('found')} total={r.get('total')}")