X-Git-Url: https://review.openocd.org/gitweb?a=blobdiff_plain;f=src%2Ftarget%2Farm_disassembler.c;h=2b854e7bfec4311cb2ccbef7efd144585c8ae750;hb=30b1bbceea7183559eae342b6228d8723dc72f2d;hp=94479261da11de1d0820c5c008cb4e64e97b7669;hpb=2e779198535580515dfa9c8bfe1f3fe08abdb84b;p=openocd.git diff --git a/src/target/arm_disassembler.c b/src/target/arm_disassembler.c index 94479261da..2b854e7bfe 100644 --- a/src/target/arm_disassembler.c +++ b/src/target/arm_disassembler.c @@ -21,6 +21,7 @@ #include "config.h" #endif +#include "target.h" #include "arm_disassembler.h" #include "log.h" @@ -63,7 +64,9 @@ int evaluate_swi(uint32_t opcode, uint32_t address, arm_instruction_t *instructi { instruction->type = ARM_SWI; - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSWI 0x%6.6" PRIx32 "", address, opcode, (opcode & 0xffffff)); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSVC %#6.6" PRIx32, + address, opcode, (opcode & 0xffffff)); return ERROR_OK; } @@ -614,7 +617,8 @@ int evaluate_ldm_stm(uint32_t opcode, uint32_t address, arm_instruction_t *instr if (U) { instruction->info.load_store_multiple.addressing_mode = 0; - addressing_mode = "IA"; + /* "IA" is the default in UAL syntax */ + addressing_mode = ""; } else { @@ -1180,6 +1184,7 @@ int arm_evaluate_opcode(uint32_t opcode, uint32_t address, arm_instruction_t *in /* clear fields, to avoid confusion */ memset(instruction, 0, sizeof(arm_instruction_t)); instruction->opcode = opcode; + instruction->instruction_size = 4; /* catch opcodes with condition field [31:28] = b1111 */ if ((opcode & 0xf0000000) == 0xf0000000) @@ -1356,9 +1361,15 @@ int evaluate_b_bl_blx_thumb(uint16_t opcode, uint32_t address, arm_instruction_t mnemonic = "BL"; break; } - /* TODO: deals correctly with dual opcodes BL/BLX ... */ - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\t%s 0x%8.8" PRIx32 , address, opcode,mnemonic, target_address); + /* TODO: deal correctly with dual opcode (prefixed) BL/BLX; + * these are effectively 32-bit instructions even in Thumb1. + * Might be simplest to always use the Thumb2 decoder. + */ + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\t%#8.8" PRIx32, + address, opcode, mnemonic, target_address); instruction->info.b_bl_bx_blx.reg_operand = -1; instruction->info.b_bl_bx_blx.target_address = target_address; @@ -1394,15 +1405,17 @@ int evaluate_add_sub_thumb(uint16_t opcode, uint32_t address, arm_instruction_t { instruction->info.data_proc.variant = 0; /*immediate*/ instruction->info.data_proc.shifter_operand.immediate.immediate = Rm_imm; - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\t%s r%i, r%i, #%d", - address, opcode, mnemonic, Rd, Rn, Rm_imm); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tr%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.8" PRIx32 "\t0x%4.4x\t%s r%i, r%i, r%i", - address, opcode, mnemonic, Rd, Rn, Rm_imm); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tr%i, r%i, r%i", + address, opcode, mnemonic, Rd, Rn, Rm_imm); } return ERROR_OK; @@ -1446,8 +1459,9 @@ int evaluate_shift_imm_thumb(uint16_t opcode, uint32_t address, arm_instruction_ 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.8" PRIx32 "\t0x%4.4x\t%s r%i, r%i, #0x%02x" , - address, opcode, mnemonic, Rd, Rm, imm); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tr%i, r%i, #%#2.2x" , + address, opcode, mnemonic, Rd, Rm, imm); return ERROR_OK; } @@ -1487,8 +1501,9 @@ int evaluate_data_proc_imm_thumb(uint16_t opcode, uint32_t address, arm_instruct break; } - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\t%s r%i, #0x%02x" , - address, opcode, mnemonic, Rd, imm); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tr%i, #%#2.2x", + address, opcode, mnemonic, Rd, imm); return ERROR_OK; } @@ -1497,6 +1512,7 @@ int evaluate_data_proc_thumb(uint16_t opcode, uint32_t address, arm_instruction_ { uint8_t high_reg, op, Rm, Rd,H1,H2; char *mnemonic = NULL; + bool nop = false; high_reg = (opcode & 0x0400) >> 10; op = (opcode & 0x03C0) >> 6; @@ -1531,6 +1547,8 @@ int evaluate_data_proc_thumb(uint16_t opcode, uint32_t address, arm_instruction_ case 0x2: instruction->type = ARM_MOV; mnemonic = "MOV"; + if (Rd == Rm) + nop = true; break; case 0x3: if ((opcode & 0x7) == 0x0) @@ -1539,18 +1557,28 @@ int evaluate_data_proc_thumb(uint16_t opcode, uint32_t address, arm_instruction_ if (H1) { instruction->type = ARM_BLX; - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\tBLX r%i", address, opcode, Rm); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 + " 0x%4.4x \tBLX\tr%i", + address, opcode, Rm); } else { instruction->type = ARM_BX; - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\tBX r%i", address, opcode, Rm); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 + " 0x%4.4x \tBX\tr%i", + address, opcode, Rm); } } else { instruction->type = ARM_UNDEFINED_INSTRUCTION; - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\tUNDEFINED INSTRUCTION", address, opcode); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 + " 0x%4.4x \t" + "UNDEFINED INSTRUCTION", + address, opcode); } return ERROR_OK; break; @@ -1646,12 +1674,25 @@ int evaluate_data_proc_thumb(uint16_t opcode, uint32_t address, arm_instruction_ } } - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\t%s r%i, r%i", + if (nop) + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tNOP\t\t\t" + "; (%s r%i, r%i)", + address, opcode, mnemonic, Rd, Rm); + else + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tr%i, r%i", address, opcode, mnemonic, Rd, Rm); return ERROR_OK; } +/* PC-relative data addressing is word-aligned even with Thumb */ +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) { uint32_t immediate; @@ -1659,14 +1700,19 @@ int evaluate_load_literal_thumb(uint16_t opcode, uint32_t address, arm_instructi instruction->type = ARM_LDR; immediate = opcode & 0x000000ff; - - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\tLDR r%i, [PC, #0x%" PRIx32 "]", address, opcode, Rd, immediate*4); + 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; + instruction->info.load_store.offset.offset = immediate; + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t" + "LDR\tr%i, [pc, #%#" PRIx32 "]\t; %#8.8" PRIx32, + address, opcode, Rd, immediate, + thumb_alignpc4(address) + immediate); return ERROR_OK; } @@ -1715,7 +1761,9 @@ int evaluate_load_store_reg_thumb(uint16_t opcode, uint32_t address, arm_instruc break; } - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\t%s r%i, [r%i, r%i]", address, opcode, mnemonic, Rd, Rn, Rm); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tr%i, [r%i, r%i]", + address, opcode, mnemonic, Rd, Rn, Rm); instruction->info.load_store.Rd = Rd; instruction->info.load_store.Rn = Rn; @@ -1759,7 +1807,9 @@ int evaluate_load_store_imm_thumb(uint16_t opcode, uint32_t address, arm_instruc shift = 0; } - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\t%s%c r%i, [r%i, #0x%" PRIx32 "]", address, opcode, mnemonic, suffix, Rd, Rn, offset << shift); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s%c\tr%i, [r%i, #%#" PRIx32 "]", + address, opcode, mnemonic, suffix, Rd, Rn, offset << shift); instruction->info.load_store.Rd = Rd; instruction->info.load_store.Rn = Rn; @@ -1788,7 +1838,9 @@ int evaluate_load_store_stack_thumb(uint16_t opcode, uint32_t address, arm_instr mnemonic = "STR"; } - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\t%s r%i, [SP, #0x%" PRIx32 "]", address, opcode, mnemonic, Rd, offset*4); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tr%i, [SP, #%#" PRIx32 "]", + address, opcode, mnemonic, Rd, offset*4); instruction->info.load_store.Rd = Rd; instruction->info.load_store.Rn = 13 /*SP*/; @@ -1820,7 +1872,9 @@ int evaluate_add_sp_pc_thumb(uint16_t opcode, uint32_t address, arm_instruction_ Rn = 15; } - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\tADD r%i, %s, #0x%" PRIx32 "", address, opcode, Rd,reg_name, imm*4); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tADD\tr%i, %s, #%#" PRIx32, + address, opcode, Rd, reg_name, imm * 4); instruction->info.data_proc.variant = 0 /* immediate */; instruction->info.data_proc.Rd = Rd; @@ -1848,7 +1902,9 @@ int evaluate_adjust_stack_thumb(uint16_t opcode, uint32_t address, arm_instructi mnemonic = "ADD"; } - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\t%s SP, #0x%" PRIx32 "", address, opcode, mnemonic, imm*4); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\tSP, #%#" PRIx32, + address, opcode, mnemonic, imm*4); instruction->info.data_proc.variant = 0 /* immediate */; instruction->info.data_proc.Rd = 13 /*SP*/; @@ -1864,7 +1920,9 @@ int evaluate_breakpoint_thumb(uint16_t opcode, uint32_t address, arm_instruction instruction->type = ARM_BKPT; - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\tBKPT 0x%02" PRIx32 "", address, opcode, imm); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tBKPT\t%#2.2" PRIx32 "", + address, opcode, imm); return ERROR_OK; } @@ -1887,12 +1945,12 @@ int evaluate_load_store_multiple_thumb(uint16_t opcode, uint32_t address, arm_in if (L) { instruction->type = ARM_LDM; - mnemonic = "LDMIA"; + mnemonic = "LDM"; } else { instruction->type = ARM_STM; - mnemonic = "STMIA"; + mnemonic = "STM"; } snprintf(ptr_name,7,"r%i!, ",Rn); } @@ -1925,9 +1983,11 @@ int evaluate_load_store_multiple_thumb(uint16_t opcode, uint32_t address, arm_in if (reg_names_p > reg_names) reg_names_p[-2] = '\0'; else /* invalid op : no registers */ - reg_names[0] = '\0'; + reg_names[0] = '\0'; - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\t%s %s{%s}", address, opcode, mnemonic, ptr_name,reg_names); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s\t%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; @@ -1945,13 +2005,17 @@ int evaluate_cond_branch_thumb(uint16_t opcode, uint32_t address, arm_instructio if (cond == 0xf) { instruction->type = ARM_SWI; - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\tSWI 0x%02" PRIx32 , address, opcode, offset); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tSVC\t%#2.2" PRIx32, + address, opcode, offset); return ERROR_OK; } else if (cond == 0xe) { instruction->type = ARM_UNDEFINED_INSTRUCTION; - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\tUNDEFINED INSTRUCTION", address, opcode); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tUNDEFINED INSTRUCTION", + address, opcode); return ERROR_OK; } @@ -1961,8 +2025,10 @@ int evaluate_cond_branch_thumb(uint16_t opcode, uint32_t address, arm_instructio target_address = address + 4 + (offset << 1); - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%4.4x\tB%s 0x%8.8" PRIx32 , address, opcode, - arm_condition_strings[cond], target_address); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tB%s\t%#8.8" PRIx32, + address, opcode, + arm_condition_strings[cond], target_address); instruction->type = ARM_B; instruction->info.b_bl_bx_blx.reg_operand = -1; @@ -1971,11 +2037,148 @@ int evaluate_cond_branch_thumb(uint16_t opcode, uint32_t address, arm_instructio return ERROR_OK; } +static int evaluate_cb_thumb(uint16_t opcode, uint32_t address, + arm_instruction_t *instruction) +{ + unsigned offset; + + /* added in Thumb2 */ + offset = (opcode >> 3) & 0x1f; + offset |= (opcode & 0x0200) >> 4; + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tCB%sZ\tr%d, %#8.8" PRIx32, + address, opcode, + (opcode & 0x0800) ? "N" : "", + opcode & 0x7, address + 4 + (offset << 1)); + + return ERROR_OK; +} + +static int evaluate_extend_thumb(uint16_t opcode, uint32_t address, + arm_instruction_t *instruction) +{ + /* added in ARMv6 */ + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%cXT%c\tr%d, r%d", + address, opcode, + (opcode & 0x0080) ? 'U' : 'S', + (opcode & 0x0040) ? 'B' : 'H', + opcode & 0x7, (opcode >> 3) & 0x7); + + return ERROR_OK; +} + +static int evaluate_cps_thumb(uint16_t opcode, uint32_t address, + arm_instruction_t *instruction) +{ + /* added in ARMv6 */ + if ((opcode & 0x0ff0) == 0x0650) + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tSETEND %s", + address, opcode, + (opcode & 0x80) ? "BE" : "LE"); + else /* ASSUME (opcode & 0x0fe0) == 0x0660 */ + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tCPSI%c\t%s%s%s", + address, opcode, + (opcode & 0x0010) ? 'D' : 'E', + (opcode & 0x0004) ? "A" : "", + (opcode & 0x0002) ? "I" : "", + (opcode & 0x0001) ? "F" : ""); + + return ERROR_OK; +} + +static int evaluate_byterev_thumb(uint16_t opcode, uint32_t address, + arm_instruction_t *instruction) +{ + char *suffix; + + /* added in ARMv6 */ + switch (opcode & 0x00c0) { + case 0: + suffix = ""; + break; + case 1: + suffix = "16"; + break; + default: + suffix = "SH"; + break; + } + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tREV%s\tr%d, r%d", + address, opcode, suffix, + opcode & 0x7, (opcode >> 3) & 0x7); + + return ERROR_OK; +} + +static int evaluate_hint_thumb(uint16_t opcode, uint32_t address, + arm_instruction_t *instruction) +{ + char *hint; + + switch ((opcode >> 4) & 0x0f) { + case 0: + hint = "NOP"; + break; + case 1: + hint = "YIELD"; + break; + case 2: + hint = "WFE"; + break; + case 3: + hint = "WFI"; + break; + case 4: + hint = "SEV"; + break; + default: + hint = "HINT (UNRECOGNIZED)"; + break; + } + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \t%s", + address, opcode, hint); + + return ERROR_OK; +} + +static int evaluate_ifthen_thumb(uint16_t opcode, uint32_t address, + arm_instruction_t *instruction) +{ + unsigned cond = (opcode >> 4) & 0x0f; + char *x = "", *y = "", *z = ""; + + if (opcode & 0x01) + z = (opcode & 0x02) ? "T" : "E"; + if (opcode & 0x03) + y = (opcode & 0x04) ? "T" : "E"; + if (opcode & 0x07) + x = (opcode & 0x08) ? "T" : "E"; + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tIT%s%s%s\t%s", + address, opcode, + x, y, z, arm_condition_strings[cond]); + + /* NOTE: strictly speaking, the next 1-4 instructions should + * now be displayed with the relevant conditional suffix... + */ + + return ERROR_OK; +} + int thumb_evaluate_opcode(uint16_t opcode, uint32_t address, arm_instruction_t *instruction) { /* clear fields, to avoid confusion */ memset(instruction, 0, sizeof(arm_instruction_t)); instruction->opcode = opcode; + instruction->instruction_size = 2; if ((opcode & 0xe000) == 0x0000) { @@ -2033,18 +2236,44 @@ int thumb_evaluate_opcode(uint16_t opcode, uint32_t address, arm_instruction_t * /* Misc */ if ((opcode & 0xf000) == 0xb000) { - if ((opcode & 0x0f00) == 0x0000) + switch ((opcode >> 8) & 0x0f) { + case 0x0: return evaluate_adjust_stack_thumb(opcode, address, instruction); - else if ((opcode & 0x0f00) == 0x0e00) + case 0x1: + case 0x3: + case 0x9: + case 0xb: + return evaluate_cb_thumb(opcode, address, instruction); + case 0x2: + return evaluate_extend_thumb(opcode, address, instruction); + case 0x4: + case 0x5: + case 0xc: + case 0xd: + return evaluate_load_store_multiple_thumb(opcode, address, + instruction); + case 0x6: + return evaluate_cps_thumb(opcode, address, instruction); + case 0xa: + if ((opcode & 0x00c0) == 0x0080) + break; + return evaluate_byterev_thumb(opcode, address, instruction); + case 0xe: 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.8" PRIx32 "\t0x%4.4x\tUNDEFINED INSTRUCTION", address, opcode); - return ERROR_OK; + case 0xf: + if (opcode & 0x000f) + return evaluate_ifthen_thumb(opcode, address, + instruction); + else + return evaluate_hint_thumb(opcode, address, + instruction); } + + instruction->type = ARM_UNDEFINED_INSTRUCTION; + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%4.4x \tUNDEFINED INSTRUCTION", + address, opcode); + return ERROR_OK; } /* Load/Store multiple */ @@ -2065,7 +2294,10 @@ int thumb_evaluate_opcode(uint16_t opcode, uint32_t address, arm_instruction_t * if ((opcode & 0xf801) == 0xe801) { instruction->type = ARM_UNDEFINED_INSTRUCTION; - snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8x\tUNDEFINED INSTRUCTION", address, opcode); + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%8.8x\t" + "UNDEFINED INSTRUCTION", + address, opcode); return ERROR_OK; } else @@ -2078,6 +2310,1141 @@ int thumb_evaluate_opcode(uint16_t opcode, uint32_t address, arm_instruction_t * return -1; } +static int t2ev_b_bl(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + unsigned offset; + unsigned b21 = 1 << 21; + unsigned b22 = 1 << 22; + + /* instead of combining two smaller 16-bit branch instructions, + * Thumb2 uses only one larger 32-bit instruction. + */ + offset = opcode & 0x7ff; + offset |= (opcode & 0x03ff0000) >> 5; + if (opcode & (1 << 26)) { + offset |= 0xff << 23; + if ((opcode & (1 << 11)) == 0) + b21 = 0; + if ((opcode & (1 << 13)) == 0) + b22 = 0; + } else { + if (opcode & (1 << 11)) + b21 = 0; + if (opcode & (1 << 13)) + b22 = 0; + } + offset |= b21; + offset |= b22; + + + address += 4; + address += offset << 1; + + instruction->type = (opcode & (1 << 14)) ? ARM_BL : ARM_B; + instruction->info.b_bl_bx_blx.reg_operand = -1; + instruction->info.b_bl_bx_blx.target_address = address; + sprintf(cp, "%s\t%#8.8" PRIx32, + (opcode & (1 << 14)) ? "BL" : "B.W", + address); + + return ERROR_OK; +} + +static int t2ev_cond_b(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + unsigned offset; + unsigned b17 = 1 << 17; + unsigned b18 = 1 << 18; + unsigned cond = (opcode >> 22) & 0x0f; + + offset = opcode & 0x7ff; + offset |= (opcode & 0x003f0000) >> 5; + if (opcode & (1 << 26)) { + offset |= 0xffff << 19; + if ((opcode & (1 << 11)) == 0) + b17 = 0; + if ((opcode & (1 << 13)) == 0) + b18 = 0; + } else { + if (opcode & (1 << 11)) + b17 = 0; + if (opcode & (1 << 13)) + b18 = 0; + } + offset |= b17; + offset |= b18; + + address += 4; + address += offset << 1; + + instruction->type = ARM_B; + instruction->info.b_bl_bx_blx.reg_operand = -1; + instruction->info.b_bl_bx_blx.target_address = address; + sprintf(cp, "B%s.W\t%#8.8" PRIx32, + arm_condition_strings[cond], + address); + + return ERROR_OK; +} + +static const char *special_name(int number) +{ + char *special = "(RESERVED)"; + + switch (number) { + case 0: + special = "apsr"; + break; + case 1: + special = "iapsr"; + break; + case 2: + special = "eapsr"; + break; + case 3: + special = "xpsr"; + break; + case 5: + special = "ipsr"; + break; + case 6: + special = "epsr"; + break; + case 7: + special = "iepsr"; + break; + case 8: + special = "msp"; + break; + case 9: + special = "psp"; + break; + case 16: + special = "primask"; + break; + case 17: + special = "basepri"; + break; + case 18: + special = "basepri_max"; + break; + case 19: + special = "faultmask"; + break; + case 20: + special = "control"; + break; + } + return special; +} + +static int t2ev_hint(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + const char *mnemonic; + + if (opcode & 0x0700) { + instruction->type = ARM_UNDEFINED_INSTRUCTION; + strcpy(cp, "UNDEFINED"); + return ERROR_OK; + } + + if (opcode & 0x00f0) { + sprintf(cp, "DBG\t#%d", (int) opcode & 0xf); + return ERROR_OK; + } + + switch (opcode & 0x0f) { + case 0: + mnemonic = "NOP.W"; + break; + case 1: + mnemonic = "YIELD.W"; + break; + case 2: + mnemonic = "WFE.W"; + break; + case 3: + mnemonic = "WFI.W"; + break; + case 4: + mnemonic = "SEV.W"; + break; + default: + mnemonic = "HINT.W (UNRECOGNIZED)"; + break; + } + strcpy(cp, mnemonic); + return ERROR_OK; +} + +static int t2ev_misc(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + const char *mnemonic; + + switch ((opcode >> 4) & 0x0f) { + case 2: + mnemonic = "CLREX"; + break; + case 4: + mnemonic = "DSB"; + break; + case 5: + mnemonic = "DMB"; + break; + case 6: + mnemonic = "ISB"; + break; + default: + return ERROR_INVALID_ARGUMENTS; + } + strcpy(cp, mnemonic); + return ERROR_OK; +} + +static int t2ev_b_misc(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + /* permanently undefined */ + if ((opcode & 0x07f07000) == 0x07f02000) { + instruction->type = ARM_UNDEFINED_INSTRUCTION; + strcpy(cp, "UNDEFINED"); + return ERROR_OK; + } + + switch ((opcode >> 12) & 0x5) { + case 0x1: + case 0x5: + return t2ev_b_bl(opcode, address, instruction, cp); + case 0x4: + goto undef; + case 0: + if (((opcode >> 23) & 0x07) != 0x07) + return t2ev_cond_b(opcode, address, instruction, cp); + if (opcode & (1 << 26)) + goto undef; + break; + } + + switch ((opcode >> 20) & 0x7f) { + case 0x38: + case 0x39: + sprintf(cp, "MSR\t%s, r%d", special_name(opcode & 0xff), + (int) (opcode >> 16) & 0x0f); + return ERROR_OK; + case 0x3a: + return t2ev_hint(opcode, address, instruction, cp); + case 0x3b: + return t2ev_misc(opcode, address, instruction, cp); + case 0x3e: + case 0x3f: + sprintf(cp, "MRS\tr%d, %s", (int) (opcode >> 8) & 0x0f, + special_name(opcode & 0xff)); + return ERROR_OK; + } + +undef: + return ERROR_INVALID_ARGUMENTS; +} + +static int t2ev_data_mod_immed(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + char *mnemonic = NULL; + int rn = (opcode >> 16) & 0xf; + int rd = (opcode >> 8) & 0xf; + unsigned immed = opcode & 0xff; + unsigned func; + bool one = false; + char *suffix = ""; + char *suffix2 = ""; + + /* ARMv7-M: A5.3.2 Modified immediate constants */ + func = (opcode >> 11) & 0x0e; + if (immed & 0x80) + func |= 1; + if (opcode & (1 << 26)) + func |= 0x10; + + /* "Modified" immediates */ + switch (func >> 1) { + case 0: + break; + case 2: + immed <<= 8; + /* FALLTHROUGH */ + case 1: + immed += immed << 16; + break; + case 3: + immed += immed << 8; + immed += immed << 16; + break; + default: + immed |= 0x80; + immed = ror(immed, func); + } + + if (opcode & (1 << 20)) + suffix = "S"; + + switch ((opcode >> 21) & 0xf) { + case 0: + if (rd == 0xf) { + instruction->type = ARM_TST; + mnemonic = "TST"; + one = true; + suffix = ""; + suffix2 = ".W"; + rd = rn; + } else { + instruction->type = ARM_AND; + mnemonic = "AND"; + } + break; + case 1: + instruction->type = ARM_BIC; + mnemonic = "BIC"; + break; + case 2: + if (rn == 0xf) { + instruction->type = ARM_MOV; + mnemonic = "MOV"; + one = true; + suffix2 = ".W"; + } else { + instruction->type = ARM_ORR; + mnemonic = "ORR"; + } + break; + case 3: + if (rn == 0xf) { + instruction->type = ARM_MVN; + mnemonic = "MVN"; + one = true; + } else { + // instruction->type = ARM_ORN; + mnemonic = "ORN"; + } + break; + case 4: + if (rd == 0xf) { + instruction->type = ARM_TEQ; + mnemonic = "TEQ"; + one = true; + suffix = ""; + rd = rn; + } else { + instruction->type = ARM_EOR; + mnemonic = "EOR"; + } + break; + case 8: + if (rd == 0xf) { + instruction->type = ARM_CMN; + mnemonic = "CMN"; + one = true; + suffix = ""; + rd = rn; + } else { + instruction->type = ARM_ADD; + mnemonic = "ADD"; + suffix2 = ".W"; + } + break; + case 10: + instruction->type = ARM_ADC; + mnemonic = "ADC"; + break; + case 11: + instruction->type = ARM_SBC; + mnemonic = "SBC"; + break; + case 13: + if (rd == 0xf) { + instruction->type = ARM_CMP; + mnemonic = "CMP"; + one = true; + suffix = ""; + rd = rn; + } else { + instruction->type = ARM_SUB; + mnemonic = "SUB"; + } + suffix2 = ".W"; + break; + case 14: + instruction->type = ARM_RSB; + mnemonic = "RSB"; + suffix2 = ".W"; + break; + default: + return ERROR_INVALID_ARGUMENTS; + } + + if (one) + sprintf(cp, "%s%s\tr%d, #%d\t; %#8.8x", + mnemonic, suffix2 ,rd, immed, immed); + else + sprintf(cp, "%s%s%s\tr%d, r%d, #%d\t; %#8.8x", + mnemonic, suffix, suffix2, + rd, rn, immed, immed); + + return ERROR_OK; +} + +static int t2ev_data_immed(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + char *mnemonic = NULL; + int rn = (opcode >> 16) & 0xf; + int rd = (opcode >> 8) & 0xf; + unsigned immed; + bool add = false; + bool is_signed = false; + + immed = (opcode & 0x0ff) | ((opcode & 0x7000) >> 12); + if (opcode & (1 << 27)) + immed |= (1 << 11); + + switch ((opcode >> 20) & 0x1f) { + case 0: + if (rn == 0xf) { + add = true; + goto do_adr; + } + mnemonic = "ADD.W"; + break; + case 4: + mnemonic = "MOV.W"; + break; + case 0x0a: + if (rn == 0xf) + goto do_adr; + mnemonic = "SUB.W"; + break; + case 0x0c: + /* move constant to top 16 bits of register */ + immed |= (opcode >> 4) & 0xf000; + sprintf(cp, "MOVT\tr%d, #%d\t; %#4.4x", rn, immed, immed); + return ERROR_OK; + case 0x10: + case 0x12: + is_signed = true; + case 0x18: + case 0x1a: + /* signed/unsigned saturated add */ + immed = (opcode >> 6) & 0x03; + immed |= (opcode >> 10) & 0x1c; + sprintf(cp, "%sSAT\tr%d, #%d, r%d, %s #%d\t", + is_signed ? "S" : "U", + rd, (int) (opcode & 0x1f) + 1, rn, + (opcode & (1 << 21)) ? "ASR" : "LSL", + immed ? immed : 32); + return ERROR_OK; + case 0x14: + is_signed = true; + /* FALLTHROUGH */ + case 0x1c: + /* signed/unsigned bitfield extract */ + immed = (opcode >> 6) & 0x03; + immed |= (opcode >> 10) & 0x1c; + sprintf(cp, "%sBFX\tr%d, r%d, #%d, #%d\t", + is_signed ? "S" : "U", + rd, rn, immed, + (int) (opcode & 0x1f) + 1); + return ERROR_OK; + case 0x16: + immed = (opcode >> 6) & 0x03; + immed |= (opcode >> 10) & 0x1c; + if (rn == 0xf) /* bitfield clear */ + sprintf(cp, "BFC\tr%d, #%d, #%d\t", + rd, immed, + (int) (opcode & 0x1f) + 1 - immed); + else /* bitfield insert */ + sprintf(cp, "BFI\tr%d, r%d, #%d, #%d\t", + rd, rn, immed, + (int) (opcode & 0x1f) + 1 - immed); + return ERROR_OK; + default: + return ERROR_INVALID_ARGUMENTS; + } + + sprintf(cp, "%s\tr%d, r%d, #%d\t; %#3.3x", mnemonic, + rd, rn, immed, immed); + return ERROR_OK; + +do_adr: + address = thumb_alignpc4(address); + if (add) + address += immed; + else + address -= immed; + /* REVISIT "ADD/SUB Rd, PC, #const ; 0x..." might be better; + * not hiding the pc-relative stuff will sometimes be useful. + */ + sprintf(cp, "ADR.W\tr%d, %#8.8" PRIx32, rd, address); + return ERROR_OK; +} + +static int t2ev_store_single(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + unsigned op = (opcode >> 20) & 0xf; + char *size = ""; + char *suffix = ""; + char *p1 = ""; + char *p2 = "]"; + unsigned immed; + unsigned rn = (opcode >> 16) & 0x0f; + unsigned rt = (opcode >> 12) & 0x0f; + + if (rn == 0xf) + return ERROR_INVALID_ARGUMENTS; + + if (opcode & 0x0800) + op |= 1; + switch (op) { + /* byte */ + case 0x8: + case 0x9: + size = "B"; + goto imm12; + case 0x1: + size = "B"; + goto imm8; + case 0x0: + size = "B"; + break; + /* halfword */ + case 0xa: + case 0xb: + size = "H"; + goto imm12; + case 0x3: + size = "H"; + goto imm8; + case 0x2: + size = "H"; + break; + /* word */ + case 0xc: + case 0xd: + goto imm12; + case 0x5: + goto imm8; + case 0x4: + break; + /* error */ + default: + return ERROR_INVALID_ARGUMENTS; + } + + sprintf(cp, "STR%s.W\tr%d, [r%d, r%d, LSL #%d]", + size, rt, rn, (int) opcode & 0x0f, + (int) (opcode >> 4) & 0x03); + +imm12: + immed = opcode & 0x0fff; + sprintf(cp, "STR%s.W\tr%d, [r%d, #%u]\t; %#3.3x", + size, rt, rn, immed, immed); + return ERROR_OK; + +imm8: + immed = opcode & 0x00ff; + + switch (opcode & 0x700) { + case 0x600: + suffix = "T"; + break; + case 0x000: + case 0x200: + return ERROR_INVALID_ARGUMENTS; + } + + /* two indexed modes will write back rn */ + if (opcode & 0x100) { + if (opcode & 0x400) /* pre-indexed */ + p2 = "]!"; + else { /* post-indexed */ + p1 = "]"; + p2 = ""; + } + } + + sprintf(cp, "STR%s%s\tr%d, [r%d%s, #%s%u%s\t; %#2.2x", + size, suffix, rt, rn, p1, + (opcode & 0x200) ? "" : "-", + immed, p2, immed); + return ERROR_OK; +} + +static int t2ev_mul32(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + int ra = (opcode >> 12) & 0xf; + + switch (opcode & 0x007000f0) { + case 0: + if (ra == 0xf) + sprintf(cp, "MUL\tr%d, r%d, r%d", + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf); + else + sprintf(cp, "MLA\tr%d, r%d, r%d, r%d", + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf, ra); + break; + case 0x10: + sprintf(cp, "MLS\tr%d, r%d, r%d, r%d", + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf, ra); + break; + default: + return ERROR_INVALID_ARGUMENTS; + } + return ERROR_OK; +} + +static int t2ev_mul64_div(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + int op = (opcode >> 4) & 0xf; + char *infix = "MUL"; + + op += (opcode >> 16) & 0x70; + switch (op) { + case 0x40: + case 0x60: + infix = "MLA"; + /* FALLTHROUGH */ + case 0: + case 0x20: + sprintf(cp, "%c%sL\tr%d, r%d, r%d, r%d", + (op & 0x20) ? 'U' : 'S', + infix, + (int) (opcode >> 12) & 0xf, + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf); + break; + case 0x1f: + case 0x3f: + sprintf(cp, "%cDIV\tr%d, r%d, r%d", + (op & 0x20) ? 'U' : 'S', + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf); + break; + default: + return ERROR_INVALID_ARGUMENTS; + } + + return ERROR_OK; +} + +static int t2ev_ldm_stm(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + int rn = (opcode >> 16) & 0xf; + int op = (opcode >> 22) & 0x6; + int t = (opcode >> 21) & 1; + unsigned registers = opcode & 0xffff; + + if (opcode & (1 << 20)) + op |= 1; + + switch (op) { + case 2: + sprintf(cp, "STMB\tr%d%s, ", rn, t ? "!" : ""); + break; + case 3: + if (rn == 13 && t) + sprintf(cp, "POP\t"); + else + sprintf(cp, "LDM.W\tr%d%s, ", rn, t ? "!" : ""); + break; + case 4: + if (rn == 13 && t) + sprintf(cp, "PUSH\t"); + else + sprintf(cp, "STM.W\tr%d%s, ", rn, t ? "!" : ""); + break; + case 5: + sprintf(cp, "LDMB\tr%d%s, ", rn, t ? "!" : ""); + break; + default: + return ERROR_INVALID_ARGUMENTS; + } + + cp = strchr(cp, 0); + *cp++ = '{'; + for (t = 0; registers; t++, registers >>= 1) { + if ((registers & 1) == 0) + continue; + registers &= ~1; + sprintf(cp, "r%d%s", t, registers ? ", " : ""); + cp = strchr(cp, 0); + } + *cp++ = '}'; + *cp++ = 0; + + return ERROR_OK; +} + +static int t2ev_data_shift(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + int op = (opcode >> 21) & 0xf; + int rd = (opcode >> 8) & 0xf; + int rn = (opcode >> 16) & 0xf; + int type = (opcode >> 4) & 0x3; + int immed = (opcode >> 6) & 0x3; + char *mnemonic; + char *suffix = ""; + + immed |= (opcode >> 10) & 0x7; + if (opcode & (1 << 21)) + suffix = "S"; + + switch (op) { + case 0: + if (rd == 0xf) { + if (!(opcode & (1 << 21))) + return ERROR_INVALID_ARGUMENTS; + instruction->type = ARM_TST; + mnemonic = "TST"; + goto two; + } + instruction->type = ARM_AND; + mnemonic = "AND"; + break; + case 1: + instruction->type = ARM_BIC; + mnemonic = "BIC"; + break; + case 2: + if (rn == 0xf) { + instruction->type = ARM_MOV; + switch (type) { + case 0: + if (immed == 0) { + sprintf(cp, "MOV%s.W\tr%d, r%d", + suffix, rd, + (int) (opcode & 0xf)); + return ERROR_OK; + } + mnemonic = "LSL"; + break; + case 1: + mnemonic = "LSR"; + break; + case 2: + mnemonic = "ASR"; + break; + default: + if (immed == 0) { + sprintf(cp, "RRX%s.W\tr%d, r%d", + suffix, rd, + (int) (opcode & 0xf)); + return ERROR_OK; + } + mnemonic = "ROR"; + break; + } + goto immediate; + } else { + instruction->type = ARM_ORR; + mnemonic = "ORR"; + } + break; + case 3: + if (rn == 0xf) { + instruction->type = ARM_MVN; + mnemonic = "MVN"; + rn = rd; + goto two; + } else { + // instruction->type = ARM_ORN; + mnemonic = "ORN"; + } + break; + case 4: + if (rd == 0xf) { + if (!(opcode & (1 << 21))) + return ERROR_INVALID_ARGUMENTS; + instruction->type = ARM_TEQ; + mnemonic = "TEQ"; + goto two; + } + instruction->type = ARM_EOR; + mnemonic = "EOR"; + break; + case 8: + if (rd == 0xf) { + if (!(opcode & (1 << 21))) + return ERROR_INVALID_ARGUMENTS; + instruction->type = ARM_CMN; + mnemonic = "CMN"; + goto two; + } + instruction->type = ARM_ADD; + mnemonic = "ADD"; + break; + case 0xa: + instruction->type = ARM_ADC; + mnemonic = "ADC"; + break; + case 0xb: + instruction->type = ARM_SBC; + mnemonic = "SBC"; + break; + case 0xd: + if (rd == 0xf) { + if (!(opcode & (1 << 21))) + return ERROR_INVALID_ARGUMENTS; + instruction->type = ARM_CMP; + mnemonic = "CMP"; + goto two; + } + instruction->type = ARM_SUB; + mnemonic = "SUB"; + break; + case 0xe: + instruction->type = ARM_RSB; + mnemonic = "RSB"; + break; + default: + return ERROR_INVALID_ARGUMENTS; + } + + sprintf(cp, "%s%s.W\tr%d, r%d, r%d", + mnemonic, suffix, rd, rn, (int) (opcode & 0xf)); + +shift: + cp = strchr(cp, 0); + + switch (type) { + case 0: + if (immed == 0) + return ERROR_OK; + suffix = "LSL"; + break; + case 1: + suffix = "LSR"; + break; + case 2: + suffix = "ASR"; + break; + case 3: + if (immed == 0) { + strcpy(cp, "RRX"); + return ERROR_OK; + } + suffix = "ROR"; + break; + } + sprintf(cp, ", %s #%d", suffix, immed ? immed : 32); + return ERROR_OK; + +two: + sprintf(cp, "%s%s.W\tr%d, r%d", + mnemonic, suffix, rn, (int) (opcode & 0xf)); + goto shift; + +immediate: + sprintf(cp, "%s%s.W\tr%d, r%d, #%d", + mnemonic, suffix, rd, + (int) (opcode & 0xf), immed ? immed : 32); + return ERROR_OK; +} + +static int t2ev_data_reg(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + char *mnemonic; + char * suffix = ""; + + if (((opcode >> 4) & 0xf) == 0) { + switch ((opcode >> 21) & 0x7) { + case 0: + mnemonic = "LSL"; + break; + case 1: + mnemonic = "LSR"; + break; + case 2: + mnemonic = "ASR"; + break; + case 3: + mnemonic = "ROR"; + break; + default: + return ERROR_INVALID_ARGUMENTS; + } + + instruction->type = ARM_MOV; + if (opcode & (1 << 20)) + suffix = "S"; + sprintf(cp, "%s%s.W\tr%d, r%d, r%d", + mnemonic, suffix, + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 16) & 0xf, + (int) (opcode >> 0) & 0xf); + + } else if (opcode & (1 << 7)) { + switch ((opcode >> 24) & 0xf) { + case 0: + case 1: + case 4: + case 5: + switch ((opcode >> 4) & 0x3) { + case 1: + suffix = ", ROR #8"; + break; + case 2: + suffix = ", ROR #16"; + break; + case 3: + suffix = ", ROR #24"; + break; + } + sprintf(cp, "%cXT%c.W\tr%d, r%d%s", + (opcode & (1 << 24)) ? 'U' : 'S', + (opcode & (1 << 26)) ? 'B' : 'H', + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 16) & 0xf, + suffix); + break; + case 8: + case 9: + case 0xa: + case 0xb: + if (opcode & (1 << 6)) + return ERROR_INVALID_ARGUMENTS; + if (~opcode & (0xff << 12)) + return ERROR_INVALID_ARGUMENTS; + if (!(opcode & (1 << 20))) + return ERROR_INVALID_ARGUMENTS; + + switch (((opcode >> 19) & 0x04) + | ((opcode >> 4) & 0x3)) { + case 0: + mnemonic = "REV.W"; + break; + case 1: + mnemonic = "REV16.W"; + break; + case 2: + mnemonic = "RBIT"; + break; + case 3: + mnemonic = "REVSH.W"; + break; + case 4: + mnemonic = "CLZ"; + break; + default: + return ERROR_INVALID_ARGUMENTS; + } + sprintf(cp, "%s\tr%d, r%d", + mnemonic, + (int) (opcode >> 8) & 0xf, + (int) (opcode >> 0) & 0xf); + break; + default: + return ERROR_INVALID_ARGUMENTS; + } + } + + return ERROR_OK; +} + +static int t2ev_load_word(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + int rn = (opcode >> 16) & 0xf; + int immed; + + instruction->type = ARM_LDR; + + if (rn == 0xf) { + immed = opcode & 0x0fff; + if (opcode & (1 << 23)) + immed = -immed; + sprintf(cp, "LDR\tr%d, %#8.8" PRIx32, + (int) (opcode >> 12) & 0xf, + thumb_alignpc4(address) + immed); + return ERROR_OK; + } + + if (opcode & (1 << 23)) { + immed = opcode & 0x0fff; + sprintf(cp, "LDR.W\tr%d, [r%d, #%d]\t; %#3.3x", + (int) (opcode >> 12) & 0xf, + rn, immed, immed); + return ERROR_OK; + } + + if (!(opcode & (0x3f << 6))) { + sprintf(cp, "LDR.W\tr%d, [r%d, r%d, LSL #%d]", + (int) (opcode >> 12) & 0xf, + rn, + (int) (opcode >> 0) & 0xf, + (int) (opcode >> 4) & 0x3); + return ERROR_OK; + } + + + if (((opcode >> 8) & 0xf) == 0xe) { + immed = opcode & 0x00ff; + + sprintf(cp, "LDRT\tr%d, [r%d, #%d]\t; %#2.2x", + (int) (opcode >> 12) & 0xf, + rn, immed, immed); + return ERROR_OK; + } + + if (((opcode >> 8) & 0xf) == 0xc || (opcode & 0x0900) == 0x0900) { + char *p1 = "]", *p2 = ""; + + if (!(opcode & 0x0600)) + return ERROR_INVALID_ARGUMENTS; + + immed = opcode & 0x00ff; + + /* two indexed modes will write back rn */ + if (opcode & 0x100) { + if (opcode & 0x400) /* pre-indexed */ + p2 = "]!"; + else { /* post-indexed */ + p1 = "]"; + p2 = ""; + } + } + + sprintf(cp, "LDR\tr%d, [r%d%s, #%s%u%s\t; %#2.2x", + (int) (opcode >> 12) & 0xf, + rn, p1, + (opcode & 0x200) ? "" : "-", + immed, p2, immed); + return ERROR_OK; + } + + return ERROR_INVALID_ARGUMENTS; +} + +/* + * REVISIT for Thumb2 instructions, instruction->type and friends aren't + * always set. That means eventual arm_simulate_step() support for Thumb2 + * will need work in this area. + */ +int thumb2_opcode(target_t *target, uint32_t address, arm_instruction_t *instruction) +{ + int retval; + uint16_t op; + uint32_t opcode; + char *cp; + + /* clear low bit ... it's set on function pointers */ + address &= ~1; + + /* clear fields, to avoid confusion */ + memset(instruction, 0, sizeof(arm_instruction_t)); + + /* read first halfword, see if this is the only one */ + retval = target_read_u16(target, address, &op); + if (retval != ERROR_OK) + return retval; + + switch (op & 0xf800) { + case 0xf800: + case 0xf000: + case 0xe800: + /* 32-bit instructions */ + instruction->instruction_size = 4; + opcode = op << 16; + retval = target_read_u16(target, address + 2, &op); + if (retval != ERROR_OK) + return retval; + opcode |= op; + instruction->opcode = opcode; + break; + default: + /* 16-bit: Thumb1 + IT + CBZ/CBNZ + ... */ + return thumb_evaluate_opcode(op, address, instruction); + } + + snprintf(instruction->text, 128, + "0x%8.8" PRIx32 " 0x%8.8" PRIx32 "\t", + address, opcode); + cp = strchr(instruction->text, 0); + retval = ERROR_FAIL; + + /* ARMv7-M: A5.3.1 Data processing (modified immediate) */ + if ((opcode & 0x1a008000) == 0x10000000) + retval = t2ev_data_mod_immed(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.3 Data processing (plain binary immediate) */ + else if ((opcode & 0x1a008000) == 0x12000000) + retval = t2ev_data_immed(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.4 Branches and miscellaneous control */ + else if ((opcode & 0x18008000) == 0x10008000) + retval = t2ev_b_misc(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.5 Load/store multiple */ + else if ((opcode & 0x1e400000) == 0x08000000) + retval = t2ev_ldm_stm(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.7 Load word */ + else if ((opcode & 0x1f700000) == 0x18500000) + retval = t2ev_load_word(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.10 Store single data item */ + else if ((opcode & 0x1f100000) == 0x18000000) + retval = t2ev_store_single(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.11 Data processing (shifted register) */ + else if ((opcode & 0x1e000000) == 0x0a000000) + retval = t2ev_data_shift(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.12 Data processing (register) */ + else if ((opcode & 0x1f000000) == 0x1a000000) + retval = t2ev_data_reg(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.14 Multiply, and multiply accumulate */ + else if ((opcode & 0x1f800000) == 0x1b000000) + retval = t2ev_mul32(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.15 Long multiply, long multiply accumulate, divide */ + 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; + + if (retval == ERROR_INVALID_ARGUMENTS) { + instruction->type = ARM_UNDEFINED_INSTRUCTION; + strcpy(cp, "UNDEFINED OPCODE"); + return ERROR_OK; + } + + LOG_DEBUG("Can't decode 32-bit Thumb2 yet (opcode=%08" PRIx32 ")", + opcode); + + strcpy(cp, "(32-bit Thumb2 ...)"); + return ERROR_OK; +} + int arm_access_size(arm_instruction_t *instruction) { if ((instruction->type == ARM_LDRB)