nx_emu_loongarch64.nx source
↩ module page · 532 lines · 22674 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 Reference
4// Manual Volume 1 (v1.10) -- NO qemu (qemu-loongarch64 stays a
5// differential BENCHMARK that must agree, never a dependency). How
6// Nishi CARRIES loongarch64 execution (operator: nishi carries;
7// externals = bench).
8//
9// ---------------------------------------------------------------------
10// 2026-09-03, ISA LANE. WHAT THIS GENERATION ADDS AND WHY.
11//
12// The previous generation decoded ELEVEN 3R ops, THREE 2RI12 ops, jirl,
13// bl and syscall -- and NOT ONE CONDITIONAL BRANCH. An interpreter with
14// no conditional branch cannot run a loop, cannot run an if, and cannot
15// run any program a compiler would emit; it could only ever execute
16// straight-line arithmetic. That is the same gap the mips64 sibling
17// carried until 2026-09-03, named by nx_isa_conform_gate and closed the
18// same day, and it is the highest-value thing missing here.
19//
20// ADDED (each encoding taken from the manual instruction tables, not
21// from any emulator behaviour):
22// branches beq bne blt bge bltu bgeu (2RI16) | beqz bnez (1RI21) | b (I26)
23// compare slt sltu slti sltui
24// logic nor andn orn andi ori xori maskeqz masknez
25// shift-imm slli.w slli.d srli.w srli.d srai.w srai.d
26// 32-bit add.w sub.w mul.w sll.w srl.w sra.w addi.w
27// const lu12i.w lu32i.d pcaddi pcaddu12i pcalau12i
28// memory ld.b ld.h ld.w st.b st.h st.w ld.bu ld.hu ld.wu
29//
30// FIXED, a real defect the ruler caught: SRL.D was implemented with
31// the NishiLang >> operator which is ARITHMETIC, so srl.d and sra.d
32// were the SAME instruction. It is invisible on every non-negative
33// value -- the exact shape of a test that cannot fail -- and shows up
34// only on a negative operand shifted far enough that the sign copies
35// reach the low byte. Logical right shift now goes through la_srl,
36// mirroring the rv64 sibling emu_srl, which carries the same comment
37// for the same reason.
38//
39// ALSO FIXED: (1) memory accesses are BOUNDS-CHECKED against mem_size
40// instead of trusting the guest -- an out-of-range guest address used
41// to read or write OUTSIDE the interpreter own arena, which is a host
42// memory-safety hole reachable from guest bytes. (2) a third
43// non-completion state, LAE_RANOFF, so "the step budget ran out / no
44// exit syscall" is distinguishable from "bad pc" -- collapsing them
45// made a structural non-completion read as a fault. (3) DIV.D of the
46// most-negative value by -1 is defined rather than handed to the host
47// divide instruction, which would fault the interpreter itself.
48//
49// Encodings (LoongArch Reference Manual Vol 1, instruction tables;
50// cross-checked against objdump of the nxc2 la64 backend output):
51// 3R opcode in bits 31..15 (w & 0xFFFF8000), rk[14:10] rj[9:5] rd[4:0]
52// 3RI6 shift-immediate .d forms, opcode in bits 31..16, ui6[15:10]
53// 2RI12 opcode in bits 31..22 (w & 0xFFC00000), si12/ui12[21:10]
54// 1RI20 opcode in bits 31..25 (w & 0xFE000000), si20[24:5]
55// 2RI16 opcode in bits 31..26 (w & 0xFC000000), offs16[25:10]
56// 1RI21 opcode in bits 31..26, offs[15:0] at [25:10], offs[20:16] at [4:0]
57// I26 opcode in bits 31..26, offs[15:0] at [25:10], offs[25:16] at [9:0]
58// syscall (w & 0xFFFF8000) == 0x002B0000 (a7=r11 holds the number,
59// a0=r4.. hold the args; Linux exit = 93)
60// Every branch target is PC + SignExtend(offs << 2): LoongArch has no
61// delay slot, so the offset is measured from the branch itself.
62//
63// license_tier: ORIGINAL
64
65import "nx_syscalls_x86_64.nx"
66
67const LA_MAGIC_200000000: i64 = 200000000
68
69const LA_GUEST_SIZE: i64 = 16777216
70const LA_SYS_READ: i64 = 63
71const LA_SYS_WRITE: i64 = 64
72const LA_SYS_EXIT: i64 = 93
73const LA_SYS_EXITG: i64 = 94
74
75// Three NAMED non-completion states. A single negative word for "it
76// did not finish" cannot say which of two opposite fixes is owed.
77const LAE_UNSUPPORTED: i64 = -1
78const LAE_RANOFF: i64 = -2
79const LAE_FAULT: i64 = -3
80
81const LA_ALL_ONES: i64 = -1
82const LA_NEG_ONE: i64 = -1
83
84// Sign-extension moduli, named for the field width they close over.
85const LA_POW12: i64 = 4096
86const LA_POW16: i64 = 65536
87const LA_POW20: i64 = 1048576
88const LA_POW21: i64 = 2097152
89const LA_POW26: i64 = 67108864
90const LA_POW32: i64 = 4294967296
91
92// ---- 3R opcodes (bits 31..15) ----
93const LA_OP_ADD_W: i64 = 0x00100000
94const LA_OP_ADD_D: i64 = 0x00108000
95const LA_OP_SUB_W: i64 = 0x00110000
96const LA_OP_SUB_D: i64 = 0x00118000
97const LA_OP_SLT: i64 = 0x00120000
98const LA_OP_SLTU: i64 = 0x00128000
99const LA_OP_MASKEQZ: i64 = 0x00130000
100const LA_OP_MASKNEZ: i64 = 0x00138000
101const LA_OP_NOR: i64 = 0x00140000
102const LA_OP_AND: i64 = 0x00148000
103const LA_OP_OR: i64 = 0x00150000
104const LA_OP_XOR: i64 = 0x00158000
105const LA_OP_ORN: i64 = 0x00160000
106const LA_OP_ANDN: i64 = 0x00168000
107const LA_OP_SLL_W: i64 = 0x00170000
108const LA_OP_SRL_W: i64 = 0x00178000
109const LA_OP_SRA_W: i64 = 0x00180000
110const LA_OP_SLL_D: i64 = 0x00188000
111const LA_OP_SRL_D: i64 = 0x00190000
112const LA_OP_SRA_D: i64 = 0x00198000
113const LA_OP_MUL_W: i64 = 0x001c0000
114const LA_OP_MUL_D: i64 = 0x001d8000
115const LA_OP_DIV_D: i64 = 0x00220000
116const LA_OP_MOD_D: i64 = 0x00228000
117const LA_INSN_SYSCALL: i64 = 0x002B0000
118
119// ---- shift-immediate opcodes ----
120// The .w forms carry a 5-bit ui5 in bits 14..10, so their opcode field
121// is the 17-bit 3R field; the .d forms carry a 6-bit ui6 in bits 15..10,
122// so their opcode field is one bit SHORTER. Two masks, not one --
123// masking a .d form with the 3R mask splits it across two values.
124const LA_OP_SLLI_W: i64 = 0x00408000
125const LA_OP_SRLI_W: i64 = 0x00448000
126const LA_OP_SRAI_W: i64 = 0x00488000
127const LA_OP_SLLI_D: i64 = 0x00410000
128const LA_OP_SRLI_D: i64 = 0x00450000
129const LA_OP_SRAI_D: i64 = 0x00490000
130
131// ---- 2RI12 opcodes (bits 31..22) ----
132// SLTI/SLTUI/ADDI/LD/ST take a SIGN-extended si12; ANDI/ORI/XORI take a
133// ZERO-extended ui12. Using one extension for both is the classic
134// LoongArch decoder bug and it is silent for small positive immediates.
135const LA_OP_SLTI: i64 = 0x02000000
136const LA_OP_SLTUI: i64 = 0x02400000
137const LA_OP_ADDI_W: i64 = 0x02800000
138const LA_OP_ADDI_D: i64 = 0x02c00000
139const LA_OP_ANDI: i64 = 0x03400000
140const LA_OP_ORI: i64 = 0x03800000
141const LA_OP_XORI: i64 = 0x03c00000
142const LA_OP_LD_B: i64 = 0x28000000
143const LA_OP_LD_H: i64 = 0x28400000
144const LA_OP_LD_W: i64 = 0x28800000
145const LA_OP_LD_D: i64 = 0x28c00000
146const LA_OP_ST_B: i64 = 0x29000000
147const LA_OP_ST_H: i64 = 0x29400000
148const LA_OP_ST_W: i64 = 0x29800000
149const LA_OP_ST_D: i64 = 0x29c00000
150const LA_OP_LD_BU: i64 = 0x2a000000
151const LA_OP_LD_HU: i64 = 0x2a400000
152const LA_OP_LD_WU: i64 = 0x2a800000
153
154// ---- 1RI20 opcodes (bits 31..25) ----
155const LA_OP_LU12I_W: i64 = 0x14000000
156const LA_OP_LU32I_D: i64 = 0x16000000
157const LA_OP_PCADDI: i64 = 0x18000000
158const LA_OP_PCALAU12I: i64 = 0x1a000000
159const LA_OP_PCADDU12I: i64 = 0x1c000000
160
161// ---- branch / jump opcodes (bits 31..26) ----
162const LA_OP_BEQZ: i64 = 0x40000000
163const LA_OP_BNEZ: i64 = 0x44000000
164const LA_OP_JIRL: i64 = 0x4c000000
165const LA_OP_B: i64 = 0x50000000
166const LA_OP_BL: i64 = 0x54000000
167const LA_OP_BEQ: i64 = 0x58000000
168const LA_OP_BNE: i64 = 0x5c000000
169const LA_OP_BLT: i64 = 0x60000000
170const LA_OP_BGE: i64 = 0x64000000
171const LA_OP_BLTU: i64 = 0x68000000
172const LA_OP_BGEU: i64 = 0x6c000000
173
174// ---- decode masks ----
175const LA_MASK_3R: i64 = 0xFFFF8000
176const LA_MASK_3RI6: i64 = 0xFFFF0000
177const LA_MASK_2RI12: i64 = 0xFFC00000
178const LA_MASK_1RI20: i64 = 0xFE000000
179const LA_MASK_OP6: i64 = 0xFC000000
180
181const LA_REG_COUNT: i64 = 32
182const LA_REG_BYTES: i64 = 256 // 32 registers of 8 bytes
183const LA_REG_SP: i64 = 3 // sp is r3
184const LA_REG_RA: i64 = 1 // ra is r1
185const LA_REG_A0: i64 = 4 // a0 is r4
186const LA_REG_A1: i64 = 5
187const LA_REG_A2: i64 = 6
188const LA_REG_A7: i64 = 11 // a7 is r11, it carries the syscall number
189
190// ===== primitives ==================================================
191
192func la_g_ld(mem: *u8, va: i64, width: i64) -> i64 { // little-endian, zero-fill
193 var v: i64 = 0
194 var i: i64 = 0
195 while i < width {
196 v = v | ((mem[va + i] & 0xff) << (i * 8))
197 i = i + 1
198 }
199 return v
200}
201
202func la_g_ld_s(mem: *u8, va: i64, width: i64) -> i64 { // little-endian, sign-extended
203 var v: i64 = la_g_ld(mem, va, width)
204 if width < 8 {
205 let sign: i64 = 1 << (width * 8 - 1)
206 if (v & sign) != 0 { v = v - (1 << (width * 8)) }
207 }
208 return v
209}
210
211func la_g_st(mem: *u8, va: i64, width: i64, val: i64) -> i64 {
212 var i: i64 = 0
213 while i < width {
214 mem[va + i] = (val >> (i * 8)) & 0xff
215 i = i + 1
216 }
217 return 0
218}
219
220// A guest address is UNTRUSTED input. Without this the guest could
221// make the interpreter read or write outside its own arena.
222func la_ea_ok(ea: i64, width: i64, mem_size: i64) -> i64 {
223 if ea < 0 { return 0 }
224 if (ea + width) > mem_size { return 0 }
225 return 1
226}
227
228func la_rd(r: *i64, n: i64) -> i64 { if n == 0 { return 0 } return r[n] }
229func la_wr(r: *i64, n: i64, v: i64) -> i64 { if n != 0 { r[n] = v } return 0 }
230
231// The NishiLang >> operator is ARITHMETIC. Logical right shift has to
232// mask the sign copies away, or srl.d silently becomes sra.d.
233func la_srl(v: i64, n: i64) -> i64 {
234 if n == 0 { return v }
235 let mask: i64 = (1 << (64 - n)) - 1
236 return (v >> n) & mask
237}
238
239// unsigned a < b (flip the sign bit, then compare signed)
240func la_ltu(a: i64, b: i64) -> i64 {
241 let m: i64 = 1 << 63
242 if (a ^ m) < (b ^ m) { return 1 }
243 return 0
244}
245
246func la_sext32(v: i64) -> i64 {
247 let lo: i64 = v & 0xffffffff
248 if (lo & 0x80000000) != 0 { return lo - LA_POW32 }
249 return lo
250}
251func la_sext12(v: i64) -> i64 { if (v & 0x800) != 0 { return v - LA_POW12 } return v }
252func la_sext16(v: i64) -> i64 { if (v & 0x8000) != 0 { return v - LA_POW16 } return v }
253func la_sext20(v: i64) -> i64 { if (v & 0x80000) != 0 { return v - LA_POW20 } return v }
254func la_sext21(v: i64) -> i64 { if (v & 0x100000) != 0 { return v - LA_POW21 } return v }
255func la_sext26(v: i64) -> i64 { if (v & 0x2000000) != 0 { return v - LA_POW26 } return v }
256
257// ===== the interpreter =============================================
258
259func emu_loongarch64_run_mem(mem: *u8, mem_size: i64, entry: i64, sp0: i64) -> i64 {
260 let r: *i64 = sys_mmap(LA_REG_BYTES) as *i64
261 var i: i64 = 0
262 while i < LA_REG_COUNT { r[i] = 0; i = i + 1 }
263 r[LA_REG_SP] = sp0
264 var pc: i64 = entry
265 var steps: i64 = 0
266 var halted: i64 = 0
267 // RANOFF, not FAULT: falling out of the loop with no exit syscall is
268 // a structural non-completion, not a bad address.
269 var result: i64 = LAE_RANOFF
270 let la_min: i64 = 1 << 63
271
272 while halted == 0 && steps < LA_MAGIC_200000000 {
273 if pc < 0 { halted = 1; result = LAE_FAULT }
274 if pc + 4 > mem_size { halted = 1; result = LAE_FAULT }
275 if halted == 0 {
276 let w: i64 = la_g_ld(mem, pc, 4)
277 let rd: i64 = w & 0x1F
278 let rj: i64 = (w >> 5) & 0x1F
279 let rk: i64 = (w >> 10) & 0x1F
280 let va: i64 = la_rd(r, rj)
281 let vb: i64 = la_rd(r, rk)
282 let vd: i64 = la_rd(r, rd)
283 var next: i64 = pc + 4
284 var handled: i64 = 0
285
286 // ---- 3R ------------------------------------------------
287 let o3: i64 = w & LA_MASK_3R
288 if o3 == LA_OP_ADD_W { handled = 1; la_wr(r, rd, la_sext32(va + vb)) }
289 if o3 == LA_OP_ADD_D { handled = 1; la_wr(r, rd, va + vb) }
290 if o3 == LA_OP_SUB_W { handled = 1; la_wr(r, rd, la_sext32(va - vb)) }
291 if o3 == LA_OP_SUB_D { handled = 1; la_wr(r, rd, va - vb) }
292 if o3 == LA_OP_SLT {
293 handled = 1
294 var s_lt: i64 = 0
295 if va < vb { s_lt = 1 }
296 la_wr(r, rd, s_lt)
297 }
298 if o3 == LA_OP_SLTU { handled = 1; la_wr(r, rd, la_ltu(va, vb)) }
299 if o3 == LA_OP_MASKEQZ {
300 handled = 1
301 var m_eq: i64 = va
302 if vb == 0 { m_eq = 0 }
303 la_wr(r, rd, m_eq)
304 }
305 if o3 == LA_OP_MASKNEZ {
306 handled = 1
307 var m_ne: i64 = 0
308 if vb == 0 { m_ne = va }
309 la_wr(r, rd, m_ne)
310 }
311 if o3 == LA_OP_NOR { handled = 1; la_wr(r, rd, LA_ALL_ONES ^ (va | vb)) }
312 if o3 == LA_OP_AND { handled = 1; la_wr(r, rd, va & vb) }
313 if o3 == LA_OP_OR { handled = 1; la_wr(r, rd, va | vb) }
314 if o3 == LA_OP_XOR { handled = 1; la_wr(r, rd, va ^ vb) }
315 if o3 == LA_OP_ORN { handled = 1; la_wr(r, rd, va | (LA_ALL_ONES ^ vb)) }
316 if o3 == LA_OP_ANDN { handled = 1; la_wr(r, rd, va & (LA_ALL_ONES ^ vb)) }
317 if o3 == LA_OP_SLL_W { handled = 1; la_wr(r, rd, la_sext32((va & 0xffffffff) << (vb & 0x1f))) }
318 if o3 == LA_OP_SRL_W { handled = 1; la_wr(r, rd, la_sext32(la_srl(va & 0xffffffff, vb & 0x1f))) }
319 if o3 == LA_OP_SRA_W { handled = 1; la_wr(r, rd, la_sext32(la_sext32(va) >> (vb & 0x1f))) }
320 if o3 == LA_OP_SLL_D { handled = 1; la_wr(r, rd, va << (vb & 0x3f)) }
321 if o3 == LA_OP_SRL_D { handled = 1; la_wr(r, rd, la_srl(va, vb & 0x3f)) }
322 if o3 == LA_OP_SRA_D { handled = 1; la_wr(r, rd, va >> (vb & 0x3f)) }
323 if o3 == LA_OP_MUL_W { handled = 1; la_wr(r, rd, la_sext32(va * vb)) }
324 if o3 == LA_OP_MUL_D { handled = 1; la_wr(r, rd, va * vb) }
325 if o3 == LA_OP_DIV_D {
326 handled = 1
327 if vb != 0 {
328 var d_q: i64 = 0
329 if va == la_min {
330 if vb == LA_NEG_ONE { d_q = la_min } else { d_q = va / vb }
331 } else {
332 d_q = va / vb
333 }
334 la_wr(r, rd, d_q)
335 }
336 }
337 if o3 == LA_OP_MOD_D {
338 handled = 1
339 if vb != 0 {
340 var d_m: i64 = 0
341 if va == la_min {
342 if vb == LA_NEG_ONE { d_m = 0 } else { d_m = va - (va / vb) * vb }
343 } else {
344 d_m = va - (va / vb) * vb
345 }
346 la_wr(r, rd, d_m)
347 }
348 }
349
350 // ---- shift immediate ------------------------------------
351 let ui5: i64 = (w >> 10) & 0x1F
352 let ui6: i64 = (w >> 10) & 0x3F
353 let osd: i64 = w & LA_MASK_3RI6
354 if o3 == LA_OP_SLLI_W { handled = 1; la_wr(r, rd, la_sext32((va & 0xffffffff) << ui5)) }
355 if o3 == LA_OP_SRLI_W { handled = 1; la_wr(r, rd, la_sext32(la_srl(va & 0xffffffff, ui5))) }
356 if o3 == LA_OP_SRAI_W { handled = 1; la_wr(r, rd, la_sext32(la_sext32(va) >> ui5)) }
357 if osd == LA_OP_SLLI_D { handled = 1; la_wr(r, rd, va << ui6) }
358 if osd == LA_OP_SRLI_D { handled = 1; la_wr(r, rd, la_srl(va, ui6)) }
359 if osd == LA_OP_SRAI_D { handled = 1; la_wr(r, rd, va >> ui6) }
360
361 // ---- 2RI12 ----------------------------------------------
362 let o2: i64 = w & LA_MASK_2RI12
363 let ui12: i64 = (w >> 10) & 0xFFF
364 let si12: i64 = la_sext12(ui12)
365 if o2 == LA_OP_ADDI_D { handled = 1; la_wr(r, rd, va + si12) }
366 if o2 == LA_OP_ADDI_W { handled = 1; la_wr(r, rd, la_sext32(va + si12)) }
367 if o2 == LA_OP_SLTI {
368 handled = 1
369 var i_lt: i64 = 0
370 if va < si12 { i_lt = 1 }
371 la_wr(r, rd, i_lt)
372 }
373 if o2 == LA_OP_SLTUI { handled = 1; la_wr(r, rd, la_ltu(va, si12)) }
374 if o2 == LA_OP_ANDI { handled = 1; la_wr(r, rd, va & ui12) }
375 if o2 == LA_OP_ORI { handled = 1; la_wr(r, rd, va | ui12) }
376 if o2 == LA_OP_XORI { handled = 1; la_wr(r, rd, va ^ ui12) }
377
378 // ---- 2RI12 memory ---------------------------------------
379 var mw: i64 = 0 // access width, 0 means not a memory op
380 var msigned: i64 = 0
381 var mstore: i64 = 0
382 if o2 == LA_OP_LD_B { mw = 1; msigned = 1 }
383 if o2 == LA_OP_LD_H { mw = 2; msigned = 1 }
384 if o2 == LA_OP_LD_W { mw = 4; msigned = 1 }
385 if o2 == LA_OP_LD_D { mw = 8 }
386 if o2 == LA_OP_LD_BU { mw = 1 }
387 if o2 == LA_OP_LD_HU { mw = 2 }
388 if o2 == LA_OP_LD_WU { mw = 4 }
389 if o2 == LA_OP_ST_B { mw = 1; mstore = 1 }
390 if o2 == LA_OP_ST_H { mw = 2; mstore = 1 }
391 if o2 == LA_OP_ST_W { mw = 4; mstore = 1 }
392 if o2 == LA_OP_ST_D { mw = 8; mstore = 1 }
393 if mw > 0 {
394 handled = 1
395 let ea: i64 = va + si12
396 if la_ea_ok(ea, mw, mem_size) == 0 {
397 result = LAE_FAULT
398 halted = 1
399 } else {
400 if mstore == 1 {
401 la_g_st(mem, ea, mw, vd)
402 } else {
403 if msigned == 1 {
404 la_wr(r, rd, la_g_ld_s(mem, ea, mw))
405 } else {
406 la_wr(r, rd, la_g_ld(mem, ea, mw))
407 }
408 }
409 }
410 }
411
412 // ---- 1RI20 constant / pc-relative -----------------------
413 let o1: i64 = w & LA_MASK_1RI20
414 let si20: i64 = (w >> 5) & 0xFFFFF
415 if o1 == LA_OP_LU12I_W { handled = 1; la_wr(r, rd, la_sext32(si20 << 12)) }
416 if o1 == LA_OP_LU32I_D { handled = 1; la_wr(r, rd, (la_sext20(si20) << 32) | (vd & 0xffffffff)) }
417 if o1 == LA_OP_PCADDI { handled = 1; la_wr(r, rd, pc + (la_sext20(si20) << 2)) }
418 if o1 == LA_OP_PCADDU12I { handled = 1; la_wr(r, rd, pc + (la_sext20(si20) << 12)) }
419 if o1 == LA_OP_PCALAU12I {
420 handled = 1
421 let pa: i64 = pc + (la_sext20(si20) << 12)
422 la_wr(r, rd, pa - (pa & 0xFFF))
423 }
424
425 // ---- branches and jumps ---------------------------------
426 // LoongArch has NO delay slot: the target is measured from
427 // the branch instruction itself, target = pc + sext(off<<2).
428 let op6: i64 = w & LA_MASK_OP6
429 let off16: i64 = la_sext16((w >> 10) & 0xFFFF)
430
431 if op6 == LA_OP_JIRL {
432 handled = 1
433 let jt: i64 = va + (off16 << 2) // read rj BEFORE writing rd
434 la_wr(r, rd, pc + 4)
435 next = jt
436 }
437 if op6 == LA_OP_BL {
438 handled = 1
439 let o26b: i64 = ((w & 0x3FF) << 16) | ((w >> 10) & 0xFFFF)
440 r[LA_REG_RA] = pc + 4
441 next = pc + (la_sext26(o26b) << 2)
442 }
443 if op6 == LA_OP_B {
444 handled = 1
445 let o26u: i64 = ((w & 0x3FF) << 16) | ((w >> 10) & 0xFFFF)
446 next = pc + (la_sext26(o26u) << 2)
447 }
448
449 var isb2: i64 = 0
450 var take2: i64 = 0
451 if op6 == LA_OP_BEQ { isb2 = 1; if va == vd { take2 = 1 } }
452 if op6 == LA_OP_BNE { isb2 = 1; if va != vd { take2 = 1 } }
453 if op6 == LA_OP_BLT { isb2 = 1; if va < vd { take2 = 1 } }
454 if op6 == LA_OP_BGE { isb2 = 1; if va >= vd { take2 = 1 } }
455 if op6 == LA_OP_BLTU { isb2 = 1; take2 = la_ltu(va, vd) }
456 if op6 == LA_OP_BGEU {
457 isb2 = 1
458 if la_ltu(va, vd) == 0 { take2 = 1 }
459 }
460 if isb2 == 1 {
461 handled = 1
462 if take2 == 1 { next = pc + (off16 << 2) }
463 }
464
465 var isb1: i64 = 0
466 var take1: i64 = 0
467 if op6 == LA_OP_BEQZ { isb1 = 1; if va == 0 { take1 = 1 } }
468 if op6 == LA_OP_BNEZ { isb1 = 1; if va != 0 { take1 = 1 } }
469 if isb1 == 1 {
470 handled = 1
471 if take1 == 1 {
472 let o21: i64 = ((w >> 10) & 0xFFFF) | ((w & 0x1F) << 16)
473 next = pc + (la_sext21(o21) << 2)
474 }
475 }
476
477 // ---- syscall (a7 = r11 holds the number) -----------------
478 if o3 == LA_INSN_SYSCALL {
479 handled = 1
480 let nr: i64 = r[LA_REG_A7]
481 if nr == LA_SYS_EXIT { result = r[LA_REG_A0] & 0xff; halted = 1 }
482 if nr == LA_SYS_EXITG { result = r[LA_REG_A0] & 0xff; halted = 1 }
483 if nr == LA_SYS_WRITE {
484 if la_ea_ok(r[LA_REG_A1], r[LA_REG_A2], mem_size) == 1 {
485 r[LA_REG_A0] = sys_write(r[LA_REG_A0], ((mem as i64) + r[LA_REG_A1]) as *u8, r[LA_REG_A2])
486 } else {
487 result = LAE_FAULT
488 halted = 1
489 }
490 }
491 if nr == LA_SYS_READ {
492 if la_ea_ok(r[LA_REG_A1], r[LA_REG_A2], mem_size) == 1 {
493 r[LA_REG_A0] = sys_read(r[LA_REG_A0], ((mem as i64) + r[LA_REG_A1]) as *u8, r[LA_REG_A2])
494 } else {
495 result = LAE_FAULT
496 halted = 1
497 }
498 }
499 }
500
501 if handled == 0 { result = LAE_UNSUPPORTED; halted = 1 }
502 pc = next
503 steps = steps + 1
504 }
505 }
506 return result
507}
508
509func emu_loongarch64_load_elf(buf: *u8, len: i64) -> i64 {
510 if len < 64 { return LAE_FAULT }
511 let e_entry: i64 = la_g_ld(buf, 24, 8)
512 let e_phoff: i64 = la_g_ld(buf, 32, 8)
513 let e_phnum: i64 = la_g_ld(buf, 56, 2)
514 let e_phent: i64 = la_g_ld(buf, 54, 2)
515 let mem: *u8 = sys_mmap(LA_GUEST_SIZE)
516 var idx: i64 = 0
517 while idx < e_phnum {
518 let ph: i64 = e_phoff + idx * e_phent
519 if la_g_ld(buf, ph, 4) == 1 {
520 let p_off: i64 = la_g_ld(buf, ph + 8, 8)
521 let p_va: i64 = la_g_ld(buf, ph + 16, 8)
522 let p_fs: i64 = la_g_ld(buf, ph + 32, 8)
523 var k: i64 = 0
524 while k < p_fs {
525 if (p_va + k) < LA_GUEST_SIZE { mem[p_va + k] = buf[p_off + k] }
526 k = k + 1
527 }
528 }
529 idx = idx + 1
530 }
531 return emu_loongarch64_run_mem(mem, LA_GUEST_SIZE, e_entry, 0x00F00000)
532}