code wiki / (root) / nx_chip8_gate.nx

nx_chip8_gate.nx source

↩ module page · 85 lines · 4588 B

1// nx_chip8_gate.nx -- NATIVE correctness gate for nx_chip8 (R0.3). Runs a hand-assembled program that 2// exercises set/add/add-with-carry/set-I and two DXYN sprite draws (draw then redraw -> XOR clears + 3// collision flag), and checks registers, the carry flag, the framebuffer, and collision. Proves the CPU + 4// display logic; the same base-relative core then compiles to wasm for in-VM + browser verification. 5import "nx_syscalls.nx" 6import "nx_chip8.nx" 7import "nx_gate_verdict.nx" 8 9func g_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 10func g_pn(v: i64) -> i64 { 11 let b: *u8 = sys_mmap(28) 12 var x: i64 = v 13 if x < 0 { b[0] = 45 as u8; sys_write(1, b, 1); x = 0 - x } 14 if x == 0 { b[0] = 48 as u8; sys_write(1, b, 1); return 0 } 15 var d: i64 = 0 16 var y: i64 = x 17 while y > 0 { d = d + 1; y = y / 10 } 18 var i: i64 = d - 1 19 y = x 20 while i >= 0 { b[i] = (48 + (y % 10)) as u8; y = y / 10; i = i - 1 } 21 sys_write(1, b, d) 22 return 0 23} 24func g_check(name: *u8, cond: i64) -> i64 { 25 if cond == 1 { g_puts(" PASS " as *u8) } else { g_puts(" FAIL " as *u8) } 26 g_puts(name); g_puts("\n" as *u8) 27 return cond 28} 29 30func main() -> i64 { 31 g_puts("nx_chip8 native gate (CPU + DXYN draw/XOR/collision)\n" as *u8) 32 var pass: i64 = 0 33 var total: i64 = 0 34 35 let base: i64 = sys_mmap(65536) as i64 36 ch8_reset(base) 37 38 // program at 0x200: 6A05 7A03 6B02 8AB4 62FF 6301 8234 A300 6000 6100 D012 D012 39 ch8_load(base, 512, 106); ch8_load(base, 513, 5) 40 ch8_load(base, 514, 122); ch8_load(base, 515, 3) 41 ch8_load(base, 516, 107); ch8_load(base, 517, 2) 42 ch8_load(base, 518, 138); ch8_load(base, 519, 180) 43 ch8_load(base, 520, 98); ch8_load(base, 521, 255) 44 ch8_load(base, 522, 99); ch8_load(base, 523, 1) 45 ch8_load(base, 524, 130); ch8_load(base, 525, 52) 46 ch8_load(base, 526, 163); ch8_load(base, 527, 0) 47 ch8_load(base, 528, 96); ch8_load(base, 529, 0) 48 ch8_load(base, 530, 97); ch8_load(base, 531, 0) 49 ch8_load(base, 532, 208); ch8_load(base, 533, 18) 50 ch8_load(base, 534, 208); ch8_load(base, 535, 18) 51 // 2-row sprite at 0x300: 0xFF (8 px) then 0x81 (col0 + col7) 52 ch8_load(base, 768, 255); ch8_load(base, 769, 129) 53 54 // run 11 instructions (through the FIRST DXYN at 0x214) 55 var s: i64 = 0 56 while s < 11 { ch8_step(base); s = s + 1 } 57 58 pass = pass + g_check("VA == 10 (5 +3 then +VB=2)" as *u8, ch8_reg(base, 10) == 10); total = total + 1 59 pass = pass + g_check("VB == 2" as *u8, ch8_reg(base, 11) == 2); total = total + 1 60 pass = pass + g_check("V2 == 0 (255+1 wrapped -> carry)" as *u8, ch8_reg(base, 2) == 0); total = total + 1 61 pass = pass + g_check("I == 0x300" as *u8, ch8_geti(base) == 768); total = total + 1 62 pass = pass + g_check("VF == 0 after first draw (no collision)" as *u8, ch8_vf(base) == 0); total = total + 1 63 pass = pass + g_check("pixel(0,0)==1 (sprite row0 0xFF)" as *u8, ch8_pixel(base, 0, 0) == 1); total = total + 1 64 pass = pass + g_check("pixel(7,0)==1" as *u8, ch8_pixel(base, 7, 0) == 1); total = total + 1 65 pass = pass + g_check("pixel(0,1)==1 (row1 0x81 col0)" as *u8, ch8_pixel(base, 0, 1) == 1); total = total + 1 66 pass = pass + g_check("pixel(7,1)==1 (row1 0x81 col7)" as *u8, ch8_pixel(base, 7, 1) == 1); total = total + 1 67 pass = pass + g_check("pixel(1,1)==0 (row1 0x81 col1 off)" as *u8, ch8_pixel(base, 1, 1) == 0); total = total + 1 68 69 // 12th instruction: redraw same sprite at same spot -> XOR clears + collision 70 ch8_step(base) 71 pass = pass + g_check("VF == 1 after redraw (collision)" as *u8, ch8_vf(base) == 1); total = total + 1 72 pass = pass + g_check("pixel(0,0)==0 (XOR cleared)" as *u8, ch8_pixel(base, 0, 0) == 0); total = total + 1 73 pass = pass + g_check("pixel(7,1)==0 (XOR cleared)" as *u8, ch8_pixel(base, 7, 1) == 0); total = total + 1 74 75 g_puts("---- chip8 native gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8) 76 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 77 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 78 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 79 let ctr__dry: *i64 = gv_ctr() 80 ctr__dry[0] = pass 81 ctr__dry[1] = total 82 let rc__dry: i64 = gv_verdict("CHIP8-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 83 sys_exit(rc__dry) 84 return rc__dry 85}