code wiki / (root) / nx_emu_sparc64.nx

nx_emu_sparc64.nx source

↩ module page · 497 lines · 23914 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// ---- CONDITION CODES + CONDITIONAL BRANCHES (added 2026-09-03) ---- 32// WHAT WAS MISSING AND WHY IT MATTERED: this decoder carried NO condition 33// code state and NO branch of any kind -- format 2 handled only SETHI -- 34// so it could not run a LOOP. "Nishi carries sparc64 execution" was a 35// claim about straight-line code. Every encoding below is taken from the 36// SPARC V9 manual (formats 2 and 3, and the Bicc/BPcc cond table); none of 37// it is read back out of this file, which would calibrate the ruler to the 38// subject. The ruler is nx_isa_sparc64_gate. 39const SP_OP2_BPCC: i64 = 1 // BPcc: disp19 plus a cc-field selector 40const SP_OP2_BICC: i64 = 2 // Bicc: disp22, always %icc 41const SP_OP2_SETHI: i64 = 4 42 43// SPARC V9 keeps TWO integer condition-code fields: icc is computed from 44// the LOW 32 BITS of a result and xcc from all 64. Folding them into one 45// set of flags makes every 64-bit compare answer a 32-bit question, and 46// the failure is silent for every value that fits in 32 bits -- which is 47// most of them. Slot layout per field: N Z V C. 48const SP_ICC_N: i64 = 0 49const SP_XCC_N: i64 = 4 50const SP_CC_SLOTS: i64 = 8 51 52const SP_IMM22_MASK: i64 = 4194303 // 0x3FFFFF: SETHI imm22 and Bicc disp22 53const SP_DISP22_SIGN: i64 = 2097152 // 0x200000 54const SP_DISP22_MOD: i64 = 4194304 // 0x400000 55const SP_DISP19_MASK: i64 = 524287 // 0x7FFFF: BPcc disp19 56const SP_DISP19_SIGN: i64 = 262144 // 0x40000 57const SP_DISP19_MOD: i64 = 524288 // 0x80000 58const SP_M32: i64 = 4294967295 // 0xFFFFFFFF 59const SP_M16: i64 = 65535 60const SP_B31: i64 = 2147483648 // 0x80000000 61const SP_2P32: i64 = 4294967296 62const SP_B15: i64 = 32768 63const SP_2P16: i64 = 65536 64const SP_B7: i64 = 128 65const SP_2P8: i64 = 256 66const SP_COND_BA: i64 = 8 // branch-always: the ONE cond whose 67 // annul bit fires on a TAKEN branch 68const SP_CCSEL_ICC: i64 = 0 // BPcc cc1cc0: 00 = %icc, 10 = %xcc; 69const SP_CCSEL_XCC: i64 = 2 // 01 and 11 are RESERVED -> refuse 70 71// This dialect's >> is ARITHMETIC (nx_emu_mips64 carries mi_srl for exactly 72// this reason), so SRLX and SRAX were the SAME expression in the incumbent 73// and one of the two was necessarily wrong for every negative operand. A 74// mask is the only way to get a true logical shift; n == 0 is guarded 75// because 1 << 64 is not a shift this machine performs. 76func sp_srl(v: i64, n: i64) -> i64 { 77 if n == 0 { return v } 78 let mask: i64 = (1 << (64 - n)) - 1 79 return (v >> n) & mask 80} 81func sp_not(v: i64) -> i64 { return (0 - v) - 1 } 82func sp_sx8(v: i64) -> i64 { let t: i64 = v & 0xff; if (t & SP_B7) != 0 { return t - SP_2P8 } return t } 83func sp_sx16(v: i64) -> i64 { let t: i64 = v & SP_M16; if (t & SP_B15) != 0 { return t - SP_2P16 } return t } 84func sp_sx32(v: i64) -> i64 { let t: i64 = v & SP_M32; if (t & SP_B31) != 0 { return t - SP_2P32 } return t } 85func sp_b31(v: i64) -> i64 { if (v & SP_B31) != 0 { return 1 } return 0 } 86func sp_b63(v: i64) -> i64 { if v < 0 { return 1 } return 0 } 87 88// UNSIGNED less-than over two i64s. The dialect's < is signed, so a bare 89// comparison answers the wrong question for exactly the values a carry 90// flag exists to describe. 91func sp_ltu(a: i64, b: i64) -> i64 { 92 if a < 0 { 93 if b < 0 { 94 if a < b { return 1 } 95 return 0 96 } 97 return 0 98 } 99 if b < 0 { return 1 } 100 if a < b { return 1 } 101 return 0 102} 103 104// SPARC V9 arithmetic-with-cc writes BOTH fields: icc from the low 32 bits, 105// xcc from all 64. C is a BORROW for subtraction -- so BLU really means 106// unsigned-less-than -- which is the OPPOSITE polarity from the ARM carry a 107// reader may be carrying in their head. 108func sp_setcc(cc: *i64, a: i64, b: i64, r: i64, is_sub: i64) -> i64 { 109 let a32: i64 = a & SP_M32 110 let b32: i64 = b & SP_M32 111 let r32: i64 = r & SP_M32 112 cc[SP_ICC_N] = sp_b31(r32) 113 var iz: i64 = 0 114 if r32 == 0 { iz = 1 } 115 cc[SP_ICC_N + 1] = iz 116 cc[SP_XCC_N] = sp_b63(r) 117 var xz: i64 = 0 118 if r == 0 { xz = 1 } 119 cc[SP_XCC_N + 1] = xz 120 let ia: i64 = sp_b31(a32) 121 let ib: i64 = sp_b31(b32) 122 let ir: i64 = sp_b31(r32) 123 let xa: i64 = sp_b63(a) 124 let xb: i64 = sp_b63(b) 125 let xr: i64 = sp_b63(r) 126 if is_sub == 1 { 127 cc[SP_ICC_N + 2] = (ia ^ ib) & (ia ^ ir) 128 cc[SP_XCC_N + 2] = (xa ^ xb) & (xa ^ xr) 129 var ic: i64 = 0 130 if a32 < b32 { ic = 1 } 131 cc[SP_ICC_N + 3] = ic 132 cc[SP_XCC_N + 3] = sp_ltu(a, b) 133 } 134 if is_sub == 0 { 135 cc[SP_ICC_N + 2] = (1 - (ia ^ ib)) & (ia ^ ir) 136 cc[SP_XCC_N + 2] = (1 - (xa ^ xb)) & (xa ^ xr) 137 var ac: i64 = 0 138 if (a32 + b32) > SP_M32 { ac = 1 } 139 cc[SP_ICC_N + 3] = ac 140 cc[SP_XCC_N + 3] = sp_ltu(r, a) 141 } 142 return 0 143} 144 145// The logical *cc forms set N and Z from the result and CLEAR V and C. 146func sp_setlog(cc: *i64, r: i64) -> i64 { 147 let r32: i64 = r & SP_M32 148 cc[SP_ICC_N] = sp_b31(r32) 149 var iz: i64 = 0 150 if r32 == 0 { iz = 1 } 151 cc[SP_ICC_N + 1] = iz 152 cc[SP_ICC_N + 2] = 0 153 cc[SP_ICC_N + 3] = 0 154 cc[SP_XCC_N] = sp_b63(r) 155 var xz: i64 = 0 156 if r == 0 { xz = 1 } 157 cc[SP_XCC_N + 1] = xz 158 cc[SP_XCC_N + 2] = 0 159 cc[SP_XCC_N + 3] = 0 160 return 0 161} 162 163// The Bicc/BPcc cond field, SPARC V9 manual. Written row by row rather 164// than packed into a table so a reader can check each line against the 165// manual without decoding a bitmask first. 166func sp_cond(cc: *i64, cond: i64, base: i64) -> i64 { 167 let n: i64 = cc[base] 168 let z: i64 = cc[base + 1] 169 let v: i64 = cc[base + 2] 170 let c: i64 = cc[base + 3] 171 let nv: i64 = n ^ v 172 var cz: i64 = 0 173 if c == 1 { cz = 1 } 174 if z == 1 { cz = 1 } 175 var le: i64 = 0 176 if z == 1 { le = 1 } 177 if nv == 1 { le = 1 } 178 if cond == 0 { return 0 } // BN never 179 if cond == 1 { return z } // BE Z 180 if cond == 2 { return le } // BLE Z or (N xor V) 181 if cond == 3 { return nv } // BL N xor V 182 if cond == 4 { return cz } // BLEU C or Z 183 if cond == 5 { return c } // BCS/BLU C 184 if cond == 6 { return n } // BNEG N 185 if cond == 7 { return v } // BVS V 186 if cond == 8 { return 1 } // BA always 187 if cond == 9 { return 1 - z } // BNE 188 if cond == 10 { return 1 - le } // BG 189 if cond == 11 { return 1 - nv } // BGE 190 if cond == 12 { return 1 - cz } // BGU 191 if cond == 13 { return 1 - c } // BCC/BGEU 192 if cond == 14 { return 1 - n } // BPOS 193 return 1 - v // BVC (cond 15) 194} 195 196// big-endian guest load/store (SPARC is MSB-first) 197func sp_g_ld(mem: *u8, va: i64, width: i64) -> i64 { 198 var v: i64 = 0 199 var i: i64 = 0 200 while i < width { v = (v << 8) | (mem[va + i] & 0xff); i = i + 1 } 201 return v 202} 203func sp_g_st(mem: *u8, va: i64, width: i64, val: i64) -> i64 { 204 var i: i64 = 0 205 while i < width { mem[va + (width - 1 - i)] = (val >> (i * 8)) & 0xff; i = i + 1 } 206 return 0 207} 208 209// windowed register read/write. cwp passed by value (save/restore mutate it 210// in the loop; reads/writes use the value at call time). 211func sp_rd(g: *i64, win: *i64, cwp: i64, r: i64) -> i64 { 212 if r == 0 { return 0 } 213 if r < 8 { return g[r] } 214 if r < 16 { return win[((cwp + 1) % SP_NWIN) * 16 + (r - 8)] } // %o 215 if r < 24 { return win[cwp * 16 + 8 + (r - 16)] } // %l 216 return win[cwp * 16 + (r - 24)] // %i 217} 218func sp_wr(g: *i64, win: *i64, cwp: i64, r: i64, v: i64) -> i64 { 219 if r == 0 { return 0 } 220 if r < 8 { g[r] = v; return 0 } 221 if r < 16 { win[((cwp + 1) % SP_NWIN) * 16 + (r - 8)] = v; return 0 } 222 if r < 24 { win[cwp * 16 + 8 + (r - 16)] = v; return 0 } 223 win[cwp * 16 + (r - 24)] = v 224 return 0 225} 226 227func emu_sparc64_run_mem(mem: *u8, mem_size: i64, entry: i64, sp0: i64) -> i64 { 228 let g: *i64 = sys_mmap(8 * 8) as *i64 229 let win: *i64 = sys_mmap(16 * SP_NWIN * 8) as *i64 230 var i: i64 = 0 231 while i < 8 { g[i] = 0; i = i + 1 } 232 i = 0 233 while i < 16 * SP_NWIN { win[i] = 0; i = i + 1 } 234 var cwp: i64 = 0 235 // The integer condition codes. Two fields of N Z V C: icc (slots 0..3) 236 // from the low 32 bits of a result, xcc (slots 4..7) from all 64. 237 let cc: *i64 = sys_mmap(SP_CC_SLOTS * 8) as *i64 238 var ci: i64 = 0 239 while ci < SP_CC_SLOTS { cc[ci] = 0; ci = ci + 1 } 240 sp_wr(g, win, cwp, 14, sp0) // %o6 = %sp 241 var pc: i64 = entry 242 var npc: i64 = entry + 4 243 var steps: i64 = 0 244 var halted: i64 = 0 245 var result: i64 = SPE_FAULT 246 while halted == 0 && steps < SP_MAGIC_200000000 { 247 if pc < 0 { halted = 1; result = SPE_FAULT } 248 if pc + 4 > mem_size { halted = 1; result = SPE_FAULT } 249 if halted == 0 { 250 let w: i64 = sp_g_ld(mem, pc, 4) 251 let op: i64 = (w >> 30) & 3 252 let rd: i64 = (w >> 25) & 0x1F 253 var next_pc: i64 = npc 254 var next_npc: i64 = npc + 4 255 var handled: i64 = 0 256 257 if op == 1 { // CALL (format 1) 258 handled = 1 259 var disp: i64 = w & 0x3FFFFFFF 260 if (disp & 0x20000000) != 0 { disp = disp - 0x40000000 } 261 sp_wr(g, win, cwp, 15, pc) // %o7 = pc 262 next_npc = pc + (disp << 2) 263 } 264 if op == 0 { // format 2: SETHI / branches 265 let op2: i64 = (w >> 22) & 7 266 if op2 == SP_OP2_SETHI { // SETHI (NOP = sethi 0,%g0) 267 handled = 1 268 sp_wr(g, win, cwp, rd, (w & SP_IMM22_MASK) << 10) 269 } 270 // In format 2 the rd FIELD is not a register: bit 29 is the 271 // annul bit and bits 28..25 are cond. Reusing rd here is 272 // exact rather than a shortcut, because rd IS (w >> 25) & 0x1F. 273 let cond: i64 = rd & 0xF 274 let anl: i64 = (rd >> 4) & 1 275 if op2 == SP_OP2_BICC { // Bicc: disp22, %icc only 276 handled = 1 277 var d22: i64 = w & SP_IMM22_MASK 278 if (d22 & SP_DISP22_SIGN) != 0 { d22 = d22 - SP_DISP22_MOD } 279 let tk: i64 = sp_cond(cc, cond, SP_ICC_N) 280 // DELAY SLOT: in this pc/npc model the instruction at npc 281 // runs BEFORE the target, which is the semantic a naive 282 // branch silently gets wrong. ANNUL skips it when the 283 // branch is UNTAKEN, and ALSO on BA (cond 8) where it is 284 // taken -- that asymmetry is the whole of the annul rule 285 // and the half an implementation typically drops. 286 var anz: i64 = 0 287 if anl == 1 { 288 if tk == 0 { anz = 1 } 289 if cond == SP_COND_BA { anz = 1 } 290 } 291 let tgt: i64 = pc + (d22 << 2) 292 if tk == 1 { 293 next_npc = tgt 294 if anz == 1 { next_pc = tgt; next_npc = tgt + 4 } 295 } 296 if tk == 0 { 297 if anz == 1 { next_pc = npc + 4; next_npc = npc + 8 } 298 } 299 } 300 if op2 == SP_OP2_BPCC { // BPcc: disp19 + cc selector 301 let ccsel: i64 = (w >> 20) & 3 302 var base: i64 = -1 303 if ccsel == SP_CCSEL_ICC { base = SP_ICC_N } 304 if ccsel == SP_CCSEL_XCC { base = SP_XCC_N } 305 // cc1cc0 of 01 and 11 are RESERVED in V9. Leaving them 306 // unhandled reports UNSUPPORTED instead of confidently 307 // answering a question the manual does not define. 308 if base >= 0 { 309 handled = 1 310 var d19: i64 = w & SP_DISP19_MASK 311 if (d19 & SP_DISP19_SIGN) != 0 { d19 = d19 - SP_DISP19_MOD } 312 let tk2: i64 = sp_cond(cc, cond, base) 313 var anz2: i64 = 0 314 if anl == 1 { 315 if tk2 == 0 { anz2 = 1 } 316 if cond == SP_COND_BA { anz2 = 1 } 317 } 318 let tgt2: i64 = pc + (d19 << 2) 319 if tk2 == 1 { 320 next_npc = tgt2 321 if anz2 == 1 { next_pc = tgt2; next_npc = tgt2 + 4 } 322 } 323 if tk2 == 0 { 324 if anz2 == 1 { next_pc = npc + 4; next_npc = npc + 8 } 325 } 326 } 327 } 328 } 329 if op == 2 { // format 3: arithmetic / control 330 let op3: i64 = (w >> 19) & 0x3F 331 let rs1: i64 = (w >> 14) & 0x1F 332 let ibit: i64 = (w >> 13) & 1 333 var o2: i64 = 0 334 if ibit == 1 { 335 o2 = w & 0x1FFF 336 if (o2 & 0x1000) != 0 { o2 = o2 - 0x2000 } 337 } else { 338 o2 = sp_rd(g, win, cwp, w & 0x1F) 339 } 340 let a: i64 = sp_rd(g, win, cwp, rs1) 341 if op3 == 0x00 { handled = 1; sp_wr(g, win, cwp, rd, a + o2) } // ADD 342 if op3 == 0x04 { handled = 1; sp_wr(g, win, cwp, rd, a - o2) } // SUB 343 if op3 == 0x01 { handled = 1; sp_wr(g, win, cwp, rd, a & o2) } // AND 344 if op3 == 0x02 { handled = 1; sp_wr(g, win, cwp, rd, a | o2) } // OR 345 if op3 == 0x03 { handled = 1; sp_wr(g, win, cwp, rd, a ^ o2) } // XOR 346 if op3 == 0x09 { handled = 1; sp_wr(g, win, cwp, rd, a * o2) } // MULX 347 if op3 == 0x2d { handled = 1; if o2 != 0 { sp_wr(g, win, cwp, rd, a / o2) } } // SDIVX 348 if op3 == 0x0d { handled = 1; if o2 != 0 { sp_wr(g, win, cwp, rd, a / o2) } } // UDIVX 349 if op3 == 0x05 { handled = 1; sp_wr(g, win, cwp, rd, a & sp_not(o2)) } // ANDN 350 if op3 == 0x06 { handled = 1; sp_wr(g, win, cwp, rd, a | sp_not(o2)) } // ORN 351 if op3 == 0x07 { handled = 1; sp_wr(g, win, cwp, rd, sp_not(a ^ o2)) } // XNOR 352 // The *cc forms are the plain op3 with bit 4 set. Without a 353 // single one of them there was nothing for a branch to test, 354 // which is why the branch gap and the cc gap are ONE gap. 355 if op3 == 0x10 { // ADDcc 356 handled = 1 357 let res_add: i64 = a + o2 358 sp_wr(g, win, cwp, rd, res_add) 359 sp_setcc(cc, a, o2, res_add, 0) 360 } 361 if op3 == 0x14 { // SUBcc (cmp) 362 handled = 1 363 let res_sub: i64 = a - o2 364 sp_wr(g, win, cwp, rd, res_sub) 365 sp_setcc(cc, a, o2, res_sub, 1) 366 } 367 if op3 == 0x11 { // ANDcc 368 handled = 1 369 let res_and: i64 = a & o2 370 sp_wr(g, win, cwp, rd, res_and) 371 sp_setlog(cc, res_and) 372 } 373 if op3 == 0x12 { // ORcc 374 handled = 1 375 let res_or: i64 = a | o2 376 sp_wr(g, win, cwp, rd, res_or) 377 sp_setlog(cc, res_or) 378 } 379 if op3 == 0x13 { // XORcc 380 handled = 1 381 let res_xor: i64 = a ^ o2 382 sp_wr(g, win, cwp, rd, res_xor) 383 sp_setlog(cc, res_xor) 384 } 385 // SHIFTS. Bit 12 is the X bit: x=1 selects the 64-bit form 386 // (shcnt 5:0), x=0 the 32-bit form (shcnt 4:0) defined on the 387 // LOW 32 BITS. op3 0x27 (SRA/SRAX) was ABSENT entirely, and 388 // 0x26 (SRL/SRLX) was spelled with this dialect's ARITHMETIC 389 // >> , so the logical and the arithmetic right shift were one 390 // expression and one of them had to be wrong for every 391 // negative operand. Masking o2 with 63 recovers the raw 392 // shcnt whether i is 0 or 1, because the 13-bit sign 393 // extension only touches bits above 12. 394 let xbit: i64 = (w >> 12) & 1 395 var shc: i64 = o2 & 63 396 if xbit == 0 { shc = o2 & 31 } 397 if op3 == 0x25 { // SLL / SLLX 398 handled = 1 399 if xbit == 1 { sp_wr(g, win, cwp, rd, a << shc) } 400 if xbit == 0 { sp_wr(g, win, cwp, rd, ((a & SP_M32) << shc) & SP_M32) } 401 } 402 if op3 == 0x26 { // SRL / SRLX 403 handled = 1 404 if xbit == 1 { sp_wr(g, win, cwp, rd, sp_srl(a, shc)) } 405 if xbit == 0 { sp_wr(g, win, cwp, rd, (a & SP_M32) >> shc) } 406 } 407 if op3 == 0x27 { // SRA / SRAX 408 handled = 1 409 if xbit == 1 { sp_wr(g, win, cwp, rd, a >> shc) } 410 if xbit == 0 { sp_wr(g, win, cwp, rd, sp_sx32(a) >> shc) } 411 } 412 if op3 == 0x3c { // SAVE: compute old window, shift, write new 413 handled = 1 414 cwp = (cwp + 1) % SP_NWIN 415 sp_wr(g, win, cwp, rd, a + o2) 416 } 417 if op3 == 0x3d { // RESTORE 418 handled = 1 419 cwp = (cwp - 1 + SP_NWIN) % SP_NWIN 420 sp_wr(g, win, cwp, rd, a + o2) 421 } 422 if op3 == 0x38 { // JMPL (ret = jmpl %i7+8,%g0) 423 handled = 1 424 sp_wr(g, win, cwp, rd, pc) 425 next_npc = a + o2 426 } 427 if op3 == 0x3a { // Tcc (trap) -> Linux syscall (g1=num) 428 handled = 1 429 let nr: i64 = g[1] 430 if nr == SP_SYS_EXIT { result = sp_rd(g, win, cwp, 8) & 0xff; halted = 1 } 431 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))) } 432 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))) } 433 } 434 } 435 if op == 3 { // format 3: load / store 436 let op3: i64 = (w >> 19) & 0x3F 437 let rs1: i64 = (w >> 14) & 0x1F 438 let ibit: i64 = (w >> 13) & 1 439 var o2: i64 = 0 440 if ibit == 1 { 441 o2 = w & 0x1FFF 442 if (o2 & 0x1000) != 0 { o2 = o2 - 0x2000 } 443 } else { 444 o2 = sp_rd(g, win, cwp, w & 0x1F) 445 } 446 let ea: i64 = sp_rd(g, win, cwp, rs1) + o2 447 if op3 == 0x0b { handled = 1; sp_wr(g, win, cwp, rd, sp_g_ld(mem, ea, 8)) } // LDX 448 if op3 == 0x0e { handled = 1; sp_g_st(mem, ea, 8, sp_rd(g, win, cwp, rd)) } // STX 449 // op3 0x00 is LDUW and op3 0x08 is LDSW in SPARC V9. The 450 // incumbent had NO 0x00 at all and decoded 0x08 as an 451 // UNSIGNED load: so the only 32-bit load it could run was the 452 // signed one, answering with the wrong sign extension, while 453 // a real LDUW reported UNSUPPORTED. Both directions are 454 // pinned by KATs in nx_isa_sparc64_gate. 455 if op3 == 0x00 { handled = 1; sp_wr(g, win, cwp, rd, sp_g_ld(mem, ea, 4)) } // LDUW 456 if op3 == 0x08 { handled = 1; sp_wr(g, win, cwp, rd, sp_sx32(sp_g_ld(mem, ea, 4))) } // LDSW 457 if op3 == 0x01 { handled = 1; sp_wr(g, win, cwp, rd, sp_g_ld(mem, ea, 1)) } // LDUB 458 if op3 == 0x09 { handled = 1; sp_wr(g, win, cwp, rd, sp_sx8(sp_g_ld(mem, ea, 1))) } // LDSB 459 if op3 == 0x02 { handled = 1; sp_wr(g, win, cwp, rd, sp_g_ld(mem, ea, 2)) } // LDUH 460 if op3 == 0x0a { handled = 1; sp_wr(g, win, cwp, rd, sp_sx16(sp_g_ld(mem, ea, 2))) } // LDSH 461 if op3 == 0x04 { handled = 1; sp_g_st(mem, ea, 4, sp_rd(g, win, cwp, rd)) } // STW 462 if op3 == 0x05 { handled = 1; sp_g_st(mem, ea, 1, sp_rd(g, win, cwp, rd)) } // STB 463 if op3 == 0x06 { handled = 1; sp_g_st(mem, ea, 2, sp_rd(g, win, cwp, rd)) } // STH 464 } 465 466 if handled == 0 { result = SPE_UNSUPPORTED; halted = 1 } 467 pc = next_pc 468 npc = next_npc 469 steps = steps + 1 470 } 471 } 472 return result 473} 474 475func emu_sparc64_load_elf(buf: *u8, len: i64) -> i64 { 476 if len < 64 { return SPE_FAULT } 477 let e_entry: i64 = sp_g_ld(buf, 24, 8) 478 let e_phoff: i64 = sp_g_ld(buf, 32, 8) 479 let e_phnum: i64 = sp_g_ld(buf, 56, 2) 480 let e_phent: i64 = sp_g_ld(buf, 54, 2) 481 let mem: *u8 = sys_mmap(SP_GUEST_SIZE) 482 var idx: i64 = 0 483 while idx < e_phnum { 484 let ph: i64 = e_phoff + idx * e_phent 485 if sp_g_ld(buf, ph, 4) == 1 { // PT_LOAD 486 let p_off: i64 = sp_g_ld(buf, ph + 8, 8) 487 let p_va: i64 = sp_g_ld(buf, ph + 16, 8) 488 let p_fs: i64 = sp_g_ld(buf, ph + 32, 8) 489 var k: i64 = 0 490 while k < p_fs { if (p_va + k) < SP_GUEST_SIZE { mem[p_va + k] = buf[p_off + k] }; k = k + 1 } 491 } 492 idx = idx + 1 493 } 494 // initial %sp: biased; sp+2047+frame must stay in RAM 495 let sp: i64 = 0x00E00000 496 return emu_sparc64_run_mem(mem, SP_GUEST_SIZE, e_entry, sp) 497}