code wiki / (root) / nx_emu_riscv32.nx

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}