Newer
Older
/***************************************************************************
* Copyright (C) 2006 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "arm_disassembler.h"
#include "log.h"
/* textual represenation of the condition field */
/* ALways (default) is ommitted (empty string) */
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)
{
return (value >> places) | (value << (32 - places));
}
int evaluate_pld(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
{
/* PLD */
if ((opcode & 0x0d70f0000) == 0x0550f000)
{
instruction->type = ARM_PLD;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD ...TODO...", address, opcode);
return ERROR_OK;
}
else
{
instruction->type = ARM_UNDEFINED_INSTRUCTION;
return ERROR_OK;
}
LOG_ERROR("should never reach this point");
return -1;
}
int evaluate_swi(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
{
instruction->type = ARM_SWI;
snprintf(instruction->text, 128,
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSVC %#6.6" PRIx32,
address, opcode, (opcode & 0xffffff));
return ERROR_OK;
}
int evaluate_blx_imm(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
{
int offset;
uint32_t immediate;
uint32_t target_address;
instruction->type = ARM_BLX;
immediate = opcode & 0x00ffffff;
/* sign extend 24-bit immediate */
if (immediate & 0x00800000)
offset = 0xff000000 | immediate;
else
offset = immediate;
/* shift two bits left */
offset <<= 2;
/* odd/event halfword */
if (opcode & 0x01000000)
offset |= 0x2;
target_address = address + 8 + offset;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tBLX 0x%8.8" PRIx32 "", address, opcode, target_address);
instruction->info.b_bl_bx_blx.reg_operand = -1;
instruction->info.b_bl_bx_blx.target_address = target_address;
return ERROR_OK;
}
int evaluate_b_bl(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
{
int offset;
immediate = opcode & 0x00ffffff;
L = (opcode & 0x01000000) >> 24;
/* sign extend 24-bit immediate */
if (immediate & 0x00800000)
offset = 0xff000000 | immediate;
else
offset = immediate;
/* shift two bits left */
offset <<= 2;
target_address = address + 8 + offset;
if (L)
instruction->type = ARM_BL;
else
instruction->type = ARM_B;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tB%s%s 0x%8.8" PRIx32 , address, opcode,
(L) ? "L" : "", COND(opcode), target_address);
instruction->info.b_bl_bx_blx.reg_operand = -1;
instruction->info.b_bl_bx_blx.target_address = target_address;
return ERROR_OK;
}
/* 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)
{
uint8_t cp_num = (opcode & 0xf00) >> 8;
/* MCRR or MRRC */
if (((opcode & 0x0ff00000) == 0x0c400000) || ((opcode & 0x0ff00000) == 0x0c400000))
{
char *mnemonic;
cp_opcode = (opcode & 0xf0) >> 4;
Rd = (opcode & 0xf000) >> 12;
Rn = (opcode & 0xf0000) >> 16;
CRm = (opcode & 0xf);
/* MCRR */
if ((opcode & 0x0ff00000) == 0x0c400000)
{
instruction->type = ARM_MCRR;
mnemonic = "MCRR";
}
/* MRRC */
if ((opcode & 0x0ff00000) == 0x0c500000)
{
instruction->type = ARM_MRRC;
mnemonic = "MRRC";
}
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s p%i, %x, r%i, r%i, c%i",
address, opcode, mnemonic, COND(opcode), cp_num, cp_opcode, Rd, Rn, CRm);
}
else /* LDC or STC */
{
uint8_t CRd, Rn, offset;
uint8_t U, N;
char *mnemonic;
char addressing_mode[32];
CRd = (opcode & 0xf000) >> 12;
Rn = (opcode & 0xf0000) >> 16;
offset = (opcode & 0xff);
/* load/store */
if (opcode & 0x00100000)
{
instruction->type = ARM_LDC;
mnemonic = "LDC";
}
else
{
instruction->type = ARM_STC;
mnemonic = "STC";
}
U = (opcode & 0x00800000) >> 23;
N = (opcode & 0x00400000) >> 22;
/* addressing modes */
if ((opcode & 0x01200000) == 0x01000000) /* immediate offset */
snprintf(addressing_mode, 32, "[r%i, #%s0x%2.2x*4]", Rn, (U) ? "" : "-", offset);
else if ((opcode & 0x01200000) == 0x01200000) /* immediate pre-indexed */
snprintf(addressing_mode, 32, "[r%i, #%s0x%2.2x*4]!", Rn, (U) ? "" : "-", offset);
else if ((opcode & 0x01200000) == 0x00200000) /* immediate post-indexed */
snprintf(addressing_mode, 32, "[r%i], #%s0x%2.2x*4", Rn, (U) ? "" : "-", offset);
else if ((opcode & 0x01200000) == 0x00000000) /* unindexed */
snprintf(addressing_mode, 32, "[r%i], #0x%2.2x", Rn, offset);
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s p%i, c%i, %s",
address, opcode, mnemonic, ((opcode & 0xf0000000) == 0xf0000000) ? COND(opcode) : "2",
(N) ? "L" : "",
cp_num, CRd, addressing_mode);
}
return ERROR_OK;
}
/* 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)
{
char* cond;
char* mnemonic;
uint8_t cp_num, opcode_1, CRd_Rd, CRn, CRm, opcode_2;
cond = ((opcode & 0xf0000000) == 0xf0000000) ? "2" : COND(opcode);
cp_num = (opcode & 0xf00) >> 8;
CRd_Rd = (opcode & 0xf000) >> 12;
CRn = (opcode & 0xf0000) >> 16;
CRm = (opcode & 0xf);
opcode_2 = (opcode & 0xe0) >> 5;
/* CDP or MRC/MCR */
if (opcode & 0x00000010) /* bit 4 set -> MRC/MCR */
{
if (opcode & 0x00100000) /* bit 20 set -> MRC */
{
instruction->type = ARM_MRC;
mnemonic = "MRC";
}
else /* bit 20 not set -> MCR */
{
instruction->type = ARM_MCR;
mnemonic = "MCR";
}
opcode_1 = (opcode & 0x00e00000) >> 21;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s p%i, 0x%2.2x, r%i, c%i, c%i, 0x%2.2x",
address, opcode, mnemonic, cond,
cp_num, opcode_1, CRd_Rd, CRn, CRm, opcode_2);
}
else /* bit 4 not set -> CDP */
{
instruction->type = ARM_CDP;
mnemonic = "CDP";
opcode_1 = (opcode & 0x00f00000) >> 20;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s p%i, 0x%2.2x, c%i, c%i, c%i, 0x%2.2x",
address, opcode, mnemonic, cond,
cp_num, opcode_1, CRd_Rd, CRn, CRm, opcode_2);
}
return ERROR_OK;
}
/* Load/store instructions */
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;
char *operation; /* "LDR" or "STR" */
char *suffix; /* "", "B", "T", "BT" */
char offset[32];
/* examine flags */
I = (opcode & 0x02000000) >> 25;
P = (opcode & 0x01000000) >> 24;
U = (opcode & 0x00800000) >> 23;
B = (opcode & 0x00400000) >> 22;
W = (opcode & 0x00200000) >> 21;
L = (opcode & 0x00100000) >> 20;
/* target register */
Rd = (opcode & 0xf000) >> 12;
/* base register */
Rn = (opcode & 0xf0000) >> 16;
instruction->info.load_store.Rd = Rd;
instruction->info.load_store.Rn = Rn;
instruction->info.load_store.U = U;
/* determine operation */
if (L)
operation = "LDR";
else
operation = "STR";
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
/* determine instruction type and suffix */
if (B)
{
if ((P == 0) && (W == 1))
{
if (L)
instruction->type = ARM_LDRBT;
else
instruction->type = ARM_STRBT;
suffix = "BT";
}
else
{
if (L)
instruction->type = ARM_LDRB;
else
instruction->type = ARM_STRB;
suffix = "B";
}
}
else
{
if ((P == 0) && (W == 1))
{
if (L)
instruction->type = ARM_LDRT;
else
instruction->type = ARM_STRT;
suffix = "T";
}
else
{
if (L)
instruction->type = ARM_LDR;
else
instruction->type = ARM_STR;
suffix = "";
}
}
if (!I) /* #+-<offset_12> */
{
uint32_t offset_12 = (opcode & 0xfff);
snprintf(offset, 32, ", #%s0x%" PRIx32 "", (U) ? "" : "-", offset_12);
instruction->info.load_store.offset_mode = 0;
instruction->info.load_store.offset.offset = offset_12;
}
else /* either +-<Rm> or +-<Rm>, <shift>, #<shift_imm> */
{
uint8_t shift_imm, shift;
uint8_t Rm;
shift_imm = (opcode & 0xf80) >> 7;
shift = (opcode & 0x60) >> 5;
Rm = (opcode & 0xf);
/* LSR encodes a shift by 32 bit as 0x0 */
if ((shift == 0x1) && (shift_imm == 0x0))
shift_imm = 0x20;
/* ASR encodes a shift by 32 bit as 0x0 */
if ((shift == 0x2) && (shift_imm == 0x0))
shift_imm = 0x20;
/* ROR by 32 bit is actually a RRX */
if ((shift == 0x3) && (shift_imm == 0x0))
shift = 0x4;
instruction->info.load_store.offset_mode = 1;
instruction->info.load_store.offset.reg.Rm = Rm;
instruction->info.load_store.offset.reg.shift = shift;
instruction->info.load_store.offset.reg.shift_imm = shift_imm;
if ((shift_imm == 0x0) && (shift == 0x0)) /* +-<Rm> */
{
snprintf(offset, 32, ", %sr%i", (U) ? "" : "-", Rm);
}
else /* +-<Rm>, <Shift>, #<shift_imm> */
{
{
snprintf(offset, 32, ", %sr%i, LSL #0x%x", (U) ? "" : "-", Rm, shift_imm);
break;
case 0x1: /* LSR */
snprintf(offset, 32, ", %sr%i, LSR #0x%x", (U) ? "" : "-", Rm, shift_imm);
break;
case 0x2: /* ASR */
snprintf(offset, 32, ", %sr%i, ASR #0x%x", (U) ? "" : "-", Rm, shift_imm);
break;
case 0x3: /* ROR */
snprintf(offset, 32, ", %sr%i, ROR #0x%x", (U) ? "" : "-", Rm, shift_imm);
break;
case 0x4: /* RRX */
snprintf(offset, 32, ", %sr%i, RRX", (U) ? "" : "-", Rm);
}
}
}
if (P == 1)
{
if (W == 0) /* offset */
{
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, [r%i%s]",
address, opcode, operation, COND(opcode), suffix,
Rd, Rn, offset);
instruction->info.load_store.index_mode = 0;
}
else /* pre-indexed */
{
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, [r%i%s]!",
address, opcode, operation, COND(opcode), suffix,
Rd, Rn, offset);
instruction->info.load_store.index_mode = 1;
}
}
else /* post-indexed */
{
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, [r%i]%s",
address, opcode, operation, COND(opcode), suffix,
Rd, Rn, offset);
instruction->info.load_store.index_mode = 2;
}
return ERROR_OK;
}
/* Miscellaneous load/store instructions */
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;
char *operation; /* "LDR" or "STR" */
char *suffix; /* "H", "SB", "SH", "D" */
char offset[32];
/* examine flags */
P = (opcode & 0x01000000) >> 24;
U = (opcode & 0x00800000) >> 23;
I = (opcode & 0x00400000) >> 22;
W = (opcode & 0x00200000) >> 21;
L = (opcode & 0x00100000) >> 20;
S = (opcode & 0x00000040) >> 6;
H = (opcode & 0x00000020) >> 5;
/* target register */
Rd = (opcode & 0xf000) >> 12;
/* base register */
Rn = (opcode & 0xf0000) >> 16;
instruction->info.load_store.Rd = Rd;
instruction->info.load_store.Rn = Rn;
instruction->info.load_store.U = U;
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
/* determine instruction type and suffix */
if (S) /* signed */
{
if (L) /* load */
{
if (H)
{
operation = "LDR";
instruction->type = ARM_LDRSH;
suffix = "SH";
}
else
{
operation = "LDR";
instruction->type = ARM_LDRSB;
suffix = "SB";
}
}
else /* there are no signed stores, so this is used to encode double-register load/stores */
{
suffix = "D";
if (H)
{
operation = "STR";
instruction->type = ARM_STRD;
}
else
{
operation = "LDR";
instruction->type = ARM_LDRD;
}
}
}
else /* unsigned */
{
suffix = "H";
if (L) /* load */
{
operation = "LDR";
instruction->type = ARM_LDRH;
}
else /* store */
{
operation = "STR";
instruction->type = ARM_STRH;
}
}
if (I) /* Immediate offset/index (#+-<offset_8>)*/
{
uint32_t offset_8 = ((opcode & 0xf00) >> 4) | (opcode & 0xf);
snprintf(offset, 32, "#%s0x%" PRIx32 "", (U) ? "" : "-", offset_8);
instruction->info.load_store.offset_mode = 0;
instruction->info.load_store.offset.offset = offset_8;
}
else /* Register offset/index (+-<Rm>) */
{
Rm = (opcode & 0xf);
snprintf(offset, 32, "%sr%i", (U) ? "" : "-", Rm);
instruction->info.load_store.offset_mode = 1;
instruction->info.load_store.offset.reg.Rm = Rm;
instruction->info.load_store.offset.reg.shift = 0x0;
instruction->info.load_store.offset.reg.shift_imm = 0x0;
}
if (P == 1)
{
if (W == 0) /* offset */
{
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, [r%i, %s]",
address, opcode, operation, COND(opcode), suffix,
Rd, Rn, offset);
instruction->info.load_store.index_mode = 0;
}
else /* pre-indexed */
{
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, [r%i, %s]!",
address, opcode, operation, COND(opcode), suffix,
Rd, Rn, offset);
instruction->info.load_store.index_mode = 1;
}
}
else /* post-indexed */
{
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, [r%i], %s",
address, opcode, operation, COND(opcode), suffix,
Rd, Rn, offset);
instruction->info.load_store.index_mode = 2;
}
return ERROR_OK;
}
/* Load/store multiples instructions */
int evaluate_ldm_stm(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
{
char *addressing_mode;
char *mnemonic;
char reg_list[69];
char *reg_list_p;
int i;
int first_reg = 1;
P = (opcode & 0x01000000) >> 24;
U = (opcode & 0x00800000) >> 23;
S = (opcode & 0x00400000) >> 22;
W = (opcode & 0x00200000) >> 21;
L = (opcode & 0x00100000) >> 20;
register_list = (opcode & 0xffff);
Rn = (opcode & 0xf0000) >> 16;
instruction->info.load_store_multiple.Rn = Rn;
instruction->info.load_store_multiple.register_list = register_list;
instruction->info.load_store_multiple.S = S;
instruction->info.load_store_multiple.W = W;
if (L)
{
instruction->type = ARM_LDM;
mnemonic = "LDM";
}
else
{
instruction->type = ARM_STM;
mnemonic = "STM";
}
if (P)
{
if (U)
{
instruction->info.load_store_multiple.addressing_mode = 1;
addressing_mode = "IB";
else
{
instruction->info.load_store_multiple.addressing_mode = 3;
addressing_mode = "DB";
}
else
{
if (U)
{
instruction->info.load_store_multiple.addressing_mode = 0;
/* "IA" is the default in UAL syntax */
addressing_mode = "";
else
{
instruction->info.load_store_multiple.addressing_mode = 2;
addressing_mode = "DA";
}
reg_list_p = reg_list;
for (i = 0; i <= 15; i++)
{
if ((register_list >> i) & 1)
{
if (first_reg)
{
first_reg = 0;
reg_list_p += snprintf(reg_list_p, (reg_list + 69 - reg_list_p), "r%i", i);
}
else
{
reg_list_p += snprintf(reg_list_p, (reg_list + 69 - reg_list_p), ", r%i", i);
}
}
}
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i%s, {%s}%s",
address, opcode, mnemonic, COND(opcode), addressing_mode,
Rn, (W) ? "!" : "", reg_list, (S) ? "^" : "");
return ERROR_OK;
}
/* Multiplies, extra load/stores */
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)
{
/* Multiply (accumulate) */
if ((opcode & 0x0f800000) == 0x00000000)
{
Rm = opcode & 0xf;
Rs = (opcode & 0xf00) >> 8;
Rn = (opcode & 0xf000) >> 12;
Rd = (opcode & 0xf0000) >> 16;
S = (opcode & 0x00100000) >> 20;
/* examine A bit (accumulate) */
if (opcode & 0x00200000)
{
instruction->type = ARM_MLA;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tMLA%s%s r%i, r%i, r%i, r%i",
address, opcode, COND(opcode), (S) ? "S" : "", Rd, Rm, Rs, Rn);
}
else
{
instruction->type = ARM_MUL;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tMUL%s%s r%i, r%i, r%i",
address, opcode, COND(opcode), (S) ? "S" : "", Rd, Rm, Rs);
}
return ERROR_OK;
}
/* Multiply (accumulate) long */
if ((opcode & 0x0f800000) == 0x00800000)
{
char* mnemonic = NULL;
Rm = opcode & 0xf;
Rs = (opcode & 0xf00) >> 8;
RdHi = (opcode & 0xf000) >> 12;
RdLow = (opcode & 0xf0000) >> 16;
S = (opcode & 0x00100000) >> 20;
switch ((opcode & 0x00600000) >> 21)
{
case 0x0:
instruction->type = ARM_UMULL;
mnemonic = "UMULL";
break;
case 0x1:
instruction->type = ARM_UMLAL;
mnemonic = "UMLAL";
break;
case 0x2:
instruction->type = ARM_SMULL;
mnemonic = "SMULL";
break;
case 0x3:
instruction->type = ARM_SMLAL;
mnemonic = "SMLAL";
break;
}
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s%s r%i, r%i, r%i, r%i",
address, opcode, mnemonic, COND(opcode), (S) ? "S" : "",
RdLow, RdHi, Rm, Rs);
return ERROR_OK;
}
/* Swap/swap byte */
if ((opcode & 0x0f800000) == 0x01000000)
{
Rm = opcode & 0xf;
Rd = (opcode & 0xf000) >> 12;
Rn = (opcode & 0xf0000) >> 16;
/* examine B flag */
instruction->type = (opcode & 0x00400000) ? ARM_SWPB : ARM_SWP;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s r%i, r%i, [r%i]",
address, opcode, (opcode & 0x00400000) ? "SWPB" : "SWP", COND(opcode), Rd, Rm, Rn);
return ERROR_OK;
}
}
return evaluate_misc_load_store(opcode, address, instruction);
}
int evaluate_mrs_msr(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
{
int R = (opcode & 0x00400000) >> 22;
char *PSR = (R) ? "SPSR" : "CPSR";
/* Move register to status register (MSR) */
if (opcode & 0x00200000)
{
instruction->type = ARM_MSR;
/* immediate variant */
if (opcode & 0x02000000)
{
uint8_t immediate = (opcode & 0xff);
uint8_t rotate = (opcode & 0xf00);
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tMSR%s %s_%s%s%s%s, 0x%8.8" PRIx32 ,
address, opcode, COND(opcode), PSR,
(opcode & 0x10000) ? "c" : "",
(opcode & 0x20000) ? "x" : "",
(opcode & 0x40000) ? "s" : "",
(opcode & 0x80000) ? "f" : "",
ror(immediate, (rotate * 2))
}
else /* register variant */
{
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tMSR%s %s_%s%s%s%s, r%i",
address, opcode, COND(opcode), PSR,
(opcode & 0x10000) ? "c" : "",
(opcode & 0x20000) ? "x" : "",
(opcode & 0x40000) ? "s" : "",
(opcode & 0x80000) ? "f" : "",
Rm
}
}
else /* Move status register to register (MRS) */
{
instruction->type = ARM_MRS;
Rd = (opcode & 0x0000f000) >> 12;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tMRS%s r%i, %s",
address, opcode, COND(opcode), Rd, PSR);
}
return ERROR_OK;
}
/* Miscellaneous instructions */
int evaluate_misc_instr(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
{
/* MRS/MSR */
if ((opcode & 0x000000f0) == 0x00000000)
{
evaluate_mrs_msr(opcode, address, instruction);
}
/* BX */
if ((opcode & 0x006000f0) == 0x00200010)
{
instruction->type = ARM_BX;
Rm = opcode & 0xf;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tBX%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)
{
instruction->type = ARM_CLZ;
Rm = opcode & 0xf;
Rd = (opcode & 0xf000) >> 12;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tCLZ%s r%i, r%i",
address, opcode, COND(opcode), Rd, Rm);
}
/* BLX(2) */
if ((opcode & 0x006000f0) == 0x00200030)
{
instruction->type = ARM_BLX;
Rm = opcode & 0xf;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tBLX%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;
}
/* Enhanced DSP add/subtracts */
if ((opcode & 0x0000000f0) == 0x00000050)
{
char *mnemonic = NULL;
Rm = opcode & 0xf;
Rd = (opcode & 0xf000) >> 12;
Rn = (opcode & 0xf0000) >> 16;
switch ((opcode & 0x00600000) >> 21)
{
case 0x0:
instruction->type = ARM_QADD;
mnemonic = "QADD";
break;
case 0x1:
instruction->type = ARM_QSUB;
mnemonic = "QSUB";
break;
case 0x2:
instruction->type = ARM_QDADD;
mnemonic = "QDADD";
break;
case 0x3:
instruction->type = ARM_QDSUB;
mnemonic = "QDSUB";
break;
}
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\t%s%s r%i, r%i, r%i",
address, opcode, mnemonic, COND(opcode), Rd, Rm, Rn);
}
/* Software breakpoints */
if ((opcode & 0x0000000f0) == 0x00000070)
{
instruction->type = ARM_BKPT;
immediate = ((opcode & 0x000fff00) >> 4) | (opcode & 0xf);
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tBKPT 0x%4.4" PRIx32 "",
}
/* Enhanced DSP multiplies */
if ((opcode & 0x000000090) == 0x00000080)
{
int x = (opcode & 0x20) >> 5;
int y = (opcode & 0x40) >> 6;
if ((opcode & 0x00600000) == 0x00000000)
{
instruction->type = ARM_SMLAxy;
Rd = (opcode & 0xf0000) >> 16;
Rm = (opcode & 0xf);
Rs = (opcode & 0xf00) >> 8;
Rn = (opcode & 0xf000) >> 12;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSMLA%s%s%s r%i, r%i, r%i, r%i",
address, opcode, (x) ? "T" : "B", (y) ? "T" : "B", COND(opcode),
Rd, Rm, Rs, Rn);
}
if ((opcode & 0x00600000) == 0x00400000)
{
instruction->type = ARM_SMLAxy;
RdHi = (opcode & 0xf0000) >> 16;
RdLow = (opcode & 0xf000) >> 12;
Rm = (opcode & 0xf);
Rs = (opcode & 0xf00) >> 8;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSMLA%s%s%s r%i, r%i, r%i, r%i",
address, opcode, (x) ? "T" : "B", (y) ? "T" : "B", COND(opcode),
RdLow, RdHi, Rm, Rs);
}
if (((opcode & 0x00600000) == 0x00100000) && (x == 0))
{
instruction->type = ARM_SMLAWy;
Rd = (opcode & 0xf0000) >> 16;
Rm = (opcode & 0xf);
Rs = (opcode & 0xf00) >> 8;
Rn = (opcode & 0xf000) >> 12;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSMLAW%s%s r%i, r%i, r%i, r%i",
address, opcode, (y) ? "T" : "B", COND(opcode),
Rd, Rm, Rs, Rn);
}
if ((opcode & 0x00600000) == 0x00300000)
{
instruction->type = ARM_SMULxy;
Rd = (opcode & 0xf0000) >> 16;
Rm = (opcode & 0xf);
Rs = (opcode & 0xf00) >> 8;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSMULW%s%s%s r%i, r%i, r%i",
address, opcode, (x) ? "T" : "B", (y) ? "T" : "B", COND(opcode),
Rd, Rm, Rs);
}
if (((opcode & 0x00600000) == 0x00100000) && (x == 1))
{
instruction->type = ARM_SMULWy;
Rd = (opcode & 0xf0000) >> 16;
Rm = (opcode & 0xf);
Rs = (opcode & 0xf00) >> 8;
snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSMULW%s%s r%i, r%i, r%i",
address, opcode, (y) ? "T" : "B", COND(opcode),
Rd, Rm, Rs);
}
}
return ERROR_OK;
}
int evaluate_data_proc(uint32_t opcode, uint32_t address, arm_instruction_t *instruction)
{
char *mnemonic = NULL;
char shifter_operand[32];
I = (opcode & 0x02000000) >> 25;
op = (opcode & 0x01e00000) >> 21;
S = (opcode & 0x00100000) >> 20;
Rd = (opcode & 0xf000) >> 12;
Rn = (opcode & 0xf0000) >> 16;
instruction->info.data_proc.Rd = Rd;
instruction->info.data_proc.Rn = Rn;
instruction->info.data_proc.S = S;