nx_emu_riscv32.nx source
↩ module page · 110 lines · 4861 B
1// nx_emu_riscv32.nx -- sovereign RV32IM interpreter (NX-EMU, 32-bit XLEN).
2// Sibling of nx_emu_rv64 for the 32-bit RISC-V target; nxc2's rv32 backend
3// represents i64 as register PAIRS, so the KAT exercises add/sub/mul/mulhu/sltu
4// (carry) over 32-bit lanes. Flat little-endian guest RAM, Linux rv32 syscalls
5// (exit=93, a7=num, a0..=args). NO qemu -- decode/execute per the RISC-V ISA.
6// license_tier: ORIGINAL
7import "nx_syscalls_x86_64.nx"
8
9const R32_GUEST: i64 = 16777216
10const R32_MASK: i64 = 0xFFFFFFFF
11const R32_SYS_EXIT: i64 = 93
12
13func r32_ld(mem: *u8, va: i64, width: i64) -> i64 {
14 var v: i64 = 0
15 var i: i64 = 0
16 while i < width { v = v | ((mem[va + i] & 0xff) << (i * 8)); i = i + 1 }
17 return v
18}
19func r32_st(mem: *u8, va: i64, width: i64, val: i64) -> i64 {
20 var i: i64 = 0
21 while i < width { mem[va + i] = (val >> (i * 8)) & 0xff; i = i + 1 }
22 return 0
23}
24func r32_sx(v: i64, bits: i64) -> i64 { let m: i64 = 1 << (bits - 1); if (v & m) != 0 { return v - (1 << bits) } return v }
25
26func emu_riscv32_run_mem(mem: *u8, mem_size: i64, entry: i64, sp0: i64) -> i64 {
27 let r: *i64 = sys_mmap(32 * 8) as *i64
28 var i: i64 = 0
29 while i < 32 { r[i] = 0; i = i + 1 }
30 r[2] = sp0
31 var pc: i64 = entry
32 var result: i64 = 0
33 var halted: i64 = 0
34 var steps: i64 = 0
35 while halted == 0 {
36 if steps > 5000000 { halted = 1 } else {
37 let w: i64 = r32_ld(mem, pc, 4)
38 let opc: i64 = w & 0x7F
39 let rd: i64 = (w >> 7) & 0x1F
40 let f3: i64 = (w >> 12) & 7
41 let rs1: i64 = (w >> 15) & 0x1F
42 let rs2: i64 = (w >> 20) & 0x1F
43 let f7: i64 = (w >> 25) & 0x7F
44 var next: i64 = pc + 4
45 var handled: i64 = 0
46 if opc == 0x13 { // addi (I)
47 handled = 1
48 let imm: i64 = r32_sx((w >> 20) & 0xFFF, 12)
49 r[rd] = (r[rs1] + imm) & R32_MASK
50 }
51 if opc == 0x37 { // lui (U)
52 handled = 1
53 r[rd] = w & 0xFFFFF000
54 }
55 if opc == 0x33 { // R-type
56 handled = 1
57 let a: i64 = r[rs1] & R32_MASK
58 let b: i64 = r[rs2] & R32_MASK
59 if f3 == 0 { if f7 == 0 { r[rd] = (a + b) & R32_MASK } if f7 == 0x20 { r[rd] = (a - b) & R32_MASK } if f7 == 1 { r[rd] = (a * b) & R32_MASK } }
60 if f3 == 3 { if f7 == 0 { if a < b { r[rd] = 1 } else { r[rd] = 0 } } if f7 == 1 { r[rd] = (a * b) >> 32 } } // sltu / mulhu (a,b<2^32)
61 if f3 == 7 { r[rd] = a & b } // and
62 if f3 == 6 { r[rd] = a | b } // or
63 if f3 == 4 { r[rd] = a ^ b } // xor
64 }
65 if opc == 0x03 { // loads
66 handled = 1
67 let imm: i64 = r32_sx((w >> 20) & 0xFFF, 12)
68 let ea: i64 = (r[rs1] + imm) & R32_MASK
69 if f3 == 2 { r[rd] = r32_ld(mem, ea, 4) & R32_MASK } // lw
70 if f3 == 0 { r[rd] = r32_sx(r32_ld(mem, ea, 1), 8) & R32_MASK }
71 }
72 if opc == 0x23 { // stores
73 handled = 1
74 let imm: i64 = r32_sx((((w >> 25) & 0x7F) << 5) | ((w >> 7) & 0x1F), 12)
75 let ea: i64 = (r[rs1] + imm) & R32_MASK
76 if f3 == 2 { r32_st(mem, ea, 4, r[rs2] & R32_MASK) } // sw
77 }
78 if opc == 0x6F { // jal (J)
79 handled = 1
80 let imm: i64 = r32_sx(((( w >> 21) & 0x3FF) << 1) | (((w >> 20) & 1) << 11) | (((w >> 12) & 0xFF) << 12) | (((w >> 31) & 1) << 20), 21)
81 r[rd] = (pc + 4) & R32_MASK
82 next = pc + imm
83 }
84 if opc == 0x67 { // jalr (I)
85 handled = 1
86 let imm: i64 = r32_sx((w >> 20) & 0xFFF, 12)
87 let t: i64 = (pc + 4) & R32_MASK
88 next = (r[rs1] + imm) & (R32_MASK - 1)
89 r[rd] = t
90 }
91 if opc == 0x73 { // ecall
92 handled = 1
93 let nr: i64 = r[17]
94 if nr == R32_SYS_EXIT { result = r[10] & 0xff; halted = 1 }
95 }
96 if handled == 0 { result = 0 - 1; halted = 1 }
97 r[0] = 0
98 pc = next
99 steps = steps + 1
100 }
101 }
102 return result
103}
104
105func emu_riscv32_run(code: *u8, code_len: i64) -> i64 {
106 let mem: *u8 = sys_mmap(R32_GUEST)
107 var i: i64 = 0
108 while i < code_len { mem[i] = code[i]; i = i + 1 }
109 return emu_riscv32_run_mem(mem, R32_GUEST, 0, 0x00800000)
110}