nx_emu_armv7a.nx source
↩ module page · 496 lines · 23282 B
1// nx_emu_armv7a.nx -- sovereign ARMv7-A (32-bit ARM) interpreter (NX-EMU).
2// Little-endian, fixed 32-bit ARM (not Thumb) encoding, 16 GPRs + NZCV.
3// Linux EABI: svc, r7=num, args r0.., exit=1 (code in r0). NO qemu.
4// license_tier: ORIGINAL
5//
6// ---- 2026-09-03, ISA LANE: WHAT WAS MEASURED AND WHAT WAS BUILT ----
7// The incumbent decoded a useful subset and had FIVE structural gaps.
8// Each was found by reading the decoder, not by guessing, and each one
9// failed in the SILENT direction -- the interpreter kept running and
10// produced a wrong number rather than saying it could not do the job:
11//
12// 1. THE CONDITION FIELD (bits 31:28) WAS NEVER READ. Every ARM
13// instruction is predicated; BEQ, MOVNE and B were all executed
14// unconditionally. So the emulator could not run a LOOP at all: a
15// backward BNE was always taken, the program spun to the step
16// budget, and the budget path returned the initial result 0 --
17// which the caller reads as exited-with-status-0, i.e. SUCCESS.
18// A hang was indistinguishable from a pass.
19// 2. THERE WERE NO N, Z OR V FLAGS, only C. Nothing could set them.
20// 3. CMP / CMN / TST / TEQ (opcodes 8..11) claimed handled=1 and then
21// matched no branch, so a comparison was a silent no-op -- the
22// worst possible shape, because it looks exactly like execution.
23// 4. EOR, RSB, RSC, BIC and MVN were the same silent no-op.
24// 5. THE SHIFTER WAS ABSENT: operand2 in register form was taken as
25// the bare register, so add r0, r0, r1, lsl 3 quietly added r1.
26// LDR/STR ignored the B (byte), P (pre/post) and W (writeback)
27// bits and misread a register-offset form as an immediate one.
28//
29// All five are implemented here. Two further classes are now NAMED
30// rather than misdecoded: the extra load/store space (halfword, signed
31// byte, SWP -- told apart from data processing by bit7=1 AND bit4=1)
32// and the cond 0b1111 unconditional-instruction space return
33// A7_UNSUPPORTED instead of being run as a data-processing op.
34//
35// NON-COMPLETION IS THREE NAMED STATES, NOT ONE NEGATIVE WORD. An
36// unimplemented instruction class and a miscomputed value need
37// OPPOSITE fixes, and a program that walked off its image is neither.
38// A7_UNSUPPORTED (-1) a class this interpreter does not decode
39// A7_RANOFF (-2) pc left the image with no exit syscall
40// A7_FAULT (-3) negative pc, out-of-range access, step budget
41// A real EABI exit status is masked to 0..255, so no sentinel can
42// collide with an answer.
43//
44// KNOWN AND DECLARED IMPRECISION (documented so the next reader does
45// not trust this as exact):
46// * r15 is an ordinary register here; real ARM reads it as pc+8.
47// * logical data-processing ops leave C alone instead of writing the
48// shifter carry-out. Only ADD/SUB/CMP/CMN/ADC/SBC/RSB/RSC move C.
49// * the extra load/store space is refused, not executed.
50// * only exit is implemented as a syscall; any other r7 is refused.
51import "nx_syscalls_x86_64.nx"
52
53const A7_GUEST: i64 = 16777216
54const A7_MASK: i64 = 0xFFFFFFFF
55const A7_SYS_EXIT: i64 = 1
56
57const A7_UNSUPPORTED: i64 = -1
58const A7_RANOFF: i64 = -2
59const A7_FAULT: i64 = -3
60
61const A7_W: i64 = 32
62const A7_WBYTES: i64 = 4
63const A7_BBYTES: i64 = 1
64const A7_SIGNBIT: i64 = 31
65const A7_NREGS: i64 = 16
66const A7_REGBYTES: i64 = 128
67const A7_R_SP: i64 = 13
68const A7_R_LR: i64 = 14
69const A7_R_SVCNUM: i64 = 7
70const A7_R_A0: i64 = 0
71const A7_PIPE: i64 = 8
72const A7_STEP_MAX: i64 = 5000000
73const A7_EXIT_MASK: i64 = 0xff
74
75const A7_LOW2: i64 = 0x3
76const A7_LOW3: i64 = 0x7
77const A7_LOW4: i64 = 0xF
78const A7_LOW5: i64 = 0x1F
79const A7_LOW8: i64 = 0xFF
80const A7_LOW12: i64 = 0xFFF
81const A7_LOW16: i64 = 0xFFFF
82const A7_LOW24: i64 = 0xFFFFFF
83const A7_ROT_STEP: i64 = 2
84const A7_COND_SH: i64 = 28
85const A7_IMM24_BITS: i64 = 24
86const A7_SH_RD: i64 = 12
87const A7_SH_RN: i64 = 16
88const A7_SH_RM8: i64 = 8
89const A7_SH_OPC: i64 = 21
90const A7_SH_SBIT: i64 = 20
91const A7_SH_IBIT: i64 = 25
92const A7_SH_PBIT: i64 = 24
93const A7_SH_UBIT: i64 = 23
94const A7_SH_BBIT: i64 = 22
95const A7_SH_TYPE: i64 = 5
96const A7_SH_AMT: i64 = 7
97const A7_SH_B4: i64 = 4
98const A7_SH_B7: i64 = 7 // the extra-load/store discriminator, NOT the shift-amount field
99const A7_SH_WBIT: i64 = 21 // writeback bit in load/store and block transfer
100const A7_SH_LBIT: i64 = 20 // load bit in load/store and block transfer
101const A7_SH_BLBIT: i64 = 24 // link bit in the branch encoding
102const A7_SH_HI16: i64 = 16
103
104// ---- condition codes, ARM ARM A8.3 Table A8-1 ----
105const A7_CC_EQ: i64 = 0
106const A7_CC_NE: i64 = 1
107const A7_CC_CS: i64 = 2
108const A7_CC_CC: i64 = 3
109const A7_CC_MI: i64 = 4
110const A7_CC_PL: i64 = 5
111const A7_CC_VS: i64 = 6
112const A7_CC_VC: i64 = 7
113const A7_CC_HI: i64 = 8
114const A7_CC_LS: i64 = 9
115const A7_CC_GE: i64 = 10
116const A7_CC_LT: i64 = 11
117const A7_CC_GT: i64 = 12
118const A7_CC_LE: i64 = 13
119const A7_CC_AL: i64 = 14
120const A7_CC_NV: i64 = 15
121
122// ---- shift types (bits 6:5) ----
123const A7_SH_LSL: i64 = 0
124const A7_SH_LSR: i64 = 1
125const A7_SH_ASR: i64 = 2
126const A7_SH_ROR: i64 = 3
127
128// ---- data-processing opcodes (bits 24:21) ----
129const A7_DP_AND: i64 = 0
130const A7_DP_EOR: i64 = 1
131const A7_DP_SUB: i64 = 2
132const A7_DP_RSB: i64 = 3
133const A7_DP_ADD: i64 = 4
134const A7_DP_ADC: i64 = 5
135const A7_DP_SBC: i64 = 6
136const A7_DP_RSC: i64 = 7
137const A7_DP_TST: i64 = 8
138const A7_DP_TEQ: i64 = 9
139const A7_DP_CMP: i64 = 10
140const A7_DP_CMN: i64 = 11
141const A7_DP_ORR: i64 = 12
142const A7_DP_MOV: i64 = 13
143const A7_DP_BIC: i64 = 14
144const A7_DP_MVN: i64 = 15
145
146// ---- multiply sub-opcodes (bits 23:21) ----
147const A7_MUL_MUL: i64 = 0
148const A7_MUL_MLA: i64 = 1
149const A7_MUL_UMULL: i64 = 4
150
151// ---- decode masks, named so a sweep can find them ----
152const A7_M_MULFAM: i64 = 0x0F0000F0
153const A7_V_MULFAM: i64 = 0x00000090
154const A7_M_MOVW: i64 = 0x0FF00000
155const A7_V_MOVW: i64 = 0x03000000
156const A7_V_MOVT: i64 = 0x03400000
157const A7_M_BXFAM: i64 = 0x0FFFFFF0
158const A7_V_BX: i64 = 0x012FFF10
159const A7_V_BLXR: i64 = 0x012FFF30
160const A7_M_CLASS: i64 = 0x0C000000
161const A7_V_DP: i64 = 0x00000000
162const A7_V_LDST: i64 = 0x04000000
163const A7_M_BRBLK: i64 = 0x0E000000
164const A7_V_BR: i64 = 0x0A000000
165const A7_V_BLK: i64 = 0x08000000
166const A7_M_SVC: i64 = 0x0F000000
167const A7_V_SVC: i64 = 0x0F000000
168
169func a7_ld(mem: *u8, va: i64, width: i64) -> i64 { var v: i64 = 0; var i: i64 = 0; while i < width { v = v | ((mem[va + i] & A7_LOW8) << (i * A7_PIPE)); i = i + 1 } return v }
170func a7_st(mem: *u8, va: i64, width: i64, val: i64) -> i64 { var i: i64 = 0; while i < width { mem[va + i] = (val >> (i * A7_PIPE)) & A7_LOW8; i = i + 1 } return 0 }
171func a7_sx(v: i64, bits: i64) -> i64 { let m: i64 = 1 << (bits - 1); if (v & m) != 0 { return v - (1 << bits) } return v }
172func a7_ror(v: i64, n: i64) -> i64 { if n == 0 { return v & A7_MASK } return ((v >> n) | (v << (A7_W - n))) & A7_MASK }
173func a7_pop(list: i64) -> i64 { var c: i64 = 0; var i: i64 = 0; while i < A7_NREGS { if (list & (1 << i)) != 0 { c = c + 1 } i = i + 1 } return c }
174
175// ---- the shifter (ARM ARM A5.1.1) ----
176// A 32-bit value already masked into an i64 is non-negative, so this
177// dialect arithmetic right shift IS a logical shift on it; ASR is the
178// only one that has to sign-extend to 64 bits first.
179func a7_lsl(v: i64, n: i64) -> i64 { if n >= A7_W { return 0 } return (v << n) & A7_MASK }
180func a7_lsr(v: i64, n: i64) -> i64 { if n >= A7_W { return 0 } return (v & A7_MASK) >> n }
181func a7_asr(v: i64, n: i64) -> i64 {
182 var s: i64 = n
183 if s > A7_SIGNBIT { s = A7_SIGNBIT }
184 return (a7_sx(v & A7_MASK, A7_W) >> s) & A7_MASK
185}
186
187// Immediate shift form: amount 0 means no-shift for LSL, but means 32
188// for LSR and ASR and means RRX for ROR. Collapsing those into one rule
189// is the classic ARM shifter bug.
190func a7_shift_imm(v: i64, ty: i64, amt: i64, cin: i64) -> i64 {
191 if ty == A7_SH_LSL { if amt == 0 { return v & A7_MASK } return a7_lsl(v, amt) }
192 if ty == A7_SH_LSR { if amt == 0 { return 0 } return a7_lsr(v, amt) }
193 if ty == A7_SH_ASR { if amt == 0 { return a7_asr(v, A7_W) } return a7_asr(v, amt) }
194 if ty == A7_SH_ROR { if amt == 0 { return (a7_lsr(v, 1) | (cin << A7_SIGNBIT)) & A7_MASK } }
195 return a7_ror(v & A7_MASK, amt)
196}
197
198// Register shift form: the amount is the low BYTE of Rs, and an amount
199// of zero means no shift for every type -- the opposite of the
200// immediate form special cases.
201func a7_shift_reg(v: i64, ty: i64, amt: i64) -> i64 {
202 if amt == 0 { return v & A7_MASK }
203 if ty == A7_SH_LSL { return a7_lsl(v, amt) }
204 if ty == A7_SH_LSR { return a7_lsr(v, amt) }
205 if ty == A7_SH_ASR { return a7_asr(v, amt) }
206 if (amt & A7_LOW5) == 0 { return v & A7_MASK }
207 return a7_ror(v & A7_MASK, amt & A7_LOW5)
208}
209
210// ---- flag arithmetic ----
211// a and b arrive masked to 32 bits, so they are non-negative i64 and a
212// plain >= is the UNSIGNED comparison the carry rule needs.
213func a7_addc(a: i64, b: i64, cin: i64) -> i64 { return ((a + b + cin) >> A7_W) & 1 }
214func a7_subc(a: i64, b: i64, cin: i64) -> i64 { if (a - b - (1 - cin)) >= 0 { return 1 } return 0 }
215func a7_addv(a: i64, b: i64, res: i64) -> i64 {
216 let sa: i64 = (a >> A7_SIGNBIT) & 1
217 let sb: i64 = (b >> A7_SIGNBIT) & 1
218 let sr: i64 = (res >> A7_SIGNBIT) & 1
219 if sa == sb { if sr != sa { return 1 } }
220 return 0
221}
222func a7_subv(a: i64, b: i64, res: i64) -> i64 {
223 let sa: i64 = (a >> A7_SIGNBIT) & 1
224 let sb: i64 = (b >> A7_SIGNBIT) & 1
225 let sr: i64 = (res >> A7_SIGNBIT) & 1
226 if sa != sb { if sr != sa { return 1 } }
227 return 0
228}
229
230// ---- the condition predicate (ARM ARM A8.3, Table A8-1) ----
231// This dialect has no disjunction operator, so the two disjunctive
232// codes (LS and LE) are computed with a flag rather than expressed.
233func a7_cond(c: i64, n: i64, z: i64, cv: i64, v: i64) -> i64 {
234 var res: i64 = 1
235 if c == A7_CC_EQ { res = z }
236 if c == A7_CC_NE { res = 1 - z }
237 if c == A7_CC_CS { res = cv }
238 if c == A7_CC_CC { res = 1 - cv }
239 if c == A7_CC_MI { res = n }
240 if c == A7_CC_PL { res = 1 - n }
241 if c == A7_CC_VS { res = v }
242 if c == A7_CC_VC { res = 1 - v }
243 if c == A7_CC_HI { res = 0; if cv == 1 { if z == 0 { res = 1 } } }
244 if c == A7_CC_LS { res = 0; if cv == 0 { res = 1 } if z == 1 { res = 1 } }
245 if c == A7_CC_GE { res = 0; if n == v { res = 1 } }
246 if c == A7_CC_LT { res = 0; if n != v { res = 1 } }
247 if c == A7_CC_GT { res = 0; if z == 0 { if n == v { res = 1 } } }
248 if c == A7_CC_LE { res = 0; if z == 1 { res = 1 } if n != v { res = 1 } }
249 if c == A7_CC_AL { res = 1 }
250 return res
251}
252
253func emu_armv7a_run_mem(mem: *u8, mem_size: i64, entry: i64, sp0: i64) -> i64 {
254 let r: *i64 = sys_mmap(A7_REGBYTES) as *i64
255 var i: i64 = 0
256 while i < A7_NREGS { r[i] = 0; i = i + 1 }
257 r[A7_R_SP] = sp0
258 var pc: i64 = entry
259 var nf: i64 = 0
260 var zf: i64 = 0
261 var cf: i64 = 0
262 var vf: i64 = 0
263 var result: i64 = A7_RANOFF
264 var halted: i64 = 0
265 var steps: i64 = 0
266 while halted == 0 {
267 var step_ok: i64 = 1
268 if steps >= A7_STEP_MAX { result = A7_FAULT; halted = 1; step_ok = 0 }
269 if step_ok == 1 { if pc < 0 { result = A7_FAULT; halted = 1; step_ok = 0 } }
270 if step_ok == 1 { if (pc + A7_WBYTES) > mem_size { result = A7_RANOFF; halted = 1; step_ok = 0 } }
271 if step_ok == 1 {
272 let w: i64 = a7_ld(mem, pc, A7_WBYTES)
273 var next: i64 = pc + A7_WBYTES
274 var handled: i64 = 0
275 let cond: i64 = (w >> A7_COND_SH) & A7_LOW4
276
277 // cond 0b1111 is NOT always-execute: it selects a different
278 // decode map (BLX imm, PLD, memory barriers). Running it as
279 // an ordinary instruction is a silent misdecode.
280 if cond == A7_CC_NV { handled = 1; result = A7_UNSUPPORTED; halted = 1 }
281 // PREDICATION: an instruction whose condition is false is a
282 // real no-op. This is what makes a loop possible at all.
283 if handled == 0 { if a7_cond(cond, nf, zf, cf, vf) == 0 { handled = 1 } }
284
285 // --- multiply family: bits27:24=0000, bits7:4=1001 ---
286 if handled == 0 { if (w & A7_M_MULFAM) == A7_V_MULFAM {
287 handled = 1
288 let sub: i64 = (w >> A7_SH_OPC) & A7_LOW3
289 let sbit: i64 = (w >> A7_SH_SBIT) & 1
290 let Rd: i64 = (w >> A7_SH_RN) & A7_LOW4
291 let Ra: i64 = (w >> A7_SH_RD) & A7_LOW4
292 let Rm: i64 = (w >> A7_SH_RM8) & A7_LOW4
293 let Rn: i64 = w & A7_LOW4
294 var mok: i64 = 0
295 if sub == A7_MUL_MUL { r[Rd] = (r[Rn] * r[Rm]) & A7_MASK; mok = 1 }
296 if sub == A7_MUL_MLA { r[Rd] = (r[Rn] * r[Rm] + r[Ra]) & A7_MASK; mok = 1 }
297 if sub == A7_MUL_UMULL {
298 let p: i64 = (r[Rn] & A7_MASK) * (r[Rm] & A7_MASK)
299 r[Ra] = p & A7_MASK
300 r[Rd] = (p >> A7_W) & A7_MASK
301 mok = 1
302 }
303 if mok == 0 { result = A7_UNSUPPORTED; halted = 1 }
304 if mok == 1 { if sbit == 1 { if sub != A7_MUL_UMULL {
305 nf = (r[Rd] >> A7_SIGNBIT) & 1
306 zf = 0
307 if r[Rd] == 0 { zf = 1 }
308 } } }
309 } }
310
311 // --- movw / movt (ARMv7 wide immediates) ---
312 if handled == 0 { if (w & A7_M_MOVW) == A7_V_MOVW {
313 handled = 1
314 let Rd: i64 = (w >> A7_SH_RD) & A7_LOW4
315 r[Rd] = (((w >> A7_SH_RN) & A7_LOW4) << A7_SH_RD) | (w & A7_LOW12)
316 } }
317 if handled == 0 { if (w & A7_M_MOVW) == A7_V_MOVT {
318 handled = 1
319 let Rd: i64 = (w >> A7_SH_RD) & A7_LOW4
320 let imm16: i64 = (((w >> A7_SH_RN) & A7_LOW4) << A7_SH_RD) | (w & A7_LOW12)
321 r[Rd] = ((r[Rd] & A7_LOW16) | (imm16 << A7_SH_HI16)) & A7_MASK
322 } }
323
324 // --- bx / blx register (MUST precede the DP catch-all) ---
325 if handled == 0 { if (w & A7_M_BXFAM) == A7_V_BX {
326 handled = 1
327 next = r[w & A7_LOW4] & (A7_MASK - 1)
328 } }
329 if handled == 0 { if (w & A7_M_BXFAM) == A7_V_BLXR {
330 handled = 1
331 r[A7_R_LR] = (pc + A7_WBYTES) & A7_MASK
332 next = r[w & A7_LOW4] & (A7_MASK - 1)
333 } }
334
335 // --- data processing (bits27:26=00) ---
336 if handled == 0 { if (w & A7_M_CLASS) == A7_V_DP {
337 handled = 1
338 let ibit: i64 = (w >> A7_SH_IBIT) & 1
339 let opc: i64 = (w >> A7_SH_OPC) & A7_LOW4
340 let sbit: i64 = (w >> A7_SH_SBIT) & 1
341 let Rn: i64 = (w >> A7_SH_RN) & A7_LOW4
342 let Rd: i64 = (w >> A7_SH_RD) & A7_LOW4
343 let b4: i64 = (w >> A7_SH_B4) & 1
344 let b7: i64 = (w >> A7_SH_B7) & 1
345 var dpok: i64 = 1
346 // The extra load/store space (LDRH/STRH/LDRSB/LDRSH/SWP)
347 // lives INSIDE this encoding space and is told apart by
348 // bit7=1 AND bit4=1. Decoding it as data processing is a
349 // silent wrong answer, so it is refused by name instead.
350 if ibit == 0 { if b4 == 1 { if b7 == 1 { dpok = 0 } } }
351 // opcodes 8..11 with S=0 are MRS/MSR/misc, not comparisons.
352 if opc >= A7_DP_TST { if opc <= A7_DP_CMN { if sbit == 0 { dpok = 0 } } }
353 if dpok == 0 { result = A7_UNSUPPORTED; halted = 1 }
354 if dpok == 1 {
355 var op2: i64 = 0
356 if ibit == 1 { op2 = a7_ror(w & A7_LOW8, ((w >> A7_SH_RM8) & A7_LOW4) * A7_ROT_STEP) }
357 if ibit == 0 {
358 let sty: i64 = (w >> A7_SH_TYPE) & A7_LOW2
359 let rmv: i64 = r[w & A7_LOW4] & A7_MASK
360 if b4 == 1 { op2 = a7_shift_reg(rmv, sty, r[(w >> A7_SH_RM8) & A7_LOW4] & A7_LOW8) }
361 if b4 == 0 { op2 = a7_shift_imm(rmv, sty, (w >> A7_SH_AMT) & A7_LOW5, cf) }
362 }
363 let a: i64 = r[Rn] & A7_MASK
364 var res: i64 = 0
365 var wb: i64 = 1
366 var arith: i64 = 0
367 var cout: i64 = cf
368 var vout: i64 = vf
369 if opc == A7_DP_AND { res = a & op2 }
370 if opc == A7_DP_EOR { res = a ^ op2 }
371 if opc == A7_DP_SUB { res = (a - op2) & A7_MASK; arith = 1; cout = a7_subc(a, op2, 1); vout = a7_subv(a, op2, res) }
372 if opc == A7_DP_RSB { res = (op2 - a) & A7_MASK; arith = 1; cout = a7_subc(op2, a, 1); vout = a7_subv(op2, a, res) }
373 if opc == A7_DP_ADD { res = (a + op2) & A7_MASK; arith = 1; cout = a7_addc(a, op2, 0); vout = a7_addv(a, op2, res) }
374 if opc == A7_DP_ADC { res = (a + op2 + cf) & A7_MASK; arith = 1; cout = a7_addc(a, op2, cf); vout = a7_addv(a, op2, res) }
375 if opc == A7_DP_SBC { res = (a - op2 - (1 - cf)) & A7_MASK; arith = 1; cout = a7_subc(a, op2, cf); vout = a7_subv(a, op2, res) }
376 if opc == A7_DP_RSC { res = (op2 - a - (1 - cf)) & A7_MASK; arith = 1; cout = a7_subc(op2, a, cf); vout = a7_subv(op2, a, res) }
377 if opc == A7_DP_TST { res = a & op2; wb = 0 }
378 if opc == A7_DP_TEQ { res = a ^ op2; wb = 0 }
379 if opc == A7_DP_CMP { res = (a - op2) & A7_MASK; wb = 0; arith = 1; cout = a7_subc(a, op2, 1); vout = a7_subv(a, op2, res) }
380 if opc == A7_DP_CMN { res = (a + op2) & A7_MASK; wb = 0; arith = 1; cout = a7_addc(a, op2, 0); vout = a7_addv(a, op2, res) }
381 if opc == A7_DP_ORR { res = a | op2 }
382 if opc == A7_DP_MOV { res = op2 }
383 if opc == A7_DP_BIC { res = a & (op2 ^ A7_MASK) }
384 if opc == A7_DP_MVN { res = op2 ^ A7_MASK }
385 res = res & A7_MASK
386 if wb == 1 { r[Rd] = res }
387 if sbit == 1 {
388 nf = (res >> A7_SIGNBIT) & 1
389 zf = 0
390 if res == 0 { zf = 1 }
391 if arith == 1 { cf = cout; vf = vout }
392 }
393 }
394 } }
395
396 // --- load/store word or byte (bits27:26=01) ---
397 if handled == 0 { if (w & A7_M_CLASS) == A7_V_LDST {
398 handled = 1
399 let i25: i64 = (w >> A7_SH_IBIT) & 1
400 let pbit: i64 = (w >> A7_SH_PBIT) & 1
401 let ubit: i64 = (w >> A7_SH_UBIT) & 1
402 let bbit: i64 = (w >> A7_SH_BBIT) & 1
403 let wbit: i64 = (w >> A7_SH_WBIT) & 1
404 let lbit: i64 = (w >> A7_SH_LBIT) & 1
405 let Rn: i64 = (w >> A7_SH_RN) & A7_LOW4
406 let Rd: i64 = (w >> A7_SH_RD) & A7_LOW4
407 var lsok: i64 = 1
408 var off: i64 = w & A7_LOW12
409 if i25 == 1 {
410 if ((w >> A7_SH_B4) & 1) == 1 { lsok = 0 }
411 if lsok == 1 { off = a7_shift_imm(r[w & A7_LOW4] & A7_MASK, (w >> A7_SH_TYPE) & A7_LOW2, (w >> A7_SH_AMT) & A7_LOW5, cf) }
412 }
413 if lsok == 0 { result = A7_UNSUPPORTED; halted = 1 }
414 if lsok == 1 {
415 let base: i64 = r[Rn] & A7_MASK
416 var wba: i64 = (base + off) & A7_MASK
417 if ubit == 0 { wba = (base - off) & A7_MASK }
418 var ea: i64 = base
419 if pbit == 1 { ea = wba }
420 var width: i64 = A7_WBYTES
421 if bbit == 1 { width = A7_BBYTES }
422 if ea < 0 { result = A7_FAULT; halted = 1 }
423 if (ea + width) > mem_size { result = A7_FAULT; halted = 1 }
424 if halted == 0 {
425 if lbit == 1 { r[Rd] = a7_ld(mem, ea, width) & A7_MASK } else { a7_st(mem, ea, width, r[Rd] & A7_MASK) }
426 var dowb: i64 = 0
427 if pbit == 0 { dowb = 1 }
428 if wbit == 1 { dowb = 1 }
429 if dowb == 1 { if Rn != Rd { r[Rn] = wba } }
430 }
431 }
432 } }
433
434 // --- branch / bl (bits27:25=101), ARM pc+8 pipeline ---
435 if handled == 0 { if (w & A7_M_BRBLK) == A7_V_BR {
436 handled = 1
437 let lbit: i64 = (w >> A7_SH_BLBIT) & 1
438 let off: i64 = a7_sx(w & A7_LOW24, A7_IMM24_BITS) << 2
439 if lbit == 1 { r[A7_R_LR] = (pc + A7_WBYTES) & A7_MASK }
440 next = pc + A7_PIPE + off
441 } }
442
443 // --- block transfer, all four addressing modes (bits27:25=100) ---
444 if handled == 0 { if (w & A7_M_BRBLK) == A7_V_BLK {
445 handled = 1
446 let pbit: i64 = (w >> A7_SH_PBIT) & 1
447 let ubit: i64 = (w >> A7_SH_UBIT) & 1
448 let wbit: i64 = (w >> A7_SH_WBIT) & 1
449 let lbit: i64 = (w >> A7_SH_LBIT) & 1
450 let Rn: i64 = (w >> A7_SH_RN) & A7_LOW4
451 let list: i64 = w & A7_LOW16
452 let cnt: i64 = a7_pop(list)
453 let base: i64 = r[Rn] & A7_MASK
454 // IA/IB/DA/DB: the lowest touched address, DERIVED from
455 // P and U rather than assumed from U alone.
456 var start: i64 = base
457 if ubit == 0 { start = base - A7_WBYTES * cnt }
458 if pbit == ubit { start = start + A7_WBYTES }
459 if start < 0 { result = A7_FAULT; halted = 1 }
460 if (start + A7_WBYTES * cnt) > mem_size { result = A7_FAULT; halted = 1 }
461 if halted == 0 {
462 var addr: i64 = start
463 var k: i64 = 0
464 while k < A7_NREGS {
465 if (list & (1 << k)) != 0 {
466 if lbit == 1 { r[k] = a7_ld(mem, addr, A7_WBYTES) & A7_MASK } else { a7_st(mem, addr, A7_WBYTES, r[k] & A7_MASK) }
467 addr = (addr + A7_WBYTES) & A7_MASK
468 }
469 k = k + 1
470 }
471 if wbit == 1 {
472 if ubit == 0 { r[Rn] = (base - A7_WBYTES * cnt) & A7_MASK } else { r[Rn] = (base + A7_WBYTES * cnt) & A7_MASK }
473 }
474 }
475 } }
476
477 // --- svc: Linux EABI, r7 carries the number ---
478 if handled == 0 { if (w & A7_M_SVC) == A7_V_SVC {
479 handled = 1
480 if r[A7_R_SVCNUM] == A7_SYS_EXIT { result = r[A7_R_A0] & A7_EXIT_MASK; halted = 1 } else { result = A7_UNSUPPORTED; halted = 1 }
481 } }
482
483 if handled == 0 { result = A7_UNSUPPORTED; halted = 1 }
484 pc = next
485 steps = steps + 1
486 }
487 }
488 return result
489}
490
491func emu_armv7a_run(code: *u8, code_len: i64) -> i64 {
492 let mem: *u8 = sys_mmap(A7_GUEST)
493 var i: i64 = 0
494 while i < code_len { mem[i] = code[i]; i = i + 1 }
495 return emu_armv7a_run_mem(mem, A7_GUEST, 0, 0x00800000)
496}