mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -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>
67 lines
2.0 KiB
Python
67 lines
2.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Pull all 128 KiB of cards[slot].data via card_buffer_dump and sha256-compare to disk.
|
|
|
|
Usage: python tools/verify_card_buffer.py
|
|
"""
|
|
import socket
|
|
import json
|
|
import hashlib
|
|
import sys
|
|
|
|
PORT = 4370
|
|
HOST = "127.0.0.1"
|
|
CHUNK = 0x4000 # 16 KiB per request
|
|
SIZE = 0x20000 # 128 KiB
|
|
|
|
def send_cmd(req):
|
|
s = socket.socket(); s.settimeout(5.0); s.connect((HOST, PORT))
|
|
s.sendall((json.dumps(req) + "\n").encode())
|
|
buf = b""
|
|
while True:
|
|
chunk = s.recv(65536)
|
|
if not chunk: break
|
|
buf += chunk
|
|
if buf.rstrip().endswith(b"}"):
|
|
try:
|
|
obj = json.loads(buf.decode().strip())
|
|
s.close()
|
|
return obj
|
|
except json.JSONDecodeError:
|
|
pass
|
|
s.close()
|
|
return json.loads(buf.decode().strip())
|
|
|
|
def fetch_card(slot):
|
|
h = hashlib.sha256()
|
|
raw = bytearray()
|
|
off = 0
|
|
while off < SIZE:
|
|
n = min(CHUNK, SIZE - off)
|
|
resp = send_cmd({"id": 1, "cmd": "card_buffer_dump",
|
|
"slot": slot, "offset": off, "len": n})
|
|
if not resp.get("ok"):
|
|
print(f"slot {slot} offset {off}: ERROR {resp}")
|
|
return None
|
|
chunk = bytes.fromhex(resp["hex"])
|
|
if len(chunk) != n:
|
|
print(f"slot {slot} offset {off}: short read {len(chunk)} != {n}")
|
|
raw.extend(chunk); h.update(chunk); off += len(chunk)
|
|
if len(chunk) == 0:
|
|
break
|
|
return bytes(raw), h.hexdigest()
|
|
|
|
def disk_sha(path):
|
|
with open(path, "rb") as f:
|
|
return hashlib.sha256(f.read()).hexdigest()
|
|
|
|
if __name__ == "__main__":
|
|
paths = ["card1.mcd", "card2.mcd"]
|
|
for slot, path in enumerate(paths):
|
|
runtime_data, runtime_sha = fetch_card(slot) or (b"", None)
|
|
disk = disk_sha(path)
|
|
match = "MATCH" if runtime_sha == disk else "DIFFER"
|
|
print(f"slot {slot} ({path}):")
|
|
print(f" disk sha256: {disk}")
|
|
print(f" runtime sha256: {runtime_sha}")
|
|
print(f" -> {match}")
|