code wiki / (root) / nx_nishios_kbd.nx

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}