nx_emu_armv7a.nx
buildroot/runtime/nx_emu_armv7a.nx
about
nx_emu_armv7a.nx -- sovereign ARMv7-A (32-bit ARM) interpreter (NX-EMU).
Little-endian, fixed 32-bit ARM (not Thumb) encoding, 16 GPRs + NZCV.
Linux EABI: svc, r7=num, args r0.., exit=1 (code in r0). NO qemu.
license_tier: ORIGINAL
---- 2026-09-03, ISA LANE: WHAT WAS MEASURED AND WHAT WAS BUILT ----
The incumbent decoded a useful subset and had FIVE structural gaps.
Each was found by reading the decoder, not by guessing, and each one
failed in the SILENT direction -- the interpreter kept running and
produced a wrong number rather than saying it could not do the job:
1. THE CONDITION FIELD (bits 31:28) WAS NEVER READ. Every ARM
instruction is predicated; BEQ, MOVNE and B were all executed
unconditionally. So the emulator could not run a LOOP at all: a
backward BNE was always taken, the program spun to the step
budget, and the budget path returned the initial result 0 --
which the caller reads as exited-with-status-0, i.e. SUCCESS.
A hang was indistinguishable from a pass.
2. THERE WERE NO N, Z OR V FLAGS, only C. Nothing could set them.
3. CMP / CMN / TST / TEQ (opcodes 8..11) claimed handled=1 and then
matched no branch, so a comparison was a silent no-op -- the
worst possible shape, because it looks exactly like execution.
4. EOR, RSB, RSC, BIC and MVN were the same silent no-op.
5. THE SHIFTER WAS ABSENT: operand2 in register form was taken as
the bare register, so add r0, r0, r1, lsl 3 quietly added r1.
LDR/STR ignored the B (byte), P (pre/post) and W (writeback)
bits and misread a register-offset form as an immediate one.
All five are implemented here. Two further classes are now NAMED
rather than misdecoded: the extra load/store space (halfword, signed
byte, SWP -- told apart from data processing by bit7=1 AND bit4=1)
and the cond 0b1111 unconditional-instruction space return
A7_UNSUPPORTED instead of being run as a data-processing op.
NON-COMPLETION IS THREE NAMED STATES, NOT ONE NEGATIVE WORD. An
unimplemented instruction class and a miscomputed value need
OPPOSITE fixes, and a program that walked off its image is neither.
A7_UNSUPPORTED (-1) a class this interpreter does not decode
A7_RANOFF (-2) pc left the image with no exit syscall
A7_FAULT (-3) negative pc, out-of-range access, step budget
dependencies 1 imports · 1 importers
imports: nx_syscalls_x86_64.nx
imported by: nx_isa_armv7a_gate.nx
structs
| none |
consts
| 53 | const A7_GUEST: i64 = 16777216 |
| 54 | const A7_MASK: i64 = 0xFFFFFFFF |
| 55 | const A7_SYS_EXIT: i64 = 1 |
| 57 | const A7_UNSUPPORTED: i64 = -1 |
| 58 | const A7_RANOFF: i64 = -2 |
| 59 | const A7_FAULT: i64 = -3 |
| 61 | const A7_W: i64 = 32 |
| 62 | const A7_WBYTES: i64 = 4 |
| 63 | const A7_BBYTES: i64 = 1 |
| 64 | const A7_SIGNBIT: i64 = 31 |
| 65 | const A7_NREGS: i64 = 16 |
| 66 | const A7_REGBYTES: i64 = 128 |
| 67 | const A7_R_SP: i64 = 13 |
| 68 | const A7_R_LR: i64 = 14 |
| 69 | const A7_R_SVCNUM: i64 = 7 |
| 70 | const A7_R_A0: i64 = 0 |
| 71 | const A7_PIPE: i64 = 8 |
| 72 | const A7_STEP_MAX: i64 = 5000000 |
| 73 | const A7_EXIT_MASK: i64 = 0xff |
| 75 | const A7_LOW2: i64 = 0x3 |
| 76 | const A7_LOW3: i64 = 0x7 |
| 77 | const A7_LOW4: i64 = 0xF |
| 78 | const A7_LOW5: i64 = 0x1F |
| 79 | const A7_LOW8: i64 = 0xFF |
| 80 | const A7_LOW12: i64 = 0xFFF |
| 81 | const A7_LOW16: i64 = 0xFFFF |
| 82 | const A7_LOW24: i64 = 0xFFFFFF |
| 83 | const A7_ROT_STEP: i64 = 2 |
| 84 | const A7_COND_SH: i64 = 28 |
| 85 | const A7_IMM24_BITS: i64 = 24 |
| 86 | const A7_SH_RD: i64 = 12 |
| 87 | const A7_SH_RN: i64 = 16 |
| 88 | const A7_SH_RM8: i64 = 8 |
| 89 | const A7_SH_OPC: i64 = 21 |
| 90 | const A7_SH_SBIT: i64 = 20 |
| 91 | const A7_SH_IBIT: i64 = 25 |
| 92 | const A7_SH_PBIT: i64 = 24 |
| 93 | const A7_SH_UBIT: i64 = 23 |
| 94 | const A7_SH_BBIT: i64 = 22 |
| 95 | const A7_SH_TYPE: i64 = 5 |
| 96 | const A7_SH_AMT: i64 = 7 |
| 97 | const A7_SH_B4: i64 = 4 |
| 98 | const A7_SH_B7: i64 = 7 // the extra-load/store discriminator, NOT the shift-amount field |
| 99 | const A7_SH_WBIT: i64 = 21 // writeback bit in load/store and block transfer |
| 100 | const A7_SH_LBIT: i64 = 20 // load bit in load/store and block transfer |
| 101 | const A7_SH_BLBIT: i64 = 24 // link bit in the branch encoding |
| 102 | const A7_SH_HI16: i64 = 16 |
| 105 | const A7_CC_EQ: i64 = 0 |
| 106 | const A7_CC_NE: i64 = 1 |
| 107 | const A7_CC_CS: i64 = 2 |
| 108 | const A7_CC_CC: i64 = 3 |
| 109 | const A7_CC_MI: i64 = 4 |
| 110 | const A7_CC_PL: i64 = 5 |
| 111 | const A7_CC_VS: i64 = 6 |
| 112 | const A7_CC_VC: i64 = 7 |
| 113 | const A7_CC_HI: i64 = 8 |
| 114 | const A7_CC_LS: i64 = 9 |
| 115 | const A7_CC_GE: i64 = 10 |
| 116 | const A7_CC_LT: i64 = 11 |
| 117 | const A7_CC_GT: i64 = 12 |
| 118 | const A7_CC_LE: i64 = 13 |
| 119 | const A7_CC_AL: i64 = 14 |
| 120 | const A7_CC_NV: i64 = 15 |
| 123 | const A7_SH_LSL: i64 = 0 |
| 124 | const A7_SH_LSR: i64 = 1 |
| 125 | const A7_SH_ASR: i64 = 2 |
| 126 | const A7_SH_ROR: i64 = 3 |
| 129 | const A7_DP_AND: i64 = 0 |
| 130 | const A7_DP_EOR: i64 = 1 |
| 131 | const A7_DP_SUB: i64 = 2 |
| 132 | const A7_DP_RSB: i64 = 3 |
| 133 | const A7_DP_ADD: i64 = 4 |
| 134 | const A7_DP_ADC: i64 = 5 |
| 135 | const A7_DP_SBC: i64 = 6 |
| 136 | const A7_DP_RSC: i64 = 7 |
| 137 | const A7_DP_TST: i64 = 8 |
| 138 | const A7_DP_TEQ: i64 = 9 |
| 139 | const A7_DP_CMP: i64 = 10 |
| 140 | const A7_DP_CMN: i64 = 11 |
| 141 | const A7_DP_ORR: i64 = 12 |
| 142 | const A7_DP_MOV: i64 = 13 |
| 143 | const A7_DP_BIC: i64 = 14 |
| 144 | const A7_DP_MVN: i64 = 15 |
| 147 | const A7_MUL_MUL: i64 = 0 |
| 148 | const A7_MUL_MLA: i64 = 1 |
| 149 | const A7_MUL_UMULL: i64 = 4 |
| 152 | const A7_M_MULFAM: i64 = 0x0F0000F0 |
| 153 | const A7_V_MULFAM: i64 = 0x00000090 |
| 154 | const A7_M_MOVW: i64 = 0x0FF00000 |
| 155 | const A7_V_MOVW: i64 = 0x03000000 |
| 156 | const A7_V_MOVT: i64 = 0x03400000 |
| 157 | const A7_M_BXFAM: i64 = 0x0FFFFFF0 |
| 158 | const A7_V_BX: i64 = 0x012FFF10 |
| 159 | const A7_V_BLXR: i64 = 0x012FFF30 |
| 160 | const A7_M_CLASS: i64 = 0x0C000000 |
| 161 | const A7_V_DP: i64 = 0x00000000 |
| 162 | const A7_V_LDST: i64 = 0x04000000 |
| 163 | const A7_M_BRBLK: i64 = 0x0E000000 |
| 164 | const A7_V_BR: i64 = 0x0A000000 |
| 165 | const A7_V_BLK: i64 = 0x08000000 |
| 166 | const A7_M_SVC: i64 = 0x0F000000 |
| 167 | const A7_V_SVC: i64 = 0x0F000000 |
functions
| 169 | func 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 } called by 1: emu_armv7a_run_mem |
| 170 | func 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 } called by 1: emu_armv7a_run_mem |
| 171 | func a7_sx(v: i64, bits: i64) -> i64 { let m: i64 = 1 << (bits - 1); if (v & m) != 0 { return v - (1 << bits) } return v } |
| 172 | func a7_ror(v: i64, n: i64) -> i64 { if n == 0 { return v & A7_MASK } return ((v >> n) | (v << (A7_W - n))) & A7_MASK } |
| 173 | func 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 } called by 1: emu_armv7a_run_mem |
| 179 | func a7_lsl(v: i64, n: i64) -> i64 { if n >= A7_W { return 0 } return (v << n) & A7_MASK } |
| 180 | func a7_lsr(v: i64, n: i64) -> i64 { if n >= A7_W { return 0 } return (v & A7_MASK) >> n } |
| 181 | func a7_asr(v: i64, n: i64) -> i64 |
| 190 | func a7_shift_imm(v: i64, ty: i64, amt: i64, cin: i64) -> i64 |
| 201 | func a7_shift_reg(v: i64, ty: i64, amt: i64) -> i64 |
| 213 | func a7_addc(a: i64, b: i64, cin: i64) -> i64 { return ((a + b + cin) >> A7_W) & 1 } called by 1: emu_armv7a_run_mem |
| 214 | func a7_subc(a: i64, b: i64, cin: i64) -> i64 { if (a - b - (1 - cin)) >= 0 { return 1 } return 0 } called by 1: emu_armv7a_run_mem |
| 215 | func a7_addv(a: i64, b: i64, res: i64) -> i64 called by 1: emu_armv7a_run_mem |
| 222 | func a7_subv(a: i64, b: i64, res: i64) -> i64 called by 1: emu_armv7a_run_mem |
| 233 | func a7_cond(c: i64, n: i64, z: i64, cv: i64, v: i64) -> i64 called by 1: emu_armv7a_run_mem |
| 253 | func emu_armv7a_run_mem(mem: *u8, mem_size: i64, entry: i64, sp0: i64) -> i64 |
| 491 | func emu_armv7a_run(code: *u8, code_len: i64) -> i64 calls 1: emu_armv7a_run_mem |