Files
psxrecomp/tools/dump_sio_trace.py
T
Matthew Stanley 711d22cff3 fix: per-slot card SIO state + card-specific IRQ delay
The SIO model had a single global mc_state shared between both card slots,
which caused 100% probe abort rate when the BIOS alternates between slot 0
and slot 1: each new 0x81 probe on the other slot would abort the in-flight
state from the previous slot.

Changes:
- Per-slot McSlotState saved/restored on slot switch via mc_save_slot/mc_load_slot
- 0x01 (pad select) preserves card state instead of aborting it
- SIO_IRQ_DELAY split into PAD (4) and CARD (8) — card delay tuned so the
  SIO chain walker can dispatch func_00004D6C before next idle path tick
  clears the protocol counter

Diagnostic tooling:
- I_MASK[7] trace ring buffer with bit-7 set/clear counts and caller info
- Counter (0x7514) field added to SIO trace so card protocol state machine
  progress is visible
- check_card.py inspects card directory contents

Runtime evidence: mc_aborts went from 100% → 0% (when slots don't overlap).
mc_max_state advanced from 1 (only MC_CMD) to 8 (MC_READ_DATA). dispatch_check
confirms _new_card AND its body 0x5688 are dispatched. Detection callback
(func_00005D94 at BFC15894) verified to clear v0=0 before return — continue
path of func_00004D6C IS being taken (133 I_MASK[7] sets observed).

Remaining: 0x7550 stays NULL despite _new_card being dispatched. Either
shell passes NULL buffer or early-return path of _new_card triggers.

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

45 lines
1.4 KiB
Python

#!/usr/bin/env python3
"""Dump SIO trace from debug server."""
import json, sys, socket, time
def to_int(v):
if isinstance(v, int):
return v
if isinstance(v, str):
return int(v, 0)
return int(v)
def main():
count = int(sys.argv[1]) if len(sys.argv) > 1 else 256
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('localhost', 4370))
s.sendall(('{"cmd":"sio_trace","count":%d}\n' % count).encode())
time.sleep(1.0)
buf = b''
while True:
chunk = s.recv(65536)
if not chunk:
break
buf += chunk
try:
data = json.loads(buf)
break
except json.JSONDecodeError:
continue
s.close()
for e in data.get('entries', []):
abort = ' <<ABORT>>' if e.get('abort') or e.get('was_abort') else ''
exc = 'EXC' if e.get('in_exc', 0) else ' '
ctr = to_int(e.get('ctr', 0))
func = to_int(e['func'])
# Only show card bytes (func >= 0x5000) or bytes near them
print('seq=%6d tx=0x%02X rx=0x%02X mc=%d->%d dev=%d->%d ctrl=0x%04X func=0x%08X ctr=%d %s%s' % (
to_int(e['seq']), to_int(e['tx']), to_int(e['rx']),
to_int(e['mc_pre']), to_int(e['mc_post']),
to_int(e['dev_pre']), to_int(e['dev_post']),
to_int(e['ctrl']), func,
ctr, exc, abort))
if __name__ == '__main__':
main()