| /******************************************************************************* |
| * Copyright (c) 2013, 2014 Xilinx, Inc. and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * and Eclipse Distribution License v1.0 which accompany this distribution. |
| * The Eclipse Public License is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * and the Eclipse Distribution License is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * You may elect to redistribute this code under either of these licenses. |
| * |
| * Contributors: |
| * Xilinx - initial API and implementation |
| *******************************************************************************/ |
| |
| #include <tcf/config.h> |
| |
| #if SERVICE_Disassembly |
| |
| #include <stdio.h> |
| #include <assert.h> |
| #include <tcf/framework/context.h> |
| #include <tcf/services/symbols.h> |
| #include <machine/arm/tcf/disassembler-arm.h> |
| |
| static char buf[128]; |
| static size_t buf_pos = 0; |
| static uint16_t instr = 0; |
| static uint32_t instr_addr = 0; |
| static uint8_t * instr_code = NULL; |
| static ContextAddress instr_size = 0; |
| static const char * it_cond_name = NULL; |
| static unsigned it_cnt = 0; |
| static unsigned it_pos = 0; |
| static unsigned it_mask = 0; |
| static unsigned it_cond = 0; |
| static DisassemblerParams * params; |
| |
| static const char * shift_names[] = { "lsl", "lsr", "asr", "ror" }; |
| |
| static const char * cond_names[] = { |
| "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", |
| "hi", "ls", "ge", "lt", "gt", "le", "", "nv" |
| }; |
| |
| static const char * reg_names[] = { |
| "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", |
| "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc" |
| }; |
| |
| static void add_char(char ch) { |
| if (buf_pos >= sizeof(buf) - 1) return; |
| buf[buf_pos++] = ch; |
| if (ch == ' ') while (buf_pos < 8) buf[buf_pos++] = ch; |
| } |
| |
| static void add_str(const char * s) { |
| while (*s) add_char(*s++); |
| } |
| |
| static void add_dec_uint32(uint32_t n) { |
| char s[32]; |
| size_t i = 0; |
| do { |
| s[i++] = '0' + n % 10; |
| n = n / 10; |
| } |
| while (n != 0); |
| while (i > 0) add_char(s[--i]); |
| } |
| |
| static void add_hex_uint32(uint32_t n) { |
| char s[32]; |
| size_t i = 0; |
| while (i < 8) { |
| uint32_t d = n & 0xf; |
| s[i++] = (char)(d < 10 ? '0' + d : 'a' + d - 10); |
| n = n >> 4; |
| } |
| while (i > 0) add_char(s[--i]); |
| } |
| |
| #define add_reg_name(reg) add_str(reg_names[(reg) & 0xf]) |
| |
| static void add_modifed_immediate_constant(uint16_t suffix) { |
| uint32_t rot = (suffix >> 12) & 7; |
| uint32_t val = suffix & 0xff; |
| uint32_t dec = 0; |
| if (instr & (1 << 10)) rot |= 8; |
| switch (rot) { |
| case 0: |
| break; |
| case 1: |
| val |= val << 16; |
| break; |
| case 2: |
| val = (val << 8) | (val << 24); |
| break; |
| case 3: |
| val |= (val << 8) | (val << 16) | (val << 24); |
| break; |
| default: |
| rot = rot << 1; |
| if (val & 0x80) rot |= 1; |
| val |= 0x80; |
| val = (val >> rot) | (val << (32 - rot)); |
| break; |
| } |
| add_char('#'); |
| dec = val; |
| #if 0 |
| if (dec & 0x80000000) { |
| add_char('-'); |
| dec = ~dec + 1; |
| } |
| #endif |
| add_dec_uint32(dec); |
| #if 0 |
| if (val > 0x10) { |
| add_str(" ; 0x"); |
| add_hex_uint32(val); |
| } |
| #endif |
| } |
| |
| static void add_addr(uint32_t addr) { |
| while (buf_pos < 16) add_char(' '); |
| add_str("; addr=0x"); |
| add_hex_uint32(addr); |
| #if ENABLE_Symbols |
| if (params->ctx != NULL) { |
| Symbol * sym = NULL; |
| char * name = NULL; |
| ContextAddress sym_addr = 0; |
| if (find_symbol_by_addr(params->ctx, STACK_NO_FRAME, addr, &sym) < 0) return; |
| if (get_symbol_name(sym, &name) < 0 || name == NULL) return; |
| if (get_symbol_address(sym, &sym_addr) < 0) return; |
| if (sym_addr <= addr) { |
| add_str(": "); |
| add_str(name); |
| if (sym_addr < addr) { |
| add_str(" + 0x"); |
| add_hex_uint32(addr - (uint32_t)sym_addr); |
| } |
| } |
| } |
| #endif |
| } |
| |
| static void add_branch_address(int32_t offset) { |
| add_char(' '); |
| if (offset < 0) { |
| add_char('-'); |
| add_dec_uint32((-offset & 0xffffffff)); |
| } |
| else { |
| add_char('+'); |
| add_dec_uint32(offset); |
| } |
| add_addr(instr_addr + offset + 4); |
| } |
| |
| static void disassemble_thumb0(void) { |
| uint32_t op; |
| uint32_t imm; |
| |
| if ((instr & 0xf800) == 0x1800) { |
| /* Add/substruct register/immediate */ |
| add_str(instr & (1 << 9) ? "sub" : "add"); |
| add_str(it_cond_name ? it_cond_name : "s"); |
| add_char(' '); |
| add_reg_name(instr & 7); |
| add_str(", "); |
| add_reg_name((instr >> 3) & 7); |
| add_str(", "); |
| if (instr & (1 << 10)) { |
| add_char('#'); |
| add_dec_uint32((instr >> 6) & 7); |
| } |
| else { |
| add_reg_name((instr >> 6) & 7); |
| } |
| return; |
| } |
| |
| /* Shift by immediate */ |
| op = (instr >> 11) & 3; |
| imm = (instr >> 6) & 0x1f; |
| switch (op) { |
| case 0: add_str(imm ? "lsl" : "mov"); break; |
| case 1: add_str("lsr"); break; |
| case 2: add_str("asr"); break; |
| } |
| add_str(it_cond_name ? it_cond_name : "s"); |
| add_char(' '); |
| add_reg_name(instr & 7); |
| add_str(", "); |
| add_reg_name((instr >> 3) & 7); |
| if (imm || op) { |
| add_str(", #"); |
| if (op >= 1 && imm == 0) imm = 32; |
| add_dec_uint32(imm); |
| } |
| } |
| |
| static void disassemble_thumb1(void) { |
| /* Add/substruct/compare/move immediate */ |
| uint32_t op = (instr >> 11) & 3; |
| switch(op) { |
| case 0: add_str("mov"); break; |
| case 1: add_str("cmp"); break; |
| case 2: add_str("add"); break; |
| case 3: add_str("sub"); break; |
| } |
| if (it_cond_name) add_str(it_cond_name); |
| else if (op != 1) add_char('s'); |
| add_char(' '); |
| add_reg_name((instr >> 8) & 7); |
| add_str(", #"); |
| add_dec_uint32(instr & 0xff); |
| } |
| |
| static void disassemble_thumb2(void) { |
| if ((instr & 0xfc00) == 0x4000) { |
| /* Data-processing register */ |
| uint32_t op = (instr >> 6) & 0xf; |
| switch (op) { |
| case 0: add_str("and"); break; |
| case 1: add_str("eor"); break; |
| case 2: add_str("lsl"); break; |
| case 3: add_str("lsr"); break; |
| case 4: add_str("asr"); break; |
| case 5: add_str("adc"); break; |
| case 6: add_str("sbc"); break; |
| case 7: add_str("ror"); break; |
| case 8: add_str("tst"); break; |
| case 9: add_str("rsb"); break; |
| case 10: add_str("cmp"); break; |
| case 11: add_str("cmn"); break; |
| case 12: add_str("orr"); break; |
| case 13: add_str("mul"); break; |
| case 14: add_str("bic"); break; |
| case 15: add_str("mvn"); break; |
| } |
| if (it_cond_name) add_str(it_cond_name); |
| else if (op != 8 && op != 10 && op != 11) add_char('s'); |
| add_char(' '); |
| add_reg_name(instr & 7); |
| add_str(", "); |
| add_reg_name((instr >> 3) & 7); |
| if (op == 9) add_str(", #0"); |
| return; |
| } |
| |
| if ((instr & 0xff00) == 0x4700) { |
| /* Branch/exchange */ |
| add_char('b'); |
| if (instr & (1 << 7)) add_char('l'); |
| add_char('x'); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name((instr >> 3) & 0xf); |
| return; |
| } |
| |
| if ((instr & 0xfc00) == 0x4400) { |
| /* Special data processing */ |
| unsigned rd = instr & 7; |
| if (instr & (1 << 7)) rd += 8; |
| switch ((instr >> 8) & 3) { |
| case 0: add_str("add"); break; |
| case 1: add_str("cmp"); break; |
| case 2: add_str("mov"); break; |
| } |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name(rd); |
| add_str(", "); |
| add_reg_name((instr >> 3) & 0xf); |
| return; |
| } |
| |
| if ((instr & 0xf800) == 0x4800) { |
| /* Load from literal pool */ |
| add_str("ldr"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name((instr >> 8) & 7); |
| add_str(", [pc, #"); |
| add_dec_uint32((instr & 0xff) << 2); |
| add_char(']'); |
| return; |
| } |
| |
| /* Load/store register offset */ |
| switch ((instr >> 9) & 7) { |
| case 0: add_str("str"); break; |
| case 1: add_str("strh"); break; |
| case 2: add_str("strb"); break; |
| case 3: add_str("ldrsb"); break; |
| case 4: add_str("ldr"); break; |
| case 5: add_str("ldrh"); break; |
| case 6: add_str("ldrb"); break; |
| case 7: add_str("ldrsh"); break; |
| } |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name(instr & 7); |
| add_str(", ["); |
| add_reg_name((instr >> 3) & 7); |
| add_str(", "); |
| add_reg_name((instr >> 6) & 7); |
| add_char(']'); |
| } |
| |
| static void disassemble_thumb3(void) { |
| /* Load/store word/byte immediate offset */ |
| uint32_t imm = (instr >> 6) & 0x1f; |
| int B = (instr & (1 << 12)) != 0; |
| if (!B) imm = imm << 2; |
| add_str(instr & (1 << 11) ? "ldr" : "str"); |
| if (B) add_char('b'); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name(instr & 7); |
| add_str(", ["); |
| add_reg_name((instr >> 3) & 7); |
| add_str(", #"); |
| add_dec_uint32(imm); |
| add_char(']'); |
| } |
| |
| static void disassemble_thumb4(void) { |
| add_str(instr & (1 << 11) ? "ldr" : "str"); |
| |
| if ((instr & 0xf000) == 0x8000) { |
| /* Load/store halfword immediate offset */ |
| add_char('h'); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name(instr & 7); |
| add_str(", ["); |
| add_reg_name((instr >> 3) & 7); |
| add_str(", #"); |
| add_dec_uint32(((instr >> 6) & 0x1f) << 1); |
| add_char(']'); |
| return; |
| } |
| |
| /* Load/store to/from stack */ |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name((instr >> 8) & 7); |
| add_str(", [sp, #"); |
| add_dec_uint32((instr & 0xff) << 2); |
| add_char(']'); |
| } |
| |
| static void disassemble_thumb5(void) { |
| if ((instr & 0xf000) == 0xa000) { |
| /* Add to SP or PC */ |
| add_str("add"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name((instr >> 8) & 7); |
| add_str(", "); |
| add_str(instr & (1 << 11) ? "sp" : "pc"); |
| add_str(", #"); |
| add_dec_uint32((instr & 0xff) << 2); |
| return; |
| } |
| |
| if ((instr & 0xff00) == 0xb000) { |
| /* Adjust stack pointer */ |
| add_str(instr & (1 << 7) ? "sub" : "add"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_str(" sp, #"); |
| add_dec_uint32((instr & 0x7f) << 2); |
| return; |
| } |
| |
| if ((instr & 0xf600) == 0xb400) { |
| /* Push/pop register list */ |
| int cnt = 0; |
| unsigned reg; |
| add_str(instr & (1 << 11) ? "pop" : "push"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_str(" {"); |
| for (reg = 0; reg < 8; reg++) { |
| if ((instr & (1 << reg)) == 0) continue; |
| if (cnt > 0) add_char(','); |
| add_reg_name(reg); |
| cnt++; |
| } |
| if (instr & (1 << 8)) { |
| if (cnt > 0) add_char(','); |
| add_str(instr & (1 << 11) ? "pc" : "lr"); |
| } |
| add_char('}'); |
| return; |
| } |
| |
| if ((instr & 0xff00) == 0xbe00) { |
| /* Software breakpoint */ |
| add_str("bkpt"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_dec_uint32(instr & 0xff); |
| return; |
| } |
| |
| if ((instr & 0xffe0) == 0xb640) { |
| add_str("setend"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_str(instr & (1 << 3) ? "be" : "le"); |
| return; |
| } |
| |
| if ((instr & 0xffe0) == 0xb660) { |
| add_str("cps"); |
| add_str(instr & (1 << 4) ? "id" : "ie"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| if (instr & (1 << 2)) add_char('a'); |
| if (instr & (1 << 1)) add_char('i'); |
| if (instr & (1 << 0)) add_char('f'); |
| return; |
| } |
| |
| if ((instr & 0xf500) == 0xb100) { |
| uint32_t offs = (instr >> 3) & 0x1f; |
| if (instr & (1 << 9)) offs |= 0x20; |
| offs = offs << 1; |
| add_str("cb"); |
| if (instr & (1 << 11)) add_char('n'); |
| add_char('z'); |
| add_char(' '); |
| add_reg_name(instr & 7); |
| add_str(", +"); |
| add_dec_uint32(offs); |
| add_char(' '); |
| add_addr(instr_addr + offs + 4); |
| return; |
| } |
| |
| if ((instr & 0xff00) == 0xb200) { |
| add_char(instr & (1 << 7) ? 'u' : 's'); |
| add_str("xt"); |
| add_char(instr & (1 << 6) ? 'b' : 'h'); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name(instr & 7); |
| add_str(", "); |
| add_reg_name((instr >> 3) & 7); |
| return; |
| } |
| |
| if ((instr & 0xff00) == 0xba00) { |
| add_str("rev"); |
| switch ((instr >> 6) & 3) { |
| case 1: add_str("16"); break; |
| case 3: add_str("sh"); break; |
| } |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name(instr & 7); |
| add_str(", "); |
| add_reg_name((instr >> 3) & 7); |
| return; |
| } |
| |
| if ((instr & 0xff00) == 0xbf00) { |
| if (instr & 0x000f) { |
| /* If-Then */ |
| it_mask = instr & 0xf; |
| it_cond = (instr >> 4) & 0xf; |
| add_str("it"); |
| it_pos = 0; |
| it_cnt = 1; |
| if (it_mask & 7) { |
| char a = it_cond & 1 ? 't' : 'e'; |
| char b = it_cond & 1 ? 'e' : 't'; |
| add_char(it_mask & 0x8 ? a : b); |
| it_cnt++; |
| if (it_mask & 3) { |
| add_char(it_mask & 0x4 ? a : b); |
| it_cnt++; |
| if (it_mask & 1) { |
| add_char(it_mask & 0x2 ? a : b); |
| it_cnt++; |
| } |
| } |
| } |
| add_char(' '); |
| add_str(cond_names[it_cond]); |
| return; |
| } |
| switch ((instr >> 4) & 0xf) { |
| case 0: add_str("nop"); break; |
| case 1: add_str("yield"); break; |
| case 2: add_str("wfe"); break; |
| case 3: add_str("wfi"); break; |
| case 4: add_str("sev"); break; |
| } |
| if (buf_pos > 0) { |
| if (it_cond_name) add_str(it_cond_name); |
| return; |
| } |
| } |
| } |
| |
| static void disassemble_thumb6(void) { |
| if ((instr & 0xff00) == 0xdf00) { |
| /* Software interrupt */ |
| add_str("svc"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_dec_uint32(instr & 0xff); |
| return; |
| } |
| |
| if ((instr & 0xff00) == 0xde00) { |
| /* Undefined instruction */ |
| return; |
| } |
| |
| if ((instr & 0xf000) == 0xd000) { |
| /* Conditional branch */ |
| int32_t offset = instr & 0x00ff; |
| if (offset & 0x0080) offset |= ~0x00ff; |
| offset = offset << 1; |
| add_char('b'); |
| add_str(cond_names[(instr >> 8) & 0xf]); |
| add_str(".n"); |
| add_branch_address(offset); |
| return; |
| } |
| |
| { |
| /* Load/store multiple */ |
| int cnt = 0; |
| uint32_t rn = (instr >> 8) & 7; |
| uint32_t regs = instr & 0xff; |
| unsigned reg; |
| add_str(instr & (1 << 11) ? "ldmia" : "stmia"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name(rn); |
| if (((regs & (1 << rn)) == 0) || |
| ((instr & (1 << 11)) == 0)) { |
| add_char('!'); |
| } |
| add_str(", {"); |
| for (reg = 0; reg < 8; reg++) { |
| if ((regs & (1 << reg)) == 0) continue; |
| if (cnt > 0) add_str(", "); |
| add_reg_name(reg); |
| cnt++; |
| } |
| add_char('}'); |
| } |
| } |
| |
| static void disassemble_load_store_32(uint16_t suffix) { |
| int W = (instr & (1u << 5)) != 0; |
| |
| if ((instr & (1 << 6)) == 0) { |
| /* Load/store multiple */ |
| unsigned i, j; |
| uint32_t op = (instr >> 7) & 3; |
| int L = (instr & (1 << 4)) != 0; |
| int W = (instr & (1 << 5)) != 0; |
| if (op == 0 || op == 3) { |
| if (!L) { |
| add_str("srs"); |
| if (op == 0) add_str("db"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_str("sp"); |
| if (W) add_char('!'); |
| add_str(", #"); |
| add_dec_uint32(suffix & 0x1f); |
| } |
| else { |
| add_str("rfe"); |
| if (op == 0) add_str("db"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name(instr & 0xf); |
| if (W) add_char('!'); |
| } |
| return; |
| } |
| if (instr == 0xe92d) { |
| add_str("push"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_str(".w"); |
| } |
| else if (instr == 0xe8bd) { |
| add_str("pop"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_str(".w"); |
| } |
| else { |
| add_str(L ? "ldm" : "stm"); |
| add_str(instr & (1 << 8) ? "db" : "ia"); |
| if (it_cond_name) add_str(it_cond_name); |
| if ((instr & (1 << 8)) == 0) add_str(".w"); |
| add_char(' '); |
| add_reg_name(instr & 0xf); |
| if (W) add_char('!'); |
| add_char(','); |
| } |
| add_str(" {"); |
| for (i = 0, j = 0; i < 16; i++) { |
| if (suffix & (1 << i)) { |
| if (j) add_char(','); |
| add_reg_name(i); |
| j++; |
| } |
| } |
| add_char('}'); |
| return; |
| } |
| |
| if ((instr & 0xffe0) == 0xe840) { |
| /* Load/store exclusive */ |
| uint32_t imm = (suffix & 0xff) << 2; |
| add_str(instr & (1u << 4) ? "ldrex" : "strex"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| if ((instr & (1u << 4)) == 0) { |
| add_reg_name((suffix >> 8) & 0xf); |
| add_str(", "); |
| } |
| add_reg_name((suffix >> 12) & 0xf); |
| add_str(", ["); |
| add_reg_name(instr & 0xf); |
| if (imm != 0) { |
| add_str(", #"); |
| add_dec_uint32(imm); |
| } |
| add_char(']'); |
| return; |
| } |
| |
| if ((instr & 0xffe0) == 0xe8c0) { |
| /* Load/store exclusive */ |
| char sz = 0; |
| switch ((suffix >> 4) & 0xf) { |
| case 4: sz = 'b'; break; |
| case 5: sz = 'h'; break; |
| case 7: sz = 'd'; break; |
| } |
| if (sz != 0) { |
| add_str(instr & (1u << 4) ? "ldrex" : "strex"); |
| add_char(sz); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| if ((instr & (1u << 4)) == 0) { |
| add_reg_name(suffix & 0xf); |
| add_str(", "); |
| } |
| add_reg_name((suffix >> 12) & 0xf); |
| if (sz == 'd') { |
| add_str(", "); |
| add_reg_name((suffix >> 8) & 0xf); |
| } |
| add_str(", ["); |
| add_reg_name(instr & 0xf); |
| add_char(']'); |
| return; |
| } |
| } |
| |
| if ((instr & 0xff60) == 0xe860 || (instr & 0xff40) == 0xe940) { |
| /* Load/store register dual */ |
| add_str(instr & (1u << 4) ? "ldrd" : "strd"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name((suffix >> 12) & 0xf); |
| add_str(", "); |
| add_reg_name((suffix >> 8) & 0xf); |
| add_str(", ["); |
| if ((instr & 0xf) != 0xf) { |
| /* Immediate */ |
| uint32_t imm = (suffix & 0xff) << 2; |
| int P = (instr & (1u << 8)) != 0; |
| int U = (instr & (1u << 7)) != 0; |
| add_reg_name(instr & 0xf); |
| if (P) { |
| if (imm != 0 || W) { |
| add_str(", #"); |
| add_char(U ? '+' : '-'); |
| add_dec_uint32(imm); |
| } |
| add_char(']'); |
| if (W) add_char('!'); |
| } |
| else { |
| add_str("], #"); |
| add_char(U ? '+' : '-'); |
| add_dec_uint32(imm); |
| } |
| } |
| else { |
| /* Literal */ |
| uint32_t imm = (suffix & 0xff) << 2; |
| int U = (instr & (1u << 7)) != 0; |
| add_str("pc, #"); |
| add_char(U ? '+' : '-'); |
| add_dec_uint32(imm); |
| add_char(']'); |
| if (W) add_char('!'); |
| add_addr(U ? instr_addr + imm + 4 : instr_addr - imm + 4); |
| } |
| return; |
| } |
| |
| if ((instr & 0xfff0) == 0xe8d0 && (suffix & 0x00e0) == 0x0000) { |
| /* Table Branch */ |
| add_str("tb"); |
| add_char(suffix & (1 << 4) ? 'h' : 'b'); |
| if (it_cond_name) add_str(it_cond_name); |
| add_str(" ["); |
| add_reg_name(instr & 0xf); |
| add_str(", "); |
| add_reg_name(suffix & 0xf); |
| if (suffix & (1 << 4)) { |
| add_str(", lsl #1"); |
| } |
| add_char(']'); |
| return; |
| } |
| } |
| |
| static void disassemble_data_processing_32(uint16_t suffix) { |
| uint32_t op_code = (instr >> 5) & 0xf; |
| uint32_t shift_imm = ((suffix >> 10) & 0x1c) | ((suffix >> 6) & 3); |
| uint32_t shift_type = (suffix >> 4) & 3; |
| int I = (instr & (1 << 9)) == 0; |
| int S = (instr & (1 << 4)) != 0; |
| uint32_t rn = instr & 0xf; |
| uint32_t rd = (suffix >> 8) & 0xf; |
| int no_rd = 0; |
| int no_rn = 0; |
| int no_S = 0; |
| int no_shift = 0; |
| |
| switch (op_code) { |
| case 0: |
| add_str(((rd == 15) && S) ? "tst" : "and"); |
| no_S = rd == 15 && S; |
| no_rd = no_S; |
| break; |
| case 1: |
| add_str("bic"); |
| break; |
| case 2: |
| /* |
| * ASR<c>.W {<Rd>,} <Rm>, <Rs> |
| * is equivalent to |
| * MOV{<c>}{<q>} <Rd>, <Rm>, ASR <Rs> |
| * and is always the preferred disassembly. |
| */ |
| if (!I && (shift_type != 0 || shift_imm != 0) && (rn == 15)) { |
| no_shift = 1; |
| if (shift_type == 3 && shift_imm == 0) { |
| add_str("rrx"); |
| } |
| else { |
| add_str(shift_names[shift_type]); |
| } |
| } |
| else { |
| add_str(rn == 15 ? "mov" : "orr"); |
| } |
| no_rn = rn == 15; |
| break; |
| case 3: |
| add_str(rn == 15 ? "mvn" : "orn"); |
| no_rn = rn == 15; |
| break; |
| case 4: |
| add_str((rd == 15 && S)? "teq" : "eor"); |
| no_S = rd == 15 && S; |
| no_rd = no_S; |
| break; |
| case 6: |
| add_str("pkh"); |
| break; |
| case 8: |
| if (rd == 15 && S) { |
| add_str("cmn"); |
| S = 0; |
| no_rd = 1; |
| } |
| else add_str("add"); |
| break; |
| case 10: |
| add_str("adc"); |
| break; |
| case 11: |
| add_str("sbc"); |
| break; |
| case 13: |
| add_str((rd == 15 && S) ? "cmp" : "sub"); |
| if (rd == 15 && S) no_rd = 1; |
| no_S = no_rd; |
| break; |
| case 14: |
| add_str("rsb"); |
| break; |
| default: |
| return; |
| } |
| |
| if (S && !no_S) add_char('s'); |
| if (it_cond_name) add_str(it_cond_name); |
| if ((op_code != 14) && (op_code != 6) && (op_code != 4 || (rd != 15 || !S)) && |
| (op_code != 3 || rn == 15)) add_str(".w"); |
| if (op_code == 6) { |
| /* add bt/tb */ |
| if ((suffix >> 5) & 1) add_str("tb"); |
| else add_str("bt"); |
| } |
| add_char(' '); |
| |
| if (!no_rd) { |
| add_reg_name(rd); |
| add_str(", "); |
| } |
| |
| if (!no_rn) { |
| add_reg_name(rn); |
| add_str(", "); |
| } |
| |
| if (!I) { |
| uint8_t rm = (suffix & 0xf); |
| |
| add_reg_name(rm); |
| if (shift_type != 0 || shift_imm != 0) { |
| if (shift_type == 3 && shift_imm == 0) { |
| if (!no_shift) { |
| add_str(", "); |
| add_str("rrx"); |
| } |
| } |
| else { |
| add_str(", "); |
| if (!no_shift) { |
| add_str(shift_names[shift_type]); |
| add_char(' '); |
| } |
| add_char('#'); |
| if (shift_type >= 1 && shift_imm == 0) shift_imm = 32; |
| add_dec_uint32(shift_imm); |
| } |
| } |
| } |
| else { |
| add_modifed_immediate_constant(suffix); |
| } |
| } |
| |
| static void disassemble_data_processing_pbi_32(uint16_t suffix) { |
| uint32_t op_code = (instr >> 4) & 0x1f; |
| uint32_t rn = instr & 0xf; |
| uint32_t imm = suffix & 0xff; |
| int sat_inst = 0; |
| |
| imm |= (suffix & 0x7000) >> 4; |
| if (instr & (1 << 10)) imm |= 0x800; |
| |
| switch (op_code) { |
| case 0: |
| add_str("addw"); |
| break; |
| case 4: |
| add_str("movw"); |
| break; |
| case 10: |
| add_str("subw"); |
| break; |
| case 12: |
| add_str("movt"); |
| break; |
| case 16: |
| add_str("ssat"); |
| sat_inst = 1; |
| break; |
| case 18: |
| add_str(suffix & 0x70c0 ? "ssat" : "ssat16"); |
| sat_inst = 1; |
| break; |
| case 20: |
| add_str("sbfx"); |
| break; |
| case 22: |
| add_str(rn == 15 ? "bfc" : "bfi"); |
| break; |
| case 24: |
| add_str("usat"); |
| sat_inst = 1; |
| break; |
| case 26: |
| add_str(suffix & 0x70c0 ? "usat" : "usat16"); |
| sat_inst = 1; |
| break; |
| case 28: |
| add_str("ubfx"); |
| break; |
| default: |
| buf_pos = 0; |
| return; |
| } |
| |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name((suffix >> 8) & 0xf); |
| |
| if (sat_inst) { |
| /* SSAT, SSAT16, USAT, USAT16 */ |
| uint32_t sat_imm = suffix & 0x1f; |
| add_str(", #"); |
| if (op_code == 16 || op_code == 18) add_dec_uint32(sat_imm + 1); |
| else add_dec_uint32(sat_imm); |
| } |
| |
| if (op_code == 4) { |
| imm |= rn << 12; |
| } |
| else if (op_code == 22 && rn == 15) { |
| /* Nothing */ |
| } |
| else if (op_code == 12) { |
| imm = ((instr & 0xf) << 12) + (((instr >> 10) & 1) << 11) + |
| (((suffix >> 12) & 0x7) << 8) + (suffix & 0xff); |
| } |
| else { |
| add_str(", "); |
| add_reg_name(rn); |
| } |
| if (!sat_inst) add_str(", #"); |
| if (op_code == 22) { |
| imm = (suffix >> 12) & 7; |
| imm = (imm << 2) | ((suffix >> 6) & 3); |
| add_dec_uint32(imm); |
| add_str(", #"); |
| add_dec_uint32((suffix & 0x1f) + 1 - imm); |
| } |
| else if (op_code == 20 || op_code == 28) { |
| imm = (suffix >> 12) & 7; |
| imm = (imm << 2) | ((suffix >> 6) & 3); |
| add_dec_uint32(imm); |
| add_str(", #"); |
| add_dec_uint32((suffix & 0x1f) + 1); |
| } |
| else if (sat_inst) { |
| /* SSAT, SSAT16, USAT, USAT16 */ |
| uint32_t sh = (instr >> 5) & 1; |
| uint32_t imm3 = (suffix >> 12) & 7; |
| uint32_t imm2 = (suffix >> 6) & 3; |
| if (sh == 1 && imm2 == 0 && imm3 == 0) { |
| /* SSAT16, USAT16 : nothing to add. */ |
| return; |
| } |
| else if (sh == 1) { |
| add_str(", asr #"); |
| } |
| else { |
| add_str(", lsl #"); |
| } |
| add_dec_uint32((imm3 << 2) + imm2); |
| } |
| else { |
| add_dec_uint32(imm); |
| } |
| } |
| |
| static void disassemble_branches_and_misc_32(uint16_t suffix) { |
| if ((suffix & 0xd000) == 0x8000) { |
| uint16_t op = (instr >> 4) & 0x7f; |
| if ((op & 0x38) != 0x38) { |
| /* Conditional branch */ |
| int J1 = (suffix & (1 << 13)) != 0; |
| int J2 = (suffix & (1 << 11)) != 0; |
| int S = (instr & (1 << 10)) != 0; |
| int32_t offset = suffix & 0x7ff; |
| offset |= (instr & 0x3f) << 11; |
| if (J1) offset |= 1 << 17; |
| if (J2) offset |= 1 << 18; |
| if (S) offset |= 0xfff80000; |
| offset = offset << 1; |
| add_char('b'); |
| add_str(cond_names[(instr >> 6) & 0xf]); |
| add_str(".w"); |
| add_branch_address(offset); |
| return; |
| } |
| if ((op & 0x7e) == 0x38) { |
| /* Move to Special Register */ |
| int R = 0; |
| uint32_t mask = (suffix >> 8) & 0xf; |
| if ((suffix & 0x0300) != 0x0000) { |
| R = (instr & (1 << 4)) != 0; |
| } |
| if (mask) { |
| add_str("msr"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_str(R ? "spsr" : "cpsr"); |
| add_char('_'); |
| if (mask & 8) add_char('f'); |
| if (mask & 4) add_char('s'); |
| if (mask & 2) add_char('x'); |
| if (mask & 1) add_char('c'); |
| add_str(", "); |
| add_reg_name(instr & 0xf); |
| return; |
| } |
| return; |
| } |
| if (op == 0x3a) { |
| /* Change Processor State, and hints */ |
| uint32_t imod = (suffix >> 9) & 3; |
| int M = (suffix & (1 << 8)) != 0; |
| /* hint instructions if imod == 00 && M == 0 */ |
| if (imod || M) { |
| uint32_t mode = suffix & 0x1f; |
| add_str("cps"); |
| if (imod >= 2) add_str(imod == 2 ? "ie" : "id"); |
| if (imod >= 2) { |
| add_str(M ? " " : ".w "); |
| if (suffix & (1 << 7)) add_char('a'); |
| if (suffix & (1 << 6)) add_char('i'); |
| if (suffix & (1 << 5)) add_char('f'); |
| if (((suffix >> 5) & 0x7) != 0 && M) add_str(", "); |
| } |
| else add_char(' '); |
| if (M) { |
| add_char('#'); |
| add_dec_uint32(mode); |
| } |
| return; |
| } |
| switch (suffix & 0xff) { |
| case 0: add_str("nop"); break; |
| case 1: add_str("yield"); break; |
| case 2: add_str("wfe"); break; |
| case 3: add_str("wfi"); break; |
| case 4: add_str("sev"); break; |
| } |
| if (buf_pos > 0) { |
| if (it_cond_name) add_str(it_cond_name); |
| add_str(".w"); |
| return; |
| } |
| if ((suffix & 0x00f0) == 0x00f0) { |
| add_str("dbg"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_str(" #"); |
| add_dec_uint32(suffix & 0xf); |
| return; |
| } |
| return; |
| } |
| if (op == 0x3b) { |
| /* Miscellaneous control instructions */ |
| uint32_t op = (suffix >> 4) & 0xf; |
| switch (op) { |
| case 0: add_str("leavex"); break; |
| case 1: add_str("enterx"); break; |
| case 2: add_str("clrex"); break; |
| case 4: add_str("dsb"); break; |
| case 5: add_str("dmb"); break; |
| case 6: add_str("isb"); break; |
| } |
| if (op >= 4 && op < 6) { |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| switch (suffix & 0xf) { |
| case 15: add_str("sy"); break; |
| case 14: add_str("st"); break; |
| case 13: add_str("ld"); break; |
| case 11: add_str("ish"); break; |
| case 10: add_str("ishst"); break; |
| case 9: add_str("ishld"); break; |
| case 7: add_str("nsh"); break; |
| case 6: add_str("nshst"); break; |
| case 5: add_str("nshld"); break; |
| case 3: add_str("osh"); break; |
| case 2: add_str("oshst"); break; |
| case 1: add_str("oshld"); break; |
| default: |
| add_str(" #"); |
| add_dec_uint32(suffix & 0xf); |
| } |
| return; |
| } |
| if (op == 6) { |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| switch (suffix & 0xf) { |
| case 15: add_str("sy"); break; |
| default: |
| add_str(" #"); |
| add_dec_uint32(suffix & 0xf); |
| } |
| return; |
| } |
| return; |
| } |
| if (op == 0x3c) { |
| /* Branch and Exchange Jazelle */ |
| add_str("bxj "); |
| add_reg_name(instr & 0x0f); |
| return; |
| } |
| if (op == 0x3d) { |
| /* Exception Return */ |
| add_str("subs"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name((suffix >> 8) & 0xf); |
| add_str(", "); |
| add_reg_name(instr & 0xf); |
| add_str(", #"); |
| add_dec_uint32(suffix & 0xff); |
| return; |
| } |
| if ((op & 0x7e) == 0x3e) { |
| /* Move from Special Register */ |
| add_str("mrs"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_char(' '); |
| add_reg_name((suffix >> 8) & 0xf); |
| add_str(", "); |
| add_str(instr & (1 << 4) ? "spsr" : "cpsr"); |
| return; |
| } |
| if (op == 0x7f) { |
| if (suffix & 0x2000) { |
| /* Permanently UNDEFINED. |
| * This space will not be allocated in future */ |
| return; |
| } |
| /* Secure Monitor Call */ |
| return; |
| } |
| /* Undefined */ |
| return; |
| } |
| |
| { |
| /* B/BL/BLX */ |
| int w = 0; |
| int J1 = (suffix & (1 << 13)) != 0; |
| int J2 = (suffix & (1 << 11)) != 0; |
| int S = (instr & (1 << 10)) != 0; |
| int32_t offset = suffix & 0x7ff; |
| offset |= (instr & 0x3ff) << 11; |
| if (S == J2) offset |= 1 << 21; |
| if (S == J1) offset |= 1 << 22; |
| if (S) offset |= 0xff800000; |
| offset = offset << 1; |
| if ((suffix & 0xd000) == 0x9000) { |
| add_str("b"); |
| w = 1; |
| } |
| else if ((suffix & 0xd000) == 0xc000) { |
| add_str("blx"); |
| } |
| else { |
| add_str("bl"); |
| } |
| if (it_cond_name) add_str(it_cond_name); |
| if (w) add_str(".w"); |
| add_branch_address(offset); |
| return; |
| } |
| } |
| |
| static void disassemble_memory_hints(uint16_t suffix) { |
| if ((instr & 0xfe50) == 0xf810) { |
| int U = instr & (1 << 7); |
| int W = instr & (1 << 5); |
| uint32_t rn = instr & 0xf; |
| uint32_t imm = suffix & 0xfff; |
| add_str(((instr >>8) & 1) ? "pli" : "pld"); |
| if (W) add_char('w'); |
| add_str(" ["); |
| add_reg_name(rn); |
| if (!U && rn != 15) { |
| if ((suffix & 0xff00) != 0xfc00) { |
| if ((suffix & 0xffc0) == 0xf000) { |
| uint32_t imm2 = (suffix >> 4) & 3; |
| add_str(", "); |
| add_reg_name(suffix & 0xf); |
| if (imm2) { |
| add_str(", lsl #"); |
| add_dec_uint32(imm2); |
| } |
| add_char(']'); |
| return; |
| } |
| buf_pos = 0; |
| return; |
| } |
| imm &= 0xff; |
| } |
| if (imm != 0 || !U) { |
| add_str(", #"); |
| if (!U) add_char('-'); |
| else add_char('+'); |
| add_dec_uint32(imm); |
| } |
| add_char(']'); |
| return; |
| } |
| } |
| |
| static void disassemble_data_processing_32_reg(uint16_t suffix) { |
| uint32_t op1 = (instr >> 4) & 0xf; |
| uint32_t op2 = (suffix >> 4) & 0xf; |
| |
| if (op2 == 0) { |
| switch (op1 >> 1) { |
| case 0: add_str("lsl"); break; |
| case 1: add_str("lsr"); break; |
| case 2: add_str("asr"); break; |
| case 3: add_str("ror"); break; |
| } |
| if (buf_pos > 0) { |
| if (instr & (1u << 4)) add_char('s'); |
| if (it_cond_name) add_str(it_cond_name); |
| add_str(".w "); |
| add_reg_name((suffix >> 8) & 0xf); |
| add_str(", "); |
| add_reg_name(instr & 0xf); |
| add_str(", "); |
| add_reg_name(suffix & 0xf); |
| return; |
| } |
| } |
| |
| if (op2 >= 8) { |
| int pc = (instr & 0xf) == 0xf; |
| switch (op1) { |
| case 0: add_str(pc ? "sxth" : "sxtah"); break; |
| case 1: add_str(pc ? "uxth" : "uxtah"); break; |
| case 2: add_str(pc ? "sxtb16" : "sxtab16"); break; |
| case 3: add_str(pc ? "uxtb16" : "uxtab16"); break; |
| case 4: add_str(pc ? "sxtb" : "sxtab"); break; |
| case 5: add_str(pc ? "uxtb" : "uxtab"); break; |
| } |
| if (buf_pos > 0) { |
| uint32_t rot = (suffix >> 4) & 3; |
| if (it_cond_name) add_str(it_cond_name); |
| if (pc) { |
| switch (op1) { |
| case 0: |
| case 1: |
| case 4: |
| case 5: |
| add_str(".w"); |
| break; |
| } |
| } |
| add_char(' '); |
| add_reg_name((suffix >> 8) & 0xf); |
| if (!pc) { |
| add_str(", "); |
| add_reg_name(instr & 0xf); |
| } |
| add_str(", "); |
| add_reg_name(suffix & 0xf); |
| switch (rot) { |
| case 1: add_str(", ror #8"); break; |
| case 2: add_str(", ror #16"); break; |
| case 3: add_str(", ror #24"); break; |
| } |
| return; |
| } |
| } |
| |
| if (op1 >= 8 && (op1 & 3) != 3 && op2 < 8 && (op2 & 3) != 3) { |
| /* Parallel addition and subtraction */ |
| op1 &= 7; |
| op2 &= 3; |
| if (suffix & (1u << 6)) add_char('u'); |
| else if (op2 != 1) add_char('s'); |
| if (op2 == 1) add_char('q'); |
| if (op2 == 2) add_char('h'); |
| switch (op1) { |
| case 1: add_str("add16"); break; |
| case 2: add_str("asx"); break; |
| case 6: add_str("sax"); break; |
| case 5: add_str("sub16"); break; |
| case 0: add_str("add8"); break; |
| case 4: add_str("sub8"); break; |
| } |
| if (it_cond_name) add_str(it_cond_name); |
| add_str(" "); |
| add_reg_name((suffix >> 8) & 0xf); |
| add_str(", "); |
| add_reg_name(instr & 0xf); |
| add_str(", "); |
| add_reg_name(suffix & 0xf); |
| return; |
| } |
| |
| if ((op1 >> 2) == 2 && (op2 >> 2) == 2) { |
| /* Miscellaneous operations */ |
| op1 &= 3; |
| op2 &= 3; |
| if (op1 == 0) { |
| switch (op2) { |
| case 0: add_str("qadd"); break; |
| case 1: add_str("qdadd"); break; |
| case 2: add_str("qsub"); break; |
| case 3: add_str("qdsub"); break; |
| } |
| if (it_cond_name) add_str(it_cond_name); |
| add_str(" "); |
| add_reg_name((suffix >> 8) & 0xf); |
| add_str(", "); |
| add_reg_name(suffix & 0xf); |
| add_str(", "); |
| add_reg_name(instr & 0xf); |
| return; |
| } |
| if (op1 == 1) { |
| switch (op2) { |
| case 0: add_str("rev"); break; |
| case 1: add_str("rev16"); break; |
| case 2: add_str("rbit"); break; |
| case 3: add_str("revsh"); break; |
| } |
| if (it_cond_name) add_str(it_cond_name); |
| if (op2 != 2) { |
| add_str(".w "); |
| } |
| else { |
| add_str(" "); |
| } |
| add_reg_name((suffix >> 8) & 0xf); |
| add_str(", "); |
| add_reg_name(suffix & 0xf); |
| return; |
| } |
| if (op1 == 2 && op2 == 0) { |
| add_str("sel"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_str(" "); |
| add_reg_name((suffix >> 8) & 0xf); |
| add_str(", "); |
| add_reg_name(instr & 0xf); |
| add_str(", "); |
| add_reg_name(suffix & 0xf); |
| return; |
| } |
| if (op1 == 3 && op2 == 0) { |
| add_str("clz"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_str(" "); |
| add_reg_name((suffix >> 8) & 0xf); |
| add_str(", "); |
| add_reg_name(suffix & 0xf); |
| return; |
| } |
| return; |
| } |
| } |
| |
| static void disassemble_multiply_32(uint16_t suffix) { |
| /* Multiply, multiply accumulate, and absolute difference */ |
| uint32_t op1 = (instr >> 4) & 7; |
| uint32_t ra = (suffix >> 12) & 0xf; |
| uint32_t op2 = (suffix >> 4) & 3; |
| int no_ra = 0; |
| int no_size = 0; |
| switch (op1) { |
| case 0: |
| if (op2 == 0) { |
| if (ra != 15) add_str("mla"); |
| else add_str("mul"); |
| no_ra = ra == 15; |
| } |
| else if (op2 == 1) { |
| add_str("mls"); |
| } |
| break; |
| case 1: |
| if (ra != 15) add_str("smla"); |
| else add_str("smul"); |
| no_ra = ra == 15; |
| no_size = ra == 15; |
| add_char(suffix & (1 << 5) ? 't' : 'b'); |
| add_char(suffix & (1 << 4) ? 't' : 'b'); |
| break; |
| case 2: |
| if (op2 < 2) { |
| if (ra != 15) add_str("smlad"); |
| else add_str("smuad"); |
| no_ra = ra == 15; |
| no_size = 1; |
| if (suffix & (1 << 4)) add_char('x'); |
| } |
| break; |
| case 3: |
| if (op2 < 2) { |
| if (ra != 15) add_str("smlaw"); |
| else add_str("smulw"); |
| no_ra = ra == 15; |
| no_size = 1; |
| add_char(suffix & (1 << 4) ? 't' : 'b'); |
| } |
| break; |
| case 4: |
| if (op2 < 2) { |
| if (ra != 15) add_str("smlsd"); |
| else add_str("smusd"); |
| no_ra = ra == 15; |
| no_size = 1; |
| if (suffix & (1 << 4)) add_char('x'); |
| } |
| break; |
| case 5: |
| if (op2 < 2) { |
| if (ra != 15) add_str("smmla"); |
| else add_str("smmul"); |
| no_ra = ra == 15; |
| no_size = 1; |
| if (suffix & (1 << 4)) add_char('r'); |
| } |
| break; |
| case 6: |
| if (op2 < 2) { |
| add_str("smmls"); |
| if (suffix & (1 << 4)) add_char('r'); |
| no_size = 1; |
| } |
| break; |
| case 7: |
| if (op2 == 0) { |
| if (ra != 15) add_str("usada8"); |
| else add_str("usad8"); |
| no_ra = ra == 15; |
| no_size = no_ra; |
| } |
| break; |
| } |
| if (buf_pos > 0) { |
| uint32_t rn = (instr >> 0) & 0xf; |
| uint32_t rd = (suffix >> 8) & 0xf; |
| uint32_t rm = (suffix >> 0) & 0xf; |
| if (it_cond_name) add_str(it_cond_name); |
| if (op2 == 0 && ra == 15 && no_size == 0) add_str(".w"); |
| add_char(' '); |
| add_reg_name(rd); |
| add_str(", "); |
| add_reg_name(rn); |
| add_str(", "); |
| add_reg_name(rm); |
| if (!no_ra) { |
| add_str(", "); |
| add_reg_name(ra); |
| } |
| return; |
| } |
| } |
| |
| static void disassemble_long_multiply_32(uint16_t suffix) { |
| /* Long multiply, long multiply accumulate, and divide */ |
| uint32_t op1 = (instr >> 4) & 7; |
| uint32_t op2 = (suffix >> 4) & 0xf; |
| switch (op1) { |
| case 0: |
| if (op2 == 0) add_str("smull"); |
| break; |
| case 1: |
| if (op2 == 15) add_str("sdiv"); |
| break; |
| case 2: |
| if (op2 == 0) add_str("umull"); |
| break; |
| case 3: |
| if (op2 == 15) add_str("udiv"); |
| break; |
| case 4: |
| if (op2 == 0) { |
| add_str("smlal"); |
| } |
| else if ((op2 >> 2) == 2) { |
| add_str("smlal"); |
| add_char(suffix & (1 << 5) ? 't' : 'b'); |
| add_char(suffix & (1 << 4) ? 't' : 'b'); |
| } |
| else if ((op2 >> 1) == 6) { |
| add_str("smlald"); |
| if (suffix & (1 << 4)) add_char('x'); |
| } |
| break; |
| case 5: |
| if ((op2 >> 1) == 6) { |
| add_str("smlsld"); |
| if (suffix & (1 << 4)) add_char('x'); |
| } |
| break; |
| case 6: |
| if (op2 == 0) add_str("umlal"); |
| else if (op2 == 6) add_str("umaal"); |
| break; |
| } |
| if (buf_pos > 0) { |
| if (it_cond_name) add_str(it_cond_name); |
| switch (op1) { |
| case 0: |
| case 2: |
| case 4: |
| case 5: |
| case 6: |
| add_char(' '); |
| add_reg_name((suffix >> 12) & 0xf); |
| add_str(", "); |
| add_reg_name((suffix >> 8) & 0xf); |
| add_str(", "); |
| add_reg_name(instr & 0xf); |
| add_str(", "); |
| add_reg_name(suffix & 0xf); |
| return; |
| case 1: |
| case 3: |
| add_char(' '); |
| add_reg_name((suffix >> 8) & 0xf); |
| add_str(", "); |
| add_reg_name(instr & 0xf); |
| add_str(", "); |
| add_reg_name(suffix & 0xf); |
| return; |
| } |
| } |
| } |
| |
| static void disassemble_thumb7(void) { |
| unsigned i; |
| uint16_t suffix = 0; |
| |
| if ((instr & 0xf800) == 0xe000) { |
| /* Unconditional branch */ |
| int32_t offset = instr & 0x07ff; |
| if (offset & 0x0400) offset |= ~0x07ff; |
| offset = offset << 1; |
| add_char('b'); |
| if (it_cond_name) add_str(it_cond_name); |
| add_str(".n"); |
| add_branch_address(offset); |
| return; |
| } |
| |
| instr_size = 4; |
| for (i = 0; i < 2; i++) suffix |= (uint16_t)*instr_code++ << (i * 8); |
| |
| if ((instr & 0xfe00) == 0xe800) { |
| disassemble_load_store_32(suffix); |
| return; |
| } |
| |
| if ((instr & 0xfe00) == 0xea00) { |
| disassemble_data_processing_32(suffix); |
| return; |
| } |
| |
| if ((instr & 0xf800) == 0xf000) { |
| if ((suffix & 0x8000) == 0) { |
| if (instr & (1 << 9)) { |
| disassemble_data_processing_pbi_32(suffix); |
| } |
| else { |
| disassemble_data_processing_32(suffix); |
| } |
| } |
| else { |
| disassemble_branches_and_misc_32(suffix); |
| } |
| return; |
| } |
| |
| if ((instr & 0xfe00) == 0xf800) { |
| uint32_t rn = (instr & 0xf); |
| int T = 0; |
| /* |
| * PLD = 0xf890f000 |
| * 0xf810fc00 |
| * 0xf81ff000 |
| * 0xf810f000 |
| * PLI = 0xf990f000 |
| * 0xf910fc00 |
| * 0xf91ff000 |
| * 0xf910f000 |
| */ |
| if ((((instr & 0xffd0) == 0xf890) && ((suffix & 0xf000) == 0xf000)) || |
| (((instr & 0xffd0) == 0xf810) && ((suffix & 0xff00) == 0xfc00)) || |
| (((instr & 0xff7f) == 0xf81f) && ((suffix & 0xf000) == 0xf000)) || |
| (((instr & 0xffd0) == 0xf810) && ((suffix & 0xffc0) == 0xf000)) || |
| (((instr & 0xfff0) == 0xf990) && ((suffix & 0xf000) == 0xf000)) || |
| (((instr & 0xfff0) == 0xf910) && ((suffix & 0xff00) == 0xfc00)) || |
| (((instr & 0xff7f) == 0xf91f) && ((suffix & 0xf000) == 0xf000)) || |
| (((instr & 0xfff0) == 0xf910) && ((suffix & 0xffc0) == 0xf000))) { |
| /* Memory hints (PLI/PLD) */ |
| disassemble_memory_hints(suffix); |
| return; |
| } |
| |
| if (((instr & 0xf84d) == 0xf84d) && |
| (((suffix & 0x0bff) == 0x0b04) || ((suffix & 0x0dff) == 0x0d04))) { |
| if ((suffix & 0x0bff) == 0x0b04) add_str("pop"); |
| else add_str("push"); |
| if (it_cond_name) add_str(it_cond_name); |
| add_str(".w {"); |
| add_reg_name((suffix >> 12) & 0xf); |
| add_char('}'); |
| return; |
| } |
| |
| /* Load/Store single data item */ |
| |
| T = (suffix & 0x0f00) == 0x0e00 && (instr & (1 << 7)) == 0; |
| add_str(instr & (1 << 4) ? "ldr" : "str"); |
| if ((instr & (1 << 6)) == 0) { |
| if (instr & (1 << 8)) add_char('s'); |
| add_char(instr & (1 << 5) ? 'h' : 'b'); |
| } |
| if (T && rn != 15) add_char('t'); |
| if (it_cond_name) add_str(it_cond_name); |
| if (!T || rn == 15) add_str(".w"); |
| add_char(' '); |
| add_reg_name((suffix >> 12) & 0xf); |
| add_str(", ["); |
| add_reg_name(instr & 0xf); |
| if ((instr & 0xf) == 15) { |
| add_str(", #"); |
| add_char(instr & (1 << 7) ? '+' : '-'); |
| add_dec_uint32(suffix & 0xfff); |
| add_char(']'); |
| } |
| else if (instr & (1 << 7)) { |
| uint32_t imm = suffix & 0xfff; |
| if (imm) { |
| add_str(", #"); |
| add_dec_uint32(imm); |
| } |
| add_char(']'); |
| } |
| else if ((suffix & (1 << 11)) == 0) { |
| uint32_t imm = (suffix >> 4) & 3; |
| add_str(", "); |
| add_reg_name(suffix & 0xf); |
| if (imm) { |
| add_str(", lsl #"); |
| add_dec_uint32(imm); |
| } |
| add_char(']'); |
| } |
| else if ((suffix & (1 << 8)) == 0) { |
| uint32_t imm = suffix & 0xff; |
| if (imm) { |
| add_str(", #"); |
| add_char(suffix & (1 << 9) ? '+' : '-'); |
| add_dec_uint32(imm); |
| } |
| add_char(']'); |
| } |
| else if (suffix & (1 << 10)) { |
| uint32_t imm = suffix & 0xff; |
| if (imm) { |
| add_str(", #"); |
| add_char(suffix & (1 << 9) ? '+' : '-'); |
| add_dec_uint32(imm); |
| } |
| add_str("]!"); |
| } |
| else { |
| uint32_t imm = suffix & 0xff; |
| add_str("], #"); |
| add_char(suffix & (1 << 9) ? '+' : '-'); |
| add_dec_uint32(imm); |
| } |
| return; |
| } |
| |
| if ((instr & 0xfe00) == 0xfa00) { |
| if ((instr & (1 << 8)) == 0) { |
| /* Data-processing (register) */ |
| disassemble_data_processing_32_reg(suffix); |
| } |
| else if ((instr & (1 << 7)) == 0) { |
| /* Multiply, multiply accumulate, and absolute difference */ |
| disassemble_multiply_32(suffix); |
| } |
| else { |
| /* Long multiply, long multiply accumulate, and divide */ |
| disassemble_long_multiply_32(suffix); |
| } |
| return; |
| } |
| } |
| |
| static DisassemblyResult * disassemble_arm_ti(uint8_t * code, ContextAddress addr, ContextAddress size) { |
| const char * p; |
| DisassemblyResult * dr = disassemble_arm(code, addr, size, params); |
| if (dr == NULL || dr->text == NULL || it_cond_name == NULL) return dr; |
| p = dr->text; |
| while (*p && *p != ' ') buf[buf_pos++] = *p++; |
| add_str(it_cond_name); |
| while (*p == ' ') p++; |
| buf[buf_pos++] = ' '; |
| while (buf_pos < 8) buf[buf_pos++] = ' '; |
| while (*p) buf[buf_pos++] = *p++; |
| buf[buf_pos] = 0; |
| dr->text = buf; |
| return dr; |
| } |
| |
| DisassemblyResult * disassemble_thumb(uint8_t * code, |
| ContextAddress addr, ContextAddress size, DisassemblerParams * prm) { |
| unsigned i; |
| static DisassemblyResult dr; |
| |
| if (size < 2) return NULL; |
| |
| instr = 0; |
| for (i = 0; i < 2; i++) instr |= (uint16_t)code[i] << (i * 8); |
| memset(&dr, 0, sizeof(dr)); |
| params = prm; |
| |
| instr_size = 2; |
| instr_addr = (uint32_t)addr; |
| instr_code = code + 2; |
| buf_pos = 0; |
| |
| it_cond_name = NULL; |
| if (it_cnt > 0) { |
| if (it_pos == 0) { |
| it_cond_name = cond_names[it_cond]; |
| } |
| else if (it_mask & (1 << (4 - it_pos))) { |
| it_cond_name = cond_names[it_cond | 1u]; |
| } |
| else { |
| it_cond_name = cond_names[it_cond & ~1u]; |
| } |
| it_pos++; |
| it_cnt--; |
| } |
| |
| if ((instr & 0xec00) == 0xec00 && size >= 4) { |
| /* Coprocessor instructions - same as ARM encoding */ |
| uint8_t tmp[4]; |
| tmp[2] = code[0]; |
| tmp[3] = code[1]; |
| tmp[0] = code[2]; |
| tmp[1] = code[3]; |
| if ((instr & 0xef00) == 0xef00) { |
| /* Advanced SIMD data-processing instructions */ |
| tmp[3] = 0xf2 | ((instr >> 12) & 1); |
| } |
| return disassemble_arm_ti(tmp, addr, 4); |
| } |
| |
| if ((instr & 0xff10) == 0xf900 && size >= 4) { |
| /* Advanced SIMD element or structure load/store instructions */ |
| uint8_t tmp[4]; |
| tmp[2] = code[0]; |
| tmp[3] = 0xf4; |
| tmp[0] = code[2]; |
| tmp[1] = code[3]; |
| return disassemble_arm_ti(tmp, addr, 4); |
| } |
| |
| switch ((instr >> 13) & 7) { |
| case 0: disassemble_thumb0(); break; |
| case 1: disassemble_thumb1(); break; |
| case 2: disassemble_thumb2(); break; |
| case 3: disassemble_thumb3(); break; |
| case 4: disassemble_thumb4(); break; |
| case 5: disassemble_thumb5(); break; |
| case 6: disassemble_thumb6(); break; |
| case 7: disassemble_thumb7(); break; |
| } |
| |
| dr.text = buf; |
| dr.size = instr_size; |
| dr.incomplete = it_cnt > 0; |
| if (buf_pos == 0) { |
| if (dr.size == 2) { |
| snprintf(buf, sizeof(buf), ".half 0x%04x", instr); |
| } |
| else if (dr.size == 4) { |
| uint16_t suffix = 0; |
| for (i = 0; i < 2; i++) suffix |= (uint16_t)code[i + 2] << (i * 8); |
| snprintf(buf, sizeof(buf), ".word 0x%04x%04x", instr, suffix); |
| } |
| else { |
| return NULL; |
| } |
| } |
| else { |
| buf[buf_pos] = 0; |
| } |
| return &dr; |
| } |
| |
| #endif /* SERVICE_Disassembly */ |