nx_emu_x86_stage2_test.nx source
↩ module page · 132 lines · 8748 B
1// nx_emu_x86_stage2_test.nx -- x86 ladder R9 rung-4: a REAL MULTI-STAGE LOADER, executed end-to-end.
2// A 512-byte boot sector cannot BE an OS -- it must LOAD one. This builds a 2-sector disk (sector 0 =
3// stage-1 MBR, sector 1 = stage-2), and the emu now models BIOS INT 13h (AH=0x02 read-sectors). The
4// real boot flow runs on the sovereign machine: stage-1 issues INT 13h to read sector 1 into memory at
5// 0x8000, then E9-jumps to it; stage-2 prints "STAGE2 OK" over INT 10h and halts. This is the
6// multi-stage loader the real-OS ladder needs -- proven by EXECUTION, not structure.
7// KAT: stage-2 was actually copied off disk by INT 13h (0x8000 was empty, now holds stage-2), the
8// console shows stage-2's output, and the cpu halted cleanly.
9// HONEST SCOPE: stage-2 here is a tiny printer. The rungs above: stage-2 -> protected/long mode ->
10// load the real kernel -> GDT/IDT/paging/MM/scheduler/syscalls -> userspace. No hw writes (Rule 26).
11// expect_exit: 0 license_tier: ORIGINAL
12import "nx_syscalls.nx"
13
14func s2_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
15func s2_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 }
16func s2_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 }
17
18// 16-bit real-mode emu WITH a disk + BIOS INT 13h (read sectors). mem = RAM, disk = the disk image.
19// returns 0 = clean HLT, -1 = unknown opcode, -2 = ran away.
20func emu_x86_real16_disk(mem: *u8, disk: *u8, entry: i64, console: *u8, clen: *i64) -> i64 {
21 var ip: i64 = entry
22 var ax: i64 = 0
23 var bx: i64 = 0
24 var cx: i64 = 0
25 var dx: i64 = 0
26 var si: i64 = 0
27 var zf: i64 = 0
28 var guard: i64 = 0
29 while guard < 200000 {
30 guard = guard + 1
31 let op: i64 = mem[ip] as i64
32 if op == 0xF4 { return 0 }
33 var h: i64 = 0
34 if h==0 { if op==0xBE { si = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } // mov si,imm16
35 if h==0 { if op==0xB8 { ax = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } // mov ax,imm16
36 if h==0 { if op==0xBB { bx = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } // mov bx,imm16
37 if h==0 { if op==0xB9 { cx = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } // mov cx,imm16
38 if h==0 { if op==0xBA { dx = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); ip=ip+3; h=1 } } // mov dx,imm16
39 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 } } // lodsb
40 if h==0 { if op==0x08 { let al2: i64 = ax & 0xFF; if al2==0 { zf=1 } else { zf=0 } ip=ip+2; h=1 } } // or al,al
41 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 } } // jz rel8
42 if h==0 { if op==0xB4 { ax = (ax & 0xFF) | ((mem[ip+1] as i64)<<8); ip=ip+2; h=1 } } // mov ah,imm8
43 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 } } // jmp rel8
44 if h==0 { if op==0xE9 { var r3: i64 = (mem[ip+1] as i64) | ((mem[ip+2] as i64)<<8); if r3>32767 { r3=r3-65536 } ip=ip+3+r3; h=1 } } // jmp rel16
45 if h==0 { if op==0xCD {
46 let vec: i64 = mem[ip+1] as i64
47 let ah: i64 = (ax >> 8) & 0xFF
48 if vec==0x10 { if ah==0x0E { console[clen[0]]=(ax & 0xFF) as u8; clen[0]=clen[0]+1 } } // BIOS teletype
49 if vec==0x13 { if ah==0x02 { // BIOS read sectors
50 let count: i64 = ax & 0xFF
51 let sector: i64 = cx & 0xFF
52 let lba: i64 = sector - 1
53 var s: i64 = 0
54 while s < count*512 { mem[bx + s] = disk[lba*512 + s]; s=s+1 }
55 ax = ax & 0xFF // AH=0 = success
56 } }
57 ip=ip+2; h=1
58 } }
59 if h==0 { return 0 - 1 }
60 }
61 return 0 - 2
62}
63
64func main() -> i64 {
65 s2_puts("x86 ladder R9 rung-4: REAL MULTI-STAGE LOADER (stage1 --INT13h--> loads stage2 --> runs it)\n" as *u8)
66 let img: *u8 = sys_mmap(2048)
67 var z: i64=0
68 while z<2048 { img[z]=0 as u8; z=z+1 }
69
70 // ---- stage-1 (sector 0, loads at 0x7C00) ----
71 img[0]=0xB8 as u8; img[1]=0x01 as u8; img[2]=0x02 as u8 // mov ax,0x0201 (AH=02 read, AL=01 one sector)
72 img[3]=0xBB as u8; img[4]=0x00 as u8; img[5]=0x80 as u8 // mov bx,0x8000 (dest)
73 img[6]=0xB9 as u8; img[7]=0x02 as u8; img[8]=0x00 as u8 // mov cx,0x0002 (CL=2 -> LBA 1 = sector 1)
74 img[9]=0xBA as u8; img[10]=0x80 as u8; img[11]=0x00 as u8 // mov dx,0x0080 (DL=0x80 first disk)
75 img[12]=0xCD as u8; img[13]=0x13 as u8 // int 0x13 (load stage2)
76 let e9off: i64 = 14
77 let rel16: i64 = 0x8000 - (0x7C00 + e9off + 3) // jmp 0x8000
78 img[14]=0xE9 as u8; img[15]=(rel16 & 0xFF) as u8; img[16]=((rel16>>8) & 0xFF) as u8
79 img[510]=0x55 as u8; img[511]=0xAA as u8
80
81 // ---- stage-2 (sector 1, loads at 0x8000); offsets q are relative to stage-2 start ----
82 var q: i64 = 0
83 img[512+q]=0xBE as u8; let si_q: i64 = q+1; q=q+3 // mov si, imm16 (patched to 0x8000+msg)
84 let loop_q: i64 = q
85 img[512+q]=0xAC as u8; q=q+1 // lodsb
86 img[512+q]=0x08 as u8; img[512+q+1]=0xC0 as u8; q=q+2 // or al,al
87 img[512+q]=0x74 as u8; let jz_q: i64 = q+1; q=q+2 // jz hang (patched)
88 img[512+q]=0xB4 as u8; img[512+q+1]=0x0E as u8; q=q+2 // mov ah,0x0E
89 img[512+q]=0xCD as u8; img[512+q+1]=0x10 as u8; q=q+2 // int 0x10
90 img[512+q]=0xEB as u8; img[512+q+1]=((loop_q-(q+2)) & 0xFF) as u8; q=q+2 // jmp loop
91 let hang_q: i64 = q
92 img[512+q]=0xF4 as u8; q=q+1 // hlt
93 let msg_q: i64 = q
94 let msg: *u8 = "STAGE2 OK\x0D\x0A\x00"
95 var mi: i64=0
96 while msg[mi]!=(0 as u8) { img[512+q]=msg[mi]; q=q+1; mi=mi+1 }
97 img[512+q]=0 as u8; q=q+1
98 // patch jz rel8 = hang_q - (jz_q+1)
99 img[512+jz_q] = ((hang_q - (jz_q+1)) & 0xFF) as u8
100 // patch si = 0x8000 + msg_q
101 let si_abs: i64 = 0x8000 + msg_q
102 img[512+si_q] = (si_abs & 0xFF) as u8
103 img[512+si_q+1] = ((si_abs>>8) & 0xFF) as u8
104
105 // ---- the machine: RAM with ONLY sector 0 loaded at 0x7C00 (stage2 must be pulled off disk by INT 13h) ----
106 let mem: *u8 = sys_mmap(65536)
107 var k: i64=0
108 while k<65536 { mem[k]=0 as u8; k=k+1 }
109 var j: i64=0
110 while j<512 { mem[0x7C00+j]=img[j]; j=j+1 }
111 let pre: i64 = mem[0x8000] as i64 // should be 0 (stage2 not loaded yet)
112
113 let console: *u8 = sys_mmap(256)
114 let clen: *i64 = sys_mmap(8) as *i64
115 clen[0]=0
116 let rc: i64 = emu_x86_real16_disk(mem, img, 0x7C00, console, clen)
117 let post: i64 = mem[0x8000] as i64 // should now be stage2's first byte (0xBE)
118
119 s2_puts(" stage1 ran INT 13h, jumped to 0x8000 -> halt rc=" as *u8); s2_num(rc); s2_puts("\n" as *u8)
120 s2_puts(" 0x8000 before load=" as *u8); s2_num(pre); s2_puts(" after load=" as *u8); s2_num(post); s2_puts(" (0xBE=190 means stage2 was copied off disk)\n" as *u8)
121 s2_puts(" SOVEREIGN-EMU console: " as *u8); sys_write(1, console, clen[0]); s2_puts(" [" as *u8); s2_num(clen[0]); s2_puts(" chars]\n" as *u8)
122
123 var pass: i64=0
124 var ttl: i64=0
125 ttl=ttl+1; s2_puts(" T1 INT 13h actually loaded stage2 off disk (0x8000: 0->0xBE): " as *u8); if pre==0 { if post==0xBE { pass=pass+1; s2_puts("PASS\n" as *u8) } else { s2_puts("FAIL\n" as *u8) } } else { s2_puts("FAIL\n" as *u8) }
126 ttl=ttl+1; s2_puts(" T2 loaded stage2 executed (console == 'STAGE2 OK\\r\\n'): " as *u8); if clen[0]==11 { if s2_beq(console, "STAGE2 OK\x0D\x0A\x00" as *u8, 11)==1 { pass=pass+1; s2_puts("PASS\n" as *u8) } else { s2_puts("FAIL\n" as *u8) } } else { s2_puts("FAIL\n" as *u8) }
127 ttl=ttl+1; s2_puts(" T3 cpu halted cleanly (rc=0): " as *u8); if rc==0 { pass=pass+1; s2_puts("PASS\n" as *u8) } else { s2_puts("FAIL\n" as *u8) }
128
129 s2_puts("X86-STAGE2-LOADER-GATE passed " as *u8); s2_num(pass); s2_puts("/" as *u8); s2_num(ttl)
130 if pass==ttl { s2_puts(" verdict=GREEN (a REAL multi-stage loader runs end-to-end on the sovereign machine; stage2->kernel->OS = the ladder above)\n" as *u8); sys_exit(0); return 0 }
131 s2_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1
132}