"""Declared native VDP/stack fixtures; not matches or human hardware trials."""
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import struct,json,hashlib
from native_probe import NativeProbe

def verify(rom,here,out):
 h=Path(here);o=Path(out);o.mkdir(parents=True,exist_ok=True);probe=NativeProbe(rom,h);b=Path(rom).read_bytes()
 first=18 if b[0x2ec008:0x2ec00c]==b'PPFR'else 23
 count=(28-first)*9;payload=count*2;packet=132+2*payload+8
 if first==18:assert struct.unpack_from('>HH',b,0x2ec00c)==(18,180),'wrong declared PP replay protocol'
 plane=[0x8000+(i&0x7ff)for i in range(2048)];wanted=struct.pack('>'+str(count)+'H',*[plane[r*64+c]for r in range(first,28)for c in range(9)])
 def command(at,read=False):return((at&0x3fff)<<16)|(at>>14)|(0 if read else 0x40000000)
 def regs_init(regs,sr):return[f' move.l #0x{v:x},%d{i}'for i,v in enumerate(regs[:8])]+[f' movea.l #0x{v:x},%a{i}'for i,v in enumerate(regs[8:])]+[f' move.w #{sr},%sr']
 def snapshot(at):return[f' movem.l %d0-%d7/%a0-%a6,0x{at:x}',f' move.w %sr,0x{at+60:x}',f' move.l %sp,0x{at+62:x}']
 def batch(index,mutant=False):
  body=[' move.w #0x8104,0xc00004',' move.w #0x8f02,0xc00004',f' move.l #0x{command(0xe000):x},0xc00004',' lea 0x230000,%a0',' move.w #2047,%d0','initial: move.w (%a0)+,0xc00000',' dbra %d0,initial',' move.w #0xcafe,0xffff8ffe',f' move.w #0xbeef,0x{0xffff9000+payload:x}',' move.w #0xe000,0xffffb00c',' move.w #6,0xffffb00e',' move.w #1302,0xffffb026',' move.w #2,0xfffff73a'];expect=[]
  for j,n in enumerate(range(index*8,index*8+8)):
   regs=[0x12345600+i for i in range(8)]+[0xffff1000+256*i for i in range(7)];regs[8]=0xffff9000;sr=0x2000+((n//32)<<8)+(n%32);at=0xffff4000+packet*j
   body+=regs_init(regs,sr)+[' jsr 0x2ec004']+snapshot(at)
   body+=[' move.w #0x2700,%sr']
   for row in range(first,28):body += [f' move.l #0x{command(0xe000+row*128):x},0xc00004',' moveq #8,%d0',f'blank{j}_{row}: move.w #0x7ff,0xc00000',f' dbra %d0,blank{j}_{row}']
   body+=regs_init(regs,sr)+[' jsr 0x2ec000']+snapshot(at+66)+[' move.w #0x2700,%sr',f' lea 0x{at+132:x},%a0']
   for row in range(first,28):body += [f' move.l #0x{command(0xe000+row*128,True):x},0xc00004',' moveq #8,%d0',f'read{j}_{row}: move.w 0xc00000,(%a0)+',f' dbra %d0,read{j}_{row}']
   body += [' lea 0xffff9000,%a1',f' moveq #{count-1},%d0',f'copy{j}: move.w (%a1)+,(%a0)+',f' dbra %d0,copy{j}',' move.w 0xffff8ffe,(%a0)+',f' move.w 0x{0xffff9000+payload:x},(%a0)+',' move.l 0xfffff670,(%a0)+'];expect.append((regs,sr))
  patches=[]
  if mutant:
   # Retain the stack pop but deliberately drop incoming SR restoration.
   at=b.find(bytes.fromhex('46df4cdf'),0x2ec000,0x2ec400);assert at>=0
   patches=[(at,bytes.fromhex('46df'),bytes.fromhex('548f'))]
  raw=probe.run(o/('negative'if mutant else str(index)),'\n'.join(body),packet*8,patches=patches,data=struct.pack('>2048H',*plane))
  for j,(regs,sr)in enumerate(expect):
   at=j*packet
   for offset in [0,66]:assert struct.unpack_from('>15IHI',raw,at+offset)==(*regs,sr,0xffff9800),('native replay HUD GPR/SR/IRQ/CCR/SP',index,j,offset)
   assert raw[at+132:at+132+payload]==wanted,'full native PP/clock panel was not restored'
   assert raw[at+132+payload:at+132+2*payload]==wanted,'stack-owned snapshot changed'
   assert raw[at+132+2*payload:at+packet]==bytes.fromhex('cafebeefffff9000'),'snapshot bound or transient pointer corruption'
  return dict(contexts=8,packet_sha256=hashlib.sha256(raw).hexdigest())
 with ThreadPoolExecutor(max_workers=3)as pool:rows=list(pool.map(batch,range(32)))
 try:batch(0,True)
 except AssertionError as e:negative=str(e);assert 'GPR/SR/IRQ/CCR/SP'in negative
 else:raise AssertionError('SR-dropping negative control escaped')
 result=dict(contexts=512,snapshot_first_row=first,snapshot_bytes=payload,all8_IRQ_and32_CCR=True,complete_GPR_SR_SP=True,actual_VDP_snapshot_and_restore=True,adjacent_canaries=True,negative_control=negative,batches=rows)
 (o/'results.json').write_text(json.dumps(result,indent=2)+'\n');return result

if __name__=='__main__':
 import argparse
 ap=argparse.ArgumentParser();ap.add_argument('rom',type=Path);ap.add_argument('output',type=Path);a=ap.parse_args();print(json.dumps(verify(a.rom,Path(__file__).resolve().parent,a.output)))
