kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

isa.c (45058B)


      1 /* ARM32 (Thumb-2) instruction descriptor table + operand print dispatch.
      2  *
      3  * Mirrors the rv64_isa.c / aa64_isa.c pattern. Each row records (mnemonic,
      4  * match, mask, format, flags); arm32_disasm_find returns the first row whose
      5  * masked bits match the word (first-match wins, so aliases use tighter masks
      6  * placed before the canonical row), and arm32_print_operands renders the
      7  * operand text from the format's field layout.
      8  *
      9  * Thumb-2 has two widths. A 32-bit instruction is decoded as the single value
     10  * (hw1 << 16) | hw2 (hw1 = first half-word in memory); a 16-bit instruction is
     11  * decoded in the low 16 bits. The descriptor's ARM_FMT_W16 flag tags 16-bit
     12  * rows, which arm32_disasm_find matches against the low-16-bit word; 32-bit
     13  * rows match the full word. The caller (disasm.c) determines the width from the
     14  * Thumb-2 rule (first half-word bits[15:11] in {11101,11110,11111} => 32-bit)
     15  * and presents the appropriate word + is16 flag. */
     16 
     17 #include "arch/arm32/isa.h"
     18 
     19 #include <string.h>
     20 
     21 #include "core/slice.h"
     22 #include "core/strbuf.h"
     23 
     24 /* Mnemonic Slice literal for a static table row (compile-time length). */
     25 #define MN(s) {{(s)}, sizeof(s) - 1}
     26 
     27 /* ---- 32-bit match/mask helpers ----
     28  * A 32-bit Thumb-2 word is (hw1 << 16) | hw2. We pin the bits that select the
     29  * instruction (opcode + family selectors) in `match` and clear the operand
     30  * fields in `mask`. Helper macros keep the common families readable. */
     31 
     32 /* Data-processing (modified-immediate, hw1=0xF0xx): op4 in word bits 24:21
     33  * (hw1[8:5]), S in word bit 20 (hw1[4]), `i` in word bit 26 (free). Mask pins
     34  * op(31:27)=11110 (0xF8000000), op4 (0x01E00000), S (0x00100000), and hw2[15]
     35  * (0x8000); it leaves rn(19:16), i(26), imm3(14:12), rd(11:8), imm8(7:0) free. */
     36 #define M32_DPIMM(op4, s) \
     37   ((0xf000u << 16) | (((u32)(op4) & 0xfu) << 21) | (((u32)(s) & 1u) << 20))
     38 #define MK_DPIMM (0xfbf08000u)
     39 
     40 /* Data-processing (shifted register, hw1=0xEAxx): op4 in word bits 24:21, S in
     41  * word bit 20. Mask pins op(31:25)=1110101 (0xFE000000), op4 (0x01E00000), S
     42  * (0x00100000), hw2[15] (0x8000); clears rn, imm3/imm2/type shift, rd, rm. */
     43 #define M32_DPREG(op4, s) \
     44   ((0xea00u << 16) | (((u32)(op4) & 0xfu) << 21) | (((u32)(s) & 1u) << 20))
     45 #define MK_DPREG (0xfff08000u)
     46 
     47 const Arm32InsnDesc arm32_insn_table[] = {
     48     /* =================================================================
     49      * 32-bit data processing — modified immediate (hw1 = 0xF0xx).
     50      * MOV/MVN use rn=1111 (own formats so the rn operand is suppressed);
     51      * CMP/CMN/TST/TEQ use rd=1111 (S=1, CMP_IMM format). Aliases come
     52      * first (tighter mask).
     53      * ================================================================= */
     54     {MN("mov.w"), M32_DPIMM(2u, 0u) | (0xfu << 16), MK_DPIMM | (0xfu << 16),
     55      ARM_FMT_MOV_IMM, ARM_ASMFL_ALIAS, {0}},
     56     {MN("movs.w"), M32_DPIMM(2u, 1u) | (0xfu << 16), MK_DPIMM | (0xfu << 16),
     57      ARM_FMT_MOV_IMM, ARM_ASMFL_ALIAS, {0}},
     58     {MN("mvn.w"), M32_DPIMM(3u, 0u) | (0xfu << 16), MK_DPIMM | (0xfu << 16),
     59      ARM_FMT_MOV_IMM, ARM_ASMFL_ALIAS, {0}},
     60     {MN("cmn.w"), M32_DPIMM(8u, 1u) | (0xfu << 8), MK_DPIMM | (0xfu << 8),
     61      ARM_FMT_CMP_IMM, ARM_ASMFL_ALIAS, {0}},
     62     {MN("cmp.w"), M32_DPIMM(13u, 1u) | (0xfu << 8), MK_DPIMM | (0xfu << 8),
     63      ARM_FMT_CMP_IMM, ARM_ASMFL_ALIAS, {0}},
     64     {MN("tst.w"), M32_DPIMM(0u, 1u) | (0xfu << 8), MK_DPIMM | (0xfu << 8),
     65      ARM_FMT_CMP_IMM, ARM_ASMFL_ALIAS, {0}},
     66     {MN("teq.w"), M32_DPIMM(4u, 1u) | (0xfu << 8), MK_DPIMM | (0xfu << 8),
     67      ARM_FMT_CMP_IMM, ARM_ASMFL_ALIAS, {0}},
     68     {MN("and.w"), M32_DPIMM(0u, 0u), MK_DPIMM, ARM_FMT_DP_IMM, 0, {0}},
     69     {MN("ands.w"), M32_DPIMM(0u, 1u), MK_DPIMM, ARM_FMT_DP_IMM, 0, {0}},
     70     {MN("bic.w"), M32_DPIMM(1u, 0u), MK_DPIMM, ARM_FMT_DP_IMM, 0, {0}},
     71     {MN("orr.w"), M32_DPIMM(2u, 0u), MK_DPIMM, ARM_FMT_DP_IMM, 0, {0}},
     72     {MN("orn.w"), M32_DPIMM(3u, 0u), MK_DPIMM, ARM_FMT_DP_IMM, 0, {0}},
     73     {MN("eor.w"), M32_DPIMM(4u, 0u), MK_DPIMM, ARM_FMT_DP_IMM, 0, {0}},
     74     {MN("add.w"), M32_DPIMM(8u, 0u), MK_DPIMM, ARM_FMT_DP_IMM, 0, {0}},
     75     {MN("adds.w"), M32_DPIMM(8u, 1u), MK_DPIMM, ARM_FMT_DP_IMM, 0, {0}},
     76     {MN("adc.w"), M32_DPIMM(10u, 0u), MK_DPIMM, ARM_FMT_DP_IMM, 0, {0}},
     77     {MN("sbc.w"), M32_DPIMM(11u, 0u), MK_DPIMM, ARM_FMT_DP_IMM, 0, {0}},
     78     {MN("sub.w"), M32_DPIMM(13u, 0u), MK_DPIMM, ARM_FMT_DP_IMM, 0, {0}},
     79     {MN("subs.w"), M32_DPIMM(13u, 1u), MK_DPIMM, ARM_FMT_DP_IMM, 0, {0}},
     80     {MN("rsb.w"), M32_DPIMM(14u, 0u), MK_DPIMM, ARM_FMT_DP_IMM, 0, {0}},
     81 
     82     /* =================================================================
     83      * 32-bit data processing — shifted register (hw1 = 0xEAxx).
     84      * MOV.W rd, rm = ORR rn=1111; MVN.W = ORN rn=1111 (own formats);
     85      * CMP/CMN/TST/TEQ use rd=1111.
     86      * ================================================================= */
     87     {MN("mov.w"), M32_DPREG(2u, 0u) | (0xfu << 16), MK_DPREG | (0xfu << 16),
     88      ARM_FMT_MOV_REG, ARM_ASMFL_ALIAS, {0}},
     89     {MN("mvn.w"), M32_DPREG(3u, 0u) | (0xfu << 16), MK_DPREG | (0xfu << 16),
     90      ARM_FMT_MOV_REG, ARM_ASMFL_ALIAS, {0}},
     91     {MN("cmn.w"), M32_DPREG(8u, 1u) | (0xfu << 8), MK_DPREG | (0xfu << 8),
     92      ARM_FMT_CMP_REG, ARM_ASMFL_ALIAS, {0}},
     93     {MN("cmp.w"), M32_DPREG(13u, 1u) | (0xfu << 8), MK_DPREG | (0xfu << 8),
     94      ARM_FMT_CMP_REG, ARM_ASMFL_ALIAS, {0}},
     95     {MN("tst.w"), M32_DPREG(0u, 1u) | (0xfu << 8), MK_DPREG | (0xfu << 8),
     96      ARM_FMT_CMP_REG, ARM_ASMFL_ALIAS, {0}},
     97     {MN("teq.w"), M32_DPREG(4u, 1u) | (0xfu << 8), MK_DPREG | (0xfu << 8),
     98      ARM_FMT_CMP_REG, ARM_ASMFL_ALIAS, {0}},
     99     {MN("and.w"), M32_DPREG(0u, 0u), MK_DPREG, ARM_FMT_DP_REG, 0, {0}},
    100     {MN("ands.w"), M32_DPREG(0u, 1u), MK_DPREG, ARM_FMT_DP_REG, 0, {0}},
    101     {MN("bic.w"), M32_DPREG(1u, 0u), MK_DPREG, ARM_FMT_DP_REG, 0, {0}},
    102     {MN("orr.w"), M32_DPREG(2u, 0u), MK_DPREG, ARM_FMT_DP_REG, 0, {0}},
    103     {MN("orn.w"), M32_DPREG(3u, 0u), MK_DPREG, ARM_FMT_DP_REG, 0, {0}},
    104     {MN("eor.w"), M32_DPREG(4u, 0u), MK_DPREG, ARM_FMT_DP_REG, 0, {0}},
    105     {MN("add.w"), M32_DPREG(8u, 0u), MK_DPREG, ARM_FMT_DP_REG, 0, {0}},
    106     {MN("adds.w"), M32_DPREG(8u, 1u), MK_DPREG, ARM_FMT_DP_REG, 0, {0}},
    107     {MN("adc.w"), M32_DPREG(10u, 0u), MK_DPREG, ARM_FMT_DP_REG, 0, {0}},
    108     {MN("sbc.w"), M32_DPREG(11u, 0u), MK_DPREG, ARM_FMT_DP_REG, 0, {0}},
    109     {MN("sub.w"), M32_DPREG(13u, 0u), MK_DPREG, ARM_FMT_DP_REG, 0, {0}},
    110     {MN("subs.w"), M32_DPREG(13u, 1u), MK_DPREG, ARM_FMT_DP_REG, 0, {0}},
    111     {MN("rsb.w"), M32_DPREG(14u, 0u), MK_DPREG, ARM_FMT_DP_REG, 0, {0}},
    112 
    113     /* =================================================================
    114      * Shifts (immediate): MOV.W rd, rm, <type> #sh — hw1 = 0xEA4F, type in
    115      * hw2[5:4], imm3:imm2 the shift amount. These overlap the MOV_REG row
    116      * above (which is shift type=LSL imm=0); the shift-imm rows are tighter
    117      * (pin type != 0 or imm != 0) so they sit BEFORE and win. We model all
    118      * four as a single shift-imm family keyed on hw2[5:4] in the printer.
    119      * ================================================================= */
    120     {MN("lsl.w"), (0xea4fu << 16) | 0x0000u, 0xffef8030u, ARM_FMT_SHIFT_IMM, 0,
    121      {0}},
    122     {MN("lsr.w"), (0xea4fu << 16) | 0x0010u, 0xffef8030u, ARM_FMT_SHIFT_IMM, 0,
    123      {0}},
    124     {MN("asr.w"), (0xea4fu << 16) | 0x0020u, 0xffef8030u, ARM_FMT_SHIFT_IMM, 0,
    125      {0}},
    126     {MN("ror.w"), (0xea4fu << 16) | 0x0030u, 0xffef8030u, ARM_FMT_SHIFT_IMM, 0,
    127      {0}},
    128 
    129     /* Shifts (register): LSL/LSR/ASR/ROR rd, rn, rm — hw1 = 0xFA0x, type in
    130      * hw1[6:5] (LSL=0xFA00, LSR=0xFA20, ASR=0xFA40, ROR=0xFA60), hw2=0xF0x0. */
    131     {MN("lsl.w"), (0xfa00u << 16) | 0xf000u, 0xffe0f0f0u, ARM_FMT_SHIFT_REG, 0,
    132      {0}},
    133     {MN("lsr.w"), (0xfa20u << 16) | 0xf000u, 0xffe0f0f0u, ARM_FMT_SHIFT_REG, 0,
    134      {0}},
    135     {MN("asr.w"), (0xfa40u << 16) | 0xf000u, 0xffe0f0f0u, ARM_FMT_SHIFT_REG, 0,
    136      {0}},
    137     {MN("ror.w"), (0xfa60u << 16) | 0xf000u, 0xffe0f0f0u, ARM_FMT_SHIFT_REG, 0,
    138      {0}},
    139 
    140     /* =================================================================
    141      * MOVW / MOVT (16-bit immediate); ADDW / SUBW (12-bit immediate).
    142      * ================================================================= */
    143     {MN("movw"), (0xf2400000u), 0xfbf08000u, ARM_FMT_MOVW, 0, {0}},
    144     {MN("movt"), (0xf2c00000u), 0xfbf08000u, ARM_FMT_MOVW, 0, {0}},
    145     {MN("addw"), (0xf2000000u), 0xfbf08000u, ARM_FMT_ADDW, 0, {0}},
    146     {MN("subw"), (0xf2a00000u), 0xfbf08000u, ARM_FMT_ADDW, 0, {0}},
    147 
    148     /* =================================================================
    149      * Multiply / divide.
    150      * ================================================================= */
    151     /* MUL is MLA with ra=1111; pin ra=1111 + place before MLA. */
    152     {MN("mul"), (0xfb000000u) | (0xfu << 12), 0xfff0f0f0u, ARM_FMT_MUL, 0, {0}},
    153     {MN("mla"), (0xfb000000u), 0xfff000f0u, ARM_FMT_MLA, 0, {0}},
    154     {MN("mls"), (0xfb000010u), 0xfff000f0u, ARM_FMT_MLA, 0, {0}},
    155     {MN("umull"), (0xfba00000u), 0xfff000f0u, ARM_FMT_MULL, 0, {0}},
    156     {MN("smull"), (0xfb800000u), 0xfff000f0u, ARM_FMT_MULL, 0, {0}},
    157     {MN("sdiv"), (0xfb90f0f0u), 0xfff0f0f0u, ARM_FMT_DIV, 0, {0}},
    158     {MN("udiv"), (0xfbb0f0f0u), 0xfff0f0f0u, ARM_FMT_DIV, 0, {0}},
    159 
    160     /* =================================================================
    161      * Sign/zero extends (hw1 = 0xFAxF, rn=1111, hw2 = 0xF080).
    162      * ================================================================= */
    163     {MN("sxth"), (0xfa0ff080u), 0xfffff0c0u, ARM_FMT_EXT, 0, {0}},
    164     {MN("uxth"), (0xfa1ff080u), 0xfffff0c0u, ARM_FMT_EXT, 0, {0}},
    165     {MN("sxtb"), (0xfa4ff080u), 0xfffff0c0u, ARM_FMT_EXT, 0, {0}},
    166     {MN("uxtb"), (0xfa5ff080u), 0xfffff0c0u, ARM_FMT_EXT, 0, {0}},
    167 
    168     /* =================================================================
    169      * REV / REV16 / REVSH / RBIT / CLZ.
    170      * ================================================================= */
    171     {MN("rev"), (0xfa90f080u), 0xfff0f0f0u, ARM_FMT_REV, 0, {0}},
    172     {MN("rev16"), (0xfa90f090u), 0xfff0f0f0u, ARM_FMT_REV, 0, {0}},
    173     {MN("rbit"), (0xfa90f0a0u), 0xfff0f0f0u, ARM_FMT_REV, 0, {0}},
    174     {MN("revsh"), (0xfa90f0b0u), 0xfff0f0f0u, ARM_FMT_REV, 0, {0}},
    175     {MN("clz"), (0xfab0f080u), 0xfff0f0f0u, ARM_FMT_REV, 0, {0}},
    176 
    177     /* =================================================================
    178      * Bitfield: BFC (rn=1111) before BFI; SBFX/UBFX.
    179      * ================================================================= */
    180     {MN("bfc"), (0xf36f0000u), 0xffff8020u, ARM_FMT_BFC, ARM_ASMFL_ALIAS, {0}},
    181     {MN("bfi"), (0xf3600000u), 0xfff08020u, ARM_FMT_BFI, 0, {0}},
    182     {MN("sbfx"), (0xf3400000u), 0xfff08020u, ARM_FMT_BFX, 0, {0}},
    183     {MN("ubfx"), (0xf3c00000u), 0xfff08020u, ARM_FMT_BFX, 0, {0}},
    184 
    185     /* =================================================================
    186      * ARMv7E-M DSP saturating. SSAT/USAT share the 0xF3xx plain-binary-imm
    187      * group with the bitfield/movw rows above; the mask pins the LSL/no-shift
    188      * forms (word[21]=0, since the ASR and SSAT16/USAT16 forms have word[21]=1)
    189      * plus hw2[15]=0 and hw2[5:4]=00, so SSAT16/USAT16/SBFX/UBFX/BFI/MOVW never
    190      * collide. The ASR-shifted SSAT/USAT (word[21]=1) is intentionally not
    191      * decoded — it falls through to .inst rather than risk misdecoding the
    192      * encoding-overlapping SSAT16. QADD/QSUB/QDADD/QDSUB pin hw1[15:4]=0xFA8,
    193      * hw2[15:12]=1111, hw2[7:4]=op (collision-free vs CLZ 0xFAB0 / REV/RBIT
    194      * 0xFA9x). Verified by round-tripping each match word through llvm-mc
    195      * (`--triple=thumbv7em-none-eabi --mattr=+dsp -disassemble`).
    196      * ================================================================= */
    197     {MN("ssat"), (0xf3000000u), 0xffe08030u, ARM_FMT_SAT, 0, {0}},
    198     {MN("usat"), (0xf3800000u), 0xffe08030u, ARM_FMT_SAT, 0, {0}},
    199     {MN("qadd"), (0xfa80f080u), 0xfff0f0f0u, ARM_FMT_QADD, 0, {0}},
    200     {MN("qdadd"), (0xfa80f090u), 0xfff0f0f0u, ARM_FMT_QADD, 0, {0}},
    201     {MN("qsub"), (0xfa80f0a0u), 0xfff0f0f0u, ARM_FMT_QADD, 0, {0}},
    202     {MN("qdsub"), (0xfa80f0b0u), 0xfff0f0f0u, ARM_FMT_QADD, 0, {0}},
    203 
    204     /* =================================================================
    205      * Loads / stores — T3 (positive imm12). hw1 selects op+width:
    206      *   LDR 0xF8D0 STR 0xF8C0 LDRB 0xF890 STRB 0xF880
    207      *   LDRH 0xF8B0 STRH 0xF8A0 LDRSB 0xF990 LDRSH 0xF9B0.
    208      * ================================================================= */
    209     {MN("strb.w"), (0xf8800000u), 0xfff00000u, ARM_FMT_LDST_T3, 0, {0}},
    210     {MN("ldrb.w"), (0xf8900000u), 0xfff00000u, ARM_FMT_LDST_T3, 0, {0}},
    211     {MN("strh.w"), (0xf8a00000u), 0xfff00000u, ARM_FMT_LDST_T3, 0, {0}},
    212     {MN("ldrh.w"), (0xf8b00000u), 0xfff00000u, ARM_FMT_LDST_T3, 0, {0}},
    213     {MN("str.w"), (0xf8c00000u), 0xfff00000u, ARM_FMT_LDST_T3, 0, {0}},
    214     {MN("ldr.w"), (0xf8d00000u), 0xfff00000u, ARM_FMT_LDST_T3, 0, {0}},
    215     {MN("ldrsb.w"), (0xf9900000u), 0xfff00000u, ARM_FMT_LDST_T3, 0, {0}},
    216     {MN("ldrsh.w"), (0xf9b00000u), 0xfff00000u, ARM_FMT_LDST_T3, 0, {0}},
    217 
    218     /* Loads / stores — T4 (±imm8, offset addressing P=1,U=add,W=0 => hw2[11:8]
    219      * = 0b1100|U). hw1 bases: LDR 0xF850 STR 0xF840 LDRB 0xF810 STRB 0xF800
    220      * LDRH 0xF830 STRH 0xF820 LDRSB 0xF910 LDRSH 0xF930. Pin hw2[11]=1,
    221      * hw2[10]=1 (P=1,W=0), hw2[8]=0; U (hw2[9]) free. */
    222     {MN("strb.w"), (0xf8000c00u), 0xfff00d00u, ARM_FMT_LDST_T4, 0, {0}},
    223     {MN("ldrb.w"), (0xf8100c00u), 0xfff00d00u, ARM_FMT_LDST_T4, 0, {0}},
    224     {MN("strh.w"), (0xf8200c00u), 0xfff00d00u, ARM_FMT_LDST_T4, 0, {0}},
    225     {MN("ldrh.w"), (0xf8300c00u), 0xfff00d00u, ARM_FMT_LDST_T4, 0, {0}},
    226     {MN("str.w"), (0xf8400c00u), 0xfff00d00u, ARM_FMT_LDST_T4, 0, {0}},
    227     {MN("ldr.w"), (0xf8500c00u), 0xfff00d00u, ARM_FMT_LDST_T4, 0, {0}},
    228     {MN("ldrsb.w"), (0xf9100c00u), 0xfff00d00u, ARM_FMT_LDST_T4, 0, {0}},
    229     {MN("ldrsh.w"), (0xf9300c00u), 0xfff00d00u, ARM_FMT_LDST_T4, 0, {0}},
    230 
    231     /* =================================================================
    232      * Exclusive load/store; barriers; table branch.
    233      * ================================================================= */
    234     {MN("ldrex"), (0xe8500f00u), 0xfff00f00u, ARM_FMT_LDREX, 0, {0}},
    235     {MN("strex"), (0xe8400000u), 0xfff00000u, ARM_FMT_STREX, 0, {0}},
    236     {MN("dsb"), (0xf3bf8f40u), 0xfffffff0u, ARM_FMT_BARRIER, 0, {0}},
    237     {MN("dmb"), (0xf3bf8f50u), 0xfffffff0u, ARM_FMT_BARRIER, 0, {0}},
    238     {MN("isb"), (0xf3bf8f60u), 0xfffffff0u, ARM_FMT_BARRIER, 0, {0}},
    239     {MN("tbb"), (0xe8d0f000u), 0xfff0fff0u, ARM_FMT_TB, 0, {0}},
    240     {MN("tbh"), (0xe8d0f010u), 0xfff0fff0u, ARM_FMT_TB, 0, {0}},
    241 
    242     /* =================================================================
    243      * PUSH.W / POP.W (STMDB sp! / LDMIA sp!).
    244      * ================================================================= */
    245     {MN("push.w"), (0xe92d0000u), 0xffff0000u, ARM_FMT_PUSHPOP, 0, {0}},
    246     {MN("pop.w"), (0xe8bd0000u), 0xffff0000u, ARM_FMT_PUSHPOP, 0, {0}},
    247 
    248     /* =================================================================
    249      * 32-bit branches: BL (T1), B.W (T4), B<cond>.W (T3).
    250      * BL: hw1=0xF0xx, hw2[15:14]=11, hw2[12]=1.
    251      * B.W: hw1=0xF0xx, hw2[15:14]=10, hw2[12]=1.
    252      * Bcond.W: hw1=0xF0xx (cond in hw1[9:6]), hw2[15:14]=10, hw2[12]=0.
    253      * Order BL and B.W before Bcond.W and pin the hw2[15,12] selectors.
    254      * ================================================================= */
    255     {MN("bl"), (0xf000d000u), 0xf800d000u, ARM_FMT_BL, 0, {0}},
    256     {MN("b.w"), (0xf0009000u), 0xf800d000u, ARM_FMT_BRANCH_T4, 0, {0}},
    257     {MN("b.w"), (0xf0008000u), 0xf800d000u, ARM_FMT_BRANCH_T3, 0, {0}},
    258 
    259     /* =================================================================
    260      * 16-bit instructions (ARM_FMT_W16 — matched against the low 16 bits).
    261      * Aliases first.
    262      * ================================================================= */
    263     {MN("nop"), 0x0000bf00u, 0x0000ffffu, ARM_FMT_NONE, ARM_FMT_W16, {0}},
    264     {MN("yield"), 0x0000bf10u, 0x0000ffffu, ARM_FMT_NONE, ARM_FMT_W16, {0}},
    265     {MN("wfe"), 0x0000bf20u, 0x0000ffffu, ARM_FMT_NONE, ARM_FMT_W16, {0}},
    266     {MN("wfi"), 0x0000bf30u, 0x0000ffffu, ARM_FMT_NONE, ARM_FMT_W16, {0}},
    267     {MN("sev"), 0x0000bf40u, 0x0000ffffu, ARM_FMT_NONE, ARM_FMT_W16, {0}},
    268     /* IT block: 0xBFxx with mask (low nibble) != 0. NOP (0xBF00) above is the
    269      * mask==0 hint; pin mask nonzero is implicit by ordering after the hints. */
    270     {MN("it"), 0x0000bf00u, 0x0000ff00u, ARM_FMT_IT, ARM_FMT_W16, {0}},
    271     {MN("bkpt"), 0x0000be00u, 0x0000ff00u, ARM_FMT_BKPT, ARM_FMT_W16, {0}},
    272     /* UDF #imm8 (T1, 0xDExx): same imm8 shape as BKPT, decoded after the 0xB0xx
    273      * hint/branch block so it does not shadow a tighter match. */
    274     {MN("udf"), 0x0000de00u, 0x0000ff00u, ARM_FMT_BKPT, ARM_FMT_W16, {0}},
    275     /* CBZ/CBNZ: 0xB1xx (op=0)/0xB9xx (op=1); mask the i/imm5/rn fields. */
    276     {MN("cbz"), 0x0000b100u, 0x0000fd00u, ARM_FMT_CBZ, ARM_FMT_W16, {0}},
    277     {MN("cbnz"), 0x0000b900u, 0x0000fd00u, ARM_FMT_CBZ, ARM_FMT_W16, {0}},
    278     /* 16-bit sign/zero-extend + byte-reverse (rd[2:0], rm[5:3]). clang prefers
    279      * these over the 32-bit forms when rd/rm are low registers. */
    280     {MN("sxth"), 0x0000b200u, 0x0000ffc0u, ARM_FMT_EXT16, ARM_FMT_W16, {0}},
    281     {MN("sxtb"), 0x0000b240u, 0x0000ffc0u, ARM_FMT_EXT16, ARM_FMT_W16, {0}},
    282     {MN("uxth"), 0x0000b280u, 0x0000ffc0u, ARM_FMT_EXT16, ARM_FMT_W16, {0}},
    283     {MN("uxtb"), 0x0000b2c0u, 0x0000ffc0u, ARM_FMT_EXT16, ARM_FMT_W16, {0}},
    284     {MN("rev"), 0x0000ba00u, 0x0000ffc0u, ARM_FMT_EXT16, ARM_FMT_W16, {0}},
    285     {MN("rev16"), 0x0000ba40u, 0x0000ffc0u, ARM_FMT_EXT16, ARM_FMT_W16, {0}},
    286     {MN("revsh"), 0x0000bac0u, 0x0000ffc0u, ARM_FMT_EXT16, ARM_FMT_W16, {0}},
    287     /* BX/BLX (register, T1): 0x4700 (BX) / 0x4780 (BLX). */
    288     {MN("bx"), 0x00004700u, 0x0000ff87u, ARM_FMT_BX, ARM_FMT_W16, {0}},
    289     {MN("blx"), 0x00004780u, 0x0000ff87u, ARM_FMT_BX, ARM_FMT_W16, {0}},
    290     /* Hi-register ADD/CMP/MOV (T1/T2): 0x4400/0x4500/0x4600. MOV is its own
    291      * format (handles sp/pc); ADD/CMP use HIREG_16. */
    292     {MN("add"), 0x00004400u, 0x0000ff00u, ARM_FMT_HIREG_16, ARM_FMT_W16, {0}},
    293     {MN("cmp"), 0x00004500u, 0x0000ff00u, ARM_FMT_HIREG_16, ARM_FMT_W16, {0}},
    294     /* MOV (register, high form T1): 0x4600, full r0..r15 incl. sp/pc. */
    295     {MN("mov"), 0x00004600u, 0x0000ff00u, ARM_FMT_MOVHI16, ARM_FMT_W16, {0}},
    296 
    297     /* 16-bit data-processing register (T1): 0x4000 | (op<<6). rdn[2:0], rm[5:3].
    298      * MUL prints 3 operands (rdm, rn, rdm); the rest print rdn, rm. */
    299     {MN("ands"), 0x00004000u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    300     {MN("eors"), 0x00004040u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    301     {MN("lsls"), 0x00004080u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    302     {MN("lsrs"), 0x000040c0u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    303     {MN("asrs"), 0x00004100u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    304     {MN("adcs"), 0x00004140u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    305     {MN("sbcs"), 0x00004180u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    306     {MN("rors"), 0x000041c0u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    307     {MN("tst"), 0x00004200u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    308     {MN("rsbs"), 0x00004240u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    309     {MN("cmp"), 0x00004280u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    310     {MN("cmn"), 0x000042c0u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    311     {MN("orrs"), 0x00004300u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    312     {MN("muls"), 0x00004340u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    313     {MN("bics"), 0x00004380u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    314     {MN("mvns"), 0x000043c0u, 0x0000ffc0u, ARM_FMT_ALU_16, ARM_FMT_W16, {0}},
    315 
    316     /* 16-bit ADD/SUB register (T1) + ADD/SUB imm3 (T1). 0x1800/0x1A00 reg,
    317      * 0x1C00/0x1E00 imm3. */
    318     {MN("adds"), 0x00001800u, 0x0000fe00u, ARM_FMT_ADDSUBR_16, ARM_FMT_W16, {0}},
    319     {MN("subs"), 0x00001a00u, 0x0000fe00u, ARM_FMT_ADDSUBR_16, ARM_FMT_W16, {0}},
    320     {MN("adds"), 0x00001c00u, 0x0000fe00u, ARM_FMT_ADDSUB3_16, ARM_FMT_W16, {0}},
    321     {MN("subs"), 0x00001e00u, 0x0000fe00u, ARM_FMT_ADDSUB3_16, ARM_FMT_W16, {0}},
    322 
    323     /* 16-bit shift-immediate (T1): 0x0000(lsls)/0x0800(lsrs)/0x1000(asrs).
    324      * These overlap movs (0x0000 lsls #0 == movs); placed after to let the
    325      * imm5!=0 forms decode. lsls #0 with imm5=0 is actually `movs` but we keep
    326      * lsls for fidelity. */
    327     {MN("lsls"), 0x00000000u, 0x0000f800u, ARM_FMT_SHIFTI_16, ARM_FMT_W16, {0}},
    328     {MN("lsrs"), 0x00000800u, 0x0000f800u, ARM_FMT_SHIFTI_16, ARM_FMT_W16, {0}},
    329     {MN("asrs"), 0x00001000u, 0x0000f800u, ARM_FMT_SHIFTI_16, ARM_FMT_W16, {0}},
    330 
    331     /* 16-bit MOV/CMP/ADD/SUB immediate-8 (T1/T2): 0x2000/0x2800/0x3000/0x3800.
    332      * rd/rn in [10:8], imm8 in [7:0]. */
    333     {MN("movs"), 0x00002000u, 0x0000f800u, ARM_FMT_DPI8_16, ARM_FMT_W16, {0}},
    334     {MN("cmp"), 0x00002800u, 0x0000f800u, ARM_FMT_DPI8_16, ARM_FMT_W16, {0}},
    335     {MN("adds"), 0x00003000u, 0x0000f800u, ARM_FMT_DPI8_16, ARM_FMT_W16, {0}},
    336     {MN("subs"), 0x00003800u, 0x0000f800u, ARM_FMT_DPI8_16, ARM_FMT_W16, {0}},
    337 
    338     /* 16-bit LDR/STR{,B,H} immediate (T1): rt[2:0], rn[5:3], imm5[10:6] scaled
    339      * by 4/1/2 by width. 0x6000 str / 0x6800 ldr / 0x7000 strb / 0x7800 ldrb /
    340      * 0x8000 strh / 0x8800 ldrh. */
    341     {MN("str"), 0x00006000u, 0x0000f800u, ARM_FMT_LDSTI5_16, ARM_FMT_W16, {0}},
    342     {MN("ldr"), 0x00006800u, 0x0000f800u, ARM_FMT_LDSTI5_16, ARM_FMT_W16, {0}},
    343     {MN("strb"), 0x00007000u, 0x0000f800u, ARM_FMT_LDSTI5_16, ARM_FMT_W16, {0}},
    344     {MN("ldrb"), 0x00007800u, 0x0000f800u, ARM_FMT_LDSTI5_16, ARM_FMT_W16, {0}},
    345     {MN("strh"), 0x00008000u, 0x0000f800u, ARM_FMT_LDSTI5_16, ARM_FMT_W16, {0}},
    346     {MN("ldrh"), 0x00008800u, 0x0000f800u, ARM_FMT_LDSTI5_16, ARM_FMT_W16, {0}},
    347 
    348     /* 16-bit LDR/STR sp-relative (T2): rt[10:8], imm8*4. 0x9000 str / 0x9800
    349      * ldr. */
    350     {MN("str"), 0x00009000u, 0x0000f800u, ARM_FMT_LDSTSP_16, ARM_FMT_W16, {0}},
    351     {MN("ldr"), 0x00009800u, 0x0000f800u, ARM_FMT_LDSTSP_16, ARM_FMT_W16, {0}},
    352 
    353     /* 16-bit ADD rd, pc/sp, #imm8*4 (T1): 0xA000 (pc) / 0xA800 (sp). */
    354     {MN("adr"), 0x0000a000u, 0x0000f800u, ARM_FMT_ADDSP_16, ARM_FMT_W16, {0}},
    355     {MN("add"), 0x0000a800u, 0x0000f800u, ARM_FMT_ADDSP_16, ARM_FMT_W16, {0}},
    356 
    357     /* 16-bit ADD/SUB sp, sp, #imm7*4 (T2): 0xB000 add / 0xB080 sub. */
    358     {MN("add"), 0x0000b000u, 0x0000ff80u, ARM_FMT_ADJSP_16, ARM_FMT_W16, {0}},
    359     {MN("sub"), 0x0000b080u, 0x0000ff80u, ARM_FMT_ADJSP_16, ARM_FMT_W16, {0}},
    360 
    361     /* 16-bit PUSH/POP (T1): 0xB400 push (M=bit8 -> LR) / 0xBC00 pop (P=bit8 ->
    362      * PC). */
    363     {MN("push"), 0x0000b400u, 0x0000fe00u, ARM_FMT_PUSHPOP_16, ARM_FMT_W16, {0}},
    364     {MN("pop"), 0x0000bc00u, 0x0000fe00u, ARM_FMT_PUSHPOP_16, ARM_FMT_W16, {0}},
    365     /* B<cond> (T1, 8-bit): 0xDxyy (cond in [11:8], not 1110/1111). The 0xDE/DF
    366      * slots are UDF/SVC; the cond test in the printer guards them. */
    367     {MN("b"), 0x0000d000u, 0x0000f000u, ARM_FMT_BCC16, ARM_FMT_W16, {0}},
    368     /* B (T2, 11-bit unconditional): 0xExxx (top 5 bits 11100). */
    369     {MN("b"), 0x0000e000u, 0x0000f800u, ARM_FMT_B16, ARM_FMT_W16, {0}},
    370 };
    371 
    372 const u32 arm32_insn_table_n =
    373     (u32)(sizeof arm32_insn_table / sizeof arm32_insn_table[0]);
    374 
    375 #undef MN
    376 
    377 const Arm32InsnDesc* arm32_disasm_find(u32 word, int is16) {
    378   u32 w = is16 ? (word & 0xffffu) : word;
    379   for (u32 i = 0; i < arm32_insn_table_n; ++i) {
    380     const Arm32InsnDesc* d = &arm32_insn_table[i];
    381     int row16 = (d->flags & ARM_FMT_W16) != 0;
    382     if (row16 != (is16 != 0)) continue;
    383     if ((w & d->mask) == d->match) return d;
    384   }
    385   return NULL;
    386 }
    387 
    388 const Arm32InsnDesc* arm32_asm_find(Slice mnemonic) {
    389   if (!mnemonic.s) return NULL;
    390   /* Prefer the canonical (non-alias) row; fall back to aliases. */
    391   for (u32 i = 0; i < arm32_insn_table_n; ++i) {
    392     const Arm32InsnDesc* d = &arm32_insn_table[i];
    393     if (d->flags & ARM_ASMFL_ALIAS) continue;
    394     if (slice_eq(d->mnemonic, mnemonic)) return d;
    395   }
    396   for (u32 i = 0; i < arm32_insn_table_n; ++i) {
    397     const Arm32InsnDesc* d = &arm32_insn_table[i];
    398     if (slice_eq(d->mnemonic, mnemonic)) return d;
    399   }
    400   return NULL;
    401 }
    402 
    403 /* =====================================================================
    404  * Condition codes.
    405  * ===================================================================== */
    406 static const char* const ARM_CC_NAMES[16] = {
    407     "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
    408     "hi", "ls", "ge", "lt", "gt", "le", "",   "",
    409 };
    410 
    411 const char* arm32_cond_name(u32 cond) {
    412   return ARM_CC_NAMES[cond & 0xfu];
    413 }
    414 
    415 int arm32_cond_from_name(Slice s) {
    416   for (u32 i = 0; i < 14u; ++i) {
    417     if (slice_eq_cstr(s, ARM_CC_NAMES[i])) return (int)i;
    418   }
    419   if (slice_eq_cstr(s, "al")) return (int)ARM_CC_AL;
    420   if (slice_eq_cstr(s, "hs")) return (int)ARM_CC_CS;
    421   if (slice_eq_cstr(s, "lo")) return (int)ARM_CC_CC;
    422   return -1;
    423 }
    424 
    425 /* =====================================================================
    426  * Operand printing.
    427  * ===================================================================== */
    428 
    429 static const char* const ARM_RNAMES[16] = {
    430     "r0", "r1", "r2",  "r3",  "r4", "r5", "r6", "r7",
    431     "r8", "r9", "r10", "r11", "ip", "sp", "lr", "pc",
    432 };
    433 
    434 static void p_reg(StrBuf* sb, u32 r) { strbuf_puts(sb, ARM_RNAMES[r & 0xfu]); }
    435 static void p_sep(StrBuf* sb) { strbuf_puts(sb, ", "); }
    436 static void p_imm(StrBuf* sb, i64 v) {
    437   strbuf_putc(sb, '#');
    438   strbuf_put_i64(sb, v);
    439 }
    440 static void p_rel(StrBuf* sb, u64 vaddr, i64 off) {
    441   if (vaddr)
    442     strbuf_put_hex_u64(sb, vaddr + (u64)off);
    443   else {
    444     strbuf_putc(sb, '#');
    445     strbuf_put_i64(sb, off);
    446   }
    447 }
    448 
    449 /* ThumbExpandImm: decode the 12-bit i:imm3:imm8 modified immediate to its
    450  * 32-bit value. Inverse of thumb_expand_imm_encode. */
    451 static u32 thumb_expand_imm_decode(u32 imm12) {
    452   u32 imm8 = imm12 & 0xffu;
    453   u32 ctl = (imm12 >> 8) & 0xfu; /* i:imm3 */
    454   if ((ctl & 0xcu) == 0u) {
    455     switch (ctl & 3u) {
    456       case 0:
    457         return imm8;
    458       case 1:
    459         return (imm8 << 16) | imm8;
    460       case 2:
    461         return (imm8 << 24) | (imm8 << 8);
    462       default:
    463         return (imm8 << 24) | (imm8 << 16) | (imm8 << 8) | imm8;
    464     }
    465   }
    466   {
    467     /* Rotated form: imm12[11:7] = rotation (8..31), imm12[6:0] = base[6:0],
    468      * base[7] implicitly 1. value = ror(base, rot). */
    469     u32 base = 0x80u | (imm8 & 0x7fu);
    470     u32 rot = (imm12 >> 7) & 0x1fu;
    471     return (base >> rot) | (base << (32u - rot));
    472   }
    473 }
    474 
    475 /* ---- 32-bit field extractors ---- */
    476 static u32 f_hw1(u32 w) { return (w >> 16) & 0xffffu; }
    477 static u32 f_hw2(u32 w) { return w & 0xffffu; }
    478 static u32 f_rn(u32 w) { return f_hw1(w) & 0xfu; }
    479 static u32 f_rd(u32 w) { return (f_hw2(w) >> 8) & 0xfu; }
    480 static u32 f_rm(u32 w) { return f_hw2(w) & 0xfu; }
    481 /* i:imm3:imm8 modified immediate from a 0xF0xx DP-imm / MOVW form. */
    482 static u32 f_modimm12(u32 w) {
    483   u32 i = (f_hw1(w) >> 10) & 1u;
    484   u32 imm3 = (f_hw2(w) >> 12) & 7u;
    485   u32 imm8 = f_hw2(w) & 0xffu;
    486   return (i << 11) | (imm3 << 8) | imm8;
    487 }
    488 
    489 /* Shift-type names for the DP-shift / MOV-shift / MVN-shift suffix
    490  * (index = hw2[5:4]). */
    491 static const char* const ARM_SHIFT_NAMES[4] = {"lsl", "lsr", "asr", "ror"};
    492 
    493 static void print_dp_reg(StrBuf* sb, u32 w) {
    494   p_reg(sb, f_rd(w));
    495   p_sep(sb);
    496   p_reg(sb, f_rn(w));
    497   p_sep(sb);
    498   p_reg(sb, f_rm(w));
    499   /* Optional shift on rm (imm3:imm2 in hw2[14:12]/[7:6], type hw2[5:4]). */
    500   {
    501     u32 imm3 = (f_hw2(w) >> 12) & 7u, imm2 = (f_hw2(w) >> 6) & 3u;
    502     u32 type = (f_hw2(w) >> 4) & 3u;
    503     u32 sh = (imm3 << 2) | imm2;
    504     if (sh != 0u || type != 0u) {
    505       p_sep(sb);
    506       strbuf_puts(sb, ARM_SHIFT_NAMES[type]);
    507       strbuf_putc(sb, ' ');
    508       p_imm(sb, (i64)(sh == 0u && type != 0u ? 32u : sh));
    509     }
    510   }
    511 }
    512 
    513 static void print_dp_imm(StrBuf* sb, u32 w) {
    514   p_reg(sb, f_rd(w));
    515   p_sep(sb);
    516   p_reg(sb, f_rn(w));
    517   p_sep(sb);
    518   p_imm(sb, (i64)(i32)thumb_expand_imm_decode(f_modimm12(w)));
    519 }
    520 
    521 static void print_mov_imm(StrBuf* sb, u32 w) {
    522   p_reg(sb, f_rd(w));
    523   p_sep(sb);
    524   p_imm(sb, (i64)(i32)thumb_expand_imm_decode(f_modimm12(w)));
    525 }
    526 
    527 const char* arm32_mov_shift_alias(u32 word, u32* type_out, u32* amount_out) {
    528   /* hw2[14:12] = imm3, hw2[7:6] = imm2, hw2[5:4] = type. */
    529   u32 imm3 = (f_hw2(word) >> 12) & 7u, imm2 = (f_hw2(word) >> 6) & 3u;
    530   u32 type = (f_hw2(word) >> 4) & 3u;
    531   u32 amount = (imm3 << 2) | imm2;
    532   if (type_out) *type_out = type;
    533   if (amount_out) *amount_out = amount;
    534   if (type == 0u && amount == 0u) return NULL; /* plain mov.w rd, rm. */
    535   switch (type) {
    536     case 0u: return "lsl.w"; /* amount != 0 here. */
    537     case 1u:
    538       if (amount == 0u) amount = 32u; /* LSR #0 means #32. */
    539       if (amount_out) *amount_out = amount;
    540       return "lsr.w";
    541     case 2u:
    542       if (amount == 0u) amount = 32u; /* ASR #0 means #32. */
    543       if (amount_out) *amount_out = amount;
    544       return "asr.w";
    545     default: /* type == 3 (ROR). */
    546       return (amount == 0u) ? "rrx" : "ror.w";
    547   }
    548 }
    549 
    550 /* MOV (ORR) form. op4 == 3 is MVN (ORN), which keeps its own mnemonic and
    551  * renders the shift as a `, <sh> #n` suffix; op4 == 2 is MOV, which is aliased
    552  * to the shift mnemonic (so here it prints just the operands). */
    553 static void print_mov_reg(StrBuf* sb, u32 w) {
    554   u32 op4 = (f_hw1(w) >> 5) & 0xfu;
    555   u32 type = 0u, amount = 0u;
    556   const char* alias = arm32_mov_shift_alias(w, &type, &amount);
    557   p_reg(sb, f_rd(w));
    558   p_sep(sb);
    559   p_reg(sb, f_rm(w));
    560   if (op4 == 3u) {
    561     /* MVN: append the shift suffix when present (mnemonic stays mvn.w). */
    562     if (alias) {
    563       p_sep(sb);
    564       strbuf_puts(sb, ARM_SHIFT_NAMES[type]);
    565       strbuf_putc(sb, ' ');
    566       p_imm(sb, (i64)amount);
    567     }
    568     return;
    569   }
    570   /* MOV (op4 == 2): the mnemonic carries the shift; append the amount unless
    571    * the alias is a plain mov.w (NULL) or rrx (ROR #0 -> two operands only). */
    572   if (alias && !(type == 3u && amount == 0u)) {
    573     p_sep(sb);
    574     p_imm(sb, (i64)amount);
    575   }
    576 }
    577 
    578 static void print_cmp_reg(StrBuf* sb, u32 w) {
    579   p_reg(sb, f_rn(w));
    580   p_sep(sb);
    581   p_reg(sb, f_rm(w));
    582 }
    583 
    584 static void print_cmp_imm(StrBuf* sb, u32 w) {
    585   p_reg(sb, f_rn(w));
    586   p_sep(sb);
    587   p_imm(sb, (i64)(i32)thumb_expand_imm_decode(f_modimm12(w)));
    588 }
    589 
    590 static void print_movw(StrBuf* sb, u32 w) {
    591   /* imm16 = imm4:i:imm3:imm8. imm4 = hw1[3:0]. */
    592   u32 imm4 = f_hw1(w) & 0xfu;
    593   u32 i = (f_hw1(w) >> 10) & 1u;
    594   u32 imm3 = (f_hw2(w) >> 12) & 7u;
    595   u32 imm8 = f_hw2(w) & 0xffu;
    596   u32 imm16 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8;
    597   p_reg(sb, f_rd(w));
    598   p_sep(sb);
    599   strbuf_putc(sb, '#');
    600   strbuf_put_u64(sb, (u64)imm16);
    601 }
    602 
    603 static void print_addw(StrBuf* sb, u32 w) {
    604   u32 i = (f_hw1(w) >> 10) & 1u;
    605   u32 imm3 = (f_hw2(w) >> 12) & 7u;
    606   u32 imm8 = f_hw2(w) & 0xffu;
    607   u32 imm12 = (i << 11) | (imm3 << 8) | imm8;
    608   p_reg(sb, f_rd(w));
    609   p_sep(sb);
    610   p_reg(sb, f_rn(w));
    611   p_sep(sb);
    612   p_imm(sb, (i64)imm12);
    613 }
    614 
    615 static void print_shift_imm(StrBuf* sb, u32 w) {
    616   u32 imm3 = (f_hw2(w) >> 12) & 7u, imm2 = (f_hw2(w) >> 6) & 3u;
    617   u32 sh = (imm3 << 2) | imm2;
    618   u32 type = (f_hw2(w) >> 4) & 3u;
    619   p_reg(sb, f_rd(w));
    620   p_sep(sb);
    621   p_reg(sb, f_rm(w));
    622   p_sep(sb);
    623   /* LSR/ASR shift of 0 means 32. */
    624   p_imm(sb, (i64)((sh == 0u && (type == 1u || type == 2u)) ? 32u : sh));
    625 }
    626 
    627 static void print_shift_reg(StrBuf* sb, u32 w) {
    628   p_reg(sb, f_rd(w));
    629   p_sep(sb);
    630   p_reg(sb, f_rn(w));
    631   p_sep(sb);
    632   p_reg(sb, f_rm(w));
    633 }
    634 
    635 static void print_mul(StrBuf* sb, u32 w) {
    636   p_reg(sb, f_rd(w));
    637   p_sep(sb);
    638   p_reg(sb, f_rn(w));
    639   p_sep(sb);
    640   p_reg(sb, f_rm(w));
    641 }
    642 
    643 static void print_mla(StrBuf* sb, u32 w) {
    644   u32 ra = (f_hw2(w) >> 12) & 0xfu;
    645   p_reg(sb, f_rd(w));
    646   p_sep(sb);
    647   p_reg(sb, f_rn(w));
    648   p_sep(sb);
    649   p_reg(sb, f_rm(w));
    650   p_sep(sb);
    651   p_reg(sb, ra);
    652 }
    653 
    654 static void print_div(StrBuf* sb, u32 w) {
    655   p_reg(sb, f_rd(w));
    656   p_sep(sb);
    657   p_reg(sb, f_rn(w));
    658   p_sep(sb);
    659   p_reg(sb, f_rm(w));
    660 }
    661 
    662 static void print_mull(StrBuf* sb, u32 w) {
    663   u32 rdlo = (f_hw2(w) >> 12) & 0xfu;
    664   u32 rdhi = (f_hw2(w) >> 8) & 0xfu;
    665   p_reg(sb, rdlo);
    666   p_sep(sb);
    667   p_reg(sb, rdhi);
    668   p_sep(sb);
    669   p_reg(sb, f_rn(w));
    670   p_sep(sb);
    671   p_reg(sb, f_rm(w));
    672 }
    673 
    674 static void print_ext(StrBuf* sb, u32 w) {
    675   p_reg(sb, f_rd(w));
    676   p_sep(sb);
    677   p_reg(sb, f_rm(w));
    678 }
    679 
    680 static void print_rev(StrBuf* sb, u32 w) {
    681   p_reg(sb, f_rd(w));
    682   p_sep(sb);
    683   p_reg(sb, f_rm(w));
    684 }
    685 
    686 static void print_bfx(StrBuf* sb, u32 w) {
    687   u32 imm3 = (f_hw2(w) >> 12) & 7u, imm2 = (f_hw2(w) >> 6) & 3u;
    688   u32 lsb = (imm3 << 2) | imm2;
    689   u32 wm1 = f_hw2(w) & 0x1fu;
    690   p_reg(sb, f_rd(w));
    691   p_sep(sb);
    692   p_reg(sb, f_rn(w));
    693   p_sep(sb);
    694   p_imm(sb, (i64)lsb);
    695   p_sep(sb);
    696   p_imm(sb, (i64)(wm1 + 1u));
    697 }
    698 
    699 static void print_bfi(StrBuf* sb, u32 w) {
    700   u32 imm3 = (f_hw2(w) >> 12) & 7u, imm2 = (f_hw2(w) >> 6) & 3u;
    701   u32 lsb = (imm3 << 2) | imm2;
    702   u32 msb = f_hw2(w) & 0x1fu;
    703   p_reg(sb, f_rd(w));
    704   p_sep(sb);
    705   p_reg(sb, f_rn(w));
    706   p_sep(sb);
    707   p_imm(sb, (i64)lsb);
    708   p_sep(sb);
    709   p_imm(sb, (i64)(msb - lsb + 1u));
    710 }
    711 
    712 static void print_bfc(StrBuf* sb, u32 w) {
    713   u32 imm3 = (f_hw2(w) >> 12) & 7u, imm2 = (f_hw2(w) >> 6) & 3u;
    714   u32 lsb = (imm3 << 2) | imm2;
    715   u32 msb = f_hw2(w) & 0x1fu;
    716   p_reg(sb, f_rd(w));
    717   p_sep(sb);
    718   p_imm(sb, (i64)lsb);
    719   p_sep(sb);
    720   p_imm(sb, (i64)(msb - lsb + 1u));
    721 }
    722 
    723 /* SSAT/USAT rd, #sat, rm. The U bit (word[23] = hw1[7]) selects USAT, which
    724  * encodes the saturation position directly; SSAT encodes sat-1. Rm sits in
    725  * hw1[3:0] (f_rn). The (LSL) shift amount lives in imm3:imm2 (hw2[14:12]:[7:6]);
    726  * it is appended only when nonzero. */
    727 static void print_sat(StrBuf* sb, u32 w) {
    728   u32 is_usat = (f_hw1(w) >> 7) & 1u;
    729   u32 sat_imm = f_hw2(w) & 0x1fu;
    730   u32 sat = is_usat ? sat_imm : (sat_imm + 1u);
    731   u32 imm3 = (f_hw2(w) >> 12) & 7u, imm2 = (f_hw2(w) >> 6) & 3u;
    732   u32 sh = (imm3 << 2) | imm2;
    733   p_reg(sb, f_rd(w));
    734   p_sep(sb);
    735   p_imm(sb, (i64)sat);
    736   p_sep(sb);
    737   p_reg(sb, f_rn(w)); /* Rm */
    738   if (sh) {
    739     strbuf_puts(sb, ", lsl ");
    740     p_imm(sb, (i64)sh);
    741   }
    742 }
    743 
    744 /* QADD/QSUB/QDADD/QDSUB rd, rm, rn. Rd = hw2[11:8], Rm = hw2[3:0], Rn lands in
    745  * hw1[3:0] (the ARM operand order is Rd, Rm, Rn). */
    746 static void print_qadd(StrBuf* sb, u32 w) {
    747   p_reg(sb, f_rd(w));
    748   p_sep(sb);
    749   p_reg(sb, f_rm(w));
    750   p_sep(sb);
    751   p_reg(sb, f_rn(w));
    752 }
    753 
    754 static void print_ldst_t3(StrBuf* sb, u32 w) {
    755   u32 rt = (f_hw2(w) >> 12) & 0xfu;
    756   u32 imm12 = f_hw2(w) & 0xfffu;
    757   p_reg(sb, rt);
    758   p_sep(sb);
    759   strbuf_putc(sb, '[');
    760   p_reg(sb, f_rn(w));
    761   if (imm12) {
    762     p_sep(sb);
    763     p_imm(sb, (i64)imm12);
    764   }
    765   strbuf_putc(sb, ']');
    766 }
    767 
    768 static void print_ldst_t4(StrBuf* sb, u32 w) {
    769   u32 rt = (f_hw2(w) >> 12) & 0xfu;
    770   u32 add = (f_hw2(w) >> 9) & 1u;
    771   u32 imm8 = f_hw2(w) & 0xffu;
    772   p_reg(sb, rt);
    773   p_sep(sb);
    774   strbuf_putc(sb, '[');
    775   p_reg(sb, f_rn(w));
    776   if (imm8) {
    777     p_sep(sb);
    778     p_imm(sb, add ? (i64)imm8 : -(i64)imm8);
    779   }
    780   strbuf_putc(sb, ']');
    781 }
    782 
    783 static void print_ldrex(StrBuf* sb, u32 w) {
    784   u32 rt = (f_hw2(w) >> 12) & 0xfu;
    785   u32 imm8 = f_hw2(w) & 0xffu;
    786   p_reg(sb, rt);
    787   p_sep(sb);
    788   strbuf_putc(sb, '[');
    789   p_reg(sb, f_rn(w));
    790   if (imm8) {
    791     p_sep(sb);
    792     p_imm(sb, (i64)(imm8 * 4u));
    793   }
    794   strbuf_putc(sb, ']');
    795 }
    796 
    797 static void print_strex(StrBuf* sb, u32 w) {
    798   u32 rd = (f_hw2(w) >> 8) & 0xfu;
    799   u32 rt = (f_hw2(w) >> 12) & 0xfu;
    800   u32 imm8 = f_hw2(w) & 0xffu;
    801   p_reg(sb, rd);
    802   p_sep(sb);
    803   p_reg(sb, rt);
    804   p_sep(sb);
    805   strbuf_putc(sb, '[');
    806   p_reg(sb, f_rn(w));
    807   if (imm8) {
    808     p_sep(sb);
    809     p_imm(sb, (i64)(imm8 * 4u));
    810   }
    811   strbuf_putc(sb, ']');
    812 }
    813 
    814 static void print_reglist(StrBuf* sb, u32 reglist) {
    815   int first = 1;
    816   strbuf_putc(sb, '{');
    817   for (u32 r = 0; r < 16u; ++r) {
    818     if (reglist & (1u << r)) {
    819       if (!first) p_sep(sb);
    820       p_reg(sb, r);
    821       first = 0;
    822     }
    823   }
    824   strbuf_putc(sb, '}');
    825 }
    826 
    827 static void print_pushpop(StrBuf* sb, u32 w) {
    828   print_reglist(sb, f_hw2(w));
    829 }
    830 
    831 static void print_barrier(StrBuf* sb, u32 w) {
    832   u32 opt = f_hw2(w) & 0xfu;
    833   if (opt == 0xfu)
    834     strbuf_puts(sb, "sy");
    835   else
    836     strbuf_put_u64(sb, (u64)opt);
    837 }
    838 
    839 static void print_tb(StrBuf* sb, u32 w) {
    840   strbuf_putc(sb, '[');
    841   p_reg(sb, f_rn(w));
    842   p_sep(sb);
    843   p_reg(sb, f_rm(w));
    844   if (((f_hw2(w) >> 4) & 1u)) strbuf_puts(sb, ", lsl #1");
    845   strbuf_putc(sb, ']');
    846 }
    847 
    848 /* B.W (T4) 24-bit signed offset: S:I1:I2:imm10:imm11 (imm32 in 2-byte units),
    849  * I1=NOT(J1^S), I2=NOT(J2^S). */
    850 static i64 decode_branch_t4(u32 w) {
    851   u32 s = (f_hw1(w) >> 10) & 1u;
    852   u32 imm10 = f_hw1(w) & 0x3ffu;
    853   u32 j1 = (f_hw2(w) >> 13) & 1u;
    854   u32 j2 = (f_hw2(w) >> 11) & 1u;
    855   u32 imm11 = f_hw2(w) & 0x7ffu;
    856   u32 i1 = (~(j1 ^ s)) & 1u;
    857   u32 i2 = (~(j2 ^ s)) & 1u;
    858   u32 raw = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1);
    859   i64 off = (i64)raw;
    860   if (s) off |= ~(i64)0x1ffffff; /* sign-extend bit 24 (after the <<1) */
    861   return off + 4;                /* Thumb PC bias */
    862 }
    863 
    864 /* B<cond>.W (T3) 20-bit signed offset: S:J2:J1:imm6:imm11 (no XOR). */
    865 static i64 decode_branch_t3(u32 w) {
    866   u32 s = (f_hw1(w) >> 10) & 1u;
    867   u32 imm6 = f_hw1(w) & 0x3fu;
    868   u32 j1 = (f_hw2(w) >> 13) & 1u;
    869   u32 j2 = (f_hw2(w) >> 11) & 1u;
    870   u32 imm11 = f_hw2(w) & 0x7ffu;
    871   u32 raw = (s << 20) | (j2 << 19) | (j1 << 18) | (imm6 << 12) | (imm11 << 1);
    872   i64 off = (i64)raw;
    873   if (s) off |= ~(i64)0x1fffff; /* sign-extend bit 20 */
    874   return off + 4;
    875 }
    876 
    877 static u32 branch_t3_cond(u32 w) { return (f_hw1(w) >> 6) & 0xfu; }
    878 
    879 static void print_branch_t4(StrBuf* sb, u32 w, u64 vaddr) {
    880   p_rel(sb, vaddr, decode_branch_t4(w));
    881 }
    882 
    883 static void print_bl(StrBuf* sb, u32 w, u64 vaddr) {
    884   p_rel(sb, vaddr, decode_branch_t4(w));
    885 }
    886 
    887 /* ---- 16-bit field extractors ---- */
    888 static void print_cbz(StrBuf* sb, u32 w, u64 vaddr) {
    889   u32 hw = w & 0xffffu;
    890   u32 rn = hw & 7u;
    891   u32 i = (hw >> 9) & 1u, imm5 = (hw >> 3) & 0x1fu;
    892   i64 off = (i64)((i << 6) | (imm5 << 1)) + 4; /* PC bias */
    893   p_reg(sb, rn);
    894   p_sep(sb);
    895   p_rel(sb, vaddr, off);
    896 }
    897 
    898 static void print_movhi16(StrBuf* sb, u32 w) {
    899   u32 hw = w & 0xffffu;
    900   u32 rd = ((hw >> 7) & 1u) << 3 | (hw & 7u);
    901   u32 rm = (hw >> 3) & 0xfu;
    902   p_reg(sb, rd);
    903   p_sep(sb);
    904   p_reg(sb, rm);
    905 }
    906 
    907 static void print_bx(StrBuf* sb, u32 w) {
    908   u32 rm = (w >> 3) & 0xfu;
    909   p_reg(sb, rm);
    910 }
    911 
    912 static void print_bkpt(StrBuf* sb, u32 w) {
    913   p_imm(sb, (i64)(w & 0xffu));
    914 }
    915 
    916 static void print_ext16(StrBuf* sb, u32 w) {
    917   u32 rd = w & 7u, rm = (w >> 3) & 7u;
    918   p_reg(sb, rd);
    919   p_sep(sb);
    920   p_reg(sb, rm);
    921 }
    922 
    923 /* ---- additional 16-bit families ---- */
    924 static void print_dpi8_16(StrBuf* sb, u32 w) {
    925   u32 rd = (w >> 8) & 7u, imm8 = w & 0xffu;
    926   p_reg(sb, rd);
    927   p_sep(sb);
    928   p_imm(sb, (i64)imm8);
    929 }
    930 
    931 static void print_addsub3_16(StrBuf* sb, u32 w) {
    932   u32 rd = w & 7u, rn = (w >> 3) & 7u, imm3 = (w >> 6) & 7u;
    933   p_reg(sb, rd);
    934   p_sep(sb);
    935   p_reg(sb, rn);
    936   p_sep(sb);
    937   p_imm(sb, (i64)imm3);
    938 }
    939 
    940 static void print_addsubr_16(StrBuf* sb, u32 w) {
    941   u32 rd = w & 7u, rn = (w >> 3) & 7u, rm = (w >> 6) & 7u;
    942   p_reg(sb, rd);
    943   p_sep(sb);
    944   p_reg(sb, rn);
    945   p_sep(sb);
    946   p_reg(sb, rm);
    947 }
    948 
    949 static void print_shifti_16(StrBuf* sb, u32 w) {
    950   u32 rd = w & 7u, rm = (w >> 3) & 7u, imm5 = (w >> 6) & 0x1fu;
    951   p_reg(sb, rd);
    952   p_sep(sb);
    953   p_reg(sb, rm);
    954   p_sep(sb);
    955   p_imm(sb, (i64)imm5);
    956 }
    957 
    958 static void print_alu_16(StrBuf* sb, u32 w, const Arm32InsnDesc* d) {
    959   u32 rdn = w & 7u, rm = (w >> 3) & 7u;
    960   /* MULS prints `rdm, rn, rdm`; the others print `rdn, rm`. */
    961   if (slice_eq_cstr(d->mnemonic, "muls")) {
    962     p_reg(sb, rdn);
    963     p_sep(sb);
    964     p_reg(sb, rm);
    965     p_sep(sb);
    966     p_reg(sb, rdn);
    967     return;
    968   }
    969   p_reg(sb, rdn);
    970   p_sep(sb);
    971   p_reg(sb, rm);
    972 }
    973 
    974 static void print_hireg_16(StrBuf* sb, u32 w) {
    975   u32 rdn = (w & 7u) | (((w >> 7) & 1u) << 3);
    976   u32 rm = (w >> 3) & 0xfu;
    977   p_reg(sb, rdn);
    978   p_sep(sb);
    979   p_reg(sb, rm);
    980 }
    981 
    982 static void print_ldsti5_16(StrBuf* sb, u32 w) {
    983   u32 rt = w & 7u, rn = (w >> 3) & 7u, imm5 = (w >> 6) & 0x1fu;
    984   u32 op = (w >> 11) & 0x1fu; /* 0x0C/0x0D str/ldr(*4); 0x0E/0x0F strb/ldrb(*1);
    985                                  0x10/0x11 strh/ldrh(*2) */
    986   u32 scale = (op <= 0x0du) ? 4u : (op <= 0x0fu) ? 1u : 2u;
    987   p_reg(sb, rt);
    988   p_sep(sb);
    989   strbuf_putc(sb, '[');
    990   p_reg(sb, rn);
    991   if (imm5) {
    992     p_sep(sb);
    993     p_imm(sb, (i64)(imm5 * scale));
    994   }
    995   strbuf_putc(sb, ']');
    996 }
    997 
    998 static void print_ldstsp_16(StrBuf* sb, u32 w) {
    999   u32 rt = (w >> 8) & 7u, imm8 = w & 0xffu;
   1000   p_reg(sb, rt);
   1001   p_sep(sb);
   1002   strbuf_putc(sb, '[');
   1003   p_reg(sb, ARM_SP);
   1004   if (imm8) {
   1005     p_sep(sb);
   1006     p_imm(sb, (i64)(imm8 * 4u));
   1007   }
   1008   strbuf_putc(sb, ']');
   1009 }
   1010 
   1011 static void print_addsp_16(StrBuf* sb, u32 w) {
   1012   u32 rd = (w >> 8) & 7u, imm8 = w & 0xffu;
   1013   u32 sp = (w >> 11) & 1u; /* 0 = pc (adr), 1 = sp */
   1014   p_reg(sb, rd);
   1015   p_sep(sb);
   1016   p_reg(sb, sp ? ARM_SP : ARM_PC);
   1017   p_sep(sb);
   1018   p_imm(sb, (i64)(imm8 * 4u));
   1019 }
   1020 
   1021 static void print_adjsp_16(StrBuf* sb, u32 w) {
   1022   u32 imm7 = w & 0x7fu;
   1023   p_reg(sb, ARM_SP);
   1024   p_sep(sb);
   1025   p_reg(sb, ARM_SP);
   1026   p_sep(sb);
   1027   p_imm(sb, (i64)(imm7 * 4u));
   1028 }
   1029 
   1030 static void print_pushpop_16(StrBuf* sb, u32 w) {
   1031   u32 list = w & 0xffu;
   1032   u32 extra = (w >> 8) & 1u; /* push: bit8 -> LR; pop: bit8 -> PC */
   1033   if ((w >> 11) & 1u)
   1034     list |= extra ? (1u << 15) : 0u; /* pop -> PC */
   1035   else
   1036     list |= extra ? (1u << 14) : 0u; /* push -> LR */
   1037   print_reglist(sb, list);
   1038 }
   1039 
   1040 static void print_b16(StrBuf* sb, u32 w, u64 vaddr) {
   1041   u32 hw = w & 0xffffu;
   1042   u32 imm11 = hw & 0x7ffu;
   1043   i64 off = (i64)(imm11 << 1);
   1044   if (imm11 & 0x400u) off |= ~(i64)0xfff; /* sign-extend 12-bit */
   1045   off += 4;
   1046   p_rel(sb, vaddr, off);
   1047 }
   1048 
   1049 static void print_bcc16(StrBuf* sb, u32 w, u64 vaddr) {
   1050   u32 hw = w & 0xffffu;
   1051   u32 imm8 = hw & 0xffu;
   1052   i64 off = (i64)(imm8 << 1);
   1053   if (imm8 & 0x80u) off |= ~(i64)0x1ff; /* sign-extend 9-bit */
   1054   off += 4;
   1055   p_rel(sb, vaddr, off);
   1056 }
   1057 
   1058 void arm32_print_operands(StrBuf* sb, const Arm32InsnDesc* desc, u32 word,
   1059                           u64 vaddr) {
   1060   switch ((Arm32Format)desc->fmt) {
   1061     case ARM_FMT_NONE:
   1062       break;
   1063     case ARM_FMT_DP_REG:
   1064       print_dp_reg(sb, word);
   1065       break;
   1066     case ARM_FMT_DP_IMM:
   1067       print_dp_imm(sb, word);
   1068       break;
   1069     case ARM_FMT_MOV_IMM:
   1070       print_mov_imm(sb, word);
   1071       break;
   1072     case ARM_FMT_CMP_REG:
   1073       print_cmp_reg(sb, word);
   1074       break;
   1075     case ARM_FMT_CMP_IMM:
   1076       print_cmp_imm(sb, word);
   1077       break;
   1078     case ARM_FMT_MOV_REG:
   1079       print_mov_reg(sb, word);
   1080       break;
   1081     case ARM_FMT_MOVW:
   1082       print_movw(sb, word);
   1083       break;
   1084     case ARM_FMT_ADDW:
   1085       print_addw(sb, word);
   1086       break;
   1087     case ARM_FMT_SHIFT_IMM:
   1088       print_shift_imm(sb, word);
   1089       break;
   1090     case ARM_FMT_SHIFT_REG:
   1091       print_shift_reg(sb, word);
   1092       break;
   1093     case ARM_FMT_MUL:
   1094       print_mul(sb, word);
   1095       break;
   1096     case ARM_FMT_MLA:
   1097       print_mla(sb, word);
   1098       break;
   1099     case ARM_FMT_DIV:
   1100       print_div(sb, word);
   1101       break;
   1102     case ARM_FMT_MULL:
   1103       print_mull(sb, word);
   1104       break;
   1105     case ARM_FMT_EXT:
   1106       print_ext(sb, word);
   1107       break;
   1108     case ARM_FMT_REV:
   1109       print_rev(sb, word);
   1110       break;
   1111     case ARM_FMT_BFX:
   1112       print_bfx(sb, word);
   1113       break;
   1114     case ARM_FMT_BFI:
   1115       print_bfi(sb, word);
   1116       break;
   1117     case ARM_FMT_BFC:
   1118       print_bfc(sb, word);
   1119       break;
   1120     case ARM_FMT_SAT:
   1121       print_sat(sb, word);
   1122       break;
   1123     case ARM_FMT_QADD:
   1124       print_qadd(sb, word);
   1125       break;
   1126     case ARM_FMT_LDST_T3:
   1127       print_ldst_t3(sb, word);
   1128       break;
   1129     case ARM_FMT_LDST_T4:
   1130       print_ldst_t4(sb, word);
   1131       break;
   1132     case ARM_FMT_LDREX:
   1133       print_ldrex(sb, word);
   1134       break;
   1135     case ARM_FMT_STREX:
   1136       print_strex(sb, word);
   1137       break;
   1138     case ARM_FMT_PUSHPOP:
   1139       print_pushpop(sb, word);
   1140       break;
   1141     case ARM_FMT_BARRIER:
   1142       print_barrier(sb, word);
   1143       break;
   1144     case ARM_FMT_TB:
   1145       print_tb(sb, word);
   1146       break;
   1147     case ARM_FMT_BRANCH_T4:
   1148       print_branch_t4(sb, word, vaddr);
   1149       break;
   1150     case ARM_FMT_BRANCH_T3:
   1151       p_rel(sb, vaddr, decode_branch_t3(word));
   1152       break;
   1153     case ARM_FMT_BL:
   1154       print_bl(sb, word, vaddr);
   1155       break;
   1156     case ARM_FMT_B16:
   1157       print_b16(sb, word, vaddr);
   1158       break;
   1159     case ARM_FMT_BCC16:
   1160       print_bcc16(sb, word, vaddr);
   1161       break;
   1162     case ARM_FMT_CBZ:
   1163       print_cbz(sb, word, vaddr);
   1164       break;
   1165     case ARM_FMT_IT:
   1166       break; /* mnemonic carries the cc suffix; no operands here */
   1167     case ARM_FMT_MOVHI16:
   1168       print_movhi16(sb, word);
   1169       break;
   1170     case ARM_FMT_BX:
   1171       print_bx(sb, word);
   1172       break;
   1173     case ARM_FMT_BKPT:
   1174       print_bkpt(sb, word);
   1175       break;
   1176     case ARM_FMT_EXT16:
   1177       print_ext16(sb, word);
   1178       break;
   1179     case ARM_FMT_DPI8_16:
   1180       print_dpi8_16(sb, word);
   1181       break;
   1182     case ARM_FMT_ADDSUB3_16:
   1183       print_addsub3_16(sb, word);
   1184       break;
   1185     case ARM_FMT_ADDSUBR_16:
   1186       print_addsubr_16(sb, word);
   1187       break;
   1188     case ARM_FMT_SHIFTI_16:
   1189       print_shifti_16(sb, word);
   1190       break;
   1191     case ARM_FMT_ALU_16:
   1192       print_alu_16(sb, word, desc);
   1193       break;
   1194     case ARM_FMT_HIREG_16:
   1195       print_hireg_16(sb, word);
   1196       break;
   1197     case ARM_FMT_LDSTI5_16:
   1198       print_ldsti5_16(sb, word);
   1199       break;
   1200     case ARM_FMT_LDSTSP_16:
   1201       print_ldstsp_16(sb, word);
   1202       break;
   1203     case ARM_FMT_ADDSP_16:
   1204       print_addsp_16(sb, word);
   1205       break;
   1206     case ARM_FMT_ADJSP_16:
   1207       print_adjsp_16(sb, word);
   1208       break;
   1209     case ARM_FMT_PUSHPOP_16:
   1210       print_pushpop_16(sb, word);
   1211       break;
   1212   }
   1213 }
   1214 
   1215 /* B<cond>.W condition field — disasm.c appends the cc to the mnemonic. */
   1216 u32 arm32_branch_t3_cond(u32 w) { return branch_t3_cond(w); }