code wiki / _hdl_build / nx_game_raster.nx
nx_game_raster.nx source
↩ module page · 126 lines · 5501 B
1// nx_game_raster.nx -- LIB: shared INTEGER (no-float) raster primitives for the genre playables. Every
2// flagged open-source genre we build renders its state into the games' packed-RGB framebuffer (R|G<<8|B<<16)
3// through these -- the SAME framebuffer SF5 proved flows through both sovereign display paths (the silicon
4// on-fabric display controller + the x86 UEFI GOP sink). So a genre that renders here IS displayable on
5// sovereign hardware. Pure i64, no float, no canvas. license_tier: ORIGINAL
6import "nx_syscalls.nx"
7import "nx_font8x8.nx"
8
9func gr_pack(r: i64, g: i64, b: i64) -> i64 { return (r & 0xff) | ((g & 0xff) << 8) | ((b & 0xff) << 16) }
10
11func gr_clear(fb: *i64, w: i64, h: i64, c: i64) -> i64 {
12 let n: i64 = w*h; var i: i64 = 0
13 while i < n { fb[i] = c; i = i + 1 }
14 return 0
15}
16
17func gr_px(fb: *i64, w: i64, h: i64, x: i64, y: i64, c: i64) -> i64 {
18 if x < 0 { return 0 } if y < 0 { return 0 } if x >= w { return 0 } if y >= h { return 0 }
19 fb[y*w + x] = c
20 return 0
21}
22
23func gr_rect(fb: *i64, w: i64, h: i64, x0: i64, y0: i64, x1: i64, y1: i64, c: i64) -> i64 {
24 var xs: i64=x0; if xs<0 {xs=0}
25 var ys: i64=y0; if ys<0 {ys=0}
26 var xe: i64=x1; if xe>w {xe=w}
27 var ye: i64=y1; if ye>h {ye=h}
28 var yy: i64=ys
29 while yy<ye { var xx: i64=xs; while xx<xe { fb[yy*w+xx]=c; xx=xx+1 } yy=yy+1 }
30 return 0
31}
32
33// integer filled disc (no float: dx*dx + dy*dy <= rad*rad) -- balls, towers, planets.
34func gr_disc(fb: *i64, w: i64, h: i64, cx: i64, cy: i64, rad: i64, c: i64) -> i64 {
35 var dy: i64 = 0 - rad
36 let r2: i64 = rad*rad
37 while dy <= rad {
38 var dx: i64 = 0 - rad
39 while dx <= rad {
40 if dx*dx + dy*dy <= r2 { gr_px(fb, w, h, cx+dx, cy+dy, c) }
41 dx = dx + 1
42 }
43 dy = dy + 1
44 }
45 return 0
46}
47
48// horizontal + vertical lines (tracks, paths, HUD rules).
49func gr_hline(fb: *i64, w: i64, h: i64, x0: i64, x1: i64, y: i64, c: i64) -> i64 { gr_rect(fb, w, h, x0, y, x1, y+1, c); return 0 }
50func gr_vline(fb: *i64, w: i64, h: i64, x: i64, y0: i64, y1: i64, c: i64) -> i64 { gr_rect(fb, w, h, x, y0, x+1, y1, c); return 0 }
51
52// read back a pixel's green/red channel (gate assertions: "the player cell is green", etc.)
53func gr_r(c: i64) -> i64 { return c & 0xff }
54func gr_g(c: i64) -> i64 { return (c >> 8) & 0xff }
55func gr_b(c: i64) -> i64 { return (c >> 16) & 0xff }
56
57// ===================================================================================================
58// TEXT on the packed-i64 framebuffer (added 2026-07-27, ws=game-interact).
59// ★WHY THIS EXISTS -- it is the ROOT-CAUSE FIX for the long-standing "labels render garbled" bug:
60// nx_gamehud's gh_text/gh_glyph/gh_px index an RGB24 BYTE buffer (`(y*w + x)*3`), but the game screens
61// hand them this framebuffer, where one pixel is an 8-byte i64. Every glyph pixel therefore landed at
62// the wrong byte offset -- horizontally compressed ~2.67x and wrapping into neighbouring rows. The
63// nameplate "solid strip" added earlier treated it as a contrast problem; it was an ADDRESSING problem.
64// (nx_nishi_font_core's nf_draw_text_mem is packed-i64 but its glyph set is only digits + B E H I N O R S W,
65// so it cannot carry real UI text -- hence this, over the full printable-ASCII font8x8 table.)
66// ONE definition, used by every packed-i64 consumer.
67// font8x8 layout: table is 96 glyphs x 8 bytes, index (ch-0x20)*8; byte = one row top->bottom;
68// bit 0 (LSB) = LEFTMOST column.
69// ===================================================================================================
70func gr_glyph(fb: *i64, w: i64, h: i64, ch: i64, x0: i64, y0: i64, c: i64) -> i64 {
71 if ch < 32 { return 0 }
72 if ch > 126 { return 0 }
73 let t: *u8 = font8x8_table()
74 let base: i64 = (ch - 32) * 8
75 var row: i64 = 0
76 while row < 8 {
77 let bits: i64 = t[base + row] as i64
78 var col: i64 = 0
79 while col < 8 {
80 if ((bits >> col) & 1) == 1 { gr_px(fb, w, h, x0 + col, y0 + row, c) }
81 col = col + 1
82 }
83 row = row + 1
84 }
85 return 0
86}
87// draw a NUL-terminated string; returns the x just past the last glyph (proportional advance)
88func gr_text(fb: *i64, w: i64, h: i64, x0: i64, y0: i64, s: *u8, c: i64) -> i64 {
89 let t: *u8 = font8x8_table()
90 var x: i64 = x0
91 var i: i64 = 0
92 while s[i] != (0 as u8) {
93 let ch: i64 = s[i] as i64
94 gr_glyph(fb, w, h, ch, x, y0, c)
95 x = x + font8x8_adv(t, ch)
96 i = i + 1
97 }
98 return x
99}
100// draw a signed integer; returns the x just past the last digit
101func gr_num(fb: *i64, w: i64, h: i64, x0: i64, y0: i64, v: i64, c: i64) -> i64 {
102 let t: *u8 = font8x8_table()
103 var x: i64 = x0
104 var m: i64 = v
105 if m < 0 { gr_glyph(fb, w, h, 45, x, y0, c); x = x + font8x8_adv(t, 45); m = 0 - m }
106 if m == 0 { gr_glyph(fb, w, h, 48, x, y0, c); return x + font8x8_adv(t, 48) }
107 let d: *u8 = sys_mmap(32) as *u8
108 var k: i64 = 0
109 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
110 var j: i64 = 0
111 while j < k {
112 let ch: i64 = d[k - 1 - j] as i64
113 gr_glyph(fb, w, h, ch, x, y0, c)
114 x = x + font8x8_adv(t, ch)
115 j = j + 1
116 }
117 return x
118}
119// pixel width a string will occupy -- lets a caller CENTER text instead of guessing
120func gr_text_w(s: *u8) -> i64 {
121 let t: *u8 = font8x8_table()
122 var wsum: i64 = 0
123 var i: i64 = 0
124 while s[i] != (0 as u8) { wsum = wsum + font8x8_adv(t, s[i] as i64); i = i + 1 }
125 return wsum
126}