code wiki / (root) / nx_emu_cortexm.nx

nx_emu_cortexm.nx

buildroot/runtime/nx_emu_cortexm.nx

41672 B852 linesdepth 3pulls 3 transitivereach 1 importersview sourcekind librarytopic emu
docsdependenciesstructsconstsfunctions

about

nx_emu_cortexm.nx -- sovereign ARMv7-M / Cortex-M (Thumb-2) interpreter (NX-EMU). Little-endian, MIXED 16/32-bit instructions (32-bit iff hw1[15:11] in {11101,11110, 11111} i.e. hw1>=0xE800), 16 GPRs + NZCV. Bare-metal: BKPT halts with result=r0 (no OS). NO qemu. license_tier: ORIGINAL 2026-09-03, ISA LANE. WHAT THIS REVISION ADDS AND WHY. The first cut decoded exactly the subset nxc2 cortex_m backend emits: push/pop, mov(hi), movs, adds/subs/adcs/sbcs (register form only), sub-sp, ldr/str, blx, movw, umull, mla, subw. Measured against the ARMv7-M Architecture Reference Manual that left the machine unable to run a PROGRAM rather than a straight line: * NO conditional branch at all (B-cond, encoding 1101 cond imm8) -- so no loop, no if, no early exit could ever execute. * NO unconditional branch (B T2, 11100 imm11). * NO CMP in either form, and only ONE flag (C). N, Z and V did not exist, so even a hand-written branch would have had nothing to test. * NO immediate add/sub (imm3 and imm8 forms), no logic (AND EOR ORR BIC MVN TST), no shifts (LSL LSR ASR ROR), no MUL, no byte/halfword access, no SP-relative or PC-relative load, no CBZ/CBNZ, no extends. An emulator that cannot take a branch cannot run a loop, and a loop is the first program anyone writes. So the gap was CLOSED rather than reported: this file now carries full NZCV, the ARM add/subtract-with-carry adder that derives all four flags from one place, the condition-code table, and the decode above. THE OTHER DEFECT FIXED HERE, AND IT IS THE DANGEROUS ONE: the step-budget path used to leave result at its initial 0 and halt. 0 is ALSO a legitimate exit status, so a runaway program and a program that exited cleanly with 0 returned the SAME VALUE. A non-completion that is indistinguishable from a real answer is worse than a crash. Non-completion now has NAMED sentinels, matching the rv64/mips64 siblings so one ruler can classify all of them: -1 UNSUPPORTED, -2 RAN-OFF-END, -3 FAULT. RAN-OFF-END is structurally unreachable in this interpreter (the loop can only exit through a halt) and that is stated rather than hidden -- the constant exists so the classifier third state has a name here too, not because this file can produce it. SCOPE, STATED SO NOBODY READS THIS AS COMPLETE: IT blocks (0xBF00), SVC (0xDF00), UDF (0xDE00), LDM/STM (0xC000), REV/REVSH, and every 32-bit Thumb-2 encoding outside {MOVW SUBW UMULL MLA LDR.W STR.W} are still UNDECODED and correctly report UNSUPPORTED. UDF is PERMANENTLY UNDEFINED by the manual, which makes it a stable negative control: nx_isa_cortexm_gate uses it to prove the refusal path fires.

dependencies 1 imports · 1 importers

nx_syscalls_x86_64.nx nx_emu_cortexm.nx nx_isa_cortexm_gate.nx

imports: nx_syscalls_x86_64.nx

imported by: nx_isa_cortexm_gate.nx

structs

none

consts

41const CM_GUEST: i64 = 16777216
42const CM_MASK: i64 = 0xFFFFFFFF
45const CM_WORD_BITS: i64 = 32
46const CM_SIGNBIT: i64 = 31
47const CM_NREGS: i64 = 16
48const CM_REG_BYTES: i64 = 128 // CM_NREGS * 8, the register file allocation
49const CM_SP: i64 = 13
50const CM_LR: i64 = 14
51const CM_PC: i64 = 15
52const CM_PC_BIAS: i64 = 4 // Thumb: PC reads as the instruction address + 4
53const CM_HW: i64 = 2 // halfword: the Thumb instruction quantum
54const CM_W32: i64 = 4
55const CM_B8: i64 = 1
56const CM_DEFAULT_SP: i64 = 8388608 // 0x00800000, used only by the run() convenience wrapper
57const CM_STEP_BUDGET: i64 = 5000000
58const CM_EXIT_MASK: i64 = 0xff
61const CM_UNSUPPORTED: i64 = -1
62const CM_RANOFF: i64 = -2
63const CM_FAULT: i64 = -3
66const CM_FLAG_N: i64 = 1
67const CM_FLAG_Z: i64 = 2
68const CM_FLAG_C: i64 = 4
69const CM_FLAG_V: i64 = 8
70const CM_FLAG_CV: i64 = 12 // CM_FLAG_C + CM_FLAG_V, the pair a logic op preserves
71const CM_FLAGMASK: i64 = 15
72const CM_FLAGSHIFT: i64 = 32 // a 32-bit result never reaches bit 32, so the pack is lossless
75const CM_SH_LSL: i64 = 0
76const CM_SH_LSR: i64 = 1
77const CM_SH_ASR: i64 = 2
78const CM_SH_ROR: i64 = 3
79const CM_ROT_MASK: i64 = 31
82const CM_C_EQ: i64 = 0
83const CM_C_NE: i64 = 1
84const CM_C_CS: i64 = 2
85const CM_C_CC: i64 = 3
86const CM_C_MI: i64 = 4
87const CM_C_PL: i64 = 5
88const CM_C_VS: i64 = 6
89const CM_C_VC: i64 = 7
90const CM_C_HI: i64 = 8
91const CM_C_LS: i64 = 9
92const CM_C_GE: i64 = 10
93const CM_C_LT: i64 = 11
94const CM_C_GT: i64 = 12
95const CM_C_LE: i64 = 13
96const CM_C_MAX: i64 = 13 // 14 is UDF and 15 is SVC in the B-cond encoding
99const CM_M32_LO: i64 = 0xE800
100const CM_MSK_F000: i64 = 0xF000
101const CM_MSK_F800: i64 = 0xF800
102const CM_MSK_FD00: i64 = 0xFD00
103const CM_MSK_FE00: i64 = 0xFE00
104const CM_MSK_FF00: i64 = 0xFF00
105const CM_MSK_FF80: i64 = 0xFF80
106const CM_MSK_FF87: i64 = 0xFF87
107const CM_MSK_FFC0: i64 = 0xFFC0
108const CM_MSK_FBF0: i64 = 0xFBF0
109const CM_MSK_FFF0: i64 = 0xFFF0
112const CM_OP_LSLI: i64 = 0x0000
113const CM_OP_LSRI: i64 = 0x0800
114const CM_OP_ASRI: i64 = 0x1000
115const CM_OP_ADDR: i64 = 0x1800
116const CM_OP_SUBR: i64 = 0x1A00
117const CM_OP_ADDI3: i64 = 0x1C00
118const CM_OP_SUBI3: i64 = 0x1E00
119const CM_OP_MOVI8: i64 = 0x2000
120const CM_OP_CMPI8: i64 = 0x2800
121const CM_OP_ADDI8: i64 = 0x3000
122const CM_OP_SUBI8: i64 = 0x3800
123const CM_OP_AND: i64 = 0x4000
124const CM_OP_EOR: i64 = 0x4040
125const CM_OP_LSLR: i64 = 0x4080
126const CM_OP_LSRR: i64 = 0x40C0
127const CM_OP_ASRR: i64 = 0x4100
128const CM_OP_ADC: i64 = 0x4140
129const CM_OP_SBC: i64 = 0x4180
130const CM_OP_ROR: i64 = 0x41C0
131const CM_OP_TST: i64 = 0x4200
132const CM_OP_RSB: i64 = 0x4240
133const CM_OP_CMPR: i64 = 0x4280
134const CM_OP_CMN: i64 = 0x42C0
135const CM_OP_ORR: i64 = 0x4300
136const CM_OP_MUL: i64 = 0x4340
137const CM_OP_BIC: i64 = 0x4380
138const CM_OP_MVN: i64 = 0x43C0
139const CM_OP_ADDHI: i64 = 0x4400
140const CM_OP_CMPHI: i64 = 0x4500
141const CM_OP_MOVHI: i64 = 0x4600
142const CM_OP_BX: i64 = 0x4700
143const CM_OP_BLX: i64 = 0x4780
144const CM_OP_LDRLIT: i64 = 0x4800
145const CM_OP_STRR: i64 = 0x5000
146const CM_OP_STRHR: i64 = 0x5200
147const CM_OP_STRBR: i64 = 0x5400
148const CM_OP_LDRSBR: i64 = 0x5600
149const CM_OP_LDRR: i64 = 0x5800
150const CM_OP_LDRHR: i64 = 0x5A00
151const CM_OP_LDRBR: i64 = 0x5C00
152const CM_OP_LDRSHR: i64 = 0x5E00
153const CM_OP_STRI5: i64 = 0x6000
154const CM_OP_LDRI5: i64 = 0x6800
155const CM_OP_STRBI5: i64 = 0x7000
156const CM_OP_LDRBI5: i64 = 0x7800
157const CM_OP_STRHI5: i64 = 0x8000
158const CM_OP_LDRHI5: i64 = 0x8800
159const CM_OP_STRSP: i64 = 0x9000
160const CM_OP_LDRSP: i64 = 0x9800
161const CM_OP_ADR: i64 = 0xA000
162const CM_OP_ADDSPR: i64 = 0xA800
163const CM_OP_ADDSPI: i64 = 0xB000
164const CM_OP_SUBSPI: i64 = 0xB080
165const CM_OP_CBZ: i64 = 0xB100
166const CM_OP_SXTH: i64 = 0xB200
167const CM_OP_SXTB: i64 = 0xB240
168const CM_OP_UXTH: i64 = 0xB280
169const CM_OP_UXTB: i64 = 0xB2C0
170const CM_OP_PUSH: i64 = 0xB400
171const CM_OP_CBNZ: i64 = 0xB900
172const CM_OP_POP: i64 = 0xBC00
173const CM_OP_BKPT: i64 = 0xBE00
174const CM_OP_BCOND: i64 = 0xD000
175const CM_OP_B: i64 = 0xE000
178const CM_OP_MOVW: i64 = 0xF240
179const CM_OP_SUBW: i64 = 0xF2A0
180const CM_OP_UMULL: i64 = 0xFBA0
181const CM_OP_MLA: i64 = 0xFB00
182const CM_OP_LDRT4: i64 = 0xF850
183const CM_OP_STRT4: i64 = 0xF840
186const CM_IMM5: i64 = 5
187const CM_IMM8: i64 = 8
188const CM_IMM16: i64 = 16
189const CM_IMM11: i64 = 11
190const CM_M_IMM3: i64 = 7
191const CM_M_IMM5: i64 = 31
192const CM_M_IMM7: i64 = 127
193const CM_M_IMM8: i64 = 255
194const CM_M_IMM11: i64 = 0x7FF
195const CM_M_REG3: i64 = 7
196const CM_M_REG4: i64 = 15
197const CM_M_BYTE: i64 = 255
198const CM_M_HALF: i64 = 0xFFFF
199const CM_ALIGN4: i64 = 3
200const CM_ODDCLR: i64 = 4294967294 // CM_MASK - 1: clears the Thumb bit on a branch target

functions

202func cm_ld(mem: *u8, va: i64, width: i64) -> i64 { var v: i64 = 0; var i: i64 = 0; while i < width { v = v | ((mem[va + i] & 0xff) << (i * 8)); i = i + 1 } return v }
called by 1: emu_cortexm_run_mem
203func cm_st(mem: *u8, va: i64, width: i64, val: i64) -> i64 { var i: i64 = 0; while i < width { mem[va + i] = (val >> (i * 8)) & 0xff; i = i + 1 } return 0 }
called by 1: emu_cortexm_run_mem
207func cm_sext(v: i64, bits: i64) -> i64
called by 1: emu_cortexm_run_mem
218func cm_addf(a0: i64, b0: i64, cin: i64) -> i64
called by 1: emu_cortexm_run_mem
237func cm_not(b: i64) -> i64 { return CM_MASK - (b & CM_MASK) }
called by 1: emu_cortexm_run_mem
240func cm_nzf(res0: i64, oldf: i64) -> i64
called by 1: emu_cortexm_run_mem
250func cm_shift(kind: i64, val0: i64, sh: i64, oldf: i64) -> i64
called by 1: emu_cortexm_run_mem
308func cm_cond(cond: i64, f: i64) -> i64
called by 1: emu_cortexm_run_mem
343func cm_rd(r: *i64, i: i64, pc: i64) -> i64
called by 1: emu_cortexm_run_mem
348func cm_align4(v: i64) -> i64 { return v - (v & CM_ALIGN4) }
called by 1: emu_cortexm_run_mem
350func emu_cortexm_run_mem(mem: *u8, mem_size: i64, entry: i64, sp0: i64) -> i64
847func emu_cortexm_run(code: *u8, code_len: i64) -> i64