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}