code wiki / (root) / nx_emu_sparc64.nx

nx_emu_sparc64.nx source

↩ module page · 195 lines · 9078 B

1// nx_emu_sparc64.nx -- sovereign SPARC V9 (sparc64) interpreter (NX-EMU). 2// Fourth architecture, the hardest: REGISTER WINDOWS + DELAY SLOTS + 3// BIG-ENDIAN. Decode/execute is pure NishiLang per the SPARC V9 ISA 4// (the spec is the oracle) -- NO qemu. qemu-sparc64 is only the 5// differential BENCHMARK that must agree. This is how Nishi CARRIES 6// sparc64 execution (operator: "nishi ecosystem must carry; externals 7// = benchmarks only"). 8// 9// Register model: 8 globals g[0..7] (g0 == 0); a window file win[16*NWIN] 10// with the standard overlap (%o of window W == %i of window W+1) selected 11// by CWP. reg field 0..7=%g, 8..15=%o, 16..23=%l, 24..31=%i. save = CWP+1 12// (compute in old window, write rd in new), restore = CWP-1. Control 13// transfers (CALL/JMPL) take effect after the delay-slot instruction via 14// the PC/nPC model. Linux/SPARC syscalls: g1=number, o0..=args, `ta` traps. 15// 16// genealogy_id: sparc_v9_isa + linux_sparc64_abi 17// lineage_id: nishi_nx_emu_sparc64_m1 18// license_tier: ORIGINAL 19 20import "nx_syscalls_x86_64.nx" 21const SP_MAGIC_200000000: i64 = 200000000 22 23const SP_GUEST_SIZE: i64 = 16777216 24const SP_NWIN: i64 = 8 25const SP_SYS_EXIT: i64 = 1 26const SP_SYS_READ: i64 = 3 27const SP_SYS_WRITE: i64 = 4 28const SPE_UNSUPPORTED: i64 = -1 29const SPE_FAULT: i64 = -3 30 31// big-endian guest load/store (SPARC is MSB-first) 32func sp_g_ld(mem: *u8, va: i64, width: i64) -> i64 { 33 var v: i64 = 0 34 var i: i64 = 0 35 while i < width { v = (v << 8) | (mem[va + i] & 0xff); i = i + 1 } 36 return v 37} 38func sp_g_st(mem: *u8, va: i64, width: i64, val: i64) -> i64 { 39 var i: i64 = 0 40 while i < width { mem[va + (width - 1 - i)] = (val >> (i * 8)) & 0xff; i = i + 1 } 41 return 0 42} 43 44// windowed register read/write. cwp passed by value (save/restore mutate it 45// in the loop; reads/writes use the value at call time). 46func sp_rd(g: *i64, win: *i64, cwp: i64, r: i64) -> i64 { 47 if r == 0 { return 0 } 48 if r < 8 { return g[r] } 49 if r < 16 { return win[((cwp + 1) % SP_NWIN) * 16 + (r - 8)] } // %o 50 if r < 24 { return win[cwp * 16 + 8 + (r - 16)] } // %l 51 return win[cwp * 16 + (r - 24)] // %i 52} 53func sp_wr(g: *i64, win: *i64, cwp: i64, r: i64, v: i64) -> i64 { 54 if r == 0 { return 0 } 55 if r < 8 { g[r] = v; return 0 } 56 if r < 16 { win[((cwp + 1) % SP_NWIN) * 16 + (r - 8)] = v; return 0 } 57 if r < 24 { win[cwp * 16 + 8 + (r - 16)] = v; return 0 } 58 win[cwp * 16 + (r - 24)] = v 59 return 0 60} 61 62func emu_sparc64_run_mem(mem: *u8, mem_size: i64, entry: i64, sp0: i64) -> i64 { 63 let g: *i64 = sys_mmap(8 * 8) as *i64 64 let win: *i64 = sys_mmap(16 * SP_NWIN * 8) as *i64 65 var i: i64 = 0 66 while i < 8 { g[i] = 0; i = i + 1 } 67 i = 0 68 while i < 16 * SP_NWIN { win[i] = 0; i = i + 1 } 69 var cwp: i64 = 0 70 sp_wr(g, win, cwp, 14, sp0) // %o6 = %sp 71 var pc: i64 = entry 72 var npc: i64 = entry + 4 73 var steps: i64 = 0 74 var halted: i64 = 0 75 var result: i64 = SPE_FAULT 76 while halted == 0 && steps < SP_MAGIC_200000000 { 77 if pc < 0 { halted = 1; result = SPE_FAULT } 78 if pc + 4 > mem_size { halted = 1; result = SPE_FAULT } 79 if halted == 0 { 80 let w: i64 = sp_g_ld(mem, pc, 4) 81 let op: i64 = (w >> 30) & 3 82 let rd: i64 = (w >> 25) & 0x1F 83 var next_pc: i64 = npc 84 var next_npc: i64 = npc + 4 85 var handled: i64 = 0 86 87 if op == 1 { // CALL (format 1) 88 handled = 1 89 var disp: i64 = w & 0x3FFFFFFF 90 if (disp & 0x20000000) != 0 { disp = disp - 0x40000000 } 91 sp_wr(g, win, cwp, 15, pc) // %o7 = pc 92 next_npc = pc + (disp << 2) 93 } 94 if op == 0 { // format 2: SETHI / branches 95 let op2: i64 = (w >> 22) & 7 96 if op2 == 4 { // SETHI (NOP = sethi 0,%g0) 97 handled = 1 98 sp_wr(g, win, cwp, rd, (w & 0x3FFFFF) << 10) 99 } 100 } 101 if op == 2 { // format 3: arithmetic / control 102 let op3: i64 = (w >> 19) & 0x3F 103 let rs1: i64 = (w >> 14) & 0x1F 104 let ibit: i64 = (w >> 13) & 1 105 var o2: i64 = 0 106 if ibit == 1 { 107 o2 = w & 0x1FFF 108 if (o2 & 0x1000) != 0 { o2 = o2 - 0x2000 } 109 } else { 110 o2 = sp_rd(g, win, cwp, w & 0x1F) 111 } 112 let a: i64 = sp_rd(g, win, cwp, rs1) 113 if op3 == 0x00 { handled = 1; sp_wr(g, win, cwp, rd, a + o2) } // ADD 114 if op3 == 0x04 { handled = 1; sp_wr(g, win, cwp, rd, a - o2) } // SUB 115 if op3 == 0x01 { handled = 1; sp_wr(g, win, cwp, rd, a & o2) } // AND 116 if op3 == 0x02 { handled = 1; sp_wr(g, win, cwp, rd, a | o2) } // OR 117 if op3 == 0x03 { handled = 1; sp_wr(g, win, cwp, rd, a ^ o2) } // XOR 118 if op3 == 0x09 { handled = 1; sp_wr(g, win, cwp, rd, a * o2) } // MULX 119 if op3 == 0x2d { handled = 1; if o2 != 0 { sp_wr(g, win, cwp, rd, a / o2) } } // SDIVX 120 if op3 == 0x0d { handled = 1; if o2 != 0 { sp_wr(g, win, cwp, rd, a / o2) } } // UDIVX 121 if op3 == 0x25 { handled = 1; sp_wr(g, win, cwp, rd, a << (o2 & 63)) } // SLLX (x=1) 122 if op3 == 0x26 { handled = 1; sp_wr(g, win, cwp, rd, (a >> (o2 & 63))) } // SRLX/SRAX 123 if op3 == 0x3c { // SAVE: compute old window, shift, write new 124 handled = 1 125 cwp = (cwp + 1) % SP_NWIN 126 sp_wr(g, win, cwp, rd, a + o2) 127 } 128 if op3 == 0x3d { // RESTORE 129 handled = 1 130 cwp = (cwp - 1 + SP_NWIN) % SP_NWIN 131 sp_wr(g, win, cwp, rd, a + o2) 132 } 133 if op3 == 0x38 { // JMPL (ret = jmpl %i7+8,%g0) 134 handled = 1 135 sp_wr(g, win, cwp, rd, pc) 136 next_npc = a + o2 137 } 138 if op3 == 0x3a { // Tcc (trap) -> Linux syscall (g1=num) 139 handled = 1 140 let nr: i64 = g[1] 141 if nr == SP_SYS_EXIT { result = sp_rd(g, win, cwp, 8) & 0xff; halted = 1 } 142 if nr == SP_SYS_WRITE { sp_wr(g, win, cwp, 8, sys_write(sp_rd(g, win, cwp, 8), ((mem as i64) + sp_rd(g, win, cwp, 9)) as *u8, sp_rd(g, win, cwp, 10))) } 143 if nr == SP_SYS_READ { sp_wr(g, win, cwp, 8, sys_read(sp_rd(g, win, cwp, 8), ((mem as i64) + sp_rd(g, win, cwp, 9)) as *u8, sp_rd(g, win, cwp, 10))) } 144 } 145 } 146 if op == 3 { // format 3: load / store 147 let op3: i64 = (w >> 19) & 0x3F 148 let rs1: i64 = (w >> 14) & 0x1F 149 let ibit: i64 = (w >> 13) & 1 150 var o2: i64 = 0 151 if ibit == 1 { 152 o2 = w & 0x1FFF 153 if (o2 & 0x1000) != 0 { o2 = o2 - 0x2000 } 154 } else { 155 o2 = sp_rd(g, win, cwp, w & 0x1F) 156 } 157 let ea: i64 = sp_rd(g, win, cwp, rs1) + o2 158 if op3 == 0x0b { handled = 1; sp_wr(g, win, cwp, rd, sp_g_ld(mem, ea, 8)) } // LDX 159 if op3 == 0x0e { handled = 1; sp_g_st(mem, ea, 8, sp_rd(g, win, cwp, rd)) } // STX 160 if op3 == 0x08 { handled = 1; sp_wr(g, win, cwp, rd, sp_g_ld(mem, ea, 4)) } // LDUW 161 if op3 == 0x04 { handled = 1; sp_g_st(mem, ea, 4, sp_rd(g, win, cwp, rd)) } // STW 162 } 163 164 if handled == 0 { result = SPE_UNSUPPORTED; halted = 1 } 165 pc = next_pc 166 npc = next_npc 167 steps = steps + 1 168 } 169 } 170 return result 171} 172 173func emu_sparc64_load_elf(buf: *u8, len: i64) -> i64 { 174 if len < 64 { return SPE_FAULT } 175 let e_entry: i64 = sp_g_ld(buf, 24, 8) 176 let e_phoff: i64 = sp_g_ld(buf, 32, 8) 177 let e_phnum: i64 = sp_g_ld(buf, 56, 2) 178 let e_phent: i64 = sp_g_ld(buf, 54, 2) 179 let mem: *u8 = sys_mmap(SP_GUEST_SIZE) 180 var idx: i64 = 0 181 while idx < e_phnum { 182 let ph: i64 = e_phoff + idx * e_phent 183 if sp_g_ld(buf, ph, 4) == 1 { // PT_LOAD 184 let p_off: i64 = sp_g_ld(buf, ph + 8, 8) 185 let p_va: i64 = sp_g_ld(buf, ph + 16, 8) 186 let p_fs: i64 = sp_g_ld(buf, ph + 32, 8) 187 var k: i64 = 0 188 while k < p_fs { if (p_va + k) < SP_GUEST_SIZE { mem[p_va + k] = buf[p_off + k] }; k = k + 1 } 189 } 190 idx = idx + 1 191 } 192 // initial %sp: biased; sp+2047+frame must stay in RAM 193 let sp: i64 = 0x00E00000 194 return emu_sparc64_run_mem(mem, SP_GUEST_SIZE, e_entry, sp) 195}