nx_x86_modrm_gate.nx source
↩ module page · 76 lines · 4636 B
1// nx_x86_modrm_gate.nx -- R1.0b: ModR/M + memory operands. Program round-trips a value through memory via
2// [disp16] and [BX], does a memory-operand ADD, and a register-direct ModR/M MOV; checks both registers and
3// the actual memory contents. Proves the 16-bit addressing core that real DOS programs depend on.
4// B8 34 12 MOV AX,0x1234
5// 89 06 00 02 MOV [0x200],AX (mod00 rm6 disp16 store)
6// 8B 1E 00 02 MOV BX,[0x200] (mod00 rm6 disp16 load) -> BX=0x1234
7// 03 1E 00 02 ADD BX,[0x200] (memory-operand add) -> BX=0x2468
8// 89 1E 02 02 MOV [0x202],BX -> mem[0x202]=0x2468
9// 89 D9 MOV CX,BX (mod11 register-direct) -> CX=0x2468
10// BB 00 03 MOV BX,0x300
11// 89 07 MOV [BX],AX (mod00 rm7 [BX] store) -> mem[0x300]=0x1234
12// 8B 17 MOV DX,[BX] (mod00 rm7 [BX] load) -> DX=0x1234
13// F4 HLT
14import "nx_syscalls.nx"
15import "nx_x86.nx"
16import "nx_gate_verdict.nx"
17
18func 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 }
19func g_pn(v: i64) -> i64 {
20 let b: *u8 = sys_mmap(28)
21 var x: i64 = v
22 if x < 0 { b[0] = 45 as u8; sys_write(1, b, 1); x = 0 - x }
23 if x == 0 { b[0] = 48 as u8; sys_write(1, b, 1); return 0 }
24 var d: i64 = 0
25 var y: i64 = x
26 while y > 0 { d = d + 1; y = y / 10 }
27 var i: i64 = d - 1
28 y = x
29 while i >= 0 { b[i] = (48 + (y % 10)) as u8; y = y / 10; i = i - 1 }
30 sys_write(1, b, d)
31 return 0
32}
33func g_check(name: *u8, cond: i64) -> i64 {
34 if cond == 1 { g_puts(" PASS " as *u8) } else { g_puts(" FAIL " as *u8) }
35 g_puts(name); g_puts("\n" as *u8)
36 return cond
37}
38
39func main() -> i64 {
40 g_puts("nx_x86 ModR/M gate (memory operands + 16-bit addressing)\n" as *u8)
41 var pass: i64 = 0
42 var total: i64 = 0
43
44 let base: i64 = sys_mmap(131072) as i64
45 x86_reset(base)
46 x86_load(base, 256, 184); x86_load(base, 257, 52); x86_load(base, 258, 18) // MOV AX,0x1234
47 x86_load(base, 259, 137); x86_load(base, 260, 6); x86_load(base, 261, 0); x86_load(base, 262, 2) // MOV [0x200],AX
48 x86_load(base, 263, 139); x86_load(base, 264, 30); x86_load(base, 265, 0); x86_load(base, 266, 2) // MOV BX,[0x200]
49 x86_load(base, 267, 3); x86_load(base, 268, 30); x86_load(base, 269, 0); x86_load(base, 270, 2) // ADD BX,[0x200]
50 x86_load(base, 271, 137); x86_load(base, 272, 30); x86_load(base, 273, 2); x86_load(base, 274, 2) // MOV [0x202],BX
51 x86_load(base, 275, 137); x86_load(base, 276, 217) // MOV CX,BX
52 x86_load(base, 277, 187); x86_load(base, 278, 0); x86_load(base, 279, 3) // MOV BX,0x300
53 x86_load(base, 280, 137); x86_load(base, 281, 7) // MOV [BX],AX
54 x86_load(base, 282, 139); x86_load(base, 283, 23) // MOV DX,[BX]
55 x86_load(base, 284, 244) // HLT
56 x86_run(base, 100)
57
58 pass = pass + g_check("AX == 0x1234" as *u8, x86_reg(base, 0) == 4660); total = total + 1
59 pass = pass + g_check("BX == 0x300 (re-set after the math)" as *u8, x86_reg(base, 3) == 768); total = total + 1
60 pass = pass + g_check("CX == 0x2468 (register-direct MOV CX,BX)" as *u8, x86_reg(base, 1) == 9320); total = total + 1
61 pass = pass + g_check("DX == 0x1234 (MOV DX,[BX])" as *u8, x86_reg(base, 2) == 4660); total = total + 1
62 pass = pass + g_check("mem[0x200] == 0x1234 ([disp16] store)" as *u8, x86_peek16(base, 512) == 4660); total = total + 1
63 pass = pass + g_check("mem[0x202] == 0x2468 (memory-operand ADD result)" as *u8, x86_peek16(base, 514) == 9320); total = total + 1
64 pass = pass + g_check("mem[0x300] == 0x1234 ([BX] store)" as *u8, x86_peek16(base, 768) == 4660); total = total + 1
65
66 g_puts("---- x86 modrm gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8)
67 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
68 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
69 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
70 let ctr__dry: *i64 = gv_ctr()
71 ctr__dry[0] = pass
72 ctr__dry[1] = total
73 let rc__dry: i64 = gv_verdict("X86-MODRM-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
74 sys_exit(rc__dry)
75 return rc__dry
76}