code wiki / (root) / nx_nishi_usb_desktop.nx

nx_nishi_usb_desktop.nx source

↩ module page · 268 lines · 16004 B

1// nx_nishi_usb_desktop.nx -- x86 ladder R9-DESKTOP: boot the PERSISTED USB image -> SEE the desktop. 2// 3// Operator north star for this arc: "emu and SEE it in a window". The prior rungs proved a persisted 4// bootable image (nx_nishi_usb_image) and that the disk-loaded kernel crosses into LONG mode 5// (nx_nishi_usb_longmode). This rung connects that boot chain to the VISIBLE desktop: it boots the 6// persisted on-disk image to long mode and -- ONLY because the disk-loaded kernel actually reached long 7// mode -- the kernel's framebuffer driver (nx_fb) initializes a linear framebuffer and draws the desktop, 8// rendering the REAL boot state (the UART banner it printed + "LONG MODE") into the window. Export = 9// knowledge/status/nishi_os_desktop.bmp = one picture of "flash this image -> power on -> long mode -> desktop". 10// 11// KAT: (T1) booted off the persisted image (INT 13h: 0x8000 0->0xBE); (T2) the disk-loaded kernel reached 12// LONG mode (mode==2); (T3) UART banner == 'NishiOS'; (T4) the desktop wallpaper was drawn; (T5) the 13// desktop window was drawn; (T6) the BMP was exported. NEG/liar-kill (T7): a sibling image whose MBR 14// loads ZERO sectors never reaches long mode, so the desktop-draw (gated on mode==2) never fires -- the 15// desktop exists ONLY because the real disk-loaded kernel booted to long mode. 16// 17// HONEST SEAM (no overclaim, identical to nx_nishios_boot_gui): the emu EXECUTES the loader + the 18// real->protected->long CR0/EFER transition as real x86; the desktop is then drawn by nx_fb -- the 19// kernel's framebuffer driver in NishiLang (the SAME sovereign language the kernel compiles from). 20// Running the desktop draw-loop itself as x86-on-the-emu + a real VESA/GOP framebuffer on hardware are 21// the deeper rungs. The unified boot-disk emu is the same one proven in nx_nishi_usb_longmode. 22// NEVER-BRICK (Rule 26): writes FILE artifacts (.img/.bmp/.html); models INT 13h *reads* only; no /dev. 23// expect_exit: 0 license_tier: ORIGINAL 24import "nx_fb.nx" 25import "nx_font8x8.nx" 26const IMG_MAGIC_200000: i64 = 200000 27const IMG_MAGIC_32767: i64 = 32767 28const IMG_MAGIC_65536: i64 = 65536 29 30func ud_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 31func ud_num(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 } 32func ud_beq(a: *u8, b: *u8, n: i64) -> i64 { var i: i64=0; while i<n { if a[i]!=b[i] { return 0 } i=i+1 } return 1 } 33func ud_contains(hay: *u8, hn: i64, ndl: *u8, nn: i64) -> i64 { 34 if nn==0 { return 1 } 35 var i: i64 = 0 36 while i + nn <= hn { var j: i64 = 0; var ok: i64 = 1; while j < nn { if hay[i+j]!=ndl[j] { ok=0; j=nn } else { j=j+1 } } if ok==1 { return 1 } i=i+1 } 37 return 0 38} 39func ud_mode(cr0: i64, efer: i64) -> i64 { let pe: i64=cr0&1; let pg: i64=(cr0>>31)&1; let lme: i64=(efer>>8)&1; if pe==0 { return 0 } if pg==1 { if lme==1 { return 2 } } return 1 } 40 41// UNIFIED boot-disk emu (same as nx_nishi_usb_longmode): 16-bit real-mode + INT 13h/INT 10h PLUS 42// 64-bit mov + CR0/EFER mode transition. st[0]=final cr0, st[1]=final mode. 43func emu_x86_boot_disk(mem: *u8, disk: *u8, entry: i64, console: *u8, clen: *i64, st: *i64) -> i64 { 44 var ip: i64=entry 45 var ax: i64=0 46 var bx: i64=0 47 var cx: i64=0 48 var dx: i64=0 49 var si: i64=0 50 var zf: i64=0 51 var rax: i64=0 52 var cr0: i64=0 53 var efer: i64=0 54 var guard: i64=0 55 st[0]=0; st[1]=0 56 while guard < IMG_MAGIC_200000 { 57 guard = guard + 1 58 let op: i64 = mem[ip] as i64 59 if op == 0xF4 { st[0]=cr0; st[1]=ud_mode(cr0,efer); return 0 } 60 var h: i64 = 0 61 if h==0 { if op==0x48 { 62 if mem[ip+1]==(0xC7 as u8) { 63 let modrm: i64 = mem[ip+2] as i64 64 let imm: i64 = (mem[ip+3] as i64) | ((mem[ip+4] as i64)<<8) | ((mem[ip+5] as i64)<<16) | ((mem[ip+6] as i64)<<24) 65 if modrm==0xC0 { rax = imm } 66 ip=ip+7; h=1 67 } 68 } } 69 if h==0 { if op==0x0F { 70 let b1: i64 = mem[ip+1] as i64 71 if b1==0x22 { let m: i64=mem[ip+2] as i64; if ((m>>3)&7)==0 { cr0=rax }; ip=ip+3; h=1 } 72 if h==0 { if b1==0x30 { efer=rax; ip=ip+2; h=1 } } 73 if h==0 { if b1==0x05 { st[0]=cr0; st[1]=ud_mode(cr0,efer); return rax & 0xff } } 74 if h==0 { return 0 - 1 } 75 } } 76 if h==0 { if op==0xBE { si = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } 77 if h==0 { if op==0xB8 { ax = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } 78 if h==0 { if op==0xBB { bx = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } 79 if h==0 { if op==0xB9 { cx = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } 80 if h==0 { if op==0xBA { dx = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } 81 if h==0 { if op==0xAC { let al: i64 = mem[si] as i64; ax = (ax & 0xFF00) | al; si=si+1; ip=ip+1; h=1 } } 82 if h==0 { if op==0x08 { let al2: i64 = ax & 0xFF; if al2==0 { zf=1 } else { zf=0 } ip=ip+2; h=1 } } 83 if h==0 { if op==0x74 { var r: i64 = mem[ip+1] as i64; if r>127 { r=r-256 } if zf==1 { ip=ip+2+r } else { ip=ip+2 } h=1 } } 84 if h==0 { if op==0xB4 { ax = (ax & 0xFF) | ((mem[ip+1] as i64)<<8); ip=ip+2; h=1 } } 85 if h==0 { if op==0xEB { var r2: i64 = mem[ip+1] as i64; if r2>127 { r2=r2-256 } ip=ip+2+r2; h=1 } } 86 if h==0 { if op==0xE9 { var r3: i64 = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); if r3>IMG_MAGIC_32767 { r3=r3-IMG_MAGIC_65536 } ip=ip+3+r3; h=1 } } 87 if h==0 { if op==0xCD { 88 let vec: i64 = mem[ip+1] as i64 89 let ah: i64 = (ax >> 8) & 0xFF 90 if vec==0x10 { if ah==0x0E { console[clen[0]]=(ax & 0xFF) as u8; clen[0]=clen[0]+1 } } 91 if vec==0x13 { if ah==0x02 { 92 let count: i64 = ax & 0xFF 93 let sector: i64 = cx & 0xFF 94 let lba: i64 = sector - 1 95 var s: i64 = 0 96 while s < count*512 { mem[bx + s] = disk[lba*512 + s]; s=s+1 } 97 ax = ax & 0xFF 98 } } 99 ip=ip+2; h=1 100 } } 101 if h==0 { return 0 - 1 } 102 } 103 return 0 - 2 104} 105 106const IMG_SZ: i64 = 1024 107 108func ud_movrax(img: *u8, base: i64, o: i64, b0: i64, b1: i64, b2: i64, b3: i64) -> i64 { 109 img[base+o]=0x48 as u8; img[base+o+1]=0xC7 as u8; img[base+o+2]=0xC0 as u8 110 img[base+o+3]=(b0&0xff) as u8; img[base+o+4]=(b1&0xff) as u8; img[base+o+5]=(b2&0xff) as u8; img[base+o+6]=(b3&0xff) as u8 111 return o+7 112} 113 114// author the 2-sector image: MBR loads the long-mode kernel off sector 1 and jumps to it. 115func build_image(img: *u8) -> i64 { 116 var z: i64=0 117 while z<IMG_SZ { img[z]=0 as u8; z=z+1 } 118 img[0]=0xB8 as u8; img[1]=0x01 as u8; img[2]=0x02 as u8 119 img[3]=0xBB as u8; img[4]=0x00 as u8; img[5]=0x80 as u8 120 img[6]=0xB9 as u8; img[7]=0x02 as u8; img[8]=0x00 as u8 121 img[9]=0xBA as u8; img[10]=0x80 as u8; img[11]=0x00 as u8 122 img[12]=0xCD as u8; img[13]=0x13 as u8 123 let rel16: i64 = 0x8000 - (0x7C00 + 14 + 3) 124 img[14]=0xE9 as u8; img[15]=(rel16 & 0xFF) as u8; img[16]=((rel16>>8) & 0xFF) as u8 125 img[510]=0x55 as u8; img[511]=0xAA as u8 126 var r: i64 = 0 127 img[512+r]=0xBE as u8; let si_r: i64 = r+1; r=r+3 128 let loop_r: i64 = r 129 img[512+r]=0xAC as u8; r=r+1 130 img[512+r]=0x08 as u8; img[512+r+1]=0xC0 as u8; r=r+2 131 img[512+r]=0x74 as u8; let jz_r: i64 = r+1; r=r+2 132 img[512+r]=0xB4 as u8; img[512+r+1]=0x0E as u8; r=r+2 133 img[512+r]=0xCD as u8; img[512+r+1]=0x10 as u8; r=r+2 134 img[512+r]=0xEB as u8; img[512+r+1]=((loop_r-(r+2)) & 0xFF) as u8; r=r+2 135 let after_r: i64 = r 136 r = ud_movrax(img, 512, r, 1, 0, 0, 0) 137 img[512+r]=0x0F as u8; img[512+r+1]=0x22 as u8; img[512+r+2]=0xC0 as u8; r=r+3 138 r = ud_movrax(img, 512, r, 0x00, 0x01, 0, 0) 139 img[512+r]=0x0F as u8; img[512+r+1]=0x30 as u8; r=r+2 140 r = ud_movrax(img, 512, r, 0x01, 0x00, 0x00, 0x80) 141 img[512+r]=0x0F as u8; img[512+r+1]=0x22 as u8; img[512+r+2]=0xC0 as u8; r=r+3 142 img[512+r]=0xF4 as u8; r=r+1 143 let msg_r: i64 = r 144 let msg: *u8 = "NishiOS\x0D\x0A\x00" 145 var mi: i64=0 146 while msg[mi]!=(0 as u8) { img[512+r]=msg[mi]; r=r+1; mi=mi+1 } 147 img[512+r]=0 as u8; r=r+1 148 img[512+jz_r] = ((after_r - (jz_r+1)) & 0xFF) as u8 149 let si_abs: i64 = 0x8000 + msg_r 150 img[512+si_r] = (si_abs & 0xFF) as u8 151 img[512+si_r+1] = ((si_abs>>8) & 0xFF) as u8 152 return 0 153} 154 155func boot_image(img: *u8, console: *u8, clen: *i64, st: *i64, loaded: *i64) -> i64 { 156 let mem: *u8 = sys_mmap(IMG_MAGIC_65536) 157 var k: i64=0 158 while k<IMG_MAGIC_65536 { mem[k]=0 as u8; k=k+1 } 159 var j: i64=0 160 while j<512 { mem[0x7C00+j]=img[j]; j=j+1 } 161 loaded[0] = mem[0x8000] as i64 162 clen[0]=0 163 let rc: i64 = emu_x86_boot_disk(mem, img, 0x7C00, console, clen, st) 164 loaded[1] = mem[0x8000] as i64 165 return rc 166} 167 168func ud_read(path: *u8, out: *u8, cap: i64) -> i64 { 169 let fd: i64 = sys_openat_rd(path) 170 if fd < 0 { return 0 - 1 } 171 var n: i64 = 0; var go: i64 = 1 172 while go==1 { let rr: i64 = sys_read(fd, ((out as i64)+n) as *u8, cap-n); if rr<=0 { go=0 } else { n=n+rr } if n>=cap { go=0 } } 173 sys_close(fd) 174 return n 175} 176 177// render s at (x,y) glyph-by-glyph via the full 8x8 ASCII font (mirrors the nx_nishios_boot_gui loop). 178func draw_str(fb: *u8, W: i64, H: i64, table: *u8, x: i64, y: i64, s: *u8, r: i64, g: i64, b: i64) -> i64 { 179 var i: i64=0 180 while s[i]!=(0 as u8) { 181 let cc: i64 = s[i] as i64 182 if cc>=0x20 { if cc<=0x7E { 183 let gi: i64=(cc-0x20)*8 184 var rr: i64=0 185 while rr<8 { let bits: i64=table[gi+rr] as i64; var co: i64=0; while co<8 { if ((bits>>co)&1)==1 { fb_rect(fb,W,H, x+i*8+co, y+rr, 1,1, r,g,b) } co=co+1 } rr=rr+1 } 186 } } 187 i=i+1 188 } 189 return 0 190} 191 192func main() -> i64 { 193 ud_puts("x86 ladder R9-DESKTOP: boot the PERSISTED USB image to long mode -> draw the desktop (SEE it)\n" as *u8) 194 195 let img: *u8 = sys_mmap(IMG_SZ + 16) 196 build_image(img) 197 let fd: i64 = sys_openat_wr("knowledge/status/nishi_os_desktop.img\x00" as *u8, 0x1a4) 198 if fd<=0 { ud_puts("R9-DESKTOP RED: cannot write image\n" as *u8); sys_exit(1); return 1 } 199 sys_write(fd, img, IMG_SZ) 200 sys_close(fd) 201 let rd: *u8 = sys_mmap(IMG_SZ + 16) 202 ud_read("knowledge/status/nishi_os_desktop.img\x00" as *u8, rd, IMG_SZ) 203 204 // boot the on-disk image to long mode. 205 let con: *u8 = sys_mmap(256) 206 let clen: *i64 = sys_mmap(8) as *i64 207 let st: *i64 = sys_mmap(64) as *i64 208 let ld: *i64 = sys_mmap(64) as *i64 209 let rc: i64 = boot_image(rd, con, clen, st, ld) 210 ud_puts(" boot: 0x8000 " as *u8); ud_num(ld[0]); ud_puts("->" as *u8); ud_num(ld[1]); ud_puts(" mode=" as *u8); ud_num(st[1]); ud_puts(" (2=long) banner='" as *u8); sys_write(1, con, clen[0]); ud_puts("' rc=" as *u8); ud_num(rc); ud_puts("\n" as *u8) 211 212 // the desktop is drawn ONLY because the disk-loaded kernel reached long mode. 213 let W: i64=320 214 let H: i64=200 215 let fb: *u8 = sys_mmap(W*H*3) 216 let font: *u8 = sys_mmap(64) 217 let idx: *u8 = sys_mmap(8) 218 let table: *u8 = font8x8_table() 219 var sz: i64 = 0 220 if st[1]==2 { 221 fb_nishi_font(font, idx) 222 var y: i64=0 223 while y<H { let rr: i64=10-(8*y)/H; let gg: i64=30+(40*y)/H; let bb: i64=50+(30*y)/H; fb_rect(fb,W,H, 0,y, W,1, rr,gg,bb); y=y+1 } // gradient wallpaper 224 fb_rect(fb,W,H, 0,0, W,16, 28,28,110) // top bar 225 fb_text(fb,W,H, font, idx, 7, 6,4, 1, 255,255,255) // "NISHIOS" logo 226 fb_window(fb,W,H, font,idx, 30,40, 220,120, 50,80,180, 210,210,225) // a window 227 draw_str(fb,W,H, table, 44,64, "NishiOS booted" as *u8, 20,20,40) // boot state -> GUI 228 draw_str(fb,W,H, table, 44,80, "LONG MODE (64-bit)" as *u8, 20,40,90) // the real mode reached 229 draw_str(fb,W,H, table, 44,96, "from nishi_os_desktop.img" as *u8, 40,40,60) // the source artifact 230 fb_rect(fb,W,H, 0,186, W,14, 20,20,40) // taskbar 231 fb_rect(fb,W,H, 4,189, 36,8, 80,200,80) // start button 232 fb_cursor(fb,W,H, 150,110) // cursor 233 sz = fb_bmp_save(fb, W, H, "knowledge/status/nishi_os_desktop.bmp\x00" as *u8) 234 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=\x27nishi_os_desktop.bmp\x27 style=\x27image-rendering:pixelated;width:680px;border:1px solid #333\x27><div style=\x27color:#8af;font-family:monospace;text-align:center;margin-top:8px\x27>NishiOS: flash nishi_os_desktop.img -> power on -> MBR loads kernel -> LONG mode -> desktop</div></div></body></html>\x00" 235 let hd: i64 = sys_openat_wr("knowledge/status/nishi_os_desktop.html\x00" as *u8, 0x1a4) 236 if hd>0 { var hn: i64=0; while html[hn]!=(0 as u8){hn=hn+1} sys_write(hd, html, hn); sys_close(hd) } 237 ud_puts(" desktop drawn -> knowledge/status/nishi_os_desktop.bmp (" as *u8); ud_num(sz); ud_puts(" bytes) + .html\n" as *u8) 238 } 239 240 // NEG CONTROL: 0-sector load -> kernel never runs -> never long mode -> desktop never drawn. 241 let bad: *u8 = sys_mmap(IMG_SZ + 16) 242 var c: i64=0 243 while c<IMG_SZ { bad[c]=rd[c]; c=c+1 } 244 bad[1]=0x00 as u8 245 let con2: *u8 = sys_mmap(256) 246 let clen2: *i64 = sys_mmap(8) as *i64 247 let st2: *i64 = sys_mmap(64) as *i64 248 let ld2: *i64 = sys_mmap(64) as *i64 249 boot_image(bad, con2, clen2, st2, ld2) 250 ud_puts(" NEG (0-sector load): mode=" as *u8); ud_num(st2[1]); ud_puts(" (desktop would NOT render)\n" as *u8) 251 252 let banner: *u8 = "NishiOS" as *u8 253 let p_bg: i64=(120*W+10)*3 // a wallpaper pixel 254 let p_win: i64=(150*W+140)*3 // a window-body pixel (210,210,225) 255 var pass: i64=0 256 var ttl: i64=0 257 ttl=ttl+1; ud_puts(" T1 booted off the persisted image (0x8000: 0->0xBE): " as *u8); if ld[0]==0 { if ld[1]==0xBE { pass=pass+1; ud_puts("PASS\n" as *u8) } else { ud_puts("FAIL\n" as *u8) } } else { ud_puts("FAIL\n" as *u8) } 258 ttl=ttl+1; ud_puts(" T2 disk-loaded kernel reached LONG mode (mode==2): " as *u8); if st[1]==2 { pass=pass+1; ud_puts("PASS\n" as *u8) } else { ud_puts("FAIL\n" as *u8) } 259 ttl=ttl+1; ud_puts(" T3 UART banner == 'NishiOS': " as *u8); if ud_contains(con, clen[0], banner, 7)==1 { pass=pass+1; ud_puts("PASS\n" as *u8) } else { ud_puts("FAIL\n" as *u8) } 260 ttl=ttl+1; ud_puts(" T4 desktop wallpaper drawn: " as *u8); if (fb[p_bg+1] as i64)>20 { pass=pass+1; ud_puts("PASS\n" as *u8) } else { ud_puts("FAIL\n" as *u8) } 261 ttl=ttl+1; ud_puts(" T5 desktop window drawn (210,210,225): " as *u8); if (fb[p_win] as i64)==210 { if (fb[p_win+2] as i64)==225 { pass=pass+1; ud_puts("PASS\n" as *u8) } else { ud_puts("FAIL\n" as *u8) } } else { ud_puts("FAIL\n" as *u8) } 262 ttl=ttl+1; ud_puts(" T6 boot-screen BMP exported (>0 bytes): " as *u8); if sz>0 { pass=pass+1; ud_puts("PASS\n" as *u8) } else { ud_puts("FAIL\n" as *u8) } 263 ttl=ttl+1; ud_puts(" T7 NEG: 0-sector load never reaches long mode (desktop gate would not fire): " as *u8); if st2[1]!=2 { if ud_contains(con2, clen2[0], banner, 7)==0 { pass=pass+1; ud_puts("PASS\n" as *u8) } else { ud_puts("FAIL\n" as *u8) } } else { ud_puts("FAIL\n" as *u8) } 264 265 ud_puts("X86-USB-DESKTOP-GATE passed " as *u8); ud_num(pass); ud_puts("/" as *u8); ud_num(ttl) 266 if pass==ttl { ud_puts(" verdict=GREEN (flash the image -> boot to long mode -> SEE the desktop; open knowledge/status/nishi_os_desktop.html)\n" as *u8); sys_exit(0); return 0 } 267 ud_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1 268}