nx_emu_loongarch64.nx source
↩ module page · 143 lines · 6779 B
1// nx_emu_loongarch64.nx -- sovereign LoongArch64 (LA64) interpreter (NX-EMU).
2// Flat 32 GPRs (r0==zero), little-endian, NO register windows, NO delay
3// slots. Decode/execute pure NishiLang per the LoongArch ISA -- NO qemu
4// (qemu-loongarch64 = differential BENCHMARK that must agree). How Nishi
5// CARRIES loongarch64 execution (operator: nishi carries; externals=bench).
6//
7// Encodings (verified via objdump of the nxc2 la64 backend output):
8// 3R (w&0xFFFF8000): add.d 0x00108000 sub.d 0x00118000 mul.d 0x001d8000
9// and 0x00148000 or 0x00150000 xor 0x00158000
10// sll.d 0x00188000 srl.d 0x00190000 sra.d 0x00198000
11// div.d 0x00220000 mod.d 0x00228000
12// 2RI12 (w&0xFFC00000): addi.d 0x02c00000 ld.d 0x28c00000 st.d 0x29c00000
13// jirl (w&0xFC000000)==0x4C000000 (ret = jirl $zero,$ra,0)
14// bl (w&0xFC000000)==0x54000000 (split offs26)
15// syscall (w&0xFFFF8000)==0x002B0000 (a7=num, a0..=args; exit=93)
16//
17// license_tier: ORIGINAL
18
19import "nx_syscalls_x86_64.nx"
20const LA_MAGIC_200000000: i64 = 200000000
21
22const LA_GUEST_SIZE: i64 = 16777216
23const LA_SYS_READ: i64 = 63
24const LA_SYS_WRITE: i64 = 64
25const LA_SYS_EXIT: i64 = 93
26const LA_SYS_EXITG: i64 = 94
27const LAE_UNSUPPORTED: i64 = -1
28const LAE_FAULT: i64 = -3
29
30func la_g_ld(mem: *u8, va: i64, width: i64) -> i64 { // little-endian
31 var v: i64 = 0
32 var i: i64 = 0
33 while i < width { v = v | ((mem[va + i] & 0xff) << (i * 8)); i = i + 1 }
34 return v
35}
36func la_g_st(mem: *u8, va: i64, width: i64, val: i64) -> i64 {
37 var i: i64 = 0
38 while i < width { mem[va + i] = (val >> (i * 8)) & 0xff; i = i + 1 }
39 return 0
40}
41func la_rd(r: *i64, n: i64) -> i64 { if n == 0 { return 0 } return r[n] }
42func la_wr(r: *i64, n: i64, v: i64) -> i64 { if n != 0 { r[n] = v } return 0 }
43
44func emu_loongarch64_run_mem(mem: *u8, mem_size: i64, entry: i64, sp0: i64) -> i64 {
45 let r: *i64 = sys_mmap(32 * 8) as *i64
46 var i: i64 = 0
47 while i < 32 { r[i] = 0; i = i + 1 }
48 r[3] = sp0 // $sp = $r3
49 var pc: i64 = entry
50 var steps: i64 = 0
51 var halted: i64 = 0
52 var result: i64 = LAE_FAULT
53 while halted == 0 && steps < LA_MAGIC_200000000 {
54 if pc < 0 { halted = 1; result = LAE_FAULT }
55 if pc + 4 > mem_size { halted = 1; result = LAE_FAULT }
56 if halted == 0 {
57 let w: i64 = la_g_ld(mem, pc, 4)
58 let rd: i64 = w & 0x1F
59 let rj: i64 = (w >> 5) & 0x1F
60 let rk: i64 = (w >> 10) & 0x1F
61 var next: i64 = pc + 4
62 var handled: i64 = 0
63
64 // 3R
65 let o3: i64 = w & 0xFFFF8000
66 if o3 == 0x00108000 { handled = 1; la_wr(r, rd, la_rd(r, rj) + la_rd(r, rk)) } // add.d
67 if o3 == 0x00118000 { handled = 1; la_wr(r, rd, la_rd(r, rj) - la_rd(r, rk)) } // sub.d
68 if o3 == 0x001d8000 { handled = 1; la_wr(r, rd, la_rd(r, rj) * la_rd(r, rk)) } // mul.d
69 if o3 == 0x00148000 { handled = 1; la_wr(r, rd, la_rd(r, rj) & la_rd(r, rk)) } // and
70 if o3 == 0x00150000 { handled = 1; la_wr(r, rd, la_rd(r, rj) | la_rd(r, rk)) } // or
71 if o3 == 0x00158000 { handled = 1; la_wr(r, rd, la_rd(r, rj) ^ la_rd(r, rk)) } // xor
72 if o3 == 0x00188000 { handled = 1; la_wr(r, rd, la_rd(r, rj) << (la_rd(r, rk) & 63)) } // sll.d
73 if o3 == 0x00190000 { handled = 1; la_wr(r, rd, la_rd(r, rj) >> (la_rd(r, rk) & 63)) } // srl.d
74 if o3 == 0x00198000 { handled = 1; la_wr(r, rd, la_rd(r, rj) >> (la_rd(r, rk) & 63)) } // sra.d (arith)
75 if o3 == 0x00220000 { handled = 1; let d: i64 = la_rd(r, rk); if d != 0 { la_wr(r, rd, la_rd(r, rj) / d) } } // div.d
76 if o3 == 0x00228000 { handled = 1; let d: i64 = la_rd(r, rk); if d != 0 { la_wr(r, rd, la_rd(r, rj) - (la_rd(r, rj) / d) * d) } } // mod.d
77
78 // 2RI12
79 let o2: i64 = w & 0xFFC00000
80 if o2 == 0x02c00000 || o2 == 0x28c00000 || o2 == 0x29c00000 {
81 handled = 1
82 var si12: i64 = (w >> 10) & 0xFFF
83 if (si12 & 0x800) != 0 { si12 = si12 - 0x1000 }
84 if o2 == 0x02c00000 { la_wr(r, rd, la_rd(r, rj) + si12) } // addi.d
85 if o2 == 0x28c00000 { la_wr(r, rd, la_g_ld(mem, la_rd(r, rj) + si12, 8)) } // ld.d
86 if o2 == 0x29c00000 { la_g_st(mem, la_rd(r, rj) + si12, 8, la_rd(r, rd)) } // st.d
87 }
88
89 // jirl (ret = jirl $zero,$ra,0)
90 if (w & 0xFC000000) == 0x4C000000 {
91 handled = 1
92 var off: i64 = (w >> 10) & 0xFFFF
93 if (off & 0x8000) != 0 { off = off - 0x10000 }
94 la_wr(r, rd, pc + 4)
95 next = la_rd(r, rj) + (off << 2)
96 }
97 // bl (call): $ra = pc+4 ; pc += offs26<<2 (offs = {bits9:0, bits25:10})
98 if (w & 0xFC000000) == 0x54000000 {
99 handled = 1
100 var off: i64 = ((w & 0x3FF) << 16) | ((w >> 10) & 0xFFFF)
101 if (off & 0x2000000) != 0 { off = off - 0x4000000 }
102 r[1] = pc + 4
103 next = pc + (off << 2)
104 }
105 // syscall (a7=num, a0..=args)
106 if (w & 0xFFFF8000) == 0x002B0000 {
107 handled = 1
108 let nr: i64 = r[11]
109 if nr == LA_SYS_EXIT { result = r[4] & 0xff; halted = 1 }
110 if nr == LA_SYS_EXITG { result = r[4] & 0xff; halted = 1 }
111 if nr == LA_SYS_WRITE { r[4] = sys_write(r[4], ((mem as i64) + r[5]) as *u8, r[6]) }
112 if nr == LA_SYS_READ { r[4] = sys_read(r[4], ((mem as i64) + r[5]) as *u8, r[6]) }
113 }
114
115 if handled == 0 { result = LAE_UNSUPPORTED; halted = 1 }
116 pc = next
117 steps = steps + 1
118 }
119 }
120 return result
121}
122
123func emu_loongarch64_load_elf(buf: *u8, len: i64) -> i64 {
124 if len < 64 { return LAE_FAULT }
125 let e_entry: i64 = la_g_ld(buf, 24, 8)
126 let e_phoff: i64 = la_g_ld(buf, 32, 8)
127 let e_phnum: i64 = la_g_ld(buf, 56, 2)
128 let e_phent: i64 = la_g_ld(buf, 54, 2)
129 let mem: *u8 = sys_mmap(LA_GUEST_SIZE)
130 var idx: i64 = 0
131 while idx < e_phnum {
132 let ph: i64 = e_phoff + idx * e_phent
133 if la_g_ld(buf, ph, 4) == 1 {
134 let p_off: i64 = la_g_ld(buf, ph + 8, 8)
135 let p_va: i64 = la_g_ld(buf, ph + 16, 8)
136 let p_fs: i64 = la_g_ld(buf, ph + 32, 8)
137 var k: i64 = 0
138 while k < p_fs { if (p_va + k) < LA_GUEST_SIZE { mem[p_va + k] = buf[p_off + k] }; k = k + 1 }
139 }
140 idx = idx + 1
141 }
142 return emu_loongarch64_run_mem(mem, LA_GUEST_SIZE, e_entry, 0x00F00000)
143}