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}