David Brownell <david-b@pacbell.net> ARM disassembly support for about five dozen non-Thumb instructions
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
This commit is contained in:
parent
ae17ce23eb
commit
997d5284cb
|
@ -438,6 +438,323 @@ int evaluate_load_store(uint32_t opcode, uint32_t address, arm_instruction_t *in
|
||||||
return ERROR_OK;
|
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 */
|
/* Miscellaneous load/store instructions */
|
||||||
int evaluate_misc_load_store(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
|
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;
|
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 */
|
/* CLZ */
|
||||||
if ((opcode & 0x006000f0) == 0x00600010)
|
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 */
|
/* catch opcodes with [27:25] = b011 */
|
||||||
if ((opcode & 0x0e000000) == 0x06000000)
|
if ((opcode & 0x0e000000) == 0x06000000)
|
||||||
{
|
{
|
||||||
/* Undefined instruction */
|
/* Load/store register offset */
|
||||||
if ((opcode & 0x00000010) == 0x00000010)
|
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;
|
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;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load/store register offset */
|
/* "media" instructions */
|
||||||
return evaluate_load_store(opcode, address, instruction);
|
return evaluate_media(opcode, address, instruction);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* catch opcodes with [27:25] = b100 */
|
/* catch opcodes with [27:25] = b100 */
|
||||||
|
|
Loading…
Reference in New Issue