David Brownell <david-b@pacbell.net> ARM disassembly support for about five dozen...
authoroharboe <oharboe@b42882b7-edfa-0310-969c-e2dbd0fdcd60>
Fri, 28 Aug 2009 06:52:08 +0000 (06:52 +0000)
committeroharboe <oharboe@b42882b7-edfa-0310-969c-e2dbd0fdcd60>
Fri, 28 Aug 2009 06:52:08 +0000 (06:52 +0000)
that were added after ARMv5TE was defined:

 - ARMv5J "BXJ" (for Java/Jazelle)
 - ARMv6 "media" instructions (for OMAP2420, i.MX31, etc)

Compile-tested.  This might not set up the simulator right for the
ARMv6 single step support; only BXJ branches though, and docs to
support Jazelle branching are non-public (still, sigh).

ARMv6 instructions known to be mis-handled by this disassembler
include:  UMAAL, LDREX, STREX, CPS, SETEND, RFE, SRS, MCRR2, MRRC2

git-svn-id: svn://svn.berlios.de/openocd/trunk@2644 b42882b7-edfa-0310-969c-e2dbd0fdcd60

src/target/arm_disassembler.c

index a994d3160fc9a0b3d36b5d82afd8b9d4be6c6d2c..56474bfcd0405b0240c2169d49973a7f58adddf9 100644 (file)
@@ -438,6 +438,323 @@ 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;
+       case 3:
+               type = "H";
+               break;
+       }
+
+       switch ((opcode >> 10) & 0x3) {
+       case 0:
+               rot = "";
+               break;
+       case 1:
+               rot = ", ROR #8";
+               break;
+       case 2:
+               rot = ", ROR #16";
+               break;
+       case 3:
+               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)
 {
@@ -821,6 +1138,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)
        {
@@ -1272,17 +1604,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 */