code wiki / (root) / nx_emu_armv7a.nx

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}