79 lines
3 KiB
Python
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)
|