code wiki / _hdl_build / nx_chip8_wasm_gate.nx

nx_chip8_wasm_gate.nx source

↩ module page · 107 lines · 5911 B

1// nx_chip8_wasm_gate.nx -- R0.3 WASM proof: compiles nx_chip8.nx to wasm via the sovereign pipeline, then 2// drives the SAME program in nx_wasm_vm (reset -> load -> step, state persisting in the VM's linear memory) 3// and checks registers + framebuffer + collision match the native gate. Proves the CHIP-8 emulator runs in 4// the browser lane, bit-identical to native. NISHI all the way (fork sovereign compilers -> nx_wasm_vm, no .sh). 5import "nx_syscalls.nx" 6import "nx_gate_emit_lib.nx" 7import "nx_wasm_vm.nx" 8import "nx_gate_verdict.nx" 9 10const CWAT_ELF: *u8 = "/tmp/nx_compile_wat.sov.elf" 11const WATC_ELF: *u8 = "/tmp/nx_wat_compiler.sov.elf" 12const SRC_NX: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/runtime/nx_chip8.nx" 13const OUT_WAT: *u8 = "/tmp/ch8.wat" 14const OUT_WASM: *u8 = "/tmp/ch8.wasm" 15 16func run_elf(elf: *u8, a1: *u8, a2: *u8) -> i64 { 17 let pid: i64 = sys_fork() 18 if pid == 0 { 19 let argv: *i64 = sys_mmap(64) as *i64 20 argv[0] = elf as i64 21 var ai: i64 = 1 22 if (a1 as i64) != 0 { argv[ai] = a1 as i64; ai = ai + 1 } 23 if (a2 as i64) != 0 { argv[ai] = a2 as i64; ai = ai + 1 } 24 argv[ai] = 0 25 let envp: *i64 = sys_mmap(16) as *i64 26 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64 27 envp[1] = 0 28 sys_execve(elf, argv, envp) 29 sys_exit(127) 30 } 31 let st: *i64 = sys_mmap(16) as *i64 32 sys_wait4(pid, st, 0) 33 return (st[0] >> 8) & 0xff 34} 35 36// wm_run helpers (base is always 0 = start of the VM's linear memory; state persists across calls). 37func wld(mod: *WasmMod, addr: i64, byte: i64) -> i64 { return wm_run(mod, "ch8_load" as *u8, 0, addr, byte, 0, 0, 3) } 38func wstep(mod: *WasmMod) -> i64 { return wm_run(mod, "ch8_step" as *u8, 0, 0, 0, 0, 0, 1) } 39func wreg(mod: *WasmMod, xi: i64) -> i64 { return wm_run(mod, "ch8_reg" as *u8, 0, xi, 0, 0, 0, 2) } 40func wpix(mod: *WasmMod, px: i64, py: i64) -> i64 { return wm_run(mod, "ch8_pixel" as *u8, 0, px, py, 0, 0, 3) } 41func wvf(mod: *WasmMod) -> i64 { return wm_run(mod, "ch8_vf" as *u8, 0, 0, 0, 0, 0, 1) } 42func wgeti(mod: *WasmMod) -> i64 { return wm_run(mod, "ch8_geti" as *u8, 0, 0, 0, 0, 0, 1) } 43 44func main() -> i64 { 45 g_puts("nx_chip8 WASM gate (sovereign compile -> nx_wasm_vm: CPU + DXYN, vs native)\n" as *u8) 46 var pass: i64 = 0 47 var total: i64 = 0 48 49 let rc1: i64 = run_elf(CWAT_ELF, SRC_NX, OUT_WAT) 50 g_puts(" [build] nx_compile_wat rc=" as *u8); g_pn(rc1); g_puts("\n" as *u8) 51 let rc2: i64 = run_elf(WATC_ELF, OUT_WAT, OUT_WASM) 52 g_puts(" [build] nx_wat_compiler rc=" as *u8); g_pn(rc2); g_puts("\n" as *u8) 53 54 let box: *i64 = sys_mmap(16) as *i64 55 let wasm: *u8 = sys_read_file(OUT_WASM, box) 56 if (wasm as i64) == 0 { g_puts(" FAIL cannot read ch8.wasm\nverdict=RED\n" as *u8); sys_exit(1); return 1 } 57 g_puts(" [measure] wasm bytes=" as *u8); g_pn(box[0]); g_puts("\n" as *u8) 58 let mod: *WasmMod = wm_new(wasm, box[0]) 59 if wm_parse(mod) != 0 { g_puts(" FAIL wasm parse\nverdict=RED\n" as *u8); sys_exit(1); return 1 } 60 mod.mem = sys_mmap(65536) as *u8 61 62 g_puts(" [measure] funcs=" as *u8); g_pn(mod.n_funcs); g_puts(" exports=" as *u8); g_pn(mod.n_exports); g_puts("\n" as *u8) 63 pass = pass + g_check("nx_chip8 compiled to wasm + ch8_step exported" as *u8, wm_find_export(mod, "ch8_step" as *u8) >= 0); total = total + 1 64 65 // reset + load the same 12-instruction program + 2-byte sprite as the native gate 66 wm_run(mod, "ch8_reset" as *u8, 0, 0, 0, 0, 0, 1) 67 wld(mod, 512, 106); wld(mod, 513, 5) 68 wld(mod, 514, 122); wld(mod, 515, 3) 69 wld(mod, 516, 107); wld(mod, 517, 2) 70 wld(mod, 518, 138); wld(mod, 519, 180) 71 wld(mod, 520, 98); wld(mod, 521, 255) 72 wld(mod, 522, 99); wld(mod, 523, 1) 73 wld(mod, 524, 130); wld(mod, 525, 52) 74 wld(mod, 526, 163); wld(mod, 527, 0) 75 wld(mod, 528, 96); wld(mod, 529, 0) 76 wld(mod, 530, 97); wld(mod, 531, 0) 77 wld(mod, 532, 208); wld(mod, 533, 18) 78 wld(mod, 534, 208); wld(mod, 535, 18) 79 wld(mod, 768, 255); wld(mod, 769, 129) 80 81 var s: i64 = 0 82 while s < 11 { wstep(mod); s = s + 1 } 83 pass = pass + g_check("VA == 10 (wasm)" as *u8, wreg(mod, 10) == 10); total = total + 1 84 pass = pass + g_check("V2 == 0 carry-wrap (wasm)" as *u8, wreg(mod, 2) == 0); total = total + 1 85 pass = pass + g_check("I == 0x300 (wasm)" as *u8, wgeti(mod) == 768); total = total + 1 86 pass = pass + g_check("VF == 0 first draw (wasm)" as *u8, wvf(mod) == 0); total = total + 1 87 pass = pass + g_check("pixel(0,0)==1 (wasm)" as *u8, wpix(mod, 0, 0) == 1); total = total + 1 88 pass = pass + g_check("pixel(7,0)==1 (wasm)" as *u8, wpix(mod, 7, 0) == 1); total = total + 1 89 pass = pass + g_check("pixel(0,1)==1 (wasm)" as *u8, wpix(mod, 0, 1) == 1); total = total + 1 90 pass = pass + g_check("pixel(7,1)==1 (wasm)" as *u8, wpix(mod, 7, 1) == 1); total = total + 1 91 pass = pass + g_check("pixel(1,1)==0 (wasm)" as *u8, wpix(mod, 1, 1) == 0); total = total + 1 92 93 wstep(mod) // redraw -> XOR clear + collision 94 pass = pass + g_check("VF == 1 collision on redraw (wasm)" as *u8, wvf(mod) == 1); total = total + 1 95 pass = pass + g_check("pixel(0,0)==0 XOR-cleared (wasm)" as *u8, wpix(mod, 0, 0) == 0); total = total + 1 96 97 g_puts("---- chip8 wasm gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8) 98 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 99 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 100 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 101 let ctr__dry: *i64 = gv_ctr() 102 ctr__dry[0] = pass 103 ctr__dry[1] = total 104 let rc__dry: i64 = gv_verdict("CHIP8-WASM-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 105 sys_exit(rc__dry) 106 return rc__dry 107}