nx_isa_cortexm_gate.nx
buildroot/runtime/nx_isa_cortexm_gate.nx
about
nx_isa_cortexm_gate.nx -- THE ARMv7-M / Cortex-M CONFORMANCE RULER.
WHY IT EXISTS: nx_emu_cortexm.nx has claimed "we emulate Cortex-M" since it was
written and had NEVER been run by anything. nx_isa_conform_gate measures five of
the estate's ten sovereign emulators and names cortexm in its own UNMEASURED list.
This organ closes that row for cortexm alone, in its own file, so two seats working
two architectures cannot clobber one shared source.
THE LAW: expectations come FROM THE ARMv7-M ARCHITECTURE REFERENCE MANUAL --
encodings out of the Thumb instruction tables, results out of the operation
pseudocode -- never from reading the emulator and writing down what it happens to
do. Reading the decoder is allowed for exactly one purpose: learning which classes
are IMPLEMENTED versus MISSING, so a gap is named rather than mistaken for a wrong
answer. Calibrating the ruler to the subject is the one failure that makes every
green here worthless.
WHAT READING THE DECODER FOUND, AND WHAT WAS DONE ABOUT IT (2026-09-03):
the emulator implemented NO conditional branch, NO unconditional branch, NO CMP in
either form, and carried ONE flag (C) with no N, Z or V. A machine that cannot
take a branch cannot run a loop, so more than half of these KATs were unrunnable by
construction. The gap was CLOSED in nx_emu_cortexm.nx rather than reported --
full NZCV off one shared ARM adder, the condition-code table, B-cond, B, CBZ/CBNZ,
the immediate add/sub forms, the whole data-processing register block, the shifter,
byte/halfword access, SP- and PC-relative addressing. Every KAT below that exercises
one of those was RED before that work and is GREEN after it; that transition is the
only evidence that this gate can fire at all.
FIVE OUTCOMES, NEVER ONE:
PASS ran and matched the manual-derived expectation
WRONG-ANSWER ran to completion and computed the wrong value
UNSUPPORTED hit an instruction class the emulator does not implement
RAN-OFF-END the interpreter loop ended without reaching an exit
FAULT bad PC, out-of-range fetch, or the step budget
Collapsing these into "FAIL" is what makes a gap unactionable: an unimplemented
instruction class and a miscomputed result need OPPOSITE fixes.
THE EXIT-STATUS MASK IS A REAL HAZARD AND IS DESIGNED AROUND. Cortex-M here halts
on BKPT with result = r0 & 0xff, so every legitimate answer is 0..255 and the three
negative sentinels can never collide with one -- but two KATs whose right and wrong
answers differ only above bit 7 would be indistinguishable. Each expectation below
dependencies 3 imports · 0 importers
imports: nx_gate_verdict.nxnx_syscalls.nxnx_emu_cortexm.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 65 | const CX_GUEST_BYTES: i64 = 1048576 // 1 MiB guest image |
| 66 | const CX_ENTRY: i64 = 0 // KAT code is loaded at guest vaddr 0 |
| 67 | const CX_SP: i64 = 983040 // 960 KiB: above the code, inside the image |
| 68 | const CX_DATA: i64 = 4096 // scratch word address, past any KAT's code |
| 69 | const CX_MODE_FILE: i64 = 420 // 0644; 420 and 0x1a4 are the SAME constant and |
| 73 | const CX_UNSUPPORTED: i64 = -1 |
| 74 | const CX_RANOFF: i64 = -2 |
| 75 | const CX_FAULT: i64 = -3 |
| 78 | const CX_O_PASS: i64 = 0 |
| 79 | const CX_O_WRONG: i64 = 1 |
| 80 | const CX_O_UNSUP: i64 = 2 |
| 81 | const CX_O_FAULT: i64 = 3 |
| 82 | const CX_O_RANOFF: i64 = 4 |
| 86 | const CX_NB_BYTES: i64 = 2048 |
| 87 | const CX_NB_OFF: i64 = 0 |
| 88 | const CX_NB_PTR: i64 = 1 |
| 89 | const CX_NB_ROW_MAX: i64 = 256 |
| 90 | const CX_BOX_BYTES: i64 = 32 |
| 92 | const CX_NL: i64 = 10 |
| 93 | const CX_SPACE: i64 = 32 |
| 94 | const CX_DIG_LO: i64 = 48 |
| 95 | const CX_DIG_HI: i64 = 57 |
| 96 | const CX_UNSEEDED: i64 = -1 |
| 97 | const CX_RBUF: i64 = 256 |
| 98 | const CX_B10: i64 = 10 |
| 101 | const CX_S3: i64 = 3 |
| 102 | const CX_S5: i64 = 5 |
| 103 | const CX_S6: i64 = 6 |
| 104 | const CX_S8: i64 = 8 |
| 105 | const CX_S9: i64 = 9 |
| 106 | const CX_S10: i64 = 10 |
| 107 | const CX_S11: i64 = 11 |
| 108 | const CX_S12: i64 = 12 |
| 109 | const CX_M3: i64 = 7 |
| 110 | const CX_M4: i64 = 15 |
| 111 | const CX_M5: i64 = 31 |
| 112 | const CX_M8: i64 = 255 |
| 113 | const CX_M11: i64 = 0x7FF |
| 114 | const CX_BYTE: i64 = 255 |
| 115 | const CX_HWB: i64 = 2 // a Thumb halfword is two bytes |
| 116 | const CX_PCBIAS: i64 = 4 // Thumb: the branch base is the instruction + 4 |
| 119 | const CX_T_LSLI: i64 = 0x0000 |
| 120 | const CX_T_LSRI: i64 = 0x0800 |
| 121 | const CX_T_ASRI: i64 = 0x1000 |
| 122 | const CX_T_ADDR: i64 = 0x1800 |
| 123 | const CX_T_SUBR: i64 = 0x1A00 |
| 124 | const CX_T_MOVI8: i64 = 0x2000 |
| 125 | const CX_T_ADDI8: i64 = 0x3000 |
| 126 | const CX_T_SUBI8: i64 = 0x3800 |
| 127 | const CX_T_AND: i64 = 0x4000 |
| 128 | const CX_T_CMPR: i64 = 0x4280 |
| 129 | const CX_T_ORR: i64 = 0x4300 |
| 130 | const CX_T_MUL: i64 = 0x4340 |
| 131 | const CX_T_STRI5: i64 = 0x6000 |
| 132 | const CX_T_LDRI5: i64 = 0x6800 |
| 133 | const CX_T_CBZ: i64 = 0xB100 |
| 134 | const CX_T_PUSH: i64 = 0xB400 |
| 135 | const CX_T_POP: i64 = 0xBC00 |
| 136 | const CX_T_BKPT: i64 = 0xBE00 |
| 137 | const CX_T_BCOND: i64 = 0xD000 |
| 138 | const CX_T_B: i64 = 0xE000 |
| 142 | const CX_T_UDF: i64 = 0xDE00 |
| 144 | const CX_T_MOVW: i64 = 0xF240 |
| 145 | const CX_T_MLA: i64 = 0xFB00 |
| 148 | const CX_C_NE: i64 = 1 |
| 149 | const CX_C_LT: i64 = 11 |
| 154 | const CX_A40: i64 = 40 |
| 155 | const CX_A2: i64 = 2 |
| 156 | const CX_A90: i64 = 90 |
| 157 | const CX_A48: i64 = 48 |
| 158 | const CX_A200: i64 = 200 |
| 159 | const CX_A7: i64 = 7 |
| 160 | const CX_A240: i64 = 240 |
| 161 | const CX_A15: i64 = 15 |
| 162 | const CX_A60: i64 = 60 |
| 163 | const CX_A6: i64 = 6 |
| 164 | const CX_A5: i64 = 5 |
| 165 | const CX_A8: i64 = 8 |
| 166 | const CX_A9: i64 = 9 |
| 167 | const CX_A33: i64 = 33 |
| 168 | const CX_A55: i64 = 55 |
| 169 | const CX_A42: i64 = 42 |
| 170 | const CX_SH24: i64 = 24 |
| 171 | const CX_SH31: i64 = 31 |
| 172 | const CX_LOOP_N: i64 = 10 |
| 173 | const CX_MOVW_TEST: i64 = 43981 // 0xABCD: imm4=A, i=1, imm3=3, imm8=CD -- all |
| 177 | const CX_W_ARITH: i64 = 42 // 40 + 2 |
| 178 | const CX_W_SUB: i64 = 42 // 90 - 48; a reversed operand order gives 214 |
| 179 | const CX_W_IMM8: i64 = 233 // 200 + 40 - 7 |
| 180 | const CX_W_LOGIC: i64 = 60 // (240 | 15) & 60 = 255 & 60; all-AND gives 0, all-OR gives 255 |
| 181 | const CX_W_LSR: i64 = 1 // 0xFF000000 >> 31 logical |
| 182 | const CX_W_ASR: i64 = 255 // 0xFF000000 >> 31 arithmetic = 0xFFFFFFFF, masked |
| 183 | const CX_W_MUL: i64 = 42 // 6 * 7 |
| 184 | const CX_W_LOOP: i64 = 45 // sum 0..9 |
| 185 | const CX_W_SIGNED: i64 = 33 // BLT taken: 0xFF000000 < 1 as SIGNED |
| 186 | const CX_W_UNSIGN: i64 = 7 // the value only an UNSIGNED compare would leave |
| 187 | const CX_W_CBZ: i64 = 55 |
| 188 | const CX_W_CBZMISS: i64 = 9 |
| 189 | const CX_W_MOVW: i64 = 171 // 0xABCD >> 8 = 0xAB |
| 190 | const CX_W_MLA: i64 = 42 // 5 * 8 + 2 |
| 191 | const CX_W_LDST: i64 = 200 |
| 192 | const CX_W_PUSHPOP: i64 = 42 |
| 555 | const CX_RATCHET: *u8 = "knowledge/status/isa_cortexm.ratchet" |
| 556 | const CX_KEY: *u8 = "cortexm" |
functions
| 196 | func cx_nl() -> i64 |
| 207 | func cx_put_le16(code: *u8, off: i64, w: i64) -> i64 |
| 217 | func cx_put_t32(code: *u8, off: i64, hw1: i64, hw2: i64) -> i64 |
| 223 | func cx_guest() -> *u8 { return sys_mmap(CX_GUEST_BYTES) } called by 16: cx_imm_arithcx_subcx_imm8_add_subcx_logiccx_shift_logicalcx_shift_arith+10 calls 1: sys_mmap |
| 227 | func cx_movs(rd: i64, imm8: i64) -> i64 { return CX_T_MOVI8 | (rd << CX_S8) | (imm8 & CX_M8) } |
| 228 | func cx_addr(rd: i64, rn: i64, rm: i64) -> i64 { return CX_T_ADDR | (rm << CX_S6) | (rn << CX_S3) | rd } |
| 229 | func cx_subr(rd: i64, rn: i64, rm: i64) -> i64 { return CX_T_SUBR | (rm << CX_S6) | (rn << CX_S3) | rd } called by 1: cx_sub |
| 230 | func cx_addi8(rdn: i64, imm8: i64) -> i64 { return CX_T_ADDI8 | (rdn << CX_S8) | (imm8 & CX_M8) } |
| 231 | func cx_subi8(rdn: i64, imm8: i64) -> i64 { return CX_T_SUBI8 | (rdn << CX_S8) | (imm8 & CX_M8) } called by 1: cx_imm8_add_sub |
| 233 | func cx_dpr(base: i64, rdn: i64, rm: i64) -> i64 { return base | (rm << CX_S3) | rdn } |
| 235 | func cx_shi(base: i64, rd: i64, rm: i64, imm5: i64) -> i64 { return base | (imm5 << CX_S6) | (rm << CX_S3) | rd } |
| 237 | func cx_lsi5(base: i64, rt: i64, rn: i64, imm5: i64) -> i64 { return base | (imm5 << CX_S6) | (rn << CX_S3) | rt } called by 1: cx_load_store |
| 238 | func cx_push(list8: i64) -> i64 { return CX_T_PUSH | (list8 & CX_M8) } called by 1: cx_push_pop |
| 239 | func cx_pop(list8: i64) -> i64 { return CX_T_POP | (list8 & CX_M8) } called by 1: cx_push_pop |
| 240 | func cx_bcond(cond: i64, hwoff: i64) -> i64 { return CX_T_BCOND | (cond << CX_S8) | (hwoff & CX_M8) } |
| 241 | func cx_b(hwoff: i64) -> i64 { return CX_T_B | (hwoff & CX_M11) } |
| 243 | func cx_cbz(rn: i64, hwoff: i64) -> i64 { return CX_T_CBZ | (((hwoff >> CX_S5) & 1) << CX_S9) | ((hwoff & CX_M5) << CX_S3) | rn } called by 1: cx_cbz_taken |
| 245 | func cx_movw_hw1(imm16: i64) -> i64 { return CX_T_MOVW | (((imm16 >> CX_S11) & 1) << CX_S10) | ((imm16 >> CX_S12) & CX_M4) } |
| 246 | func cx_movw_hw2(rd: i64, imm16: i64) -> i64 { return (((imm16 >> CX_S8) & CX_M3) << CX_S12) | (rd << CX_S8) | (imm16 & CX_M8) } |
| 248 | func cx_mla_hw1(rn: i64) -> i64 { return CX_T_MLA | rn } called by 1: cx_thumb2_mla |
| 249 | func cx_mla_hw2(rd: i64, ra: i64, rm: i64) -> i64 { return (ra << CX_S12) | (rd << CX_S8) | rm } called by 1: cx_thumb2_mla |
| 254 | func cx_hwoff(here: i64, target: i64) -> i64 |
| 262 | func cx_classify(got: i64, want: i64) -> i64 |
| 270 | func cx_outcome_name(o: i64) -> *u8 |
| 280 | func cx_report(kat: *u8, got: i64, want: i64, bx: *i64) -> i64 |
| 316 | func cx_imm_arith() -> i64 |
| 329 | func cx_sub() -> i64 |
| 342 | func cx_imm8_add_sub() -> i64 |
| 355 | func cx_logic() -> i64 |
| 371 | func cx_shift_logical() -> i64 |
| 381 | func cx_shift_arith() -> i64 |
| 394 | func cx_mul() -> i64 |
| 407 | func cx_branch_loop() -> i64 |
| 427 | func cx_signed_cond() -> i64 |
| 450 | func cx_cbz_taken() -> i64 |
| 473 | func cx_thumb2_movw() -> i64 |
| 484 | func cx_thumb2_mla() -> i64 |
| 502 | func cx_load_store() -> i64 |
| 517 | func cx_push_pop() -> i64 |
| 532 | func cx_udf_refused() -> i64 |
| 543 | func cx_fault_entry() -> i64 |
| 558 | func cx_match_at(buf: *u8, n: i64, p: i64, s: *u8) -> i64 called by 1: cx_ratchet_read |
| 572 | func cx_digits_at(buf: *u8, n: i64, p0: i64) -> i64 called by 1: cx_ratchet_read |
| 596 | func cx_ratchet_read() -> i64 |
| 628 | func cx_ratchet_write(n: i64) -> i64 |
| 645 | func main(argc: i64, argv: *i64) -> i64 |