nx_x86_dosfs_gate.nx source
↩ module page · 88 lines · 4880 B
1// nx_x86_dosfs_gate.nx -- R-DOS1: Nishi-DOS file I/O over a sovereign virtual FS (our own, clean-room).
2// The host pre-loads one file "FILE" = "NISHI-DOS!$" into the Nishi FS. The program then, via our own
3// INT 21h: opens "FILE" (3Dh), reads it (3Fh) into a buffer, and prints it (09h). Proves a real program
4// loads and reads its own data file through DOS services we wrote ourselves -- no MS-DOS, no FreeDOS.
5// B4 3D / B0 00 / BA 40 01 / CD 21 open "FILE"@0x140 -> AX=handle
6// 89 C3 MOV BX,AX
7// B4 3F / B9 0B 00 / BA 00 03 / CD 21 read 11 bytes -> buffer@0x300
8// B4 09 / BA 00 03 / CD 21 print buffer ($-terminated)
9// B4 4C / CD 21 terminate
10import "nx_syscalls.nx"
11import "nx_x86.nx"
12import "nx_gate_verdict.nx"
13
14func 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 }
15func g_pn(v: i64) -> i64 {
16 let b: *u8 = sys_mmap(28)
17 var x: i64 = v
18 if x < 0 { b[0] = 45 as u8; sys_write(1, b, 1); x = 0 - x }
19 if x == 0 { b[0] = 48 as u8; sys_write(1, b, 1); return 0 }
20 var d: i64 = 0
21 var y: i64 = x
22 while y > 0 { d = d + 1; y = y / 10 }
23 var i: i64 = d - 1
24 y = x
25 while i >= 0 { b[i] = (48 + (y % 10)) as u8; y = y / 10; i = i - 1 }
26 sys_write(1, b, d)
27 return 0
28}
29func g_check(name: *u8, cond: i64) -> i64 {
30 if cond == 1 { g_puts(" PASS " as *u8) } else { g_puts(" FAIL " as *u8) }
31 g_puts(name); g_puts("\n" as *u8)
32 return cond
33}
34
35func main() -> i64 {
36 g_puts("nx_x86 Nishi-DOS FS gate (R-DOS1: INT 21h open/read over our own virtual filesystem)\n" as *u8)
37 var pass: i64 = 0
38 var total: i64 = 0
39
40 let base: i64 = sys_mmap(131072) as i64
41 x86_reset(base)
42 x86_fs_put(base, "FILE" as *u8, "NISHI-DOS!$" as *u8, 11) // place one file into the Nishi FS
43
44 // program @0x100
45 x86_load(base, 256, 180); x86_load(base, 257, 61) // MOV AH,3Dh
46 x86_load(base, 258, 176); x86_load(base, 259, 0) // MOV AL,0
47 x86_load(base, 260, 186); x86_load(base, 261, 64); x86_load(base, 262, 1) // MOV DX,0x140
48 x86_load(base, 263, 205); x86_load(base, 264, 33) // INT 21h (open)
49 x86_load(base, 265, 137); x86_load(base, 266, 195) // MOV BX,AX
50 x86_load(base, 267, 180); x86_load(base, 268, 63) // MOV AH,3Fh
51 x86_load(base, 269, 185); x86_load(base, 270, 11); x86_load(base, 271, 0) // MOV CX,11
52 x86_load(base, 272, 186); x86_load(base, 273, 0); x86_load(base, 274, 3) // MOV DX,0x300
53 x86_load(base, 275, 205); x86_load(base, 276, 33) // INT 21h (read)
54 x86_load(base, 277, 180); x86_load(base, 278, 9) // MOV AH,09h
55 x86_load(base, 279, 186); x86_load(base, 280, 0); x86_load(base, 281, 3) // MOV DX,0x300
56 x86_load(base, 282, 205); x86_load(base, 283, 33) // INT 21h (print)
57 x86_load(base, 284, 180); x86_load(base, 285, 76) // MOV AH,4Ch
58 x86_load(base, 286, 205); x86_load(base, 287, 33) // INT 21h (exit)
59 // filename "FILE\0" @0x140
60 x86_load(base, 320, 70); x86_load(base, 321, 73); x86_load(base, 322, 76); x86_load(base, 323, 69); x86_load(base, 324, 0)
61
62 x86_run(base, 500)
63
64 let clen: i64 = x86_con_len(base)
65 g_puts(" [output] '" as *u8)
66 sys_write(1, (base + O_CON) as *u8, clen)
67 g_puts("' (len=" as *u8); g_pn(clen); g_puts(")\n" as *u8)
68
69 // the read must have landed the file's first bytes 'N','I' into guest mem at 0x300
70 pass = pass + g_check("read landed file bytes in guest mem (mem[0x300..]=='NI')" as *u8, x86_peek16(base, 768) == (78 + 73 * 256)); total = total + 1
71 pass = pass + g_check("console length == 10" as *u8, clen == 10); total = total + 1
72 let want: *u8 = "NISHI-DOS!" as *u8
73 var ok: i64 = 1
74 var i: i64 = 0
75 while i < 10 { if x86_con_byte(base, i) != (want[i] as i64) { ok = 0 } i = i + 1 }
76 pass = pass + g_check("console == 'NISHI-DOS!' (opened+read from the Nishi FS, then printed)" as *u8, ok); total = total + 1
77
78 g_puts("---- Nishi-DOS FS gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8)
79 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
80 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
81 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
82 let ctr__dry: *i64 = gv_ctr()
83 ctr__dry[0] = pass
84 ctr__dry[1] = total
85 let rc__dry: i64 = gv_verdict("X86-DOSFS-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
86 sys_exit(rc__dry)
87 return rc__dry
88}