mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 10:19:47 -04:00
86 lines
3.1 KiB
Python
86 lines
3.1 KiB
Python
"""Compare runtime (4470) vs DuckStation (4371) at OPTIONS.
|
|
Handles scratchpad correctly on both sides.
|
|
Samples values over 3 seconds to spot static-vs-dynamic differences."""
|
|
import socket, json, time, struct, sys
|
|
|
|
def call(port, cmd, **kw):
|
|
try:
|
|
s = socket.create_connection(('127.0.0.1', port), timeout=3)
|
|
s.sendall((json.dumps({'id':1,'cmd':cmd, **kw})+'\n').encode())
|
|
buf = b''
|
|
while not buf.endswith(b'\n'):
|
|
c = s.recv(65536)
|
|
if not c: break
|
|
buf += c
|
|
s.close()
|
|
return json.loads(buf.decode())
|
|
except Exception as e:
|
|
return {'error': str(e)}
|
|
|
|
def read_word(port, addr):
|
|
"""Read a 32-bit LE word, handling scratchpad correctly on DuckStation."""
|
|
is_scratchpad = (addr & 0xFFFFE000) == 0x1F800000
|
|
if port == 4371 and is_scratchpad:
|
|
r = call(port, 'read_scratch', addr=f'0x{(addr & 0x3FF):04X}', len=4)
|
|
else:
|
|
r = call(port, 'read_ram', addr=f'0x{addr:08X}', len=4)
|
|
if 'error' in r or not r.get('ok'):
|
|
return None
|
|
return int.from_bytes(bytes.fromhex(r['hex']), 'little')
|
|
|
|
def ping(port):
|
|
return call(port, 'ping')
|
|
|
|
# Verify both alive
|
|
rt = ping(4470)
|
|
duck = ping(4371)
|
|
print(f"runtime: {rt}")
|
|
print(f"duckstation: {duck}")
|
|
print()
|
|
|
|
if 'error' in rt or 'error' in duck:
|
|
print("Both backends must be running & at OPTIONS. Re-launch any that errored.")
|
|
sys.exit(1)
|
|
|
|
# Static comparison — values that should NOT change frame to frame
|
|
print("=== STATIC values (these should match if both at same state) ===")
|
|
print(f"{'addr':>14} {'runtime':>12} {'duckstation':>12} {'match':>6}")
|
|
for addr, label in [
|
|
(0x1F8001D4, "scratchpad[0x1D4] state struct ptr"),
|
|
(0x80097520, "fn ptr table+0"),
|
|
(0x80097524, "fn ptr table+4 (state-0 helper)"),
|
|
(0x80097528, "fn ptr table+8"),
|
|
(0x8009752C, "fn ptr table+C"),
|
|
(0x80000108, "kernel TCB list ptr"),
|
|
(0x80000110, "kernel TCBH"),
|
|
(0x80000120, "kernel EvCB ptr"),
|
|
]:
|
|
rt_v = read_word(4470, addr)
|
|
du_v = read_word(4371, addr)
|
|
if rt_v is None or du_v is None:
|
|
print(f" 0x{addr:08X} err err ? {label}")
|
|
continue
|
|
m = "OK" if rt_v == du_v else "DIFF"
|
|
print(f" 0x{addr:08X} 0x{rt_v:08X} 0x{du_v:08X} {m:>6} {label}")
|
|
print()
|
|
|
|
# Dynamic comparison — sample 10 times, see which values CHANGE
|
|
print("=== DYNAMIC values (sample 10 frames; see who's animating) ===")
|
|
addrs = [
|
|
(0x8009B3D4, "state-0 helper struct +0x34"),
|
|
(0x8009B3A0, "state-0 helper struct base"),
|
|
]
|
|
|
|
for addr, label in addrs:
|
|
print(f"\n Address 0x{addr:08X} ({label}):")
|
|
rt_vals = []
|
|
du_vals = []
|
|
for i in range(10):
|
|
rt_vals.append(read_word(4470, addr))
|
|
du_vals.append(read_word(4371, addr))
|
|
time.sleep(0.05)
|
|
rt_unique = len(set(rt_vals))
|
|
du_unique = len(set(du_vals))
|
|
print(f" runtime values: {[hex(v) if v is not None else '?' for v in rt_vals]} ({rt_unique} unique --> {'FROZEN' if rt_unique == 1 else 'animating'})")
|
|
print(f" duck values: {[hex(v) if v is not None else '?' for v in du_vals]} ({du_unique} unique --> {'FROZEN' if du_unique == 1 else 'animating'})")
|