code wiki / _hdl_build / nishi_game.nx

nishi_game.nx source

↩ module page · 305 lines · 12421 B

1// nishi_game.nx -- GAME-GUI R1: the sovereign GAME ENGINE WINDOW on native Windows. Presents ANIMATED 2// frames rendered by the SAME baselined textured rasterizer (nx_textured_tri -> nx_tex_sample bilinear, 3// pure integer) into a real Win32 window via SetDIBitsToDevice -- the game ecosystem's GUI+video rung: 4// sovereign frames, hardware-up, no gcc/SDL/GL/toolkit. Scene: textured scrolling-checker band + a 5// BOUNCING textured triangle + animated HUD bar + live frame counter (two screenshots differ => video). 6// Built by nx_pe_compile_win_game.nx (compile me with nx_compile_x86 -> /tmp/nxgame.s first). 7import "nx_syscalls.nx" 8import "nx_font8x8.nx" 9import "nx_textured_tri_fast.nx" // E1-R1 fast path (bit-identical to nx_textured_tri, ~2.8x) 10import "nx_scene3d.nx" // E6-R3: depth-buffered Lambert-lit 3D cube in the live window 11 12func win32(id: i64, args: *i64) -> i64 { return id } 13func win32addr(id: i64) -> i64 { return id } 14 15// user32/gdi32/kernel32 imports by FFI id (IAT slot), provided by the keystone wrapper. 16const W_GetModuleHandleA: i64 = 6 17const W_RegisterClassExA: i64 = 7 18const W_CreateWindowExA: i64 = 8 19const W_ShowWindow: i64 = 9 20const W_PeekMessageA: i64 = 10 21const W_TranslateMessage: i64 = 11 22const W_DispatchMessageA: i64 = 12 23const W_DefWindowProcA: i64 = 13 24const W_GetDC: i64 = 14 25const W_ReleaseDC: i64 = 15 26const W_IsWindow: i64 = 16 27const W_LoadCursorA: i64 = 17 28const W_SetDIBitsToDevice: i64 = 18 29const W_GetClientRect: i64 = 24 30 31const GW: i64 = 640 32const GH: i64 = 480 33 34func w16(p: *u8, o: i64, v: i64) -> i64 { p[o]=(v&0xff) as u8; p[o+1]=((v>>8)&0xff) as u8; return 0 } 35func w32(p: *u8, o: i64, v: i64) -> i64 { 36 p[o]=(v&0xff) as u8; p[o+1]=((v>>8)&0xff) as u8; p[o+2]=((v>>16)&0xff) as u8; p[o+3]=((v>>24)&0xff) as u8; return 0 37} 38func wp(p: *u8, o: i64, v: i64) -> i64 { w32(p,o,v); w32(p,o+4, v / 4294967296); return 0 } 39func rd32(p: *u8, o: i64) -> i64 { return (p[o]&0xff) | ((p[o+1]&0xff)<<8) | ((p[o+2]&0xff)<<16) | ((p[o+3]&0xff)<<24) } 40 41// RGB (3-byte Image) drawing helpers -- everything renders into the sovereign RGB target, 42// then ONE rgb->bgra bridge feeds the DIB blit (same split the browser uses). 43func fill_rect3(fb: *Image, x0: i64, y0: i64, rw: i64, rh: i64, r: i64, g: i64, b: i64) -> i64 { 44 let px: *u8 = fb.pixels 45 var y: i64 = y0 46 if y < 0 { y = 0 } 47 var ye: i64 = y0 + rh 48 if ye > fb.height { ye = fb.height } 49 while y < ye { 50 var x: i64 = x0 51 if x < 0 { x = 0 } 52 var xe: i64 = x0 + rw 53 if xe > fb.width { xe = fb.width } 54 while x < xe { 55 let o: i64 = y * fb.stride + x * 3 56 px[o]=r as u8; px[o+1]=g as u8; px[o+2]=b as u8 57 x = x + 1 58 } 59 y = y + 1 60 } 61 return 0 62} 63func draw_char3(fb: *Image, ft: *u8, c: i64, x0: i64, y0: i64, sc: i64, r: i64, g: i64, b: i64) -> i64 { 64 var cc: i64 = c 65 if cc < 0x20 { return 0 } 66 if cc > 0x7F { cc = 0x3F } 67 let idx: i64 = (cc - 0x20) * 8 68 var row: i64 = 0 69 while row < 8 { 70 let bits: i64 = ft[idx + row] & 0xff 71 var col: i64 = 0 72 while col < 8 { 73 if ((bits >> col) & 1) == 1 { fill_rect3(fb, x0 + col*sc, y0 + row*sc, sc, sc, r, g, b) } 74 col = col + 1 75 } 76 row = row + 1 77 } 78 return 0 79} 80func draw_text3(fb: *Image, ft: *u8, x0: i64, y0: i64, s: *u8, sc: i64, r: i64, g: i64, b: i64) -> i64 { 81 var x: i64 = x0 82 var i: i64 = 0 83 while s[i] != (0 as u8) { 84 let c: i64 = s[i] & 0xff 85 draw_char3(fb, ft, c, x, y0, sc, r, g, b) 86 x = x + 9 * sc 87 i = i + 1 88 } 89 return x 90} 91// int -> chars drawn (uses caller scratch, no per-frame alloc) 92func draw_num3(fb: *Image, ft: *u8, x0: i64, y0: i64, v: i64, sc: i64, r: i64, g: i64, b: i64, tmp: *u8) -> i64 { 93 var n: i64 = v 94 if n < 0 { n = 0 } 95 var k: i64 = 0 96 if n == 0 { tmp[0]=48 as u8; k=1 } 97 while n > 0 { tmp[k]=(48+n%10) as u8; n=n/10; k=k+1 } 98 var x: i64 = x0 99 var j: i64 = k - 1 100 while j >= 0 { 101 draw_char3(fb, ft, tmp[j] as i64, x, y0, sc, r, g, b) 102 x = x + 9 * sc 103 j = j - 1 104 } 105 return x 106} 107 108// the sovereign RGB frame -> Windows BGRA DIB bridge 109func rgb_to_bgra(dst: *u8, src: *u8, n: i64) -> i64 { 110 var i: i64 = 0 111 while i < n { 112 dst[i*4+0] = src[i*3+2] 113 dst[i*4+1] = src[i*3+1] 114 dst[i*4+2] = src[i*3+0] 115 dst[i*4+3] = 0 as u8 116 i = i + 1 117 } 118 return 0 119} 120 121// render one game frame: title chrome + textured band + PLAYER triangle + gold pickups + HUD + 122// win banner. All integer, all through the E1-R2 fast raster path. 123func game_frame(fb: *Image, tex: *Image, ft: *u8, t: i64, bx: i64, by: i64, tmp: *u8, gxa: *i64, gya: *i64, cmask: i64, gold: i64) -> i64 { 124 fill_rect3(fb, 0, 0, GW, GH, 12, 14, 24) 125 fill_rect3(fb, 0, 0, GW, 34, 24, 28, 44) 126 draw_text3(fb, ft, 12, 9, "NISHI GAME -- SOVEREIGN FRAMES" as *u8, 2, 140, 220, 160) 127 draw_text3(fb, ft, 12, 42, "frame" as *u8, 1, 150, 150, 160) 128 draw_num3(fb, ft, 66, 42, t, 1, 230, 200, 90, tmp) 129 draw_text3(fb, ft, 170, 42, "gold" as *u8, 1, 230, 200, 90) 130 let gx2: i64 = draw_num3(fb, ft, 215, 42, gold, 1, 250, 220, 100, tmp) 131 draw_text3(fb, ft, gx2, 42, "/5" as *u8, 1, 150, 150, 160) 132 draw_text3(fb, ft, 12, 424, "arrows / WASD move -- collect the gold -- ESC quits | E1-R2 fast raster, pure integer" as *u8, 1, 110, 115, 130) 133 // textured band (the baselined full-fragment path over a wide quad, E1-R2 fast lane) 134 txtri_render_fast(fb, tex, 0, 60, 0, 0, GW-1, 60, 1023, 0, 0, 219, 0, 1023) 135 txtri_render_fast(fb, tex, GW-1, 60, 1023, 0, GW-1, 219, 1023, 1023, 0, 219, 0, 1023) 136 // gold pickups (uncollected) 137 var gi: i64 = 0 138 while gi < 5 { 139 if ((cmask >> gi) & 1) == 0 { fill_rect3(fb, gxa[gi]-11, gya[gi]-11, 22, 22, 240, 200, 70) } 140 gi = gi + 1 141 } 142 // the PLAYER: textured triangle (same raster path) 143 txtri_render_fast(fb, tex, bx, by-60, 512, 0, bx+55, by+45, 1023, 1023, bx-55, by+45, 0, 1023) 144 if gold >= 5 { draw_text3(fb, ft, 132, 190, "YOU WIN" as *u8, 6, 140, 230, 150) } 145 return 0 146} 147 148func main() -> i64 { 149 let a: *i64 = sys_mmap(2048) as *i64 150 let tmp: *u8 = sys_mmap(64) 151 152 a[0] = 0 153 let hInst: i64 = win32(W_GetModuleHandleA, a) 154 a[0] = 0; a[1] = 32512 155 let hCur: i64 = win32(W_LoadCursorA, a) 156 157 let cls: *u8 = "nishigame" as *u8 158 let title: *u8 = "Nishi Game -- sovereign engine window (native Windows, hardware-up)" as *u8 159 160 let wc: *u8 = sys_mmap(80) 161 w32(wc, 0, 80); w32(wc, 4, 0) 162 wp (wc, 8, win32addr(W_DefWindowProcA)) 163 w32(wc, 16, 0); w32(wc, 20, 0) 164 wp (wc, 24, hInst); wp (wc, 32, 0); wp (wc, 40, hCur); wp (wc, 48, 0); wp (wc, 56, 0) 165 wp (wc, 64, cls as i64); wp (wc, 72, 0) 166 a[0] = wc as i64 167 win32(W_RegisterClassExA, a) 168 169 a[0]=0; a[1]=cls as i64; a[2]=title as i64; a[3]=0x10CF0000 170 a[4]=90; a[5]=70; a[6]=GW; a[7]=GH; a[8]=0; a[9]=0; a[10]=hInst; a[11]=0 171 let hwnd: i64 = win32(W_CreateWindowExA, a) 172 if hwnd == 0 { sys_exit(1); return 1 } 173 a[0]=hwnd; a[1]=5 174 win32(W_ShowWindow, a) 175 176 // establish the window: drain show/paint/activate BEFORE the render loop (anti-teardown lesson) 177 let msg: *u8 = sys_mmap(64) 178 var pw0: i64 = 0 179 while pw0 < 50 { 180 a[0]=msg as i64; a[1]=0; a[2]=0; a[3]=0; a[4]=1 181 if win32(W_PeekMessageA, a) != 0 { win32(W_TranslateMessage, a); win32(W_DispatchMessageA, a) } 182 pw0 = pw0 + 1 183 } 184 185 // sovereign RGB render target + BGRA display DIB + BITMAPINFO 186 let fbi: *Image = nx_image_alloc(GW, GH, 3) 187 let fb: *u8 = sys_mmap(GW * GH * 4) 188 let bmi: *u8 = sys_mmap(64) 189 w32(bmi, 0, 40); w32(bmi, 4, GW); w32(bmi, 8, 0 - GH) 190 w16(bmi, 12, 1); w16(bmi, 14, 32); w32(bmi, 16, 0) 191 let rectbuf: *u8 = sys_mmap(16) 192 let ft: *u8 = font8x8_table() 193 194 // checkerboard texture (the bench scene's texture) 195 let tex: *Image = nx_image_alloc(64, 64, 1) 196 let tp: *u8 = tex.pixels 197 var yy: i64 = 0 198 while yy < 64 { 199 var xx: i64 = 0 200 while xx < 64 { 201 var val: i64 = 30 202 if ((xx / 8) + (yy / 8)) % 2 == 0 { val = 220 } 203 tp[yy*64 + xx] = val as u8 204 xx = xx + 1 205 } 206 yy = yy + 1 207 } 208 209 // E6-R3: a 3D lit cube + its own depth buffer, rendered into the SAME sovereign RGB target 210 let cube: *Cube3D = sc_cube_new(95, 460, 660) 211 let zb3: *i64 = sys_mmap(GW * GH * 8) as *i64 212 213 // gold pickup table (fixed, data-driven) + player state 214 let gxa: *i64 = sys_mmap(64) as *i64 215 let gya: *i64 = sys_mmap(64) as *i64 216 gxa[0]=120; gya[0]=300 217 gxa[1]=240; gya[1]=380 218 gxa[2]=360; gya[2]=320 219 gxa[3]=480; gya[3]=400 220 gxa[4]=560; gya[4]=280 221 var cmask: i64 = 0 222 var gold: i64 = 0 223 var running: i64 = 1 224 var t: i64 = 0 225 var bx: i64 = 100 226 var by: i64 = 340 227 while running == 1 { 228 // pump: WM_CLOSE/ESC = clean quit; WM_KEYDOWN (0x100) = player input; rest dispatched 229 var pd: i64 = 1 230 while pd == 1 { 231 a[0]=msg as i64; a[1]=0; a[2]=0; a[3]=0; a[4]=1 232 if win32(W_PeekMessageA, a) != 0 { 233 let m: i64 = rd32(msg, 8) 234 if m == 0x10 { running = 0 } else { 235 if m == 0x100 { 236 let vk: i64 = rd32(msg, 16) 237 if vk == 0x1B { running = 0 } 238 if vk == 0x25 { bx = bx - 14 } 239 if vk == 0x41 { bx = bx - 14 } 240 if vk == 0x27 { bx = bx + 14 } 241 if vk == 0x44 { bx = bx + 14 } 242 if vk == 0x26 { by = by - 14 } 243 if vk == 0x57 { by = by - 14 } 244 if vk == 0x28 { by = by + 14 } 245 if vk == 0x53 { by = by + 14 } 246 } 247 a[0]=msg as i64; win32(W_TranslateMessage, a) 248 a[0]=msg as i64; win32(W_DispatchMessageA, a) 249 } 250 } else { pd = 0 } 251 } 252 // playfield bounds 253 if bx < 70 { bx = 70 } 254 if bx > 570 { bx = 570 } 255 if by < 250 { by = 250 } 256 if by > 410 { by = 410 } 257 // pickup collision (player center vs gold center) 258 var ci: i64 = 0 259 while ci < 5 { 260 if ((cmask >> ci) & 1) == 0 { 261 var ddx: i64 = bx - gxa[ci] 262 if ddx < 0 { ddx = 0 - ddx } 263 var ddy: i64 = by - gya[ci] 264 if ddy < 0 { ddy = 0 - ddy } 265 if ddx < 40 { if ddy < 40 { cmask = cmask | (1 << ci); gold = gold + 1 } } 266 } 267 ci = ci + 1 268 } 269 270 // E6-R3: a spinning, depth-sorted, Lambert-lit 3D cube in the live sovereign window 271 fill_rect3(fbi, 0, 0, GW, GH, 12, 14, 24) 272 fill_rect3(fbi, 0, 0, GW, 40, 22, 26, 40) 273 depth_clear(zb3, GW * GH, 1000000000) 274 sc_cube_draw(cube, fbi, zb3, GW / 2, GH / 2 + 24, t * 3, 0 - 24) 275 draw_text3(fbi, ft, 12, 10, "NISHI 3D -- SOVEREIGN LIT CUBE" as *u8, 2, 150, 220, 170) 276 draw_text3(fbi, ft, 12, GH - 22, "depth-buffered - Lambert-lit - pure integer - no gcc/GL - frame" as *u8, 1, 120, 130, 150) 277 draw_num3(fbi, ft, 470, GH - 22, t, 1, 230, 200, 90, tmp) 278 rgb_to_bgra(fb, fbi.pixels, GW * GH) 279 280 // blit the client-visible region (clamped to the render size) 281 a[0]=hwnd; a[1]=rectbuf as i64; win32(W_GetClientRect, a) 282 var cw: i64 = rd32(rectbuf, 8) 283 var ch: i64 = rd32(rectbuf, 12) 284 if cw > GW { cw = GW } 285 if ch > GH { ch = GH } 286 if cw > 0 { if ch > 0 { 287 // keep the DIB header + scanline count matched to the CLIENT height (the browser's proven 288 // contract) so the frame is TOP-anchored -- a taller source with cScanLines=GH shows the 289 // BOTTOM rows and cuts the chrome strip 290 w32(bmi, 8, 0 - ch) 291 a[0]=hwnd 292 let hdc: i64 = win32(W_GetDC, a) 293 a[0]=hdc; a[1]=0; a[2]=0; a[3]=cw; a[4]=ch; a[5]=0; a[6]=0; a[7]=0; a[8]=ch; a[9]=fb as i64; a[10]=bmi as i64; a[11]=0 294 win32(W_SetDIBitsToDevice, a) 295 a[0]=hwnd; a[1]=hdc; win32(W_ReleaseDC, a) 296 } } 297 298 a[0]=hwnd 299 if win32(W_IsWindow, a) == 0 { running = 0 } 300 sys_sleep_ms(16) 301 t = t + 1 302 } 303 sys_exit(0) 304 return 0 305}