X-Git-Url: https://review.openocd.org/gitweb?a=blobdiff_plain;f=src%2Ftarget%2Farm_disassembler.c;h=0478ee9c6797ce35a1f347f8720ce61749c574e5;hb=49e2267f1f0a689eadbf44da7852659326324044;hp=218233aebf1ef83de2e317c94254f47398ffac36;hpb=84e86e9aee9f38f31769be53a5d0b794648ccb5c;p=openocd.git diff --git a/src/target/arm_disassembler.c b/src/target/arm_disassembler.c index 218233aebf..0478ee9c67 100644 --- a/src/target/arm_disassembler.c +++ b/src/target/arm_disassembler.c @@ -2,6 +2,8 @@ * Copyright (C) 2006 by Dominic Rath * * Dominic.Rath@gmx.de * * * + * Copyright (C) 2009 by David Brownell * + * * * 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 * @@ -1642,7 +1644,7 @@ int evaluate_data_proc_thumb(uint16_t opcode, uint32_t address, arm_instruction_ break; case 0x9: instruction->type = ARM_RSB; - mnemonic = "NEGS"; + mnemonic = "RSBS"; instruction->info.data_proc.variant = 0 /*immediate*/; instruction->info.data_proc.shifter_operand.immediate.immediate = 0; instruction->info.data_proc.Rn = Rm; @@ -1710,7 +1712,7 @@ int evaluate_load_literal_thumb(uint16_t opcode, uint32_t address, arm_instructi snprintf(instruction->text, 128, "0x%8.8" PRIx32 " 0x%4.4x \t" - "LDR\tr%i, [pc, #%#" PRIx32 "]\t; %#8.8x", + "LDR\tr%i, [pc, #%#" PRIx32 "]\t; %#8.8" PRIx32, address, opcode, Rd, immediate, thumb_alignpc4(address) + immediate); @@ -1942,17 +1944,21 @@ int evaluate_load_store_multiple_thumb(uint16_t opcode, uint32_t address, arm_in if ((opcode & 0xf000) == 0xc000) { /* generic load/store multiple */ + char *wback = "!"; + if (L) { instruction->type = ARM_LDM; mnemonic = "LDM"; + if (opcode & (1 << Rn)) + wback = ""; } else { instruction->type = ARM_STM; mnemonic = "STM"; } - snprintf(ptr_name,7,"r%i!, ",Rn); + snprintf(ptr_name, sizeof ptr_name, "r%i%s, ", Rn, wback); } else { /* push/pop */ @@ -2080,7 +2086,7 @@ static int evaluate_cps_thumb(uint16_t opcode, uint32_t address, (opcode & 0x80) ? "BE" : "LE"); else /* ASSUME (opcode & 0x0fe0) == 0x0660 */ snprintf(instruction->text, 128, - "0x%8.8" PRIx32 " 0x%4.4x \tCPSI%c %s%s%s", + "0x%8.8" PRIx32 " 0x%4.4x \tCPSI%c\t%s%s%s", address, opcode, (opcode & 0x0010) ? 'D' : 'E', (opcode & 0x0004) ? "A" : "", @@ -2096,7 +2102,7 @@ static int evaluate_byterev_thumb(uint16_t opcode, uint32_t address, char *suffix; /* added in ARMv6 */ - switch (opcode & 0x00c0) { + switch ((opcode >> 6) & 3) { case 0: suffix = ""; break; @@ -2452,7 +2458,7 @@ static int t2ev_hint(uint32_t opcode, uint32_t address, } if (opcode & 0x00f0) { - sprintf(cp, "DBG\t#%d", opcode & 0xf); + sprintf(cp, "DBG\t#%d", (int) opcode & 0xf); return ERROR_OK; } @@ -2522,7 +2528,7 @@ static int t2ev_b_misc(uint32_t opcode, uint32_t address, case 0x4: goto undef; case 0: - if (((opcode >> 23) & 0x07) == 0x07) + if (((opcode >> 23) & 0x07) != 0x07) return t2ev_cond_b(opcode, address, instruction, cp); if (opcode & (1 << 26)) goto undef; @@ -2533,7 +2539,7 @@ static int t2ev_b_misc(uint32_t opcode, uint32_t address, case 0x38: case 0x39: sprintf(cp, "MSR\t%s, r%d", special_name(opcode & 0xff), - (opcode >> 16) & 0x0f); + (int) (opcode >> 16) & 0x0f); return ERROR_OK; case 0x3a: return t2ev_hint(opcode, address, instruction, cp); @@ -2541,7 +2547,7 @@ static int t2ev_b_misc(uint32_t opcode, uint32_t address, return t2ev_misc(opcode, address, instruction, cp); case 0x3e: case 0x3f: - sprintf(cp, "MRS\tr%d, %s", (opcode >> 16) & 0x0f, + sprintf(cp, "MRS\tr%d, %s", (int) (opcode >> 8) & 0x0f, special_name(opcode & 0xff)); return ERROR_OK; } @@ -2560,6 +2566,7 @@ static int t2ev_data_mod_immed(uint32_t opcode, uint32_t address, unsigned func; bool one = false; char *suffix = ""; + char *suffix2 = ""; /* ARMv7-M: A5.3.2 Modified immediate constants */ func = (opcode >> 11) & 0x0e; @@ -2612,6 +2619,7 @@ static int t2ev_data_mod_immed(uint32_t opcode, uint32_t address, instruction->type = ARM_MOV; mnemonic = "MOV"; one = true; + suffix2 = ".W"; } else { instruction->type = ARM_ORR; mnemonic = "ORR"; @@ -2649,11 +2657,13 @@ static int t2ev_data_mod_immed(uint32_t opcode, uint32_t address, } else { instruction->type = ARM_ADD; mnemonic = "ADD"; + suffix2 = ".W"; } break; case 10: instruction->type = ARM_ADC; mnemonic = "ADC"; + suffix2 = ".W"; break; case 11: instruction->type = ARM_SBC; @@ -2670,21 +2680,24 @@ static int t2ev_data_mod_immed(uint32_t opcode, uint32_t address, 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\tr%d, #%d\t; %#8.8x", - mnemonic, rd, immed, immed); + sprintf(cp, "%s%s\tr%d, #%d\t; %#8.8x", + mnemonic, suffix2 ,rd, immed, immed); else - sprintf(cp, "%s%s\tr%d, r%d, #%d\t; %#8.8x", - mnemonic, suffix, rd, rn, immed, immed); + sprintf(cp, "%s%s%s\tr%d, r%d, #%d\t; %#8.8x", + mnemonic, suffix, suffix2, + rd, rn, immed, immed); return ERROR_OK; } @@ -2699,8 +2712,8 @@ static int t2ev_data_immed(uint32_t opcode, uint32_t address, bool add = false; bool is_signed = false; - immed = (opcode & 0x0ff) | ((opcode & 0x7000) >> 12); - if (opcode & (1 << 27)) + immed = (opcode & 0x0ff) | ((opcode & 0x7000) >> 4); + if (opcode & (1 << 26)) immed |= (1 << 11); switch ((opcode >> 20) & 0x1f) { @@ -2709,15 +2722,16 @@ static int t2ev_data_immed(uint32_t opcode, uint32_t address, add = true; goto do_adr; } - mnemonic = "ADD.W"; + mnemonic = "ADDW"; break; case 4: - mnemonic = "MOV.W"; - break; + immed |= (opcode >> 4) & 0xf000; + sprintf(cp, "MOVW\tr%d, #%d\t; %#3.3x", rd, immed, immed); + return ERROR_OK; case 0x0a: if (rn == 0xf) goto do_adr; - mnemonic = "SUB.W"; + mnemonic = "SUBW"; break; case 0x0c: /* move constant to top 16 bits of register */ @@ -2734,7 +2748,7 @@ static int t2ev_data_immed(uint32_t opcode, uint32_t address, immed |= (opcode >> 10) & 0x1c; sprintf(cp, "%sSAT\tr%d, #%d, r%d, %s #%d\t", is_signed ? "S" : "U", - rd, (opcode & 0x1f) + 1, rn, + rd, (int) (opcode & 0x1f) + is_signed, rn, (opcode & (1 << 21)) ? "ASR" : "LSL", immed ? immed : 32); return ERROR_OK; @@ -2748,7 +2762,7 @@ static int t2ev_data_immed(uint32_t opcode, uint32_t address, sprintf(cp, "%sBFX\tr%d, r%d, #%d, #%d\t", is_signed ? "S" : "U", rd, rn, immed, - (opcode & 0x1f) + 1); + (int) (opcode & 0x1f) + 1); return ERROR_OK; case 0x16: immed = (opcode >> 6) & 0x03; @@ -2756,11 +2770,11 @@ static int t2ev_data_immed(uint32_t opcode, uint32_t address, if (rn == 0xf) /* bitfield clear */ sprintf(cp, "BFC\tr%d, #%d, #%d\t", rd, immed, - (opcode & 0x1f) + 1 - immed); + (int) (opcode & 0x1f) + 1 - immed); else /* bitfield insert */ sprintf(cp, "BFI\tr%d, r%d, #%d, #%d\t", rd, rn, immed, - (opcode & 0x1f) + 1 - immed); + (int) (opcode & 0x1f) + 1 - immed); return ERROR_OK; default: return ERROR_INVALID_ARGUMENTS; @@ -2783,6 +2797,814 @@ do_adr: 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); + return ERROR_OK; + +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, "STM.W\tr%d%s, ", rn, t ? "!" : ""); + break; + case 3: + if (rn == 13 && t) + sprintf(cp, "POP.W\t"); + else + sprintf(cp, "LDM.W\tr%d%s, ", rn, t ? "!" : ""); + break; + case 4: + if (rn == 13 && t) + sprintf(cp, "PUSH.W\t"); + else + sprintf(cp, "STMDB\tr%d%s, ", rn, t ? "!" : ""); + break; + case 5: + sprintf(cp, "LDMDB.W\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 >> 20) & 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 >> 0) & 0xf, + suffix); + break; + case 8: + case 9: + case 0xa: + case 0xb: + if (opcode & (1 << 6)) + return ERROR_INVALID_ARGUMENTS; + if (((opcode >> 12) & 0xf) != 0xf) + 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)) == 0) + 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 & 0x0500)) + 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; +} + +static int t2ev_load_byte_hints(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + int rn = (opcode >> 16) & 0xf; + int rt = (opcode >> 12) & 0xf; + int op2 = (opcode >> 6) & 0x3f; + unsigned immed; + char *p1 = "", *p2 = "]"; + char *mnemonic; + + switch ((opcode >> 23) & 0x3) { + case 0: + if ((rn & rt) == 0xf) { +pld_literal: + immed = opcode & 0xfff; + address = thumb_alignpc4(address); + if (opcode & (1 << 23)) + address += immed; + else + address -= immed; + sprintf(cp, "PLD\tr%d, %#8.8" PRIx32, + rt, address); + return ERROR_OK; + } + if (rn == 0x0f && rt != 0x0f) { +ldrb_literal: + immed = opcode & 0xfff; + address = thumb_alignpc4(address); + if (opcode & (1 << 23)) + address += immed; + else + address -= immed; + sprintf(cp, "LDRB\tr%d, %#8.8" PRIx32, + rt, address); + return ERROR_OK; + } + if (rn == 0x0f) + break; + if ((op2 & 0x3c) == 0x38) { + immed = opcode & 0xff; + sprintf(cp, "LDRBT\tr%d, [r%d, #%d]\t; %#2.2x", + rt, rn, immed, immed); + return ERROR_OK; + } + if ((op2 & 0x3c) == 0x30) { + if (rt == 0x0f) { + immed = opcode & 0xff; + immed = -immed; +preload_immediate: + p1 = (opcode & (1 << 21)) ? "W" : ""; + sprintf(cp, "PLD%s\t[r%d, #%d]\t; %#6.6x", + p1, rn, immed, immed); + return ERROR_OK; + } + mnemonic = "LDRB"; +ldrxb_immediate_t3: + immed = opcode & 0xff; + if (!(opcode & 0x200)) + immed = -immed; + + /* two indexed modes will write back rn */ + if (opcode & 0x100) { + if (opcode & 0x400) /* pre-indexed */ + p2 = "]!"; + else { /* post-indexed */ + p1 = "]"; + p2 = ""; + } + } +ldrxb_immediate_t2: + sprintf(cp, "%s\tr%d, [r%d%s, #%d%s\t; %#8.8x", + mnemonic, rt, rn, p1, + immed, p2, immed); + return ERROR_OK; + } + if ((op2 & 0x24) == 0x24) { + mnemonic = "LDRB"; + goto ldrxb_immediate_t3; + } + if (op2 == 0) { + int rm = opcode & 0xf; + + if (rt == 0x0f) + sprintf(cp, "PLD\t"); + else + sprintf(cp, "LDRB.W\tr%d, ", rt); + immed = (opcode >> 4) & 0x3; + cp = strchr(cp, 0); + sprintf(cp, "[r%d, r%d, LSL #%d]", rn, rm, immed); + return ERROR_OK; + } + break; + case 1: + if ((rn & rt) == 0xf) + goto pld_literal; + if (rt == 0xf) { + immed = opcode & 0xfff; + goto preload_immediate; + } + if (rn == 0x0f) + goto ldrb_literal; + mnemonic = "LDRB.W"; + immed = opcode & 0xfff; + goto ldrxb_immediate_t2; + case 2: + if ((rn & rt) == 0xf) { + immed = opcode & 0xfff; + address = thumb_alignpc4(address); + if (opcode & (1 << 23)) + address += immed; + else + address -= immed; + sprintf(cp, "PLI\t%#8.8" PRIx32, address); + return ERROR_OK; + } + if (rn == 0xf && rt != 0xf) { +ldrsb_literal: + immed = opcode & 0xfff; + address = thumb_alignpc4(address); + if (opcode & (1 << 23)) + address += immed; + else + address -= immed; + sprintf(cp, "LDRSB\t%#8.8" PRIx32, address); + return ERROR_OK; + } + if (rn == 0xf) + break; + if ((op2 & 0x3c) == 0x38) { + immed = opcode & 0xff; + sprintf(cp, "LDRSBT\tr%d, [r%d, #%d]\t; %#2.2x", + rt, rn, immed, immed); + return ERROR_OK; + } + if ((op2 & 0x3c) == 0x30) { + if (rt == 0xf) { + immed = opcode & 0xff; + immed = -immed; // pli + sprintf(cp, "PLI\t[r%d, #%d]\t; -%#2.2x", + rn, immed, -immed); + return ERROR_OK; + } + mnemonic = "LDRSB"; + goto ldrxb_immediate_t3; + } + if ((op2 & 0x24) == 0x24) { + mnemonic = "LDRSB"; + goto ldrxb_immediate_t3; + } + if (op2 == 0) { + int rm = opcode & 0xf; + + if (rt == 0x0f) + sprintf(cp, "PLI\t"); + else + sprintf(cp, "LDRSB.W\tr%d, ", rt); + immed = (opcode >> 4) & 0x3; + cp = strchr(cp, 0); + sprintf(cp, "[r%d, r%d, LSL #%d]", rn, rm, immed); + return ERROR_OK; + } + break; + case 3: + if (rt == 0xf) { + immed = opcode & 0xfff; + sprintf(cp, "PLI\t[r%d, #%d]\t; %#3.3" PRIx32, + rn, immed, immed); + return ERROR_OK; + } + if (rn == 0xf) + goto ldrsb_literal; + immed = opcode & 0xfff; + mnemonic = "LDRSB"; + goto ldrxb_immediate_t2; + } + + return ERROR_INVALID_ARGUMENTS; +} + +static int t2ev_load_halfword(uint32_t opcode, uint32_t address, + arm_instruction_t *instruction, char *cp) +{ + int rn = (opcode >> 16) & 0xf; + int rt = (opcode >> 12) & 0xf; + int op2 = (opcode >> 6) & 0x3f; + char *sign = ""; + unsigned immed; + + if (rt == 0xf) { + sprintf(cp, "HINT (UNALLOCATED)"); + return ERROR_OK; + } + + if (opcode & (1 << 24)) + sign = "S"; + + if ((opcode & (1 << 23)) == 0) { + if (rn == 0xf) { +ldrh_literal: + immed = opcode & 0xfff; + address = thumb_alignpc4(address); + if (opcode & (1 << 23)) + address += immed; + else + address -= immed; + sprintf(cp, "LDR%sH\tr%d, %#8.8" PRIx32, + sign, rt, address); + return ERROR_OK; + } + if (op2 == 0) { + int rm = opcode & 0xf; + + immed = (opcode >> 4) & 0x3; + sprintf(cp, "LDR%sH.W\tr%d, [r%d, r%d, LSL #%d]", + sign, rt, rn, rm, immed); + return ERROR_OK; + } + if ((op2 & 0x3c) == 0x38) { + immed = opcode & 0xff; + sprintf(cp, "LDR%sHT\tr%d, [r%d, #%d]\t; %#2.2x", + sign, rt, rn, immed, immed); + return ERROR_OK; + } + if ((op2 & 0x3c) == 0x30 || (op2 & 0x24) == 0x24) { + char *p1 = "", *p2 = "]"; + + immed = opcode & 0xff; + if (!(opcode & 0x200)) + immed = -immed; + + /* two indexed modes will write back rn */ + if (opcode & 0x100) { + if (opcode & 0x400) /* pre-indexed */ + p2 = "]!"; + else { /* post-indexed */ + p1 = "]"; + p2 = ""; + } + } + sprintf(cp, "LDR%sH\tr%d, [r%d%s, #%d%s\t; %#8.8x", + sign, rt, rn, p1, immed, p2, immed); + return ERROR_OK; + } + } else { + if (rn == 0xf) + goto ldrh_literal; + + immed = opcode & 0xfff; + sprintf(cp, "LDR%sH%s\tr%d, [r%d, #%d]\t; %#6.6x", + sign, *sign ? "" : ".W", + rt, rn, immed, 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 @@ -2842,6 +3664,44 @@ int thumb2_opcode(target_t *target, uint32_t address, arm_instruction_t *instruc 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.8 Load halfword, unallocated memory hints */ + else if ((opcode & 0x1e700000) == 0x18300000) + retval = t2ev_load_halfword(opcode, address, instruction, cp); + + /* ARMv7-M: A5.3.9 Load byte, memory hints */ + else if ((opcode & 0x1e700000) == 0x18100000) + retval = t2ev_load_byte_hints(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) + * and A5.3.13 Miscellaneous operations + */ + 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) @@ -2853,7 +3713,8 @@ int thumb2_opcode(target_t *target, uint32_t address, arm_instruction_t *instruc return ERROR_OK; } - LOG_DEBUG("Can't decode 32-bit Thumb2 yet (opcode=%08x)", opcode); + LOG_DEBUG("Can't decode 32-bit Thumb2 yet (opcode=%08" PRIx32 ")", + opcode); strcpy(cp, "(32-bit Thumb2 ...)"); return ERROR_OK;