X-Git-Url: https://review.openocd.org/gitweb?a=blobdiff_plain;f=src%2Ftarget%2Farm_disassembler.c;h=321f56cb5892a377c78d51f725b4833ba6bf6a68;hb=86173cdbddde781b19ac630602f2d450a59b32b5;hp=090241027b399dc46c89444a67484c143a20ac7c;hpb=82d2633b5f550115e9e7c7d0520babb6680aa38f;p=openocd.git diff --git a/src/target/arm_disassembler.c b/src/target/arm_disassembler.c index 090241027b..321f56cb58 100644 --- a/src/target/arm_disassembler.c +++ b/src/target/arm_disassembler.c @@ -22,10 +22,8 @@ #endif #include "arm_disassembler.h" - #include "log.h" -#include /* textual represenation of the condition field */ /* ALways (default) is ommitted (empty string) */ @@ -57,7 +55,7 @@ int evaluate_pld(u32 opcode, u32 address, arm_instruction_t *instruction) return ERROR_OK; } - ERROR("should never reach this point"); + LOG_ERROR("should never reach this point"); return -1; } @@ -104,7 +102,7 @@ int evaluate_blx_imm(u32 opcode, u32 address, arm_instruction_t *instruction) int evaluate_b_bl(u32 opcode, u32 address, arm_instruction_t *instruction) { - u8 L; + uint8_t L; u32 immediate; int offset; u32 target_address; @@ -141,12 +139,12 @@ int evaluate_b_bl(u32 opcode, u32 address, arm_instruction_t *instruction) /* both normal and extended instruction space (condition field b1111) */ int evaluate_ldc_stc_mcrr_mrrc(u32 opcode, u32 address, arm_instruction_t *instruction) { - u8 cp_num = (opcode & 0xf00) >> 8; + uint8_t cp_num = (opcode & 0xf00) >> 8; /* MCRR or MRRC */ if (((opcode & 0x0ff00000) == 0x0c400000) || ((opcode & 0x0ff00000) == 0x0c400000)) { - u8 cp_opcode, Rd, Rn, CRm; + uint8_t cp_opcode, Rd, Rn, CRm; char *mnemonic; cp_opcode = (opcode & 0xf0) >> 4; @@ -173,8 +171,8 @@ int evaluate_ldc_stc_mcrr_mrrc(u32 opcode, u32 address, arm_instruction_t *instr } else /* LDC or STC */ { - u8 CRd, Rn, offset; - u8 U, N; + uint8_t CRd, Rn, offset; + uint8_t U, N; char *mnemonic; char addressing_mode[32]; @@ -223,7 +221,7 @@ int evaluate_cdp_mcr_mrc(u32 opcode, u32 address, arm_instruction_t *instruction { char* cond; char* mnemonic; - u8 cp_num, opcode_1, CRd_Rd, CRn, CRm, opcode_2; + uint8_t cp_num, opcode_1, CRd_Rd, CRn, CRm, opcode_2; cond = ((opcode & 0xf0000000) == 0xf0000000) ? "2" : COND(opcode); cp_num = (opcode & 0xf00) >> 8; @@ -270,8 +268,8 @@ int evaluate_cdp_mcr_mrc(u32 opcode, u32 address, arm_instruction_t *instruction /* Load/store instructions */ int evaluate_load_store(u32 opcode, u32 address, arm_instruction_t *instruction) { - u8 I, P, U, B, W, L; - u8 Rn, Rd; + 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]; @@ -343,15 +341,18 @@ int evaluate_load_store(u32 opcode, u32 address, arm_instruction_t *instruction) if (!I) /* #+- */ { u32 offset_12 = (opcode & 0xfff); - snprintf(offset, 32, "#%s0x%x", (U) ? "" : "-", offset_12); + if (offset_12) + snprintf(offset, 32, ", #%s0x%x", (U) ? "" : "-", offset_12); + else + snprintf(offset, 32, "%s", ""); instruction->info.load_store.offset_mode = 0; instruction->info.load_store.offset.offset = offset_12; } else /* either +- or +-, , # */ { - u8 shift_imm, shift; - u8 Rm; + uint8_t shift_imm, shift; + uint8_t Rm; shift_imm = (opcode & 0xf80) >> 7; shift = (opcode & 0x60) >> 5; @@ -376,26 +377,26 @@ int evaluate_load_store(u32 opcode, u32 address, arm_instruction_t *instruction) if ((shift_imm == 0x0) && (shift == 0x0)) /* +- */ { - snprintf(offset, 32, "%sr%i", (U) ? "" : "-", Rm); + snprintf(offset, 32, ", %sr%i", (U) ? "" : "-", Rm); } else /* +-, , # */ { switch (shift) { case 0x0: /* LSL */ - snprintf(offset, 32, "%sr%i, LSL #0x%x", (U) ? "" : "-", Rm, 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); + 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); + 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); + snprintf(offset, 32, ", %sr%i, ROR #0x%x", (U) ? "" : "-", Rm, shift_imm); break; case 0x4: /* RRX */ - snprintf(offset, 32, "%sr%i, RRX", (U) ? "" : "-", Rm); + snprintf(offset, 32, ", %sr%i, RRX", (U) ? "" : "-", Rm); break; } } @@ -405,7 +406,7 @@ int evaluate_load_store(u32 opcode, u32 address, arm_instruction_t *instruction) { if (W == 0) /* offset */ { - snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i, %s]", + snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i%s]", address, opcode, operation, COND(opcode), suffix, Rd, Rn, offset); @@ -413,7 +414,7 @@ int evaluate_load_store(u32 opcode, u32 address, arm_instruction_t *instruction) } else /* pre-indexed */ { - snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i, %s]!", + snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i%s]!", address, opcode, operation, COND(opcode), suffix, Rd, Rn, offset); @@ -422,7 +423,7 @@ int evaluate_load_store(u32 opcode, u32 address, arm_instruction_t *instruction) } else /* post-indexed */ { - snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i], %s", + snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i]%s", address, opcode, operation, COND(opcode), suffix, Rd, Rn, offset); @@ -435,8 +436,8 @@ int evaluate_load_store(u32 opcode, u32 address, arm_instruction_t *instruction) /* Miscellaneous load/store instructions */ int evaluate_misc_load_store(u32 opcode, u32 address, arm_instruction_t *instruction) { - u8 P, U, I, W, L, S, H; - u8 Rn, Rd; + 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]; @@ -518,7 +519,7 @@ int evaluate_misc_load_store(u32 opcode, u32 address, arm_instruction_t *instruc } else /* Register offset/index (+-) */ { - u8 Rm; + uint8_t Rm; Rm = (opcode & 0xf); snprintf(offset, 32, "%sr%i", (U) ? "" : "-", Rm); @@ -562,7 +563,7 @@ int evaluate_misc_load_store(u32 opcode, u32 address, arm_instruction_t *instruc /* Load/store multiples instructions */ int evaluate_ldm_stm(u32 opcode, u32 address, arm_instruction_t *instruction) { - u8 P, U, S, W, L, Rn; + uint8_t P, U, S, W, L, Rn; u32 register_list; char *addressing_mode; char *mnemonic; @@ -655,7 +656,7 @@ int evaluate_mul_and_extra_ld_st(u32 opcode, u32 address, arm_instruction_t *ins /* Multiply (accumulate) */ if ((opcode & 0x0f800000) == 0x00000000) { - u8 Rm, Rs, Rn, Rd, S; + uint8_t Rm, Rs, Rn, Rd, S; Rm = opcode & 0xf; Rs = (opcode & 0xf00) >> 8; Rn = (opcode & 0xf000) >> 12; @@ -682,8 +683,8 @@ int evaluate_mul_and_extra_ld_st(u32 opcode, u32 address, arm_instruction_t *ins /* Multiply (accumulate) long */ if ((opcode & 0x0f800000) == 0x00800000) { - char* mnemonic; - u8 Rm, Rs, RdHi, RdLow, S; + char* mnemonic = NULL; + uint8_t Rm, Rs, RdHi, RdLow, S; Rm = opcode & 0xf; Rs = (opcode & 0xf00) >> 8; RdHi = (opcode & 0xf000) >> 12; @@ -720,7 +721,7 @@ int evaluate_mul_and_extra_ld_st(u32 opcode, u32 address, arm_instruction_t *ins /* Swap/swap byte */ if ((opcode & 0x0f800000) == 0x01000000) { - u8 Rm, Rd, Rn; + uint8_t Rm, Rd, Rn; Rm = opcode & 0xf; Rd = (opcode & 0xf000) >> 12; Rn = (opcode & 0xf0000) >> 16; @@ -751,8 +752,8 @@ int evaluate_mrs_msr(u32 opcode, u32 address, arm_instruction_t *instruction) /* immediate variant */ if (opcode & 0x02000000) { - u8 immediate = (opcode & 0xff); - u8 rotate = (opcode & 0xf00); + uint8_t immediate = (opcode & 0xff); + uint8_t rotate = (opcode & 0xf00); snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tMSR%s %s_%s%s%s%s, 0x%8.8x", address, opcode, COND(opcode), PSR, @@ -765,7 +766,7 @@ int evaluate_mrs_msr(u32 opcode, u32 address, arm_instruction_t *instruction) } else /* register variant */ { - u8 Rm = opcode & 0xf; + uint8_t Rm = opcode & 0xf; snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tMSR%s %s_%s%s%s%s, r%i", address, opcode, COND(opcode), PSR, (opcode & 0x10000) ? "c" : "", @@ -779,7 +780,7 @@ int evaluate_mrs_msr(u32 opcode, u32 address, arm_instruction_t *instruction) } else /* Move status register to register (MRS) */ { - u8 Rd; + uint8_t Rd; instruction->type = ARM_MRS; Rd = (opcode & 0x0000f000) >> 12; @@ -803,7 +804,7 @@ int evaluate_misc_instr(u32 opcode, u32 address, arm_instruction_t *instruction) /* BX */ if ((opcode & 0x006000f0) == 0x00200010) { - u8 Rm; + uint8_t Rm; instruction->type = ARM_BX; Rm = opcode & 0xf; @@ -815,9 +816,9 @@ int evaluate_misc_instr(u32 opcode, u32 address, arm_instruction_t *instruction) } /* CLZ */ - if ((opcode & 0x0060000f0) == 0x00300010) + if ((opcode & 0x006000f0) == 0x00600010) { - u8 Rm, Rd; + uint8_t Rm, Rd; instruction->type = ARM_CLZ; Rm = opcode & 0xf; Rd = (opcode & 0xf000) >> 12; @@ -826,22 +827,25 @@ int evaluate_misc_instr(u32 opcode, u32 address, arm_instruction_t *instruction) address, opcode, COND(opcode), Rd, Rm); } - /* BLX */ - if ((opcode & 0x0060000f0) == 0x00200030) + /* BLX(2) */ + if ((opcode & 0x006000f0) == 0x00200030) { - u8 Rm; + uint8_t Rm; instruction->type = ARM_BLX; Rm = opcode & 0xf; snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\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) { - u8 Rm, Rd, Rn; - char *mnemonic; + uint8_t Rm, Rd, Rn; + char *mnemonic = NULL; Rm = opcode & 0xf; Rd = (opcode & 0xf000) >> 12; Rn = (opcode & 0xf0000) >> 16; @@ -890,7 +894,7 @@ int evaluate_misc_instr(u32 opcode, u32 address, arm_instruction_t *instruction) /* SMLA */ if ((opcode & 0x00600000) == 0x00000000) { - u8 Rd, Rm, Rs, Rn; + uint8_t Rd, Rm, Rs, Rn; instruction->type = ARM_SMLAxy; Rd = (opcode & 0xf0000) >> 16; Rm = (opcode & 0xf); @@ -905,7 +909,7 @@ int evaluate_misc_instr(u32 opcode, u32 address, arm_instruction_t *instruction) /* SMLAL */ if ((opcode & 0x00600000) == 0x00400000) { - u8 RdLow, RdHi, Rm, Rs; + uint8_t RdLow, RdHi, Rm, Rs; instruction->type = ARM_SMLAxy; RdHi = (opcode & 0xf0000) >> 16; RdLow = (opcode & 0xf000) >> 12; @@ -920,7 +924,7 @@ int evaluate_misc_instr(u32 opcode, u32 address, arm_instruction_t *instruction) /* SMLAW */ if (((opcode & 0x00600000) == 0x00100000) && (x == 0)) { - u8 Rd, Rm, Rs, Rn; + uint8_t Rd, Rm, Rs, Rn; instruction->type = ARM_SMLAWy; Rd = (opcode & 0xf0000) >> 16; Rm = (opcode & 0xf); @@ -935,7 +939,7 @@ int evaluate_misc_instr(u32 opcode, u32 address, arm_instruction_t *instruction) /* SMUL */ if ((opcode & 0x00600000) == 0x00300000) { - u8 Rd, Rm, Rs; + uint8_t Rd, Rm, Rs; instruction->type = ARM_SMULxy; Rd = (opcode & 0xf0000) >> 16; Rm = (opcode & 0xf); @@ -949,7 +953,7 @@ int evaluate_misc_instr(u32 opcode, u32 address, arm_instruction_t *instruction) /* SMULW */ if (((opcode & 0x00600000) == 0x00100000) && (x == 1)) { - u8 Rd, Rm, Rs; + uint8_t Rd, Rm, Rs; instruction->type = ARM_SMULWy; Rd = (opcode & 0xf0000) >> 16; Rm = (opcode & 0xf); @@ -966,8 +970,8 @@ int evaluate_misc_instr(u32 opcode, u32 address, arm_instruction_t *instruction) int evaluate_data_proc(u32 opcode, u32 address, arm_instruction_t *instruction) { - u8 I, op, S, Rn, Rd; - char *mnemonic; + uint8_t I, op, S, Rn, Rd; + char *mnemonic = NULL; char shifter_operand[32]; I = (opcode & 0x02000000) >> 25; @@ -1051,8 +1055,8 @@ int evaluate_data_proc(u32 opcode, u32 address, arm_instruction_t *instruction) if (I) /* immediate shifter operand (#)*/ { - u8 immed_8 = opcode & 0xff; - u8 rotate_imm = (opcode & 0xf00) >> 8; + uint8_t immed_8 = opcode & 0xff; + uint8_t rotate_imm = (opcode & 0xf00) >> 8; u32 immediate; immediate = ror(immed_8, rotate_imm * 2); @@ -1064,13 +1068,13 @@ int evaluate_data_proc(u32 opcode, u32 address, arm_instruction_t *instruction) } else /* register-based shifter operand */ { - u8 shift, Rm; + uint8_t shift, Rm; shift = (opcode & 0x60) >> 5; Rm = (opcode & 0xf); if ((opcode & 0x10) != 0x10) /* Immediate shifts ("" or ", #") */ { - u8 shift_imm; + uint8_t shift_imm; shift_imm = (opcode & 0xf80) >> 7; instruction->info.data_proc.variant = 1; @@ -1078,6 +1082,18 @@ int evaluate_data_proc(u32 opcode, u32 address, arm_instruction_t *instruction) instruction->info.data_proc.shifter_operand.immediate_shift.shift_imm = shift_imm; instruction->info.data_proc.shifter_operand.immediate_shift.shift = shift; + /* 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; + if ((shift_imm == 0x0) && (shift == 0x0)) { snprintf(shifter_operand, 32, "r%i", Rm); @@ -1090,28 +1106,25 @@ int evaluate_data_proc(u32 opcode, u32 address, arm_instruction_t *instruction) } else if (shift == 0x1) /* LSR */ { - if (shift_imm == 0x0) - shift_imm = 0x32; snprintf(shifter_operand, 32, "r%i, LSR #0x%x", Rm, shift_imm); } else if (shift == 0x2) /* ASR */ { - if (shift_imm == 0x0) - shift_imm = 0x32; snprintf(shifter_operand, 32, "r%i, ASR #0x%x", Rm, shift_imm); } - else if (shift == 0x3) /* ROR or RRX */ + else if (shift == 0x3) /* ROR */ { - if (shift_imm == 0x0) /* RRX */ - snprintf(shifter_operand, 32, "r%i, RRX", Rm); - else - snprintf(shifter_operand, 32, "r%i, ROR #0x%x", Rm, shift_imm); + snprintf(shifter_operand, 32, "r%i, ROR #0x%x", Rm, shift_imm); + } + else if (shift == 0x4) /* RRX */ + { + snprintf(shifter_operand, 32, "r%i, RRX", Rm); } } } else /* Register shifts (", ") */ { - u8 Rs = (opcode & 0xf00) >> 8; + uint8_t Rs = (opcode & 0xf00) >> 8; instruction->info.data_proc.variant = 2; instruction->info.data_proc.shifter_operand.register_shift.Rm = Rm; @@ -1130,7 +1143,7 @@ int evaluate_data_proc(u32 opcode, u32 address, arm_instruction_t *instruction) { snprintf(shifter_operand, 32, "r%i, ASR r%i", Rm, Rs); } - else if (shift == 0x3) /* ROR or RRX */ + else if (shift == 0x3) /* ROR */ { snprintf(shifter_operand, 32, "r%i, ROR r%i", Rm, Rs); } @@ -1145,7 +1158,10 @@ int evaluate_data_proc(u32 opcode, u32 address, arm_instruction_t *instruction) } else if ((op == 0xd) || (op == 0xf)) /* {}{S} , */ { - snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, %s", + if (opcode==0xe1a00000) /* print MOV r0,r0 as NOP */ + snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tNOP",address, opcode); + else + snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, %s", address, opcode, mnemonic, COND(opcode), (S) ? "S" : "", Rd, shifter_operand); } @@ -1159,7 +1175,7 @@ int evaluate_data_proc(u32 opcode, u32 address, arm_instruction_t *instruction) return ERROR_OK; } -int evaluate_opcode(u32 opcode, u32 address, arm_instruction_t *instruction) +int arm_evaluate_opcode(u32 opcode, u32 address, arm_instruction_t *instruction) { /* clear fields, to avoid confusion */ memset(instruction, 0, sizeof(arm_instruction_t)); @@ -1247,7 +1263,7 @@ int evaluate_opcode(u32 opcode, u32 address, arm_instruction_t *instruction) } /* catch opcodes with [27:25] = b011 */ - if ((opcode & 0x0e000000) == 0x04000000) + if ((opcode & 0x0e000000) == 0x06000000) { /* Undefined instruction */ if ((opcode & 0x00000010) == 0x00000010) @@ -1299,6 +1315,800 @@ int evaluate_opcode(u32 opcode, u32 address, arm_instruction_t *instruction) return evaluate_cdp_mcr_mrc(opcode, address, instruction); } - ERROR("should never reach this point"); + LOG_ERROR("should never reach this point"); return -1; } + +int evaluate_b_bl_blx_thumb(u16 opcode, u32 address, arm_instruction_t *instruction) +{ + u32 offset = opcode & 0x7ff; + u32 opc = (opcode >> 11) & 0x3; + u32 target_address; + char *mnemonic = NULL; + + /* sign extend 11-bit offset */ + if (((opc==0) || (opc==2)) && (offset & 0x00000400)) + offset = 0xfffff800 | offset; + + target_address = address + 4 + (offset<<1); + + switch(opc) + { + /* unconditional branch */ + case 0: + instruction->type = ARM_B; + mnemonic = "B"; + break; + /* BLX suffix */ + case 1: + instruction->type = ARM_BLX; + mnemonic = "BLX"; + break; + /* BL/BLX prefix */ + case 2: + instruction->type = ARM_UNKNOWN_INSTUCTION; + mnemonic = "prefix"; + target_address = offset<<12; + break; + /* BL suffix */ + case 3: + instruction->type = ARM_BL; + mnemonic = "BL"; + break; + } + /* TODO: deals correctly with dual opcodes BL/BLX ... */ + + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s 0x%8.8x", address, opcode,mnemonic, 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_add_sub_thumb(u16 opcode, u32 address, arm_instruction_t *instruction) +{ + uint8_t Rd = (opcode >> 0) & 0x7; + uint8_t Rn = (opcode >> 3) & 0x7; + uint8_t Rm_imm = (opcode >> 6) & 0x7; + u32 opc = opcode & (1<<9); + u32 reg_imm = opcode & (1<<10); + char *mnemonic; + + if (opc) + { + instruction->type = ARM_SUB; + mnemonic = "SUBS"; + } + else + { + instruction->type = ARM_ADD; + mnemonic = "ADDS"; + } + + instruction->info.data_proc.Rd = Rd; + instruction->info.data_proc.Rn = Rn; + instruction->info.data_proc.S = 1; + + if (reg_imm) + { + instruction->info.data_proc.variant = 0; /*immediate*/ + instruction->info.data_proc.shifter_operand.immediate.immediate = Rm_imm; + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s r%i, r%i, #%d", + address, opcode, mnemonic, Rd, Rn, Rm_imm); + } + else + { + instruction->info.data_proc.variant = 1; /*immediate shift*/ + instruction->info.data_proc.shifter_operand.immediate_shift.Rm = Rm_imm; + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s r%i, r%i, r%i", + address, opcode, mnemonic, Rd, Rn, Rm_imm); + } + + return ERROR_OK; +} + +int evaluate_shift_imm_thumb(u16 opcode, u32 address, arm_instruction_t *instruction) +{ + uint8_t Rd = (opcode >> 0) & 0x7; + uint8_t Rm = (opcode >> 3) & 0x7; + uint8_t imm = (opcode >> 6) & 0x1f; + uint8_t opc = (opcode >> 11) & 0x3; + char *mnemonic = NULL; + + switch(opc) + { + case 0: + instruction->type = ARM_MOV; + mnemonic = "LSLS"; + instruction->info.data_proc.shifter_operand.immediate_shift.shift = 0; + break; + case 1: + instruction->type = ARM_MOV; + mnemonic = "LSRS"; + instruction->info.data_proc.shifter_operand.immediate_shift.shift = 1; + break; + case 2: + instruction->type = ARM_MOV; + mnemonic = "ASRS"; + instruction->info.data_proc.shifter_operand.immediate_shift.shift = 2; + break; + } + + if ((imm==0) && (opc!=0)) + imm = 32; + + instruction->info.data_proc.Rd = Rd; + instruction->info.data_proc.Rn = -1; + instruction->info.data_proc.S = 1; + + instruction->info.data_proc.variant = 1; /*immediate_shift*/ + instruction->info.data_proc.shifter_operand.immediate_shift.Rm = Rm; + instruction->info.data_proc.shifter_operand.immediate_shift.shift_imm = imm; + + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s r%i, r%i, #0x%02x", + address, opcode, mnemonic, Rd, Rm, imm); + + return ERROR_OK; +} + +int evaluate_data_proc_imm_thumb(u16 opcode, u32 address, arm_instruction_t *instruction) +{ + uint8_t imm = opcode & 0xff; + uint8_t Rd = (opcode >> 8) & 0x7; + u32 opc = (opcode >> 11) & 0x3; + char *mnemonic = NULL; + + instruction->info.data_proc.Rd = Rd; + instruction->info.data_proc.Rn = Rd; + instruction->info.data_proc.S = 1; + instruction->info.data_proc.variant = 0; /*immediate*/ + instruction->info.data_proc.shifter_operand.immediate.immediate = imm; + + switch(opc) + { + case 0: + instruction->type = ARM_MOV; + mnemonic = "MOVS"; + instruction->info.data_proc.Rn = -1; + break; + case 1: + instruction->type = ARM_CMP; + mnemonic = "CMP"; + instruction->info.data_proc.Rd = -1; + break; + case 2: + instruction->type = ARM_ADD; + mnemonic = "ADDS"; + break; + case 3: + instruction->type = ARM_SUB; + mnemonic = "SUBS"; + break; + } + + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s r%i, #0x%02x", + address, opcode, mnemonic, Rd, imm); + + return ERROR_OK; +} + +int evaluate_data_proc_thumb(u16 opcode, u32 address, arm_instruction_t *instruction) +{ + uint8_t high_reg, op, Rm, Rd,H1,H2; + char *mnemonic = NULL; + + high_reg = (opcode & 0x0400) >> 10; + op = (opcode & 0x03C0) >> 6; + + Rd = (opcode & 0x0007); + Rm = (opcode & 0x0038) >> 3; + H1 = (opcode & 0x0080) >> 7; + H2 = (opcode & 0x0040) >> 6; + + instruction->info.data_proc.Rd = Rd; + instruction->info.data_proc.Rn = Rd; + instruction->info.data_proc.S = (!high_reg || (instruction->type == ARM_CMP)); + instruction->info.data_proc.variant = 1 /*immediate shift*/; + instruction->info.data_proc.shifter_operand.immediate_shift.Rm = Rm; + + if (high_reg) + { + Rd |= H1 << 3; + Rm |= H2 << 3; + op >>= 2; + + switch (op) + { + case 0x0: + instruction->type = ARM_ADD; + mnemonic = "ADD"; + break; + case 0x1: + instruction->type = ARM_CMP; + mnemonic = "CMP"; + break; + case 0x2: + instruction->type = ARM_MOV; + mnemonic = "MOV"; + break; + case 0x3: + if ((opcode & 0x7) == 0x0) + { + instruction->info.b_bl_bx_blx.reg_operand = Rm; + if (H1) + { + instruction->type = ARM_BLX; + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tBLX r%i", address, opcode, Rm); + } + else + { + instruction->type = ARM_BX; + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tBX r%i", address, opcode, Rm); + } + } + else + { + instruction->type = ARM_UNDEFINED_INSTRUCTION; + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tUNDEFINED INSTRUCTION", address, opcode); + } + return ERROR_OK; + break; + } + } + else + { + switch (op) + { + case 0x0: + instruction->type = ARM_AND; + mnemonic = "ANDS"; + break; + case 0x1: + instruction->type = ARM_EOR; + mnemonic = "EORS"; + break; + case 0x2: + instruction->type = ARM_MOV; + mnemonic = "LSLS"; + instruction->info.data_proc.variant = 2 /*register shift*/; + instruction->info.data_proc.shifter_operand.register_shift.shift = 0; + instruction->info.data_proc.shifter_operand.register_shift.Rm = Rd; + instruction->info.data_proc.shifter_operand.register_shift.Rs = Rm; + break; + case 0x3: + instruction->type = ARM_MOV; + mnemonic = "LSRS"; + instruction->info.data_proc.variant = 2 /*register shift*/; + instruction->info.data_proc.shifter_operand.register_shift.shift = 1; + instruction->info.data_proc.shifter_operand.register_shift.Rm = Rd; + instruction->info.data_proc.shifter_operand.register_shift.Rs = Rm; + break; + case 0x4: + instruction->type = ARM_MOV; + mnemonic = "ASRS"; + instruction->info.data_proc.variant = 2 /*register shift*/; + instruction->info.data_proc.shifter_operand.register_shift.shift = 2; + instruction->info.data_proc.shifter_operand.register_shift.Rm = Rd; + instruction->info.data_proc.shifter_operand.register_shift.Rs = Rm; + break; + case 0x5: + instruction->type = ARM_ADC; + mnemonic = "ADCS"; + break; + case 0x6: + instruction->type = ARM_SBC; + mnemonic = "SBCS"; + break; + case 0x7: + instruction->type = ARM_MOV; + mnemonic = "RORS"; + instruction->info.data_proc.variant = 2 /*register shift*/; + instruction->info.data_proc.shifter_operand.register_shift.shift = 3; + instruction->info.data_proc.shifter_operand.register_shift.Rm = Rd; + instruction->info.data_proc.shifter_operand.register_shift.Rs = Rm; + break; + case 0x8: + instruction->type = ARM_TST; + mnemonic = "TST"; + break; + case 0x9: + instruction->type = ARM_RSB; + mnemonic = "NEGS"; + instruction->info.data_proc.variant = 0 /*immediate*/; + instruction->info.data_proc.shifter_operand.immediate.immediate = 0; + instruction->info.data_proc.Rn = Rm; + break; + case 0xA: + instruction->type = ARM_CMP; + mnemonic = "CMP"; + break; + case 0xB: + instruction->type = ARM_CMN; + mnemonic = "CMN"; + break; + case 0xC: + instruction->type = ARM_ORR; + mnemonic = "ORRS"; + break; + case 0xD: + instruction->type = ARM_MUL; + mnemonic = "MULS"; + break; + case 0xE: + instruction->type = ARM_BIC; + mnemonic = "BICS"; + break; + case 0xF: + instruction->type = ARM_MVN; + mnemonic = "MVNS"; + break; + } + } + + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s r%i, r%i", + address, opcode, mnemonic, Rd, Rm); + + return ERROR_OK; +} + +int evaluate_load_literal_thumb(u16 opcode, u32 address, arm_instruction_t *instruction) +{ + u32 immediate; + uint8_t Rd = (opcode >> 8) & 0x7; + + instruction->type = ARM_LDR; + immediate = opcode & 0x000000ff; + + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tLDR r%i, [PC, #0x%x]", address, opcode, Rd, immediate*4); + + instruction->info.load_store.Rd = Rd; + instruction->info.load_store.Rn = 15 /*PC*/; + instruction->info.load_store.index_mode = 0; /*offset*/ + instruction->info.load_store.offset_mode = 0; /*immediate*/ + instruction->info.load_store.offset.offset = immediate*4; + + return ERROR_OK; +} + +int evaluate_load_store_reg_thumb(u16 opcode, u32 address, arm_instruction_t *instruction) +{ + uint8_t Rd = (opcode >> 0) & 0x7; + uint8_t Rn = (opcode >> 3) & 0x7; + uint8_t Rm = (opcode >> 6) & 0x7; + uint8_t opc = (opcode >> 9) & 0x7; + char *mnemonic = NULL; + + switch(opc) + { + case 0: + instruction->type = ARM_STR; + mnemonic = "STR"; + break; + case 1: + instruction->type = ARM_STRH; + mnemonic = "STRH"; + break; + case 2: + instruction->type = ARM_STRB; + mnemonic = "STRB"; + break; + case 3: + instruction->type = ARM_LDRSB; + mnemonic = "LDRSB"; + break; + case 4: + instruction->type = ARM_LDR; + mnemonic = "LDR"; + break; + case 5: + instruction->type = ARM_LDRH; + mnemonic = "LDRH"; + break; + case 6: + instruction->type = ARM_LDRB; + mnemonic = "LDRB"; + break; + case 7: + instruction->type = ARM_LDRSH; + mnemonic = "LDRSH"; + break; + } + + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s r%i, [r%i, r%i]", address, opcode, mnemonic, Rd, Rn, Rm); + + instruction->info.load_store.Rd = Rd; + instruction->info.load_store.Rn = Rn; + instruction->info.load_store.index_mode = 0; /*offset*/ + instruction->info.load_store.offset_mode = 1; /*register*/ + instruction->info.load_store.offset.reg.Rm = Rm; + + return ERROR_OK; +} + +int evaluate_load_store_imm_thumb(u16 opcode, u32 address, arm_instruction_t *instruction) +{ + u32 offset = (opcode >> 6) & 0x1f; + uint8_t Rd = (opcode >> 0) & 0x7; + uint8_t Rn = (opcode >> 3) & 0x7; + u32 L = opcode & (1<<11); + u32 B = opcode & (1<<12); + char *mnemonic; + char suffix = ' '; + u32 shift = 2; + + if (L) + { + instruction->type = ARM_LDR; + mnemonic = "LDR"; + } + else + { + instruction->type = ARM_STR; + mnemonic = "STR"; + } + + if ((opcode&0xF000)==0x8000) + { + suffix = 'H'; + shift = 1; + } + else if (B) + { + suffix = 'B'; + shift = 0; + } + + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s%c r%i, [r%i, #0x%x]", address, opcode, mnemonic, suffix, Rd, Rn, offset<info.load_store.Rd = Rd; + instruction->info.load_store.Rn = Rn; + instruction->info.load_store.index_mode = 0; /*offset*/ + instruction->info.load_store.offset_mode = 0; /*immediate*/ + instruction->info.load_store.offset.offset = offset<> 8) & 0x7; + u32 L = opcode & (1<<11); + char *mnemonic; + + if (L) + { + instruction->type = ARM_LDR; + mnemonic = "LDR"; + } + else + { + instruction->type = ARM_STR; + mnemonic = "STR"; + } + + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s r%i, [SP, #0x%x]", address, opcode, mnemonic, Rd, offset*4); + + instruction->info.load_store.Rd = Rd; + instruction->info.load_store.Rn = 13 /*SP*/; + instruction->info.load_store.index_mode = 0; /*offset*/ + instruction->info.load_store.offset_mode = 0; /*immediate*/ + instruction->info.load_store.offset.offset = offset*4; + + return ERROR_OK; +} + +int evaluate_add_sp_pc_thumb(u16 opcode, u32 address, arm_instruction_t *instruction) +{ + u32 imm = opcode & 0xff; + uint8_t Rd = (opcode >> 8) & 0x7; + uint8_t Rn; + u32 SP = opcode & (1<<11); + char *reg_name; + + instruction->type = ARM_ADD; + + if (SP) + { + reg_name = "SP"; + Rn = 13; + } + else + { + reg_name = "PC"; + Rn = 15; + } + + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tADD r%i, %s, #0x%x", address, opcode, Rd,reg_name, imm*4); + + instruction->info.data_proc.variant = 0 /* immediate */; + instruction->info.data_proc.Rd = Rd; + instruction->info.data_proc.Rn = Rn; + instruction->info.data_proc.shifter_operand.immediate.immediate = imm*4; + + return ERROR_OK; +} + +int evaluate_adjust_stack_thumb(u16 opcode, u32 address, arm_instruction_t *instruction) +{ + u32 imm = opcode & 0x7f; + uint8_t opc = opcode & (1<<7); + char *mnemonic; + + + if (opc) + { + instruction->type = ARM_SUB; + mnemonic = "SUB"; + } + else + { + instruction->type = ARM_ADD; + mnemonic = "ADD"; + } + + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s SP, #0x%x", address, opcode, mnemonic, imm*4); + + instruction->info.data_proc.variant = 0 /* immediate */; + instruction->info.data_proc.Rd = 13 /*SP*/; + instruction->info.data_proc.Rn = 13 /*SP*/; + instruction->info.data_proc.shifter_operand.immediate.immediate = imm*4; + + return ERROR_OK; +} + +int evaluate_breakpoint_thumb(u16 opcode, u32 address, arm_instruction_t *instruction) +{ + u32 imm = opcode & 0xff; + + instruction->type = ARM_BKPT; + + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tBKPT 0x%02x", address, opcode, imm); + + return ERROR_OK; +} + +int evaluate_load_store_multiple_thumb(u16 opcode, u32 address, arm_instruction_t *instruction) +{ + u32 reg_list = opcode & 0xff; + u32 L = opcode & (1<<11); + u32 R = opcode & (1<<8); + uint8_t Rn = (opcode >> 8) & 7; + uint8_t addr_mode = 0 /* IA */; + char reg_names[40]; + char *reg_names_p; + char *mnemonic; + char ptr_name[7] = ""; + int i; + + if ((opcode & 0xf000) == 0xc000) + { /* generic load/store multiple */ + if (L) + { + instruction->type = ARM_LDM; + mnemonic = "LDMIA"; + } + else + { + instruction->type = ARM_STM; + mnemonic = "STMIA"; + } + snprintf(ptr_name,7,"r%i!, ",Rn); + } + else + { /* push/pop */ + Rn = 13; /* SP */ + if (L) + { + instruction->type = ARM_LDM; + mnemonic = "POP"; + if (R) + reg_list |= (1<<15) /*PC*/; + } + else + { + instruction->type = ARM_STM; + mnemonic = "PUSH"; + addr_mode = 3; /*DB*/ + if (R) + reg_list |= (1<<14) /*LR*/; + } + } + + reg_names_p = reg_names; + for (i = 0; i <= 15; i++) + { + if (reg_list & (1<reg_names) + reg_names_p[-2] = '\0'; + else /* invalid op : no registers */ + reg_names[0] = '\0'; + + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\t%s %s{%s}", address, opcode, mnemonic, ptr_name,reg_names); + + instruction->info.load_store_multiple.register_list = reg_list; + instruction->info.load_store_multiple.Rn = Rn; + instruction->info.load_store_multiple.addressing_mode = addr_mode; + + return ERROR_OK; +} + +int evaluate_cond_branch_thumb(u16 opcode, u32 address, arm_instruction_t *instruction) +{ + u32 offset = opcode & 0xff; + uint8_t cond = (opcode >> 8) & 0xf; + u32 target_address; + + if (cond == 0xf) + { + instruction->type = ARM_SWI; + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tSWI 0x%02x", address, opcode, offset); + return ERROR_OK; + } + else if (cond == 0xe) + { + instruction->type = ARM_UNDEFINED_INSTRUCTION; + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tUNDEFINED INSTRUCTION", address, opcode); + return ERROR_OK; + } + + /* sign extend 8-bit offset */ + if (offset & 0x00000080) + offset = 0xffffff00 | offset; + + target_address = address + 4 + (offset<<1); + + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tB%s 0x%8.8x", address, opcode, + arm_condition_strings[cond], target_address); + + instruction->type = ARM_B; + instruction->info.b_bl_bx_blx.reg_operand = -1; + instruction->info.b_bl_bx_blx.target_address = target_address; + + return ERROR_OK; +} + +int thumb_evaluate_opcode(u16 opcode, u32 address, arm_instruction_t *instruction) +{ + /* clear fields, to avoid confusion */ + memset(instruction, 0, sizeof(arm_instruction_t)); + instruction->opcode = opcode; + + if ((opcode & 0xe000) == 0x0000) + { + /* add/substract register or immediate */ + if ((opcode & 0x1800) == 0x1800) + return evaluate_add_sub_thumb(opcode, address, instruction); + /* shift by immediate */ + else + return evaluate_shift_imm_thumb(opcode, address, instruction); + } + + /* Add/substract/compare/move immediate */ + if ((opcode & 0xe000) == 0x2000) + { + return evaluate_data_proc_imm_thumb(opcode, address, instruction); + } + + /* Data processing instructions */ + if ((opcode & 0xf800) == 0x4000) + { + return evaluate_data_proc_thumb(opcode, address, instruction); + } + + /* Load from literal pool */ + if ((opcode & 0xf800) == 0x4800) + { + return evaluate_load_literal_thumb(opcode, address, instruction); + } + + /* Load/Store register offset */ + if ((opcode & 0xf000) == 0x5000) + { + return evaluate_load_store_reg_thumb(opcode, address, instruction); + } + + /* Load/Store immediate offset */ + if (((opcode & 0xe000) == 0x6000) + ||((opcode & 0xf000) == 0x8000)) + { + return evaluate_load_store_imm_thumb(opcode, address, instruction); + } + + /* Load/Store from/to stack */ + if ((opcode & 0xf000) == 0x9000) + { + return evaluate_load_store_stack_thumb(opcode, address, instruction); + } + + /* Add to SP/PC */ + if ((opcode & 0xf000) == 0xa000) + { + return evaluate_add_sp_pc_thumb(opcode, address, instruction); + } + + /* Misc */ + if ((opcode & 0xf000) == 0xb000) + { + if ((opcode & 0x0f00) == 0x0000) + return evaluate_adjust_stack_thumb(opcode, address, instruction); + else if ((opcode & 0x0f00) == 0x0e00) + return evaluate_breakpoint_thumb(opcode, address, instruction); + else if ((opcode & 0x0600) == 0x0400) /* push pop */ + return evaluate_load_store_multiple_thumb(opcode, address, instruction); + else + { + instruction->type = ARM_UNDEFINED_INSTRUCTION; + snprintf(instruction->text, 128, "0x%8.8x\t0x%4.4x\tUNDEFINED INSTRUCTION", address, opcode); + return ERROR_OK; + } + } + + /* Load/Store multiple */ + if ((opcode & 0xf000) == 0xc000) + { + return evaluate_load_store_multiple_thumb(opcode, address, instruction); + } + + /* Conditional branch + SWI */ + if ((opcode & 0xf000) == 0xd000) + { + return evaluate_cond_branch_thumb(opcode, address, instruction); + } + + if ((opcode & 0xe000) == 0xe000) + { + /* Undefined instructions */ + if ((opcode & 0xf801) == 0xe801) + { + instruction->type = ARM_UNDEFINED_INSTRUCTION; + snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tUNDEFINED INSTRUCTION", address, opcode); + return ERROR_OK; + } + else + { /* Branch to offset */ + return evaluate_b_bl_blx_thumb(opcode, address, instruction); + } + } + + LOG_ERROR("should never reach this point (opcode=%04x)",opcode); + return -1; +} + +int arm_access_size(arm_instruction_t *instruction) +{ + if ((instruction->type == ARM_LDRB) + || (instruction->type == ARM_LDRBT) + || (instruction->type == ARM_LDRSB) + || (instruction->type == ARM_STRB) + || (instruction->type == ARM_STRBT)) + { + return 1; + } + else if ((instruction->type == ARM_LDRH) + || (instruction->type == ARM_LDRSH) + || (instruction->type == ARM_STRH)) + { + return 2; + } + else if ((instruction->type == ARM_LDR) + || (instruction->type == ARM_LDRT) + || (instruction->type == ARM_STR) + || (instruction->type == ARM_STRT)) + { + return 4; + } + else if ((instruction->type == ARM_LDRD) + || (instruction->type == ARM_STRD)) + { + return 8; + } + else + { + LOG_ERROR("BUG: instruction type %i isn't a load/store instruction", instruction->type); + return 0; + } +}