ETM: remove old mid-level ETM handle
[fw/openocd] / src / target / arm_disassembler.c
index f963b999103689eb42e63fcbcb95880ac9d2c23c..ee087b1215372f044b79db41b003518b2102980e 100644 (file)
 #include "log.h"
 
 
+/*
+ * This disassembler supports two main functions for OpenOCD:
+ *
+ *  - Various "disassemble" commands.  OpenOCD can serve as a
+ *    machine-language debugger, without help from GDB.
+ *
+ *  - Single stepping.  Not all ARM cores support hardware single
+ *    stepping.  To work without that support, the debugger must
+ *    be able to decode instructions to find out where to put a
+ *    "next instruction" breakpoint.
+ *
+ * In addition, interpretation of ETM trace data needs some of the
+ * decoding mechanisms.
+ *
+ * At this writing (September 2009) neither function is complete.
+ *
+ *  - ARM decoding
+ *     * Old-style syntax (not UAL) is generally used
+ *     * VFP instructions are not understood (ARMv5 and later)
+ *       except as coprocessor 10/11 operations
+ *     * Most ARM instructions through ARMv6 are decoded, but some
+ *       of the post-ARMv4 opcodes may not be handled yet
+ *     * NEON instructions are not understood (ARMv7-A)
+ *
+ *  - Thumb/Thumb2 decoding
+ *     * UAL syntax should be consistently used
+ *     * Any Thumb2 instructions used in Cortex-M3 (ARMv7-M) should
+ *       be handled properly.  Accordingly, so should the subset
+ *       used in Cortex-M0/M1; and "original" 16-bit Thumb from
+ *       ARMv4T and ARMv5T.
+ *     * Conditional effects of Thumb2 "IT" (if-then) instructions
+ *       are not handled:  the affected instructions are not shown
+ *       with their now-conditional suffixes.
+ *     * Some ARMv6 and ARMv7-M Thumb2 instructions may not be
+ *       handled (minimally for coprocessor access).
+ *     * SIMD instructions, and some other Thumb2 instructions
+ *       from ARMv7-A, are not understood.
+ *
+ *  - ThumbEE decoding
+ *     * As a Thumb2 variant, the Thumb2 comments (above) apply.
+ *     * Opcodes changed by ThumbEE mode are not handled; these
+ *       instructions wrongly decode as LDM and STM.
+ *
+ *  - Jazelle decoding ...  no support whatsoever for Jazelle mode
+ *    or decoding.  ARM encourages use of the more generic ThumbEE
+ *    mode, instead of Jazelle mode, in current chips.
+ *
+ *  - Single-step/emulation ... spotty support, which is only weakly
+ *    tested.  Thumb2 is not supported.  (Arguably a full simulator
+ *    is not needed to support just single stepping.  Recognizing
+ *    branch vs non-branch instructions suffices, except when the
+ *    instruction faults and triggers a synchronous exception which
+ *    can be intercepted using other means.)
+ *
+ * ARM DDI 0406B "ARM Architecture Reference Manual, ARM v7-A and
+ * ARM v7-R edition" gives the most complete coverage of the various
+ * generations of ARM instructions.  At this writing it is publicly
+ * accessible to anyone willing to create an account at the ARM
+ * web site; see http://www.arm.com/documentation/ for information.
+ *
+ * ARM DDI 0403C "ARMv7-M Architecture Reference Manual" provides
+ * more details relevant to the Thumb2-only processors (such as
+ * the Cortex-M implementations).
+ */
+
 /* textual represenation of the condition field */
 /* ALways (default) is ommitted (empty string) */
-char *arm_condition_strings[] =
+static const char *arm_condition_strings[] =
 {
        "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC", "HI", "LS", "GE", "LT", "GT", "LE", "", "NV"
 };
 
 /* make up for C's missing ROR */
-uint32_t ror(uint32_t value, int places)
+static uint32_t ror(uint32_t value, int places)
 {
        return (value >> places) | (value << (32 - places));
 }
 
-int evaluate_pld(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_pld(uint32_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        /* PLD */
-       if ((opcode & 0x0d70f0000) == 0x0550f000)
+       if ((opcode & 0x0d70f000) == 0x0550f000)
        {
                instruction->type = ARM_PLD;
 
@@ -62,7 +128,8 @@ int evaluate_pld(uint32_t opcode, uint32_t address, arm_instruction_t *instructi
        return -1;
 }
 
-int evaluate_swi(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_swi(uint32_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        instruction->type = ARM_SWI;
 
@@ -73,7 +140,8 @@ int evaluate_swi(uint32_t opcode, uint32_t address, arm_instruction_t *instructi
        return ERROR_OK;
 }
 
-int evaluate_blx_imm(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_blx_imm(uint32_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        int offset;
        uint32_t immediate;
@@ -105,7 +173,8 @@ int evaluate_blx_imm(uint32_t opcode, uint32_t address, arm_instruction_t *instr
        return ERROR_OK;
 }
 
-int evaluate_b_bl(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_b_bl(uint32_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint8_t L;
        uint32_t immediate;
@@ -142,7 +211,8 @@ int evaluate_b_bl(uint32_t opcode, uint32_t address, arm_instruction_t *instruct
 
 /* Coprocessor load/store and double register transfers */
 /* both normal and extended instruction space (condition field b1111) */
-int evaluate_ldc_stc_mcrr_mrrc(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_ldc_stc_mcrr_mrrc(uint32_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint8_t cp_num = (opcode & 0xf00) >> 8;
 
@@ -222,9 +292,10 @@ int evaluate_ldc_stc_mcrr_mrrc(uint32_t opcode, uint32_t address, arm_instructio
 /* Coprocessor data processing instructions */
 /* Coprocessor register transfer instructions */
 /* both normal and extended instruction space (condition field b1111) */
-int evaluate_cdp_mcr_mrc(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_cdp_mcr_mrc(uint32_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
-       char* cond;
+       const char *cond;
        char* mnemonic;
        uint8_t cp_num, opcode_1, CRd_Rd, CRn, CRm, opcode_2;
 
@@ -271,7 +342,8 @@ int evaluate_cdp_mcr_mrc(uint32_t opcode, uint32_t address, arm_instruction_t *i
 }
 
 /* Load/store instructions */
-int evaluate_load_store(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_load_store(uint32_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint8_t I, P, U, B, W, L;
        uint8_t Rn, Rd;
@@ -438,8 +510,326 @@ int evaluate_load_store(uint32_t opcode, uint32_t address, arm_instruction_t *in
        return ERROR_OK;
 }
 
+static int evaluate_extend(uint32_t opcode, uint32_t address, char *cp)
+{
+       unsigned rm = (opcode >> 0) & 0xf;
+       unsigned rd = (opcode >> 12) & 0xf;
+       unsigned rn = (opcode >> 16) & 0xf;
+       char *type, *rot;
+
+       switch ((opcode >> 24) & 0x3) {
+       case 0:
+               type = "B16";
+               break;
+       case 1:
+               sprintf(cp, "UNDEFINED");
+               return ARM_UNDEFINED_INSTRUCTION;
+       case 2:
+               type = "B";
+               break;
+       default:
+               type = "H";
+               break;
+       }
+
+       switch ((opcode >> 10) & 0x3) {
+       case 0:
+               rot = "";
+               break;
+       case 1:
+               rot = ", ROR #8";
+               break;
+       case 2:
+               rot = ", ROR #16";
+               break;
+       default:
+               rot = ", ROR #24";
+               break;
+       }
+
+       if (rn == 0xf) {
+               sprintf(cp, "%cXT%s%s\tr%d, r%d%s",
+                               (opcode & (1 << 22)) ? 'U' : 'S',
+                               type, COND(opcode),
+                               rd, rm, rot);
+               return ARM_MOV;
+       } else {
+               sprintf(cp, "%cXTA%s%s\tr%d, r%d, r%d%s",
+                               (opcode & (1 << 22)) ? 'U' : 'S',
+                               type, COND(opcode),
+                               rd, rn, rm, rot);
+               return ARM_ADD;
+       }
+}
+
+static int evaluate_p_add_sub(uint32_t opcode, uint32_t address, char *cp)
+{
+       char *prefix;
+       char *op;
+       int type;
+
+       switch ((opcode >> 20) & 0x7) {
+       case 1:
+               prefix = "S";
+               break;
+       case 2:
+               prefix = "Q";
+               break;
+       case 3:
+               prefix = "SH";
+               break;
+       case 5:
+               prefix = "U";
+               break;
+       case 6:
+               prefix = "UQ";
+               break;
+       case 7:
+               prefix = "UH";
+               break;
+       default:
+               goto undef;
+       }
+
+       switch ((opcode >> 5) & 0x7) {
+       case 0:
+               op = "ADD16";
+               type = ARM_ADD;
+               break;
+       case 1:
+               op = "ADDSUBX";
+               type = ARM_ADD;
+               break;
+       case 2:
+               op = "SUBADDX";
+               type = ARM_SUB;
+               break;
+       case 3:
+               op = "SUB16";
+               type = ARM_SUB;
+               break;
+       case 4:
+               op = "ADD8";
+               type = ARM_ADD;
+               break;
+       case 7:
+               op = "SUB8";
+               type = ARM_SUB;
+               break;
+       default:
+               goto undef;
+       }
+
+       sprintf(cp, "%s%s%s\tr%d, r%d, r%d", prefix, op, COND(opcode),
+                       (int) (opcode >> 12) & 0xf,
+                       (int) (opcode >> 16) & 0xf,
+                       (int) (opcode >> 0) & 0xf);
+       return type;
+
+undef:
+       /* these opcodes might be used someday */
+       sprintf(cp, "UNDEFINED");
+       return ARM_UNDEFINED_INSTRUCTION;
+}
+
+/* ARMv6 and later support "media" instructions (includes SIMD) */
+static int evaluate_media(uint32_t opcode, uint32_t address,
+               arm_instruction_t *instruction)
+{
+       char *cp = instruction->text;
+       char *mnemonic = NULL;
+
+       sprintf(cp,
+               "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t",
+               address, opcode);
+       cp = strchr(cp, 0);
+
+       /* parallel add/subtract */
+       if ((opcode & 0x01800000) == 0x00000000) {
+               instruction->type = evaluate_p_add_sub(opcode, address, cp);
+               return ERROR_OK;
+       }
+
+       /* halfword pack */
+       if ((opcode & 0x01f00020) == 0x00800000) {
+               char *type, *shift;
+               unsigned imm = (unsigned) (opcode >> 7) & 0x1f;
+
+               if (opcode & (1 << 6)) {
+                       type = "TB";
+                       shift = "ASR";
+                       if (imm == 0)
+                               imm = 32;
+               } else {
+                       type = "BT";
+                       shift = "LSL";
+               }
+               sprintf(cp, "PKH%s%s\tr%d, r%d, r%d, %s #%d",
+                       type, COND(opcode),
+                       (int) (opcode >> 12) & 0xf,
+                       (int) (opcode >> 16) & 0xf,
+                       (int) (opcode >> 0) & 0xf,
+                       shift, imm);
+               return ERROR_OK;
+       }
+
+       /* word saturate */
+       if ((opcode & 0x01a00020) == 0x00a00000) {
+               char *shift;
+               unsigned imm = (unsigned) (opcode >> 7) & 0x1f;
+
+               if (opcode & (1 << 6)) {
+                       shift = "ASR";
+                       if (imm == 0)
+                               imm = 32;
+               } else {
+                       shift = "LSL";
+               }
+
+               sprintf(cp, "%cSAT%s\tr%d, #%d, r%d, %s #%d",
+                       (opcode & (1 << 22)) ? 'U' : 'S',
+                       COND(opcode),
+                       (int) (opcode >> 12) & 0xf,
+                       (int) (opcode >> 16) & 0x1f,
+                       (int) (opcode >> 0) & 0xf,
+                       shift, imm);
+               return ERROR_OK;
+       }
+
+       /* sign extension */
+       if ((opcode & 0x018000f0) == 0x00800070) {
+               instruction->type = evaluate_extend(opcode, address, cp);
+               return ERROR_OK;
+       }
+
+       /* multiplies */
+       if ((opcode & 0x01f00080) == 0x01000000) {
+               unsigned rn = (opcode >> 12) & 0xf;
+
+               if (rn != 0xf)
+                       sprintf(cp, "SML%cD%s%s\tr%d, r%d, r%d, r%d",
+                               (opcode & (1 << 6)) ? 'S' : 'A',
+                               (opcode & (1 << 5)) ? "X" : "",
+                               COND(opcode),
+                               (int) (opcode >> 16) & 0xf,
+                               (int) (opcode >> 0) & 0xf,
+                               (int) (opcode >> 8) & 0xf,
+                               rn);
+               else
+                       sprintf(cp, "SMU%cD%s%s\tr%d, r%d, r%d",
+                               (opcode & (1 << 6)) ? 'S' : 'A',
+                               (opcode & (1 << 5)) ? "X" : "",
+                               COND(opcode),
+                               (int) (opcode >> 16) & 0xf,
+                               (int) (opcode >> 0) & 0xf,
+                               (int) (opcode >> 8) & 0xf);
+               return ERROR_OK;
+       }
+       if ((opcode & 0x01f00000) == 0x01400000) {
+               sprintf(cp, "SML%cLD%s%s\tr%d, r%d, r%d, r%d",
+                       (opcode & (1 << 6)) ? 'S' : 'A',
+                       (opcode & (1 << 5)) ? "X" : "",
+                       COND(opcode),
+                       (int) (opcode >> 12) & 0xf,
+                       (int) (opcode >> 16) & 0xf,
+                       (int) (opcode >> 0) & 0xf,
+                       (int) (opcode >> 8) & 0xf);
+               return ERROR_OK;
+       }
+       if ((opcode & 0x01f00000) == 0x01500000) {
+               unsigned rn = (opcode >> 12) & 0xf;
+
+               switch (opcode & 0xc0) {
+               case 3:
+                       if (rn == 0xf)
+                               goto undef;
+                       /* FALL THROUGH */
+               case 0:
+                       break;
+               default:
+                       goto undef;
+               }
+
+               if (rn != 0xf)
+                       sprintf(cp, "SMML%c%s%s\tr%d, r%d, r%d, r%d",
+                               (opcode & (1 << 6)) ? 'S' : 'A',
+                               (opcode & (1 << 5)) ? "R" : "",
+                               COND(opcode),
+                               (int) (opcode >> 16) & 0xf,
+                               (int) (opcode >> 0) & 0xf,
+                               (int) (opcode >> 8) & 0xf,
+                               rn);
+               else
+                       sprintf(cp, "SMMUL%s%s\tr%d, r%d, r%d",
+                               (opcode & (1 << 5)) ? "R" : "",
+                               COND(opcode),
+                               (int) (opcode >> 16) & 0xf,
+                               (int) (opcode >> 0) & 0xf,
+                               (int) (opcode >> 8) & 0xf);
+               return ERROR_OK;
+       }
+
+
+       /* simple matches against the remaining decode bits */
+       switch (opcode & 0x01f000f0) {
+       case 0x00a00030:
+       case 0x00e00030:
+               /* parallel halfword saturate */
+               sprintf(cp, "%cSAT16%s\tr%d, #%d, r%d",
+                       (opcode & (1 << 22)) ? 'U' : 'S',
+                       COND(opcode),
+                       (int) (opcode >> 12) & 0xf,
+                       (int) (opcode >> 16) & 0xf,
+                       (int) (opcode >> 0) & 0xf);
+               return ERROR_OK;
+       case 0x00b00030:
+               mnemonic = "REV";
+               break;
+       case 0x00b000b0:
+               mnemonic = "REV16";
+               break;
+       case 0x00f000b0:
+               mnemonic = "REVSH";
+               break;
+       case 0x008000b0:
+               /* select bytes */
+               sprintf(cp, "SEL%s\tr%d, r%d, r%d", COND(opcode),
+                       (int) (opcode >> 12) & 0xf,
+                       (int) (opcode >> 16) & 0xf,
+                       (int) (opcode >> 0) & 0xf);
+               return ERROR_OK;
+       case 0x01800010:
+               /* unsigned sum of absolute differences */
+               if (((opcode >> 12) & 0xf) == 0xf)
+                       sprintf(cp, "USAD8%s\tr%d, r%d, r%d", COND(opcode),
+                               (int) (opcode >> 16) & 0xf,
+                               (int) (opcode >> 0) & 0xf,
+                               (int) (opcode >> 8) & 0xf);
+               else
+                       sprintf(cp, "USADA8%s\tr%d, r%d, r%d, r%d", COND(opcode),
+                               (int) (opcode >> 16) & 0xf,
+                               (int) (opcode >> 0) & 0xf,
+                               (int) (opcode >> 8) & 0xf,
+                               (int) (opcode >> 12) & 0xf);
+               return ERROR_OK;
+       }
+       if (mnemonic) {
+               unsigned rm = (opcode >> 0) & 0xf;
+               unsigned rd = (opcode >> 12) & 0xf;
+
+               sprintf(cp, "%s%s\tr%d, r%d", mnemonic, COND(opcode), rm, rd);
+               return ERROR_OK;
+       }
+
+undef:
+       /* these opcodes might be used someday */
+       sprintf(cp, "UNDEFINED");
+       return ERROR_OK;
+}
+
 /* Miscellaneous load/store instructions */
-int evaluate_misc_load_store(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_misc_load_store(uint32_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint8_t P, U, I, W, L, S, H;
        uint8_t Rn, Rd;
@@ -566,7 +956,8 @@ int evaluate_misc_load_store(uint32_t opcode, uint32_t address, arm_instruction_
 }
 
 /* Load/store multiples instructions */
-int evaluate_ldm_stm(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_ldm_stm(uint32_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint8_t P, U, S, W, L, Rn;
        uint32_t register_list;
@@ -654,7 +1045,8 @@ int evaluate_ldm_stm(uint32_t opcode, uint32_t address, arm_instruction_t *instr
 }
 
 /* Multiplies, extra load/stores */
-int evaluate_mul_and_extra_ld_st(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_mul_and_extra_ld_st(uint32_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        /* Multiply (accumulate) (long) and Swap/swap byte */
        if ((opcode & 0x000000f0) == 0x00000090)
@@ -745,7 +1137,8 @@ int evaluate_mul_and_extra_ld_st(uint32_t opcode, uint32_t address, arm_instruct
        return evaluate_misc_load_store(opcode, address, instruction);
 }
 
-int evaluate_mrs_msr(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_mrs_msr(uint32_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        int R = (opcode & 0x00400000) >> 22;
        char *PSR = (R) ? "SPSR" : "CPSR";
@@ -799,7 +1192,8 @@ int evaluate_mrs_msr(uint32_t opcode, uint32_t address, arm_instruction_t *instr
 }
 
 /* Miscellaneous instructions */
-int evaluate_misc_instr(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_misc_instr(uint32_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        /* MRS/MSR */
        if ((opcode & 0x000000f0) == 0x00000000)
@@ -821,6 +1215,21 @@ int evaluate_misc_instr(uint32_t opcode, uint32_t address, arm_instruction_t *in
                instruction->info.b_bl_bx_blx.target_address = -1;
        }
 
+       /* BXJ - "Jazelle" support (ARMv5-J) */
+       if ((opcode & 0x006000f0) == 0x00200020)
+       {
+               uint8_t Rm;
+               instruction->type = ARM_BX;
+               Rm = opcode & 0xf;
+
+               snprintf(instruction->text, 128,
+                               "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tBXJ%s r%i",
+                                address, opcode, COND(opcode), Rm);
+
+               instruction->info.b_bl_bx_blx.reg_operand = Rm;
+               instruction->info.b_bl_bx_blx.target_address = -1;
+       }
+
        /* CLZ */
        if ((opcode & 0x006000f0) == 0x00600010)
        {
@@ -974,7 +1383,8 @@ int evaluate_misc_instr(uint32_t opcode, uint32_t address, arm_instruction_t *in
        return ERROR_OK;
 }
 
-int evaluate_data_proc(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_data_proc(uint32_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint8_t I, op, S, Rn, Rd;
        char *mnemonic = NULL;
@@ -1272,17 +1682,24 @@ int arm_evaluate_opcode(uint32_t opcode, uint32_t address, arm_instruction_t *in
        /* catch opcodes with [27:25] = b011 */
        if ((opcode & 0x0e000000) == 0x06000000)
        {
-               /* Undefined instruction */
-               if ((opcode & 0x00000010) == 0x00000010)
+               /* Load/store register offset */
+               if ((opcode & 0x00000010) == 0x00000000)
+                       return evaluate_load_store(opcode, address, instruction);
+
+               /* Architecturally Undefined instruction
+                * ... don't expect these to ever be used
+                */
+               if ((opcode & 0x07f000f0) == 0x07f000f0)
                {
                        instruction->type = ARM_UNDEFINED_INSTRUCTION;
-                       snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tUNDEFINED INSTRUCTION", address, opcode);
+                       snprintf(instruction->text, 128,
+                               "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tUNDEF",
+                               address, opcode);
                        return ERROR_OK;
                }
 
-               /* Load/store register offset */
-               return evaluate_load_store(opcode, address, instruction);
-
+               /* "media" instructions */
+               return evaluate_media(opcode, address, instruction);
        }
 
        /* catch opcodes with [27:25] = b100 */
@@ -1326,7 +1743,8 @@ int arm_evaluate_opcode(uint32_t opcode, uint32_t address, arm_instruction_t *in
        return -1;
 }
 
-int evaluate_b_bl_blx_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_b_bl_blx_thumb(uint16_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint32_t offset = opcode & 0x7ff;
        uint32_t opc = (opcode >> 11) & 0x3;
@@ -1350,6 +1768,7 @@ int evaluate_b_bl_blx_thumb(uint16_t opcode, uint32_t address, arm_instruction_t
                case 1:
                        instruction->type = ARM_BLX;
                        mnemonic = "BLX";
+                       target_address &= 0xfffffffc;
                        break;
                /* BL/BLX prefix */
                case 2:
@@ -1379,7 +1798,8 @@ int evaluate_b_bl_blx_thumb(uint16_t opcode, uint32_t address, arm_instruction_t
        return ERROR_OK;
 }
 
-int evaluate_add_sub_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_add_sub_thumb(uint16_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint8_t Rd = (opcode >> 0) & 0x7;
        uint8_t Rn = (opcode >> 3) & 0x7;
@@ -1424,7 +1844,8 @@ int evaluate_add_sub_thumb(uint16_t opcode, uint32_t address, arm_instruction_t
        return ERROR_OK;
 }
 
-int evaluate_shift_imm_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_shift_imm_thumb(uint16_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint8_t Rd = (opcode >> 0) & 0x7;
        uint8_t Rm = (opcode >> 3) & 0x7;
@@ -1469,7 +1890,8 @@ int evaluate_shift_imm_thumb(uint16_t opcode, uint32_t address, arm_instruction_
        return ERROR_OK;
 }
 
-int evaluate_data_proc_imm_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_data_proc_imm_thumb(uint16_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint8_t imm = opcode & 0xff;
        uint8_t Rd = (opcode >> 8) & 0x7;
@@ -1511,7 +1933,8 @@ int evaluate_data_proc_imm_thumb(uint16_t opcode, uint32_t address, arm_instruct
        return ERROR_OK;
 }
 
-int evaluate_data_proc_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_data_proc_thumb(uint16_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint8_t high_reg, op, Rm, Rd,H1,H2;
        char *mnemonic = NULL;
@@ -1696,7 +2119,8 @@ static inline uint32_t thumb_alignpc4(uint32_t addr)
        return (addr + 4) & ~3;
 }
 
-int evaluate_load_literal_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_load_literal_thumb(uint16_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint32_t immediate;
        uint8_t Rd = (opcode >> 8) & 0x7;
@@ -1720,7 +2144,8 @@ int evaluate_load_literal_thumb(uint16_t opcode, uint32_t address, arm_instructi
        return ERROR_OK;
 }
 
-int evaluate_load_store_reg_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_load_store_reg_thumb(uint16_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint8_t Rd = (opcode >> 0) & 0x7;
        uint8_t Rn = (opcode >> 3) & 0x7;
@@ -1777,7 +2202,8 @@ int evaluate_load_store_reg_thumb(uint16_t opcode, uint32_t address, arm_instruc
        return ERROR_OK;
 }
 
-int evaluate_load_store_imm_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_load_store_imm_thumb(uint16_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint32_t offset = (opcode >> 6) & 0x1f;
        uint8_t Rd = (opcode >> 0) & 0x7;
@@ -1823,7 +2249,8 @@ int evaluate_load_store_imm_thumb(uint16_t opcode, uint32_t address, arm_instruc
        return ERROR_OK;
 }
 
-int evaluate_load_store_stack_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_load_store_stack_thumb(uint16_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint32_t offset = opcode  & 0xff;
        uint8_t Rd = (opcode >> 8) & 0x7;
@@ -1854,7 +2281,8 @@ int evaluate_load_store_stack_thumb(uint16_t opcode, uint32_t address, arm_instr
        return ERROR_OK;
 }
 
-int evaluate_add_sp_pc_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_add_sp_pc_thumb(uint16_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint32_t imm = opcode  & 0xff;
        uint8_t Rd = (opcode >> 8) & 0x7;
@@ -1887,7 +2315,8 @@ int evaluate_add_sp_pc_thumb(uint16_t opcode, uint32_t address, arm_instruction_
        return ERROR_OK;
 }
 
-int evaluate_adjust_stack_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_adjust_stack_thumb(uint16_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint32_t imm = opcode  & 0x7f;
        uint8_t opc = opcode & (1 << 7);
@@ -1917,7 +2346,8 @@ int evaluate_adjust_stack_thumb(uint16_t opcode, uint32_t address, arm_instructi
        return ERROR_OK;
 }
 
-int evaluate_breakpoint_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_breakpoint_thumb(uint16_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint32_t imm = opcode  & 0xff;
 
@@ -1930,7 +2360,8 @@ int evaluate_breakpoint_thumb(uint16_t opcode, uint32_t address, arm_instruction
        return ERROR_OK;
 }
 
-int evaluate_load_store_multiple_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_load_store_multiple_thumb(uint16_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint32_t reg_list = opcode  & 0xff;
        uint32_t L = opcode & (1 << 11);
@@ -1943,6 +2374,10 @@ int evaluate_load_store_multiple_thumb(uint16_t opcode, uint32_t address, arm_in
        char ptr_name[7] = "";
        int i;
 
+       /* REVISIT:  in ThumbEE mode, there are no LDM or STM instructions.
+        * The STMIA and LDMIA opcodes are used for other instructions.
+        */
+
        if ((opcode & 0xf000) == 0xc000)
        { /* generic load/store multiple */
                char *wback = "!";
@@ -2003,7 +2438,8 @@ int evaluate_load_store_multiple_thumb(uint16_t opcode, uint32_t address, arm_in
        return ERROR_OK;
 }
 
-int evaluate_cond_branch_thumb(uint16_t opcode, uint32_t address, arm_instruction_t *instruction)
+static int evaluate_cond_branch_thumb(uint16_t opcode,
+               uint32_t address, arm_instruction_t *instruction)
 {
        uint32_t offset = opcode  & 0xff;
        uint8_t cond = (opcode >> 8) & 0xf;
@@ -2493,6 +2929,12 @@ static int t2ev_misc(uint32_t opcode, uint32_t address,
        const char *mnemonic;
 
        switch ((opcode >> 4) & 0x0f) {
+       case 0:
+               mnemonic = "LEAVEX";
+               break;
+       case 1:
+               mnemonic = "ENTERX";
+               break;
        case 2:
                mnemonic = "CLREX";
                break;
@@ -2546,6 +2988,9 @@ static int t2ev_b_misc(uint32_t opcode, uint32_t address,
                return t2ev_hint(opcode, address, instruction, cp);
        case 0x3b:
                return t2ev_misc(opcode, address, instruction, cp);
+       case 0x3c:
+               sprintf(cp, "BXJ\tr%d", (int) (opcode >> 16) & 0x0f);
+               return ERROR_OK;
        case 0x3e:
        case 0x3f:
                sprintf(cp, "MRS\tr%d, %s", (int) (opcode >> 8) & 0x0f,
@@ -3007,6 +3452,133 @@ static int t2ev_ldm_stm(uint32_t opcode, uint32_t address,
        return ERROR_OK;
 }
 
+/* load/store dual or exclusive, table branch */
+static int t2ev_ldrex_strex(uint32_t opcode, uint32_t address,
+               arm_instruction_t *instruction, char *cp)
+{
+       unsigned op1op2 = (opcode >> 20) & 0x3;
+       unsigned op3 = (opcode >> 4) & 0xf;
+       char *mnemonic;
+       unsigned rn = (opcode >> 16) & 0xf;
+       unsigned rt = (opcode >> 12) & 0xf;
+       unsigned rd = (opcode >> 8) & 0xf;
+       unsigned imm = opcode & 0xff;
+       char *p1 = "";
+       char *p2 = "]";
+
+       op1op2 |= (opcode >> 21) & 0xc;
+       switch (op1op2) {
+       case 0:
+               mnemonic = "STREX";
+               goto strex;
+       case 1:
+               mnemonic = "LDREX";
+               goto ldrex;
+       case 2:
+       case 6:
+       case 8:
+       case 10:
+       case 12:
+       case 14:
+               mnemonic = "STRD";
+               goto immediate;
+       case 3:
+       case 7:
+       case 9:
+       case 11:
+       case 13:
+       case 15:
+               mnemonic = "LDRD";
+               if (rn == 15)
+                       goto literal;
+               else
+                       goto immediate;
+       case 4:
+               switch (op3) {
+               case 4:
+                       mnemonic = "STREXB";
+                       break;
+               case 5:
+                       mnemonic = "STREXH";
+                       break;
+               default:
+                       return ERROR_INVALID_ARGUMENTS;
+               }
+               rd = opcode & 0xf;
+               imm = 0;
+               goto strex;
+       case 5:
+               switch (op3) {
+               case 0:
+                       sprintf(cp, "TBB\t[r%u, r%u]", rn, imm & 0xf);
+                       return ERROR_OK;
+               case 1:
+                       sprintf(cp, "TBH\t[r%u, r%u, LSL #1]", rn, imm & 0xf);
+                       return ERROR_OK;
+               case 4:
+                       mnemonic = "LDREXB";
+                       break;
+               case 5:
+                       mnemonic = "LDREXH";
+                       break;
+               default:
+                       return ERROR_INVALID_ARGUMENTS;
+               }
+               imm = 0;
+               goto ldrex;
+       }
+       return ERROR_INVALID_ARGUMENTS;
+
+strex:
+       imm <<= 2;
+       if (imm)
+               sprintf(cp, "%s\tr%u, r%u, [r%u, #%u]\t; %#2.2x",
+                               mnemonic, rd, rt, rn, imm, imm);
+       else
+               sprintf(cp, "%s\tr%u, r%u, [r%u]",
+                               mnemonic, rd, rt, rn);
+       return ERROR_OK;
+
+ldrex:
+       imm <<= 2;
+       if (imm)
+               sprintf(cp, "%s\tr%u, [r%u, #%u]\t; %#2.2x",
+                               mnemonic, rt, rn, imm, imm);
+       else
+               sprintf(cp, "%s\tr%u, [r%u]",
+                               mnemonic, rt, rn);
+       return ERROR_OK;
+
+immediate:
+       /* two indexed modes will write back rn */
+       if (opcode & (1 << 21)) {
+               if (opcode & (1 << 24)) /* pre-indexed */
+                       p2 = "]!";
+               else {                  /* post-indexed */
+                       p1 = "]";
+                       p2 = "";
+               }
+       }
+
+       imm <<= 2;
+       sprintf(cp, "%s\tr%u, r%u, [r%u%s, #%s%u%s\t; %#2.2x",
+                       mnemonic, rt, rd, rn, p1,
+                       (opcode & (1 << 23)) ? "" : "-",
+                       imm, p2, imm);
+       return ERROR_OK;
+
+literal:
+       address = thumb_alignpc4(address);
+       imm <<= 2;
+       if (opcode & (1 << 23))
+               address += imm;
+       else
+               address -= imm;
+       sprintf(cp, "%s\tr%u, r%u, %#8.8" PRIx32,
+                       mnemonic, rt, rd, address);
+       return ERROR_OK;
+}
+
 static int t2ev_data_shift(uint32_t opcode, uint32_t address,
                arm_instruction_t *instruction, char *cp)
 {
@@ -3522,7 +4094,7 @@ ldrsb_literal:
        case 3:
                if (rt == 0xf) {
                        immed = opcode & 0xfff;
-                       sprintf(cp, "PLI\t[r%d, #%d]\t; %#3.3" PRIx32,
+                       sprintf(cp, "PLI\t[r%d, #%d]\t; %#3.3x",
                                        rn, immed, immed);
                        return ERROR_OK;
                }
@@ -3677,6 +4249,10 @@ int thumb2_opcode(target_t *target, uint32_t address, arm_instruction_t *instruc
        else if ((opcode & 0x1e400000) == 0x08000000)
                retval = t2ev_ldm_stm(opcode, address, instruction, cp);
 
+       /* ARMv7-M: A5.3.6 Load/store dual or exclusive, table branch */
+       else if ((opcode & 0x1e400000) == 0x08400000)
+               retval = t2ev_ldrex_strex(opcode, address, instruction, cp);
+
        /* ARMv7-M: A5.3.7 Load word */
        else if ((opcode & 0x1f700000) == 0x18500000)
                retval = t2ev_load_word(opcode, address, instruction, cp);
@@ -3711,11 +4287,14 @@ int thumb2_opcode(target_t *target, uint32_t address, arm_instruction_t *instruc
        else if ((opcode & 0x1f800000) == 0x1b800000)
                retval = t2ev_mul64_div(opcode, address, instruction, cp);
 
-       /* FIXME decode more 32-bit instructions */
-
        if (retval == ERROR_OK)
                return retval;
 
+       /*
+        * Thumb2 also supports coprocessor, ThumbEE, and DSP/Media (SIMD)
+        * instructions; not yet handled here.
+        */
+
        if (retval == ERROR_INVALID_ARGUMENTS) {
                instruction->type = ARM_UNDEFINED_INSTRUCTION;
                strcpy(cp, "UNDEFINED OPCODE");