mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
8b0fc34714
Real card protocol restarts on every fresh 0x81 — SELECT deassert is
a bus-wide reset signal. Earlier model preserved mc_state across
deassert via mc_save_slot, which caused every subsequent 0x81 to land
on a stale non-IDLE saved state and trigger abort_reselect bookkeeping.
Audit confirmed the BIOS-visible RX was still correct (the abort path
itself reset mc_state), but the retained sector/cmd/counter context
across logically-independent transactions was wrong per no$psx.
Clear volatile protocol state on SELECT deassert (state, cmd, sector,
sector_msb, sector_lsb, data_idx, checksum on both slots). Preserve
mc_slots[].flag (persistent "new card" metadata) and mc_slots[].data
(sector buffer, harmless to retain).
Result on a 4-press boot run: 6 transactions reach READ_DATA (ts=10)
and successfully read the "MC" header magic (RX bytes 0x4D 0x43);
remaining txns continue as 4-byte presence pings. Pre-fix: 0 reads
of card data ever; post-fix: card protocol can complete the setup
phase and enter data phase.
The byte-15 abort that follows is a separate blocker — kernel chain
dispatcher interleaves pad polling into the card data read. Tracked
for next session via the chain dispatcher disassembly.
Also includes:
- Slot-state capture in SioTraceEntry + sio_trace JSON emission
- dirty_ram_interp: replace mid-block fprintf+fflush with a counter
(CLAUDE.md §3 — synchronous Windows console I/O at high rate
starves the dispatch loop)
- Passive Python TCP probes (raw_tcp, trace_audit, trace_range,
txn_summary, txn_dist, txn_bytes, drs_filter, readram_test,
step_audit{,2}, verify_card_buffer, wtrace_filter)
- docs/MEMCARD_AUDIT_2026_04_28.md (prior session's 7-layer audit)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
81 lines
2.2 KiB
Python
81 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Send a raw JSON command and dump the raw response bytes.
|
|
|
|
Usage: python tools/raw_tcp.py <port> <cmd> [k=v ...]
|
|
Example: python tools/raw_tcp.py 4370 card_txn_dump
|
|
python tools/raw_tcp.py 4370 read_ram addr=0x80007568 len=16
|
|
"""
|
|
import socket
|
|
import sys
|
|
import json
|
|
|
|
if len(sys.argv) < 3:
|
|
print(__doc__)
|
|
sys.exit(1)
|
|
|
|
port = int(sys.argv[1])
|
|
cmd = sys.argv[2]
|
|
req = {"id": 1, "cmd": cmd}
|
|
for kv in sys.argv[3:]:
|
|
k, v = kv.split("=", 1)
|
|
# Keep hex strings as strings (server's read_ram etc. expect "addr":"0x..").
|
|
# Decimal-looking values become ints.
|
|
if v.startswith("0x") or v.startswith("0X"):
|
|
pass # leave as string
|
|
else:
|
|
try:
|
|
v = int(v)
|
|
except ValueError:
|
|
pass
|
|
req[k] = v
|
|
|
|
s = socket.socket()
|
|
s.settimeout(5.0)
|
|
s.connect(("127.0.0.1", port))
|
|
s.sendall((json.dumps(req) + "\n").encode())
|
|
data = b""
|
|
depth_c = 0
|
|
depth_s = 0
|
|
in_str = False
|
|
esc = False
|
|
started = False
|
|
done = False
|
|
try:
|
|
while not done:
|
|
chunk = s.recv(65536)
|
|
if not chunk:
|
|
break
|
|
for b in chunk:
|
|
data += bytes([b])
|
|
ch = chr(b)
|
|
if in_str:
|
|
if esc: esc = False
|
|
elif ch == "\\": esc = True
|
|
elif ch == '"': in_str = False
|
|
continue
|
|
if ch == '"': in_str = True
|
|
elif ch == "{": depth_c += 1; started = True
|
|
elif ch == "}": depth_c -= 1
|
|
elif ch == "[": depth_s += 1
|
|
elif ch == "]": depth_s -= 1
|
|
if started and depth_c == 0 and depth_s == 0:
|
|
done = True
|
|
break
|
|
except socket.timeout:
|
|
pass
|
|
s.close()
|
|
print(f"=== raw bytes (len={len(data)}) ===")
|
|
sys.stdout.write(data.decode("utf-8", errors="replace"))
|
|
print()
|
|
print("=== json parse attempt ===")
|
|
try:
|
|
obj = json.loads(data.decode().strip())
|
|
print("OK keys:", list(obj.keys()) if isinstance(obj, dict) else type(obj))
|
|
except Exception as e:
|
|
print(f"FAIL: {e}")
|
|
txt = data.decode("utf-8", errors="replace")
|
|
if hasattr(e, "pos"):
|
|
lo, hi = max(0, e.pos - 30), min(len(txt), e.pos + 30)
|
|
print(f"context [{lo}..{hi}]: {txt[lo:hi]!r}")
|
|
print(f"byte at pos {e.pos}: {txt[e.pos]!r}")
|