code wiki / _hdl_build / nx_gamehud_gate.nx
nx_gamehud_gate.nx source
↩ module page · 268 lines · 12020 B
1// nx_gamehud_gate.nx -- certification gate for nx_gamehud (gamebench capability 18 ui-menus-hud).
2// The bug classes each tooth exists to kill (every one shipped in a real engine somewhere):
3// T1 stale HUD -- HUD renders CACHED text instead of the live bound value
4// T2 impure render -- same state renders different pixels (kills replay/lockstep evidence)
5// T3 cursor clamp -- menu cursor clamps at the ends instead of wrapping (MUTATION TARGET)
6// T4 modal residue -- closing a menu does not restore the exact pre-menu frame
7// T5 out-of-bounds -- off-screen text/panel writes past the framebuffer (canary proves it can't)
8// T6 save amnesia -- HUD/menu state does not survive nx_gamesave round-trip (parts-compose tooth)
9// T7 nondeterminism -- the same nav script produces different selections/frames on a second run
10// T8 anti-vacuity -- guards that the equality teeth compared REAL ink and REAL distinct choices
11// (GX-9 law: a green gate that cannot fail is not evidence; T8 must stay GREEN under mutations)
12// license_tier: ORIGINAL expect_exit: 0
13import "nx_syscalls.nx"
14import "nx_gamehud.nx"
15import "nx_gamesave.nx"
16
17const W: i64 = 200
18const H: i64 = 120
19const FBN: i64 = 72000 // W*H*3
20const CAN: i64 = 64 // canary bytes past the fb
21const SCHEMA: i64 = 7411
22const INKMIN: i64 = 50
23
24func ww(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
25func wn(v: i64) -> i64 {
26 if v==0 { sys_write(1,"0" as *u8,1); return 0 }
27 var m: i64=v; if m<0 { sys_write(1,"-" as *u8,1); m=0-m }
28 let t: *u8=sys_mmap(32); var k: i64=0
29 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
30 let o: *u8=sys_mmap(32); var q: i64=k-1; var i: i64=0
31 while q>=0 { o[i]=t[q]; i=i+1; q=q-1 }
32 sys_write(1,o,i); return 0
33}
34func fbnew() -> *u8 {
35 let fb: *u8 = sys_mmap(FBN + CAN)
36 var i: i64 = 0
37 while i < FBN { fb[i] = 0 as u8; i = i + 1 }
38 var c: i64 = 0
39 while c < CAN { fb[FBN + c] = 0xA5 as u8; c = c + 1 }
40 return fb
41}
42func fbclear(fb: *u8) -> i64 {
43 var i: i64 = 0
44 while i < FBN { fb[i] = 0 as u8; i = i + 1 }
45 return 0
46}
47func fbdiff(a: *u8, b: *u8) -> i64 {
48 var d: i64 = 0
49 var i: i64 = 0
50 while i < FBN { if (a[i] & 0xff) != (b[i] & 0xff) { d = d + 1 } i = i + 1 }
51 return d
52}
53func canck(fb: *u8) -> i64 {
54 var bad: i64 = 0
55 var c: i64 = 0
56 while c < CAN { if (fb[FBN + c] & 0xff) != 0xA5 { bad = bad + 1 } c = c + 1 }
57 return bad
58}
59// the scripted nav run for T7/T8: returns sel0*1e9 + sel1*1e6 + nsel*1000 + distinct, and writes
60// final state ck to out[0], final frame ck to out[1]
61func navrun(font: *u8, labels: *i64, mlabels: *i64, out: *i64) -> i64 {
62 let g: *i64 = sys_mmap(GH_WORDS * 8) as *i64
63 gh_init(g, 4)
64 gh_slot_set(g, 0, 17)
65 gh_slot_set(g, 1, 3)
66 gh_slot_set(g, 2, 2)
67 gh_slot_set(g, 3, 9)
68 gh_menu_open(g, 4)
69 gh_menu_item(g, 0, 7, 100)
70 gh_menu_item(g, 1, 11, 200)
71 gh_menu_item(g, 2, 13, 300)
72 gh_menu_item(g, 3, 17, 400)
73 gh_menu_move(g, 5) // 0+5 wraps to 1
74 let s0: i64 = gh_menu_sel(g) // id 11, closes
75 gh_slot_set(g, 0, 17 + s0) // selection FEEDS BACK into the HUD (causal, not decorative)
76 gh_menu_open(g, 3)
77 gh_menu_item(g, 0, 21, 500)
78 gh_menu_item(g, 1, 23, 600)
79 gh_menu_item(g, 2, 29, 700)
80 gh_menu_move(g, 0 - 4) // 0-4 wraps to 2
81 let s1: i64 = gh_menu_sel(g) // id 29
82 gh_slot_set(g, 3, s1)
83 let fb: *u8 = fbnew()
84 gh_render(g, fb, W, H, font, labels, mlabels)
85 out[0] = gh_ck(g)
86 out[1] = gh_frame_ck(fb, W, H)
87 var distinct: i64 = 1
88 if s1 != s0 { distinct = 2 }
89 return s0 * 1000000000 + s1 * 1000000 + g[GH_F_NSEL] * 1000 + distinct
90}
91
92func main() -> i64 {
93 var pass: i64 = 0
94 let font: *u8 = font8x8_table()
95 let labels: *i64 = sys_mmap(16 * 8) as *i64
96 labels[0] = "HP" as i64
97 labels[1] = "LVL" as i64
98 labels[2] = "DEPTH" as i64
99 labels[3] = "KILLS" as i64
100 let mlabels: *i64 = sys_mmap(16 * 8) as *i64
101 mlabels[0] = "ration" as i64
102 mlabels[1] = "feast" as i64
103 mlabels[2] = "scroll" as i64
104 mlabels[3] = "potion" as i64
105 var i: i64 = 0
106 // 16-item labels for the T5 overflow menu reuse the 4 above cyclically
107 let biglab: *i64 = sys_mmap(16 * 8) as *i64
108 i = 0
109 while i < 16 { biglab[i] = mlabels[i % 4]; i = i + 1 }
110
111 ww("=== nx_gamehud_gate ===\n")
112
113 // ---------- T1 binding + non-vacuity: live values, not cached text ----------
114 let g1: *i64 = sys_mmap(GH_WORDS * 8) as *i64
115 gh_init(g1, 4)
116 gh_slot_set(g1, 0, 17)
117 gh_slot_set(g1, 1, 3)
118 gh_slot_set(g1, 2, 2)
119 gh_slot_set(g1, 3, 9)
120 let fa: *u8 = fbnew()
121 gh_render(g1, fa, W, H, font, labels, mlabels)
122 let ink0: i64 = gh_ink(fa, W, H)
123 let cka: i64 = gh_frame_ck(fa, W, H)
124 gh_slot_set(g1, 0, 18)
125 let fB: *u8 = fbnew()
126 gh_render(g1, fB, W, H, font, labels, mlabels)
127 let ckb: i64 = gh_frame_ck(fB, W, H)
128 var t1: i64 = 0
129 if ink0 >= INKMIN { if cka != ckb { t1 = 1 } }
130 if t1 == 1 {
131 ww("T1 GREEN binding: ink "); wn(ink0); ww(" px, HP 17->18 moved the frame ck\n"); pass = pass + 1
132 }
133 if t1 == 0 { ww("T1 RED binding: ink="); wn(ink0); ww(" cka="); wn(cka); ww(" ckb="); wn(ckb); ww("\n") }
134
135 // ---------- T2 render purity: same state -> byte-identical pixels ----------
136 let fc: *u8 = fbnew()
137 let fd: *u8 = fbnew()
138 gh_render(g1, fc, W, H, font, labels, mlabels)
139 gh_render(g1, fd, W, H, font, labels, mlabels)
140 let d2: i64 = fbdiff(fc, fd)
141 let ink2: i64 = gh_ink(fc, W, H)
142 var t2: i64 = 0
143 if d2 == 0 { if ink2 >= INKMIN { t2 = 1 } }
144 if t2 == 1 { ww("T2 GREEN purity: two renders byte-identical ("); wn(ink2); ww(" ink px)\n"); pass = pass + 1 }
145 if t2 == 0 { ww("T2 RED purity: diffs="); wn(d2); ww(" ink="); wn(ink2); ww("\n") }
146
147 // ---------- T3 wrap-around both directions + select id (MUTATION TARGET) ----------
148 let g3: *i64 = sys_mmap(GH_WORDS * 8) as *i64
149 gh_init(g3, 1)
150 gh_menu_open(g3, 3)
151 gh_menu_item(g3, 0, 7, 0)
152 gh_menu_item(g3, 1, 11, 0)
153 gh_menu_item(g3, 2, 13, 0)
154 let w1: i64 = gh_menu_move(g3, 0 - 1) // 0 -> 2 (wrap down)
155 let w2: i64 = gh_menu_move(g3, 1) // 2 -> 0 (wrap up)
156 let w3: i64 = gh_menu_move(g3, 1) // 0 -> 1
157 let sid: i64 = gh_menu_sel(g3) // id 11
158 var t3: i64 = 0
159 if w1 == 2 { if w2 == 0 { if w3 == 1 { if sid == 11 { if g3[GH_F_MOPEN] == 0 { t3 = 1 } } } } }
160 if t3 == 1 { ww("T3 GREEN menu wrap: -1 from 0 -> 2, +1 -> 0, select under cursor = id 11, modal-closed\n"); pass = pass + 1 }
161 if t3 == 0 { ww("T3 RED wrap: w1="); wn(w1); ww(" w2="); wn(w2); ww(" w3="); wn(w3); ww(" sel="); wn(sid); ww("\n") }
162
163 // ---------- T4 modality: menu paints over, close restores the EXACT frame ----------
164 let g4: *i64 = sys_mmap(GH_WORDS * 8) as *i64
165 gh_init(g4, 4)
166 gh_slot_set(g4, 0, 17)
167 gh_slot_set(g4, 1, 3)
168 gh_slot_set(g4, 2, 2)
169 gh_slot_set(g4, 3, 9)
170 let f4a: *u8 = fbnew()
171 gh_render(g4, f4a, W, H, font, labels, mlabels)
172 gh_menu_open(g4, 4)
173 gh_menu_item(g4, 0, 7, 100)
174 gh_menu_item(g4, 1, 11, 200)
175 gh_menu_item(g4, 2, 13, 300)
176 gh_menu_item(g4, 3, 17, 400)
177 let f4b: *u8 = fbnew()
178 gh_render(g4, f4b, W, H, font, labels, mlabels)
179 let dopen: i64 = fbdiff(f4a, f4b)
180 gh_menu_close(g4)
181 let f4c: *u8 = fbnew()
182 gh_render(g4, f4c, W, H, font, labels, mlabels)
183 let dclose: i64 = fbdiff(f4a, f4c)
184 var t4: i64 = 0
185 if dopen >= 100 { if dclose == 0 { t4 = 1 } }
186 if t4 == 1 { ww("T4 GREEN modality: open moved "); wn(dopen); ww(" bytes, close restored the exact frame\n"); pass = pass + 1 }
187 if t4 == 0 { ww("T4 RED modality: dopen="); wn(dopen); ww(" dclose="); wn(dclose); ww("\n") }
188
189 // ---------- T5 bounds: off-screen text + oversized panel cannot escape the fb ----------
190 let g5: *i64 = sys_mmap(GH_WORDS * 8) as *i64
191 gh_init(g5, 4)
192 gh_slot_set(g5, 0, 1234567) // long number pushes the bar toward the right edge
193 gh_slot_set(g5, 1, 7654321)
194 gh_slot_set(g5, 2, 9999999)
195 gh_slot_set(g5, 3, 8888888)
196 gh_menu_open(g5, 16) // panel bottom = 14+4+160+2 = 180 > H=120 -> clipped
197 var mi: i64 = 0
198 while mi < 16 { gh_menu_item(g5, mi, 100 + mi, 1000000 + mi); mi = mi + 1 }
199 let f5: *u8 = fbnew()
200 gh_render(g5, f5, W, H, font, labels, biglab)
201 gh_text(f5, W, H, font, W - 5, H - 3, "overflow-right-bottom" as *u8)
202 gh_text(f5, W, H, font, 0 - 30, 0 - 30, "negative-origin" as *u8)
203 gh_numdraw(f5, W, H, font, W - 2, 4, 987654321)
204 let bad5: i64 = canck(f5)
205 let ink5: i64 = gh_ink(f5, W, H)
206 var t5: i64 = 0
207 if bad5 == 0 { if ink5 >= INKMIN { t5 = 1 } }
208 if t5 == 1 { ww("T5 GREEN bounds: oversized panel + edge/negative text, canary intact ("); wn(ink5); ww(" ink px)\n"); pass = pass + 1 }
209 if t5 == 0 { ww("T5 RED bounds: canary-bytes-hit="); wn(bad5); ww(" ink="); wn(ink5); ww("\n") }
210
211 // ---------- T6 save/load round-trip through nx_gamesave (parts-compose tooth) ----------
212 let P: *u8 = "knowledge/nx_gamehud_t.sav" as *u8
213 sys_unlinkat(P)
214 let g6: *i64 = sys_mmap(GH_WORDS * 8) as *i64
215 gh_init(g6, 4)
216 gh_slot_set(g6, 0, 28)
217 gh_slot_set(g6, 1, 4)
218 gh_slot_set(g6, 2, 3)
219 gh_slot_set(g6, 3, 16)
220 gh_menu_open(g6, 4)
221 gh_menu_item(g6, 0, 7, 100)
222 gh_menu_item(g6, 1, 11, 200)
223 gh_menu_item(g6, 2, 13, 300)
224 gh_menu_item(g6, 3, 17, 400)
225 gh_menu_move(g6, 1) // cursor 1: cursor position must survive the save
226 let sr: i64 = gs_save(P, SCHEMA, g6, GH_NSV, 777001)
227 let g6b: *i64 = sys_mmap(GH_WORDS * 8) as *i64
228 let meta: *i64 = sys_mmap(8 * 8) as *i64
229 let lr: i64 = gs_load(P, g6b, GH_WORDS, meta)
230 let ck6a: i64 = gh_ck(g6)
231 let ck6b: i64 = gh_ck(g6b)
232 let f6a: *u8 = fbnew()
233 let f6b: *u8 = fbnew()
234 gh_render(g6, f6a, W, H, font, labels, mlabels)
235 gh_render(g6b, f6b, W, H, font, labels, mlabels)
236 let d6: i64 = fbdiff(f6a, f6b)
237 let ink6: i64 = gh_ink(f6a, W, H)
238 var t6: i64 = 0
239 let expw: i64 = GS_HDR + GH_NSV * 8 // gs_save returns TOTAL BYTES WRITTEN (header + payload)
240 if sr == expw { if lr == GH_NSV { if ck6a == ck6b { if d6 == 0 { if ink6 >= INKMIN { t6 = 1 } } } } }
241 if t6 == 1 {
242 ww("T6 GREEN save/load: state ck "); wn(ck6a); ww(" and render survive the round-trip (cursor kept)\n")
243 pass = pass + 1
244 }
245 if t6 == 0 { ww("T6 RED save/load: sr="); wn(sr); ww(" lr="); wn(lr); ww(" cka="); wn(ck6a); ww(" ckb="); wn(ck6b); ww(" d="); wn(d6); ww("\n") }
246
247 // ---------- T7 scripted determinism: same nav script twice from scratch ----------
248 let o7a: *i64 = sys_mmap(4 * 8) as *i64
249 let o7b: *i64 = sys_mmap(4 * 8) as *i64
250 let r7a: i64 = navrun(font, labels, mlabels, o7a)
251 let r7b: i64 = navrun(font, labels, mlabels, o7b)
252 var t7: i64 = 0
253 if r7a == r7b { if o7a[0] == o7b[0] { if o7a[1] == o7b[1] { t7 = 1 } } }
254 if t7 == 1 { ww("T7 GREEN deterministic: nav script twice -> identical selections + state ck + frame ck ("); wn(o7a[0]); ww(")\n"); pass = pass + 1 }
255 if t7 == 0 { ww("T7 RED determinism: r7a="); wn(r7a); ww(" r7b="); wn(r7b); ww("\n") }
256
257 // ---------- T8 anti-vacuity: the compared things were REAL (stays GREEN under mutations) ----------
258 let dist: i64 = r7a % 1000
259 let nsel: i64 = (r7a / 1000) % 1000
260 var t8: i64 = 0
261 if dist >= 2 { if nsel >= 2 { if ink0 >= INKMIN { if dopen >= 100 { t8 = 1 } } } }
262 if t8 == 1 { ww("T8 GREEN anti-vacuity: "); wn(nsel); ww(" selections, "); wn(dist); ww(" distinct ids, real ink both frames\n"); pass = pass + 1 }
263 if t8 == 0 { ww("T8 RED vacuous: distinct="); wn(dist); ww(" nsel="); wn(nsel); ww(" ink0="); wn(ink0); ww(" dopen="); wn(dopen); ww("\n") }
264
265 ww("nx_gamehud_gate: "); wn(pass); ww("/8\n")
266 if pass == 8 { return 0 }
267 return 1
268}