from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import json,struct,hashlib,subprocess,os,random
import argparse
ap=argparse.ArgumentParser(description='Native future pass-course arithmetic and complete register/SR contract.');ap.add_argument('rom',type=Path);ap.add_argument('output',type=Path);a=ap.parse_args();h=Path(__file__).resolve().parent;out=a.output.resolve();out.mkdir(parents=True,exist_ok=True);rom=a.rom;env={k:v for k,v in os.environ.items() if not k.startswith('GPGX_')}
def trunc(n,d):return abs(n)//d*(1 if n>=0 else -1)
def oracle(z):
 bx,by,px,py,vx,vy=z
 if not(-192<=bx<=192 and -352<=by<=352 and -192<=px<=192 and -352<=py<=352):return bx,by,0
 while abs(vx)|abs(vy)>127:vx>>=1;vy>>=1
 n=vx*vx+vy*vy;dx,dy=bx-px,by-py
 if n==0 or dx*vx+dy*vy<=0:return bx,by,0
 cross=dx*vy-dy*vx;ox,oy=trunc(cross*vy,n),trunc(cross*vx,n)
 if abs(ox)>32 or abs(oy)>32:return bx,by,0
 return bx-ox,by+oy,1
rng=random.Random(20261101);cases=[(bx,by,px,py,vx,vy) for bx,by,px,py in [(0,220,110,180),(0,-220,110,-180),(92,252,-160,0),(-192,-352,192,352)] for vx in [-32768,-2048,-127,-1,0,1,127,2048,32767] for vy in [-32768,-2048,-127,-1,0,1,127,2048,32767]]
cases += [(rng.randrange(-193,194),rng.randrange(-353,354),rng.randrange(-193,194),rng.randrange(-353,354),rng.randrange(-32768,32768),rng.randrange(-32768,32768)) for _ in range(1024)]
cases += [(x,220,0,220,256,0) for x in [-193,-192,0,192,193]]+[(0,y,0,0,0,256) for y in [-353,-352,0,352,353]]
def run(p,source,size,data=b''):
 p.mkdir();(p/'driver.s').write_text(' .text\n .globl _start\n_start:\n move.w #0x2700,%sr\n movea.l #0xffff9800,%sp\n'+source+'\n move.l #0x50524f4a,0xffff3ff0\nend: bra.s end\n');subprocess.run([str(h.parent/'m68kasm.sh'),str(p/'driver.s'),'2d5000',str(p/'code.bin')],check=True,stdout=subprocess.DEVNULL);b=bytearray(rom.read_bytes());code=(p/'code.bin').read_bytes();assert len(code)<0x3000 and b[0x2d5000:0x2d5000+len(code)]==b'\xff'*len(code);b[0x2d5000:0x2d5000+len(code)]=code;assert b[0x230000:0x230000+len(data)]==b'\xff'*len(data);b[0x230000:0x230000+len(data)]=data;struct.pack_into('>I',b,4,0x2d5000);(p/'rom.bin').write_bytes(b);(p/'commands.txt').write_text(f'1500 dump ff3ff0 4 done\n1500 dump ff4000 {size} result\n1501 exit\n')
 with(p/'log').open('w') as log:subprocess.run(['python3',str(h/'test_core.py'),str(p/'rom.bin'),str(p/'commands.txt'),str(p)],env=env,stdout=log,stderr=log,check=True,timeout=60)
 rows={x[1]:bytes.fromhex(' '.join(x[2:])) for line in (p/'commands.out').read_text().splitlines() if (x:=line.split())};assert rows['done']==b'PROJ';return rows['result']
def functional(j):
 ss=cases[j*256:j*256+256];data=b''.join(struct.pack('>6h',*z) for z in ss);code=' lea 0x230000,%a0\n lea 0xffff4000,%a1\n'+f' move.w #{len(ss)-1},%d7\n'+'each: movem.w (%a0)+,%d0-%d5\n jsr 0x225008\n move.l %d0,(%a1)+\n move.l %d1,(%a1)+\n move.l %d2,(%a1)+\n dbra %d7,each';raw=run(out/f'grid-{j}',code,12*len(ss),data)
 for i,z in enumerate(ss):
  x,y,ok=oracle(z);want=(((0xffff0000 if z[0]<0 else 0)|(x&65535)),((0xffff0000 if z[1]<0 else 0)|(y&65535)),ok);actual=struct.unpack_from('>3I',raw,i*12);assert actual==want,('projection/signed product/normalization/bounds/upperwords',z,want,actual)
 return dict(cases=len(ss),valid_targets=sum(oracle(z)[2] for z in ss),sha256=hashlib.sha256(raw).hexdigest())
with ThreadPoolExecutor(max_workers=3) as pool:rows=list(pool.map(functional,range((len(cases)+255)//256)))
print('PASS',len(cases),'native projection geometry cases',flush=True)
abi=[]
for batch in range(16):
 ss=[(i//32,i%32) for i in range(16*batch,16*batch+16)];source=[];expect=[]
 for j,(irq,ccr) in enumerate(ss):
  z=cases[(batch*16+j)*7%len(cases)];regs=[0x12340000|(z[0]&65535),0x56780000|(z[1]&65535),0x11440000|(z[2]&65535),0x22550000|(z[3]&65535),0x33660000|(z[4]&65535),0x44770000|(z[5]&65535),0x11223344,0x55667788]+[0xffff1000+256*i for i in range(7)]
  source += [f' move.l #{v},%d{i}' for i,v in enumerate(regs[:8])]+[f' movea.l #{v},%a{i}' for i,v in enumerate(regs[8:])]+[f' move.w #{0x2000+(irq<<8)+ccr},%sr',' jsr 0x225008',f' movem.l %d0-%d7/%a0-%a6,0x{0xffff4000+66*j:x}',f' move.w %sr,0x{0xffff4000+66*j+60:x}',f' move.l %sp,0x{0xffff4000+66*j+62:x}'];x,y,ok=oracle(z);regs[0]=(regs[0]&0xffff0000)|(x&65535);regs[1]=(regs[1]&0xffff0000)|(y&65535);regs[2]=ok;expect.append((regs,0x2000+(irq<<8)+ccr,0xffff9800))
 raw=run(out/f'ABI-{batch}','\n'.join(source),66*len(ss))
 for j,want in enumerate(expect):
  actual=(list(struct.unpack_from('>15I',raw,66*j)),*struct.unpack_from('>HI',raw,66*j+60));assert actual==want,('pure complete ABI',batch,j,actual,want)
 abi+=ss
report=dict(rom_sha256=hashlib.sha256(rom.read_bytes()).hexdigest(),functional_cases=len(cases),batches=rows,full_ABI_contexts=len(abi),mathematics='Closest point on normalized actual native velocity ray; only approaching valid world pairs, correction <=32percomponent; signed division truncates toward0. No outcome writes.');(out/'RESULT.json').write_text(json.dumps(report,indent=2)+'\n');print('PASS',len(abi),'fullCCR/IRQ/GPR/SP geometry contexts')
