code wiki / (root) / nx_nishios_irq.nx

nx_nishios_irq.nx source

↩ module page · 171 lines · 13729 B

1// nx_nishios_irq.nx -- NishiOS GUI rung-8: LIVE PS/2 IRQ1 (real interrupt-driven keyboard). 2// Closes the "input is scripted" seam. The emu now models hardware INTERRUPT DELIVERY: between 3// instructions, if IF (interrupt flag) is set and the keyboard controller has a pending scancode, the 4// CPU TAKES the interrupt -- pushes the return address, vectors through the IDT entry idt[0x21] to the 5// handler, which reads the scancode via INB port 0x60, stores it, and IRETs. The handler runs ONLY 6// because an interrupt was delivered, not because anyone called it. Kernel: install idt[0x21], STI, 7// then wait until N keys handled. We inject the scancodes for "nishi" as IRQs. 8// KAT: all 5 keys arrive via the interrupt path (handler fired 5x, scancodes match); a NEGATIVE CONTROL 9// with interrupts masked (no STI) collects ZERO -> proves delivery is genuinely IF-gated (real interrupt, 10// not a disguised direct call); the scancodes translate to "nishi"; rendered to a terminal BMP. 11// HONEST SEAM: scancodes are injected by the harness (a real PS/2 controller chip on hardware is R10); 12// the IDT vectoring + IF-gating + INB + IRET are real. No hw writes (Rule 26). expect_exit: 0 tier: ORIGINAL 13import "nx_fb.nx" 14import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 15import "nx_font8x8.nx" 16const K_MAGIC_3000000: i64 = 3000000 17const K_MAGIC_4096: i64 = 4096 18const K_MAGIC_8192: i64 = 8192 19const K_MAGIC_4104: i64 = 4104 20const K_MAGIC_4112: i64 = 4112 21const K_MAGIC_16384: i64 = 16384 22 23func iq_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 24// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 25// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 26// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 27// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 28func iq_num(v: i64) -> i64 { nxi_out(v); return 0 } 29func iq_b(c: *u8, o: i64, b: i64) -> i64 { c[o]=(b & 0xff) as u8; return o+1 } 30func iq_i32(c: *u8, o: i64, v: i64) -> i64 { c[o]=(v&0xff) as u8; c[o+1]=((v>>8)&0xff) as u8; c[o+2]=((v>>16)&0xff) as u8; c[o+3]=((v>>24)&0xff) as u8; return o+4 } 31func iq_ld64(mem: *u8, a: i64) -> i64 { var v: i64=0; var i: i64=0; while i<8 { v=v|((mem[a+i] as i64)<<(i*8)); i=i+1 } return v } 32func iq_st64(mem: *u8, a: i64, v: i64) -> i64 { var i: i64=0; while i<8 { mem[a+i]=((v>>(i*8))&0xff) as u8; i=i+1 } return 0 } 33func iq_i32r(code: *u8, off: i64) -> i64 { var v: i64=(code[off] as i64)|((code[off+1] as i64)<<8)|((code[off+2] as i64)<<16)|((code[off+3] as i64)<<24); if (v & 0x80000000)!=0 { v=v-(1<<32) } return v } 34 35// x86 emu WITH interrupt delivery. idt[0x21] at mem offset 520. scq[] = scancode queue, sctot = count. 36// sti_en=0 models interrupts masked (STI becomes a no-op). returns final count handled. 37func emu_irq(code: *u8, len: i64, mem: *u8, scq: *u8, sctot: i64, sti_en: i64) -> i64 { 38 let reg: *i64 = sys_mmap(8*8) as *i64 39 var pc: i64=0 40 var iflag: i64=0 41 var zf: i64=0 42 var schead: i64=0 43 var guard: i64=0 44 while guard < K_MAGIC_3000000 { 45 guard=guard+1 46 var delivered: i64=0 47 if iflag==1 { if schead<sctot { reg[4]=reg[4]-8; iq_st64(mem, reg[4], pc); iflag=0; pc=iq_ld64(mem, 520); delivered=1 } } 48 if delivered==0 { 49 let b: i64 = code[pc] as i64 50 var h: i64=0 51 if b==0x0F { if (code[pc+1] as i64)==0x05 { if reg[0]==60 { return reg[7] & 0xff } pc=pc+2; h=1 } else { return 0-1 } } 52 if h==0 { if b==0xFB { if sti_en==1 { iflag=1 } pc=pc+1; h=1 } } // sti 53 if h==0 { if b==0xFA { iflag=0; pc=pc+1; h=1 } } // cli 54 if h==0 { if b==0xE4 { reg[0]=scq[schead] as i64; schead=schead+1; pc=pc+2; h=1 } } // inb al, imm8 55 if h==0 { if b==0xCF { pc=iq_ld64(mem, reg[4]); reg[4]=reg[4]+8; iflag=1; h=1 } } // iret 56 if h==0 { if b==0x75 { var r: i64=code[pc+1] as i64; if r>127 {r=r-256} if zf==0 {pc=pc+2+r} else {pc=pc+2} h=1 } } 57 if h==0 { if b==0xEB { var r: i64=code[pc+1] as i64; if r>127 {r=r-256} pc=pc+2+r; h=1 } } 58 if h==0 { if b==0x48 { 59 let op: i64=code[pc+1] as i64 60 if op==0xC7 { reg[(code[pc+2] as i64)&7]=iq_i32r(code,pc+3); pc=pc+7; h=1 } 61 if op==0x89 { let m: i64=code[pc+2] as i64; let md: i64=(m>>6)&3; if md==3 { reg[m&7]=reg[(m>>3)&7] } else { iq_st64(mem, reg[m&7], reg[(m>>3)&7]) } pc=pc+3; h=1 } 62 if op==0x8B { let m: i64=code[pc+2] as i64; let md: i64=(m>>6)&3; if md==3 { reg[(m>>3)&7]=reg[m&7] } else { reg[(m>>3)&7]=iq_ld64(mem, reg[m&7]) } pc=pc+3; h=1 } 63 if op==0x01 { let m: i64=code[pc+2] as i64; reg[m&7]=reg[m&7]+reg[(m>>3)&7]; pc=pc+3; h=1 } 64 if op==0x39 { let m: i64=code[pc+2] as i64; let t: i64=reg[m&7]-reg[(m>>3)&7]; if t==0 {zf=1} else {zf=0} pc=pc+3; h=1 } 65 } } 66 if h==0 { return 0-3 } 67 } 68 } 69 return iq_ld64(mem, K_MAGIC_4096) // guard hit (kernel waited forever) -> report count handled 70} 71 72func kt_char(fb: *u8, W: i64, H: i64, table: *u8, x: i64, y: i64, ch: i64, r: i64, g: i64, b: i64) -> i64 { if ch<0x20 { return 0 } if ch>0x7E { return 0 } let gi: i64=(ch-0x20)*8; var row: i64=0; 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 } return 0 } 73func kt_str(fb: *u8, W: i64, H: i64, table: *u8, x: i64, y: i64, s: *u8, r: i64, g: i64, b: i64) -> i64 { var i: i64=0; 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 } return 0 } 74 75func main() -> i64 { 76 iq_puts("NishiOS GUI rung-8: LIVE PS/2 IRQ1 -- keys arrive as interrupts the CPU TAKES via the IDT\n" as *u8) 77 78 // ---- author the kernel ---- 79 let c: *u8 = sys_mmap(512) 80 var o: i64=0 81 o=iq_b(c,o,0x48); o=iq_b(c,o,0xC7); o=iq_b(c,o,0xC0); let hoff_imm: i64=o; o=iq_i32(c,o,0) // mov rax, HANDLER (patch) 82 o=iq_b(c,o,0x48); o=iq_b(c,o,0xC7); o=iq_b(c,o,0xC3); o=iq_i32(c,o,520) // mov rbx, 520 (idt[0x21] slot) 83 o=iq_b(c,o,0x48); o=iq_b(c,o,0x89); o=iq_b(c,o,0x03) // mov [rbx], rax 84 o=iq_b(c,o,0x48); o=iq_b(c,o,0xC7); o=iq_b(c,o,0xC4); o=iq_i32(c,o,K_MAGIC_8192) // mov rsp, K_MAGIC_8192 85 o=iq_b(c,o,0x48); o=iq_b(c,o,0xC7); o=iq_b(c,o,0xC6); o=iq_i32(c,o,5) // mov rsi, 5 (N) 86 o=iq_b(c,o,0xFB) // sti 87 let wait_off: i64=o 88 o=iq_b(c,o,0x48); o=iq_b(c,o,0xC7); o=iq_b(c,o,0xC3); o=iq_i32(c,o,K_MAGIC_4096) // mov rbx, K_MAGIC_4096 (COUNT) 89 o=iq_b(c,o,0x48); o=iq_b(c,o,0x8B); o=iq_b(c,o,0x03) // mov rax, [rbx] 90 o=iq_b(c,o,0x48); o=iq_b(c,o,0x39); o=iq_b(c,o,0xF0) // cmp rax, rsi 91 o=iq_b(c,o,0x75); o=iq_b(c,o,(wait_off-(o+1)) & 0xff) // jne wait 92 o=iq_b(c,o,0x48); o=iq_b(c,o,0xC7); o=iq_b(c,o,0xC7); o=iq_i32(c,o,0) // mov rdi, 0 93 o=iq_b(c,o,0x48); o=iq_b(c,o,0xC7); o=iq_b(c,o,0xC0); o=iq_i32(c,o,60) // mov rax, 60 94 o=iq_b(c,o,0x0F); o=iq_b(c,o,0x05) // syscall 95 let handler_off: i64=o 96 c[hoff_imm]=(handler_off&0xff) as u8; c[hoff_imm+1]=((handler_off>>8)&0xff) as u8; c[hoff_imm+2]=((handler_off>>16)&0xff) as u8; c[hoff_imm+3]=((handler_off>>24)&0xff) as u8 97 o=iq_b(c,o,0xE4); o=iq_b(c,o,0x60) // inb al, 0x60 (scancode) 98 o=iq_b(c,o,0x48); o=iq_b(c,o,0xC7); o=iq_b(c,o,0xC3); o=iq_i32(c,o,K_MAGIC_4104) // mov rbx, K_MAGIC_4104 (PTR) 99 o=iq_b(c,o,0x48); o=iq_b(c,o,0x8B); o=iq_b(c,o,0x0B) // mov rcx, [rbx] 100 o=iq_b(c,o,0x48); o=iq_b(c,o,0xC7); o=iq_b(c,o,0xC2); o=iq_i32(c,o,K_MAGIC_4112) // mov rdx, K_MAGIC_4112 (BUF) 101 o=iq_b(c,o,0x48); o=iq_b(c,o,0x01); o=iq_b(c,o,0xCA) // add rdx, rcx 102 o=iq_b(c,o,0x48); o=iq_b(c,o,0x89); o=iq_b(c,o,0x02) // mov [rdx], rax (buf[ptr]=scancode) 103 o=iq_b(c,o,0x48); o=iq_b(c,o,0xC7); o=iq_b(c,o,0xC7); o=iq_i32(c,o,8) // mov rdi, 8 104 o=iq_b(c,o,0x48); o=iq_b(c,o,0x01); o=iq_b(c,o,0xF9) // add rcx, rdi (ptr+=8) 105 o=iq_b(c,o,0x48); o=iq_b(c,o,0x89); o=iq_b(c,o,0x0B) // mov [rbx], rcx 106 o=iq_b(c,o,0x48); o=iq_b(c,o,0xC7); o=iq_b(c,o,0xC3); o=iq_i32(c,o,K_MAGIC_4096) // mov rbx, K_MAGIC_4096 (COUNT) 107 o=iq_b(c,o,0x48); o=iq_b(c,o,0x8B); o=iq_b(c,o,0x03) // mov rax, [rbx] 108 o=iq_b(c,o,0x48); o=iq_b(c,o,0xC7); o=iq_b(c,o,0xC7); o=iq_i32(c,o,1) // mov rdi, 1 109 o=iq_b(c,o,0x48); o=iq_b(c,o,0x01); o=iq_b(c,o,0xF8) // add rax, rdi (count++) 110 o=iq_b(c,o,0x48); o=iq_b(c,o,0x89); o=iq_b(c,o,0x03) // mov [rbx], rax 111 o=iq_b(c,o,0xCF) // iret 112 let codelen: i64=o 113 114 // scancodes for "nishi": n=0x31 i=0x17 s=0x1F h=0x23 i=0x17 115 let scq: *u8 = sys_mmap(16) 116 scq[0]=0x31 as u8; scq[1]=0x17 as u8; scq[2]=0x1F as u8; scq[3]=0x23 as u8; scq[4]=0x17 as u8 117 let N: i64=5 118 119 // RUN 1: interrupts enabled -> keys delivered via IRQ 120 let mem: *u8 = sys_mmap(K_MAGIC_16384) 121 var z: i64=0 122 while z<K_MAGIC_16384 { mem[z]=0 as u8; z=z+1 } 123 emu_irq(c, codelen, mem, scq, N, 1) 124 let count: i64 = iq_ld64(mem, K_MAGIC_4096) 125 126 // RUN 2 (NEGATIVE CONTROL): interrupts masked -> nothing should be delivered 127 let mem2: *u8 = sys_mmap(K_MAGIC_16384) 128 var z2: i64=0 129 while z2<K_MAGIC_16384 { mem2[z2]=0 as u8; z2=z2+1 } 130 emu_irq(c, codelen, mem2, scq, N, 0) 131 let count_masked: i64 = iq_ld64(mem2, K_MAGIC_4096) 132 133 // translate the interrupt-collected scancodes -> chars 134 let km: *u8 = sys_mmap(128) 135 var ki: i64=0 136 while ki<128 { km[ki]=0 as u8; ki=ki+1 } 137 km[0x31]=110 as u8; km[0x17]=105 as u8; km[0x1F]=115 as u8; km[0x23]=104 as u8 // n i s h 138 let typed: *u8 = sys_mmap(16) 139 var ti: i64=0 140 while ti<count { let scn: i64 = iq_ld64(mem, K_MAGIC_4112+ti*8); typed[ti]=km[scn] as u8; ti=ti+1 } 141 typed[count]=0 as u8 142 143 iq_puts(" keys handled via IRQ1="); iq_num(count); iq_puts(" (masked-control="); iq_num(count_masked); iq_puts(") typed via interrupts: '"); sys_write(1, typed, count); iq_puts("'\n" as *u8) 144 145 // render a terminal showing the interrupt-typed line 146 let W: i64=240 147 let H: i64=80 148 let fb: *u8 = sys_mmap(W*H*3) 149 let table: *u8 = font8x8_table() 150 fb_rect(fb,W,H, 0,0, W,H, 12,16,12) 151 fb_rect(fb,W,H, 0,0, W,12, 30,90,45) 152 kt_str(fb,W,H,table, 4,2, "NISHIOS terminal (IRQ1)\x00" as *u8, 220,255,220) 153 kt_str(fb,W,H,table, 4,24, "NISHIOS> \x00" as *u8, 120,255,120) 154 kt_str(fb,W,H,table, 4+9*8,24, typed, 235,255,235) 155 kt_str(fb,W,H,table, 4,44, "^ each key arrived as an IRQ\x00" as *u8, 120,160,120) 156 let sz: i64 = fb_bmp_save(fb, W, H, "knowledge/status/nishios_irq.bmp\x00" as *u8) 157 let hd: i64 = sys_openat_wr("knowledge/status/nishios_irq.html\x00" as *u8, 0x1a4) 158 if hd>0 { let html: *u8 = "<!doctype html><html><body style=\x27background:#0a0a12;color:#8af;font-family:monospace;text-align:center\x27><h3>NishiOS: keyboard via real IRQ1 interrupt delivery</h3><img src=\x27nishios_irq.bmp\x27 style=\x27image-rendering:pixelated;width:720px;border:1px solid #333\x27></body></html>\x00"; var hn: i64=0; while html[hn]!=(0 as u8){hn=hn+1} sys_write(hd, html, hn); sys_close(hd) } 159 160 var pass: i64=0 161 var ttl: i64=0 162 ttl=ttl+1; iq_puts(" T1 all 5 keys arrived via the interrupt path (count==5): " as *u8); if count==5 { pass=pass+1; iq_puts("PASS\n" as *u8) } else { iq_puts("FAIL\n" as *u8) } 163 ttl=ttl+1; iq_puts(" T2 scancodes collected in order (buf matches injected IRQs): " as *u8); var okm: i64=1; var bi: i64=0; while bi<5 { if iq_ld64(mem, K_MAGIC_4112+bi*8)!=(scq[bi] as i64) { okm=0 } bi=bi+1 } if okm==1 { pass=pass+1; iq_puts("PASS\n" as *u8) } else { iq_puts("FAIL\n" as *u8) } 164 ttl=ttl+1; iq_puts(" T3 NEG CONTROL -- interrupts masked => 0 delivered (IF-gated, real interrupt): " as *u8); if count_masked==0 { pass=pass+1; iq_puts("PASS\n" as *u8) } else { iq_puts("FAIL\n" as *u8) } 165 ttl=ttl+1; iq_puts(" T4 interrupt-collected keys translate to 'nishi': " as *u8); var oks: i64=1; if count!=5 { oks=0 } else { let exp: *u8 = "nishi\x00"; var ei: i64=0; while ei<5 { if typed[ei]!=exp[ei] { oks=0 } ei=ei+1 } } if oks==1 { pass=pass+1; iq_puts("PASS\n" as *u8) } else { iq_puts("FAIL\n" as *u8) } 166 ttl=ttl+1; iq_puts(" T5 rendered terminal BMP: " as *u8); if sz>0 { pass=pass+1; iq_puts("PASS\n" as *u8) } else { iq_puts("FAIL\n" as *u8) } 167 168 iq_puts("NISHIOS-IRQ-GATE passed "); iq_num(pass); iq_puts("/"); iq_num(ttl) 169 if pass==ttl { iq_puts(" verdict=GREEN (keyboard driven by REAL IRQ1 interrupt delivery via the IDT; input-is-scripted seam closed)\n" as *u8); sys_exit(0); return 0 } 170 iq_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1 171}