X-Git-Url: https://review.openocd.org/gitweb?a=blobdiff_plain;f=src%2Ftarget%2Farm_disassembler.c;h=29bc039410b9968bd7cb608bce6bf8eb7e0cfec2;hb=930269b4835c3206f22e9c195bd8f61f36fe03e2;hp=69d5de87df678d6a1cb6f4bf7975256c750c7db7;hpb=8b89224c6e206dcfa2d8bae10dcfb8251b3cb333;p=openocd.git diff --git a/src/target/arm_disassembler.c b/src/target/arm_disassembler.c index 69d5de87df..29bc039410 100644 --- a/src/target/arm_disassembler.c +++ b/src/target/arm_disassembler.c @@ -1512,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; @@ -1546,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) @@ -1671,13 +1674,25 @@ int evaluate_data_proc_thumb(uint16_t opcode, uint32_t address, arm_instruction_ } } - snprintf(instruction->text, 128, - "0x%8.8" PRIx32 " 0x%4.4x \t%s\tr%i, r%i", - address, opcode, mnemonic, Rd, Rm); + 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; @@ -1685,16 +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 " 0x%4.4x \tLDR\tr%i, [pc, #%#" 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.8x", + address, opcode, Rd, immediate, + thumb_alignpc4(address) + immediate); return ERROR_OK; } @@ -2532,6 +2550,444 @@ 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 = ""; + + /* 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 = ""; + 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; + } 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"; + } + 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"; + } + break; + case 14: + instruction->type = ARM_RSB; + mnemonic = "RSB"; + break; + default: + return ERROR_INVALID_ARGUMENTS; + } + + if (one) + sprintf(cp, "%s\tr%d, #%d\t; %#8.8x", + mnemonic, rd, immed, immed); + else + sprintf(cp, "%s%s\tr%d, r%d, #%d\t; %#8.8x", + mnemonic, suffix, 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, (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, + (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, + (opcode & 0x1f) + 1 - immed); + else /* bitfield insert */ + sprintf(cp, "BFI\tr%d, r%d, #%d, #%d\t", + rd, rn, immed, + (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, opcode & 0x0f, + (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", + (opcode >> 8) & 0xf, (opcode >> 16) & 0xf, + (opcode >> 0) & 0xf); + else + sprintf(cp, "MLA\tr%d, r%d, r%d, r%d", + (opcode >> 8) & 0xf, (opcode >> 16) & 0xf, + (opcode >> 0) & 0xf, ra); + break; + case 0x10: + sprintf(cp, "MLS\tr%d, r%d, r%d, r%d", + (opcode >> 8) & 0xf, (opcode >> 16) & 0xf, + (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, + (opcode >> 12) & 0xf, + (opcode >> 8) & 0xf, + (opcode >> 16) & 0xf, + (opcode >> 0) & 0xf); + break; + case 0x1f: + case 0x3f: + sprintf(cp, "%cDIV\tr%d, r%d, r%d", + (op & 0x20) ? 'U' : 'S', + (opcode >> 8) & 0xf, + (opcode >> 16) & 0xf, + (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\tr%d%s, ", rn, t ? "!" : ""); + break; + case 4: + if (rn == 13 && t) + sprintf(cp, "PUSH\t"); + else + sprintf(cp, "STM\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; +} /* * REVISIT for Thumb2 instructions, instruction->type and friends aren't @@ -2580,10 +3036,34 @@ int thumb2_opcode(target_t *target, uint32_t address, arm_instruction_t *instruc 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 */ - if ((opcode & 0x18008000) == 0x10008000) + 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.10 Store single data item */ + else if ((opcode & 0x1f100000) == 0x18000000) + retval = t2ev_store_single(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)