#!/usr/bin/env python3
"""Real native PP-display clear preserves both caller team pointers; old loss reproduced."""
from pathlib import Path
import argparse,subprocess,struct,os,json,hashlib
ap=argparse.ArgumentParser();ap.add_argument('rom',type=Path);ap.add_argument('output',type=Path);a=ap.parse_args();out=a.output.resolve();out.mkdir(parents=True,exist_ok=True);here=Path(__file__).resolve().parent;site=Path('/home/jdc/mount/truenas/www/dela.jdc.se/nhl94');asm=here.parent/'m68kasm.sh' if (here.parent/'m68kasm.sh').exists() else site/'tools/m68kasm.sh';core=here/'test_core.py' if (here/'test_core.py').exists() else site/'tools/experimental/test_core.py';rom=a.rom.read_bytes();assert rom[0x12b32:0x12b38]==bytes.fromhex('4ef9002dcc00')
ui=[0xfc00,0xdc00,0xe000,6,0xc000,6,0xf000,5,0x466,0x408,0,0x1d6,0,0x1e6,0x54d,0x3ff,0x1c8,0x4ea,0x28e,0x516,3,0x1a,0x8000,0,0,0xf6,0x5c]
cases=[(team,showing) for team in [0,1] for showing in [False,True]];body=[' .text',' .globl _start','_start:',' move.w #0x2700,%sr',' movea.l #0xFFF800,%sp']
for i,(team,showing) in enumerate(cases):
 body+=[' lea 0xFFFF0000,%a0',' move.w #0xF000/4-1,%d0',f'zero{i}: clr.l (%a0)+',f' dbra %d0,zero{i}']
 for j,v in enumerate(ui):body.append(f' move.w #{v},0x{0xffb000+2*j:X}')
 body += [f' move.b #{32 if showing else 0},0xFFFFC2EE',' move.w #1,0xFFFFBF78']
 for j in range(8):body.append(f' move.l #0x{0x10203040+j*0x01010101:X},%d{j}')
 for j in [0,1,4,5,6]:body.append(f' movea.l #0x{0xffe100+j*32:X},%a{j}')
 own=0xffffc6ce+team*0x364;opp=0xffffc6ce+(1-team)*0x364;body += [f' movea.l #0x{own:X},%a2',f' movea.l #0x{opp:X},%a3',' jsr 0x12B32',f' movem.l %d0-%d7/%a0-%a6,0x{0xfff100+64*i:X}',f' move.w 0xFFFFC2EE,0x{0xfff13c+64*i:X}']
body+=[' move.l #0x5057434C,0xFFFFF0FC','end: bra.s end'];(out/'driver.s').write_text('\n'.join(body)+'\n');subprocess.run([str(asm),str(out/'driver.s'),'2D9000',str(out/'driver.bin')],check=True,stdout=subprocess.DEVNULL);code=(out/'driver.bin').read_bytes();assert len(code)<0x3000 and rom[0x2d9000:0x2d9000+len(code)]==b'\xff'*len(code)
def run(label,old):
 p=out/label;p.mkdir(exist_ok=True);b=bytearray(rom);b[0x2d9000:0x2d9000+len(code)]=code;struct.pack_into('>I',b,4,0x2d9000)
 if old:b[0x12b32:0x12b38]=bytes.fromhex('08b80005c2ee')
 (p/'rom.bin').write_bytes(b);(p/'commands.txt').write_text(f'500 dump fff0fc 4 done\n500 dump fff100 {64*len(cases)} results\n500 dump ffb000 16384 graphics\n501 exit\n');env={k:v for k,v in os.environ.items() if not k.startswith('GPGX_')};env['GPGX_DEV']='513'
 with (p/'log.txt').open('w') as log:subprocess.run(['nice','-n','19','python3',str(core),str(p/'rom.bin'),str(p/'commands.txt'),str(p)],env=env,stdout=log,stderr=log,check=True,timeout=60)
 rows={l.split()[1]:bytes.fromhex(' '.join(l.split()[2:])) for l in (p/'commands.out').read_text().splitlines()};assert rows['done']==b'PWCL',(label,'native display did not return');return rows
old=run('original',True);new=run('fixed',False);report=[]
assert old['graphics']==new['graphics'],'native graphics RAM writes changed'
for i,(team,showing) in enumerate(cases):
 x=old['results'][64*i:64*i+64];y=new['results'][64*i:64*i+64];o=struct.unpack_from('>15I',x);n=struct.unpack_from('>15I',y);own=0xffffc6ce+team*0x364;opp=0xffffc6ce+(1-team)*0x364
 assert n[10:12]==(own,opp),(team,showing,n)
 if showing:
  assert o[10]==0x30a,'old native EASNLogo team-pointer loss did not reproduce'
  assert all(o[k]==n[k] for k in range(15) if k not in (10,11)),'unrelated native registers changed'
 else:assert x==y,'no-display guard path changed'
 assert y[60:62]==x[60:62]==bytes(2),'native PP display flag not cleared'
 report.append(dict(team=team,display_was_active=showing,passed=True,old_team_pointer=hex(o[10]),fixed_team_pointer=hex(n[10])))
r=dict(status='passed',rom_sha256=hashlib.sha256(rom).hexdigest(),cases=report,native_graphics_ram_identical=True,fix='preserve A2/A3 only around native EASNLogo during power-play display clear');(out/'results.json').write_text(json.dumps(r,indent=2)+'\n');print('PASS',len(cases),'real native PP clear cases; original lostA2=030A reproduced, graphics RAM identical')
