#!/usr/bin/env python3
"""Headless flash lifecycle/cache/DMA regression; writes only into work_dir.

Usage: verify_critical_flash.py current.bin /absolute/private/work
No live SRAM or emulator window. The isolated test ROM replaces its reset
vector and native frame-cache continuation with a test return instruction.
The real assembly helpers and existing goalie pickup gate execute unchanged.
"""
from pathlib import Path
import argparse
import json
import os
import struct
import subprocess


def main():
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument('rom', type=Path)
    ap.add_argument('work_dir', type=Path)
    args = ap.parse_args()
    here = Path(__file__).resolve().parent
    work = args.work_dir.resolve()
    work.mkdir(parents=True, exist_ok=True)
    source = here/'testcases/critical_flash_driver.s'
    driver = work/'driver-code.bin'
    subprocess.run([str(here.parent/'m68kasm.sh'), str(source), '2FEA00', str(driver)], check=True)
    code = driver.read_bytes()
    data = bytearray(args.rom.read_bytes())
    assert len(code) <= 0x600 and all(x == 255 for x in data[0x2FEA00:0x2FEA00+len(code)])
    data[0x2FEA00:0x2FEA00+len(code)] = code
    struct.pack_into('>I', data, 4, 0x2FEA00)
    data[0x16800:0x16802] = bytes.fromhex('4e75')
    test_rom = work/'driver.bin'
    test_rom.write_bytes(data)
    cmd = work/'commands.txt'
    cmd.write_text('1000 dump ff0000 272 phases\n1000 dump fff606 6 result\n1001 exit\n')
    env = {k:v for k,v in os.environ.items() if k not in ['GPGX_SRM', 'GPGX_HOOK', 'GPGX_WAV']}
    env['GPGX_DEV'] = '513'
    subprocess.run(['python3', str(here/'test_core.py'), str(test_rom), str(cmd), str(work)], env=env, check=True)
    results = {}
    for line in cmd.with_suffix('.out').read_text().splitlines():
        _, name, raw = line.split(' ', 2)
        results[name] = bytes.fromhex(raw)
    assert results['result'] == b'\0\0CRIT', ('driver errors or incomplete', results['result'].hex())
    previous = 0
    for i, (phase, invalidated) in enumerate(zip(results['phases'][::2], results['phases'][1::2])):
        expected = int(i < 127 and bool((127-i) & 4))
        assert phase == expected, (i, 'wrong phase', phase, expected)
        assert invalidated == (255 if phase != previous else 0), (i, 'cache not refreshed at colour transition')
        previous = phase
    report = dict(render_frames=136, cadence='4 red / 4 ordinary', max_lifetime=127,
                  cache_invalidation='passed', pickup_and_dead_puck_reset='passed',
                  failed_goalie_pickup_preserves_effect='passed', ordinary_shot_reset='passed',
                  eight_tile_dma_mapping='passed', shadow_exclusion='passed',
                  sprite_flip_and_tile_preservation='passed', frame_register_preservation='passed')
    (work/'results.json').write_text(json.dumps(report, indent=2)+'\n')
    print('PASS flash cadence/lifetime, cache, possession/whistle, native pickup gate, tiles, shadow and register checks')


if __name__ == '__main__':
    main()
