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}