nx_x86_dos_gate.nx source
↩ module page · 79 lines · 3955 B
1// nx_x86_dos_gate.nx -- R1.1: run a REAL DOS .COM on the sovereign x86 emulator. Hello-World program:
2// B4 09 MOV AH,09h (DOS print-string function)
3// BA 0B 01 MOV DX,0x010B (offset of the $-terminated string)
4// CD 21 INT 21h -> prints "Hello, World!" into the console buffer
5// B4 4C MOV AH,4Ch (terminate)
6// CD 21 INT 21h -> sets the exit flag -> emulator halts
7// 0x10B: "Hello, World!$"
8// Proves 8-bit reg moves (B0+r) + INT (CD) + the HLE DOS INT 21h handler -> an actual program produces output.
9import "nx_syscalls.nx"
10import "nx_x86.nx"
11import "nx_gate_verdict.nx"
12
13func g_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
14func g_pn(v: i64) -> i64 {
15 let b: *u8 = sys_mmap(28)
16 var x: i64 = v
17 if x < 0 { b[0] = 45 as u8; sys_write(1, b, 1); x = 0 - x }
18 if x == 0 { b[0] = 48 as u8; sys_write(1, b, 1); return 0 }
19 var d: i64 = 0
20 var y: i64 = x
21 while y > 0 { d = d + 1; y = y / 10 }
22 var i: i64 = d - 1
23 y = x
24 while i >= 0 { b[i] = (48 + (y % 10)) as u8; y = y / 10; i = i - 1 }
25 sys_write(1, b, d)
26 return 0
27}
28func g_check(name: *u8, cond: i64) -> i64 {
29 if cond == 1 { g_puts(" PASS " as *u8) } else { g_puts(" FAIL " as *u8) }
30 g_puts(name); g_puts("\n" as *u8)
31 return cond
32}
33
34func main() -> i64 {
35 g_puts("nx_x86 DOS gate (a real .COM: INT 21h Hello-World on the sovereign emulator)\n" as *u8)
36 var pass: i64 = 0
37 var total: i64 = 0
38
39 let base: i64 = sys_mmap(131072) as i64
40 x86_reset(base)
41 // code
42 x86_load(base, 256, 180); x86_load(base, 257, 9) // MOV AH,09
43 x86_load(base, 258, 186); x86_load(base, 259, 11); x86_load(base, 260, 1) // MOV DX,0x010B
44 x86_load(base, 261, 205); x86_load(base, 262, 33) // INT 21h
45 x86_load(base, 263, 180); x86_load(base, 264, 76) // MOV AH,4Ch
46 x86_load(base, 265, 205); x86_load(base, 266, 33) // INT 21h
47 // string "Hello, World!$" at 0x10B (267)
48 x86_load(base, 267, 72); x86_load(base, 268, 101); x86_load(base, 269, 108); x86_load(base, 270, 108)
49 x86_load(base, 271, 111); x86_load(base, 272, 44); x86_load(base, 273, 32); x86_load(base, 274, 87)
50 x86_load(base, 275, 111); x86_load(base, 276, 114); x86_load(base, 277, 108); x86_load(base, 278, 100)
51 x86_load(base, 279, 33); x86_load(base, 280, 36)
52
53 x86_run(base, 500)
54
55 let clen: i64 = x86_con_len(base)
56 g_puts(" [output] '" as *u8)
57 sys_write(1, (base + O_CON) as *u8, clen)
58 g_puts("' (len=" as *u8); g_pn(clen); g_puts(")\n" as *u8)
59
60 pass = pass + g_check("console length == 13" as *u8, clen == 13); total = total + 1
61 let want: *u8 = "Hello, World!" as *u8
62 var ok: i64 = 1
63 var i: i64 = 0
64 while i < 13 { if x86_con_byte(base, i) != (want[i] as i64) { ok = 0 } i = i + 1 }
65 pass = pass + g_check("console bytes == 'Hello, World!'" as *u8, ok); total = total + 1
66 // AH=4Ch path must have written 0x4C into AH (high byte of AX) before exit
67 pass = pass + g_check("AH == 0x4C at exit (8-bit reg move worked)" as *u8, ((x86_reg(base, 0) >> 8) & 0xff) == 76); total = total + 1
68
69 g_puts("---- x86 DOS gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8)
70 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
71 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
72 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
73 let ctr__dry: *i64 = gv_ctr()
74 ctr__dry[0] = pass
75 ctr__dry[1] = total
76 let rc__dry: i64 = gv_verdict("X86-DOS-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
77 sys_exit(rc__dry)
78 return rc__dry
79}