nx_nishios_kbd.nx source
↩ module page · 140 lines · 9098 B
1// nx_nishios_kbd.nx -- NishiOS GUI rung-5: LIVE KEYBOARD INPUT + a real shell line editor.
2// Turns the terminal from a `cat` into something you TYPE into. Models the PS/2 keyboard path a kernel
3// actually walks: read scancodes from the controller (port 0x60), translate set-1 make codes -> ASCII
4// via a keymap, handle BACKSPACE line-editing, echo, and on ENTER dispatch a shell builtin. The demo
5// types 'v','e','e',<backspace>,'r',<enter> -> the line editor yields "ver" (the stray 'e' erased), the
6// shell runs it -> "NishiOS 0.1 sovereign", and the terminal renders prompt+command+output.
7// KAT: the line editor produced "ver" (backspace worked), the shell dispatched the right output, both the
8// command line and the output render, and the BMP exports.
9// HONEST SEAM: the scancode SOURCE is scripted (no live PS/2 controller modeled on the emu -- that IRQ1
10// wiring is the hardware rung); the driver LOGIC (port read, set-1 keymap, line edit, echo, dispatch) is
11// real. No hw writes (Rule 26). expect_exit: 0 license_tier: ORIGINAL
12import "nx_fb.nx"
13import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
14import "nx_font8x8.nx"
15const K_MAGIC_192054: i64 = 192054
16
17func kb_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
18// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
19// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
20// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
21// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
22func kb_num(v: i64) -> i64 { nxi_out(v); return 0 }
23func kb_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
24func kb_streq(a: *u8, alen: i64, b: *u8) -> i64 { let bl: i64=kb_slen(b); if alen!=bl { return 0 } var i: i64=0; while i<alen { if a[i]!=b[i] { return 0 } i=i+1 } return 1 }
25func kb_scopy(dst: *u8, doff: i64, src: *u8) -> i64 { var i: i64=0; while src[i]!=(0 as u8) { dst[doff+i]=src[i]; i=i+1 } return doff+i }
26
27// font8x8 text (bit0 = leftmost column)
28func kt_char(fb: *u8, W: i64, H: i64, table: *u8, x: i64, y: i64, ch: i64, r: i64, g: i64, b: i64) -> i64 {
29 if ch<0x20 { return 0 }
30 if ch>0x7E { return 0 }
31 let gi: i64=(ch-0x20)*8
32 var row: i64=0
33 while row<8 { let bits: i64=table[gi+row] as i64; var col: i64=0; while col<8 { if ((bits>>col)&1)==1 { fb_setpx(fb,W,H, x+col, y+row, r,g,b) } col=col+1 } row=row+1 }
34 return 0
35}
36func kt_str(fb: *u8, W: i64, H: i64, table: *u8, x: i64, y: i64, s: *u8, r: i64, g: i64, b: i64) -> i64 {
37 var i: i64=0
38 while s[i]!=(0 as u8) { kt_char(fb,W,H,table, x+i*8, y, s[i] as i64, r,g,b); i=i+1 }
39 return 0
40}
41func kt_count(fb: *u8, W: i64, x0: i64, y0: i64, x1: i64, y1: i64) -> i64 {
42 var c: i64=0; var y: i64=y0
43 while y<y1 { var x: i64=x0; while x<x1 { if (fb[(y*W+x)*3+1] as i64) > 180 { c=c+1 } x=x+1 } y=y+1 }
44 return c
45}
46
47// set-1 make-code -> ASCII keymap (the letters/space/digits we need)
48func build_keymap(km: *u8) -> i64 {
49 var i: i64=0
50 while i<128 { km[i]=0 as u8; i=i+1 }
51 km[0x10]=113 as u8; km[0x11]=119 as u8; km[0x12]=101 as u8; km[0x13]=114 as u8; km[0x14]=116 as u8; km[0x15]=121 as u8; km[0x16]=117 as u8; km[0x17]=105 as u8; km[0x18]=111 as u8; km[0x19]=112 as u8 // q w e r t y u i o p
52 km[0x1E]=97 as u8; km[0x1F]=115 as u8; km[0x20]=100 as u8; km[0x21]=102 as u8; km[0x22]=103 as u8; km[0x23]=104 as u8; km[0x24]=106 as u8; km[0x25]=107 as u8; km[0x26]=108 as u8 // a s d f g h j k l
53 km[0x2C]=122 as u8; km[0x2D]=120 as u8; km[0x2E]=99 as u8; km[0x2F]=118 as u8; km[0x30]=98 as u8; km[0x31]=110 as u8; km[0x32]=109 as u8 // z x c v b n m
54 km[0x39]=32 as u8 // space
55 return 0
56}
57
58// shell dispatch -> writes output string to `out`, returns its length
59func run_cmd(line: *u8, linelen: i64, out: *u8) -> i64 {
60 if kb_streq(line,linelen,"ver\x00" as *u8)==1 { let n: i64=kb_scopy(out,0,"NishiOS 0.1 sovereign\x00" as *u8); out[n]=0 as u8; return n }
61 if kb_streq(line,linelen,"help\x00" as *u8)==1 { let n: i64=kb_scopy(out,0,"builtins: help ver clear\x00" as *u8); out[n]=0 as u8; return n }
62 if kb_streq(line,linelen,"clear\x00" as *u8)==1 { out[0]=0 as u8; return 0 }
63 var o: i64=kb_scopy(out,0,"unknown: \x00" as *u8)
64 var i: i64=0
65 while i<linelen { out[o]=line[i]; o=o+1; i=i+1 }
66 out[o]=0 as u8
67 return o
68}
69
70func main() -> i64 {
71 kb_puts("NishiOS GUI rung-5: LIVE KEYBOARD INPUT (PS/2 scancodes -> keymap -> line editor -> shell)\n" as *u8)
72
73 // PS/2 scancode stream (set-1 make codes): v, e, e, <backspace>, r, <enter>
74 let sc: *u8 = sys_mmap(16)
75 sc[0]=0x2F as u8; sc[1]=0x12 as u8; sc[2]=0x12 as u8; sc[3]=0x0E as u8; sc[4]=0x13 as u8; sc[5]=0x1C as u8
76 let nsc: i64 = 6
77 let km: *u8 = sys_mmap(128)
78 build_keymap(km)
79
80 // the keyboard driver + line editor
81 let line: *u8 = sys_mmap(128)
82 var linelen: i64=0
83 var submitted: i64=0
84 var p: i64=0
85 while p<nsc {
86 let code: i64 = sc[p] as i64
87 if code==0x1C { submitted=1; p=nsc } else {
88 if code==0x0E { if linelen>0 { linelen=linelen-1 } } else {
89 let ascii: i64 = km[code] as i64
90 if ascii!=0 { line[linelen]=ascii as u8; linelen=linelen+1 }
91 }
92 p=p+1
93 }
94 }
95 line[linelen]=0 as u8
96
97 let out: *u8 = sys_mmap(128)
98 let outlen: i64 = run_cmd(line, linelen, out)
99 kb_puts(" typed line (after edit) = '" as *u8); sys_write(1,line,linelen); kb_puts("' submitted=" as *u8); kb_num(submitted); kb_puts(" -> output = '" as *u8); sys_write(1,out,outlen); kb_puts("'\n" as *u8)
100
101 // ---- render the terminal: prompt + typed command + output + next prompt ----
102 let W: i64=320
103 let H: i64=200
104 let fb: *u8 = sys_mmap(W*H*3)
105 let table: *u8 = font8x8_table()
106 fb_rect(fb,W,H, 0,0, W,H, 8,30,45)
107 fb_rect(fb,W,H, 18,26, 284,150, 60,120,80)
108 fb_rect(fb,W,H, 19,27, 282,148, 12,16,12)
109 fb_rect(fb,W,H, 19,27, 282,12, 30,90,45)
110 kt_str(fb,W,H,table, 23,28, "NISHIOS terminal\x00" as *u8, 220,255,220)
111 // line 0: prompt + the typed command (echoed)
112 let pr: *u8 = "NISHIOS> \x00"
113 kt_str(fb,W,H,table, 24,46, pr, 120,255,120)
114 kt_str(fb,W,H,table, 24+9*8, 46, line, 230,255,230)
115 // line 1: the command output
116 kt_str(fb,W,H,table, 24,60, out, 180,230,180)
117 // line 2: next prompt + cursor block
118 kt_str(fb,W,H,table, 24,74, pr, 120,255,120)
119 fb_rect(fb,W,H, 24+9*8, 74, 6,8, 120,255,120)
120
121 let sz: i64 = fb_bmp_save(fb, W, H, "knowledge/status/nishios_kbd.bmp\x00" as *u8)
122 let html: *u8 = "<!doctype html><html><body style=\x27margin:0;background:#0a0a12;display:flex;align-items:center;justify-content:center;height:100vh\x27><div><img src=\x27nishios_kbd.bmp\x27 style=\x27image-rendering:pixelated;width:720px;border:1px solid #333\x27><div style=\x27color:#8af;font-family:monospace;text-align:center;margin-top:8px\x27>NishiOS terminal -- typed via the PS/2 keyboard driver (scancodes -> line editor -> shell)</div></div></body></html>\x00"
123 let hd: i64 = sys_openat_wr("knowledge/status/nishios_kbd.html\x00" as *u8, 0x1a4)
124 if hd>0 { var hn: i64=0; while html[hn]!=(0 as u8){hn=hn+1} sys_write(hd, html, hn); sys_close(hd) }
125 kb_puts(" rendered terminal -> knowledge/status/nishios_kbd.bmp + .html\n" as *u8)
126
127 var pass: i64=0
128 var ttl: i64=0
129 ttl=ttl+1; kb_puts(" T1 line editor produced 'ver' (backspace erased the stray 'e'): " as *u8); if kb_streq(line,linelen,"ver\x00" as *u8)==1 { pass=pass+1; kb_puts("PASS\n" as *u8) } else { kb_puts("FAIL\n" as *u8) }
130 ttl=ttl+1; kb_puts(" T2 shell dispatched -> 'NishiOS 0.1 sovereign': " as *u8); if kb_streq(out,outlen,"NishiOS 0.1 sovereign\x00" as *u8)==1 { pass=pass+1; kb_puts("PASS\n" as *u8) } else { kb_puts("FAIL\n" as *u8) }
131 let cmd_px: i64 = kt_count(fb, W, 24, 46, 300, 54)
132 ttl=ttl+1; kb_puts(" T3 command line rendered (text pixels>40): " as *u8); if cmd_px>40 { pass=pass+1; kb_puts("PASS\n" as *u8) } else { kb_puts("FAIL\n" as *u8) }
133 let out_px: i64 = kt_count(fb, W, 24, 60, 300, 68)
134 ttl=ttl+1; kb_puts(" T4 output line rendered (text pixels>40): " as *u8); if out_px>40 { pass=pass+1; kb_puts("PASS\n" as *u8) } else { kb_puts("FAIL\n" as *u8) }
135 ttl=ttl+1; kb_puts(" T5 BMP exported: " as *u8); if sz==K_MAGIC_192054 { pass=pass+1; kb_puts("PASS\n" as *u8) } else { kb_puts("FAIL\n" as *u8) }
136
137 kb_puts("NISHIOS-KBD-GATE passed " as *u8); kb_num(pass); kb_puts("/" as *u8); kb_num(ttl)
138 if pass==ttl { kb_puts(" verdict=GREEN (a real keyboard-driven shell line editor; open nishios_kbd.html to SEE the typed session)\n" as *u8); sys_exit(0); return 0 }
139 kb_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1
140}