joeylib2/tools/stsim/compare.py

79 lines
3 KiB
Python

#!/usr/bin/env python3
# Compare stsim output against a VICE trace segment.
# compare.py <trace.jsonl> <startTick> <stsim-output.jsonl> [maxReports]
import json, sys
trace = [json.loads(l) for l in open(sys.argv[1])]
start = int(sys.argv[2])
sim = [json.loads(l) for l in open(sys.argv[3])]
maxRep = int(sys.argv[4]) if len(sys.argv) > 4 else 12
def fromTrace(r):
g = bytes.fromhex(r['g7140'])
pos = bytes.fromhex(r['pos'])
hud = bytes.fromhex(r['hud']) # 0x0799 .. 0x07E6
def G(a): return g[a - 0x7140]
def H(a): return hud[a - 0x0799]
d = {}
d['x'] = '%02X%02X.%02X' % (pos[9], pos[8], pos[7]) # 7D63 7D62 7D61
d['y'] = '%02X.%02X' % (pos[11], pos[10])
d['vx'] = '%02X%02X' % (G(0x714D), G(0x714C))
d['vy'] = '%02X%02X' % (G(0x714F), G(0x714E))
d['pad'] = G(0x7150)
d['st'] = G(0x7163)
d['ph'] = G(0x7164)
d['in'] = '%02X' % G(0x7169)
d['dir'] = '%02X' % G(0x716A)
d['ptr0'] = '%02X' % G(0x7197)
d['ptr1'] = '%02X' % G(0x7198)
d['s1'] = '%02X%02X.%02X' % (G(0x7186), G(0x7176), G(0x717E))
d['en'] = '%02X' % G(0x7196)
d['t4740']= r['t4740'].upper()
d['rng'] = '%02X%02X' % (G(0x7171), G(0x7172))
d['fuel'] = G(0x71C9)
d['slots']= G(0x715C)
d['asi'] = pos[0x7D8E - 0x7D5A] if len(pos) > 0x7D8E - 0x7D5A else -1
d['score']= ''.join('%02X' % H(0x07C2 + k) for k in range(7))
d['fare'] = ''.join('%02X' % H(0x07E0 + k) for k in range(7))
d['fuelc']= ''.join('%02X' % H(0x07A6 + k) for k in range(12))
# sprite shadow positions $71A7.. interleaved X,Y for 8 sprites + msb $718D
msb = G(0x718D)
sx = ''
for k in range(8):
x = G(0x71A7 + 2*k) | (0x100 if (msb >> k) & 1 else 0)
sx += '%03X%02X' % (x, G(0x71A8 + 2*k))
d['sprx'] = sx
return d
fields = ['x','y','vx','vy','pad','st','ph','in','dir','ptr0','ptr1','s1','en','t4740','rng','fuel','slots','score','fare','fuelc','sprx']
reports = 0
firstBad = None
for s in sim:
if 'end' in s:
print('sim ended at tick', s['tick'], 'result', s['end'])
break
t = s['tick']
if start + t >= len(trace):
print('trace exhausted at sim tick', t); break
r = fromTrace(trace[start + t])
# Disabled sprites keep stale shadow values on the C64; only compare
# positions/pointers of enabled ones.
en = int(r['en'], 16)
def spr(sx):
return ''.join(sx[k*5:k*5+5] if (en >> k) & 1 else '-----' for k in range(8))
s2 = dict(s); r2 = dict(r)
s2['sprx'] = spr(s['sprx']); r2['sprx'] = spr(r['sprx'])
if not (en & 2):
s2['ptr1'] = r2['ptr1'] = '--'; s2['s1'] = r2['s1'] = '--'
bad = [f for f in fields if f in s2 and str(s2[f]) != str(r2[f])]
s = s2; r = r2
if bad:
if firstBad is None:
firstBad = t
if reports < maxRep:
print(f'tick {t}: ' + ' '.join(f'{f}: sim={s[f]} c64={r[f]}' for f in bad))
reports += 1
if firstBad is None:
print('ALL', len(sim), 'ticks match')
else:
print('first mismatch at tick', firstBad)