1129198Scognet/* $NetBSD: disassem.c,v 1.14 2003/03/27 16:58:36 mycroft Exp $ */ 2129198Scognet 3139735Simp/*- 4129198Scognet * Copyright (c) 1996 Mark Brinicombe. 5129198Scognet * Copyright (c) 1996 Brini. 6129198Scognet * 7129198Scognet * All rights reserved. 8129198Scognet * 9129198Scognet * Redistribution and use in source and binary forms, with or without 10129198Scognet * modification, are permitted provided that the following conditions 11129198Scognet * are met: 12129198Scognet * 1. Redistributions of source code must retain the above copyright 13129198Scognet * notice, this list of conditions and the following disclaimer. 14129198Scognet * 2. Redistributions in binary form must reproduce the above copyright 15129198Scognet * notice, this list of conditions and the following disclaimer in the 16129198Scognet * documentation and/or other materials provided with the distribution. 17129198Scognet * 3. All advertising materials mentioning features or use of this software 18129198Scognet * must display the following acknowledgement: 19129198Scognet * This product includes software developed by Brini. 20129198Scognet * 4. The name of the company nor the name of the author may be used to 21129198Scognet * endorse or promote products derived from this software without specific 22129198Scognet * prior written permission. 23129198Scognet * 24129198Scognet * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED 25129198Scognet * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 26129198Scognet * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 27129198Scognet * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28129198Scognet * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29129198Scognet * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30129198Scognet * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31129198Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32129198Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33129198Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34129198Scognet * SUCH DAMAGE. 35129198Scognet * 36129198Scognet * RiscBSD kernel project 37129198Scognet * 38129198Scognet * db_disasm.c 39129198Scognet * 40129198Scognet * Kernel disassembler 41129198Scognet * 42129198Scognet * Created : 10/02/96 43129198Scognet * 44129198Scognet * Structured after the sparc/sparc/db_disasm.c by David S. Miller & 45129198Scognet * Paul Kranenburg 46129198Scognet * 47129198Scognet * This code is not complete. Not all instructions are disassembled. 48129198Scognet */ 49129198Scognet 50129198Scognet#include <sys/cdefs.h> 51129198Scognet__FBSDID("$FreeBSD$"); 52129198Scognet#include <sys/param.h> 53129198Scognet 54129198Scognet 55129198Scognet#include <sys/systm.h> 56129198Scognet#include <machine/disassem.h> 57129198Scognet#include <machine/armreg.h> 58129198Scognet#include <ddb/ddb.h> 59129198Scognet 60129198Scognet/* 61129198Scognet * General instruction format 62129198Scognet * 63129198Scognet * insn[cc][mod] [operands] 64129198Scognet * 65129198Scognet * Those fields with an uppercase format code indicate that the field 66129198Scognet * follows directly after the instruction before the separator i.e. 67129198Scognet * they modify the instruction rather than just being an operand to 68129198Scognet * the instruction. The only exception is the writeback flag which 69129198Scognet * follows a operand. 70129198Scognet * 71129198Scognet * 72129198Scognet * 2 - print Operand 2 of a data processing instruction 73129198Scognet * d - destination register (bits 12-15) 74129198Scognet * n - n register (bits 16-19) 75129198Scognet * s - s register (bits 8-11) 76129198Scognet * o - indirect register rn (bits 16-19) (used by swap) 77129198Scognet * m - m register (bits 0-3) 78129198Scognet * a - address operand of ldr/str instruction 79129198Scognet * l - register list for ldm/stm instruction 80129198Scognet * f - 1st fp operand (register) (bits 12-14) 81129198Scognet * g - 2nd fp operand (register) (bits 16-18) 82129198Scognet * h - 3rd fp operand (register/immediate) (bits 0-4) 83129198Scognet * b - branch address 84129198Scognet * t - thumb branch address (bits 24, 0-23) 85129198Scognet * k - breakpoint comment (bits 0-3, 8-19) 86129198Scognet * X - block transfer type 87129198Scognet * Y - block transfer type (r13 base) 88129198Scognet * c - comment field bits(0-23) 89129198Scognet * p - saved or current status register 90129198Scognet * F - PSR transfer fields 91129198Scognet * D - destination-is-r15 (P) flag on TST, TEQ, CMP, CMN 92129198Scognet * L - co-processor transfer size 93129198Scognet * S - set status flag 94129198Scognet * P - fp precision 95129198Scognet * Q - fp precision (for ldf/stf) 96129198Scognet * R - fp rounding 97129198Scognet * v - co-processor data transfer registers + addressing mode 98129198Scognet * W - writeback flag 99129198Scognet * x - instruction in hex 100129198Scognet * # - co-processor number 101129198Scognet * y - co-processor data processing registers 102129198Scognet * z - co-processor register transfer registers 103129198Scognet */ 104129198Scognet 105129198Scognetstruct arm32_insn { 106129198Scognet u_int mask; 107129198Scognet u_int pattern; 108129198Scognet char* name; 109129198Scognet char* format; 110129198Scognet}; 111129198Scognet 112129198Scognetstatic const struct arm32_insn arm32_i[] = { 113129198Scognet { 0x0fffffff, 0x0ff00000, "imb", "c" }, /* Before swi */ 114129198Scognet { 0x0fffffff, 0x0ff00001, "imbrange", "c" }, /* Before swi */ 115129198Scognet { 0x0f000000, 0x0f000000, "swi", "c" }, 116129198Scognet { 0xfe000000, 0xfa000000, "blx", "t" }, /* Before b and bl */ 117129198Scognet { 0x0f000000, 0x0a000000, "b", "b" }, 118129198Scognet { 0x0f000000, 0x0b000000, "bl", "b" }, 119129198Scognet { 0x0fe000f0, 0x00000090, "mul", "Snms" }, 120129198Scognet { 0x0fe000f0, 0x00200090, "mla", "Snmsd" }, 121129198Scognet { 0x0fe000f0, 0x00800090, "umull", "Sdnms" }, 122129198Scognet { 0x0fe000f0, 0x00c00090, "smull", "Sdnms" }, 123129198Scognet { 0x0fe000f0, 0x00a00090, "umlal", "Sdnms" }, 124129198Scognet { 0x0fe000f0, 0x00e00090, "smlal", "Sdnms" }, 125129198Scognet { 0x0d700000, 0x04200000, "strt", "daW" }, 126129198Scognet { 0x0d700000, 0x04300000, "ldrt", "daW" }, 127129198Scognet { 0x0d700000, 0x04600000, "strbt", "daW" }, 128129198Scognet { 0x0d700000, 0x04700000, "ldrbt", "daW" }, 129129198Scognet { 0x0c500000, 0x04000000, "str", "daW" }, 130129198Scognet { 0x0c500000, 0x04100000, "ldr", "daW" }, 131129198Scognet { 0x0c500000, 0x04400000, "strb", "daW" }, 132129198Scognet { 0x0c500000, 0x04500000, "ldrb", "daW" }, 133269956Simp#if __ARM_ARCH >= 6 134239687Sgonzo { 0xffffffff, 0xf57ff01f, "clrex", "c" }, 135239687Sgonzo { 0x0ff00ff0, 0x01800f90, "strex", "dmo" }, 136239687Sgonzo { 0x0ff00fff, 0x01900f9f, "ldrex", "do" }, 137239687Sgonzo { 0x0ff00ff0, 0x01a00f90, "strexd", "dmo" }, 138239687Sgonzo { 0x0ff00fff, 0x01b00f9f, "ldrexd", "do" }, 139239687Sgonzo { 0x0ff00ff0, 0x01c00f90, "strexb", "dmo" }, 140239687Sgonzo { 0x0ff00fff, 0x01d00f9f, "ldrexb", "do" }, 141239687Sgonzo { 0x0ff00ff0, 0x01e00f90, "strexh", "dmo" }, 142239687Sgonzo { 0x0ff00fff, 0x01f00f9f, "ldrexh", "do" }, 143239687Sgonzo#endif 144129198Scognet { 0x0e1f0000, 0x080d0000, "stm", "YnWl" },/* separate out r13 base */ 145236991Simp { 0x0e1f0000, 0x081d0000, "ldm", "YnWl" },/* separate out r13 base */ 146129198Scognet { 0x0e100000, 0x08000000, "stm", "XnWl" }, 147236991Simp { 0x0e100000, 0x08100000, "ldm", "XnWl" }, 148129198Scognet { 0x0e1000f0, 0x00100090, "ldrb", "de" }, 149129198Scognet { 0x0e1000f0, 0x00000090, "strb", "de" }, 150129198Scognet { 0x0e1000f0, 0x001000d0, "ldrsb", "de" }, 151129198Scognet { 0x0e1000f0, 0x001000b0, "ldrh", "de" }, 152129198Scognet { 0x0e1000f0, 0x000000b0, "strh", "de" }, 153129198Scognet { 0x0e1000f0, 0x001000f0, "ldrsh", "de" }, 154129198Scognet { 0x0f200090, 0x00200090, "und", "x" }, /* Before data processing */ 155129198Scognet { 0x0e1000d0, 0x000000d0, "und", "x" }, /* Before data processing */ 156129198Scognet { 0x0ff00ff0, 0x01000090, "swp", "dmo" }, 157129198Scognet { 0x0ff00ff0, 0x01400090, "swpb", "dmo" }, 158129198Scognet { 0x0fbf0fff, 0x010f0000, "mrs", "dp" }, /* Before data processing */ 159129198Scognet { 0x0fb0fff0, 0x0120f000, "msr", "pFm" },/* Before data processing */ 160129198Scognet { 0x0fb0f000, 0x0320f000, "msr", "pF2" },/* Before data processing */ 161129198Scognet { 0x0ffffff0, 0x012fff10, "bx", "m" }, 162129198Scognet { 0x0fff0ff0, 0x016f0f10, "clz", "dm" }, 163129198Scognet { 0x0ffffff0, 0x012fff30, "blx", "m" }, 164129198Scognet { 0xfff000f0, 0xe1200070, "bkpt", "k" }, 165129198Scognet { 0x0de00000, 0x00000000, "and", "Sdn2" }, 166129198Scognet { 0x0de00000, 0x00200000, "eor", "Sdn2" }, 167129198Scognet { 0x0de00000, 0x00400000, "sub", "Sdn2" }, 168129198Scognet { 0x0de00000, 0x00600000, "rsb", "Sdn2" }, 169129198Scognet { 0x0de00000, 0x00800000, "add", "Sdn2" }, 170129198Scognet { 0x0de00000, 0x00a00000, "adc", "Sdn2" }, 171129198Scognet { 0x0de00000, 0x00c00000, "sbc", "Sdn2" }, 172129198Scognet { 0x0de00000, 0x00e00000, "rsc", "Sdn2" }, 173129198Scognet { 0x0df00000, 0x01100000, "tst", "Dn2" }, 174129198Scognet { 0x0df00000, 0x01300000, "teq", "Dn2" }, 175129198Scognet { 0x0de00000, 0x01400000, "cmp", "Dn2" }, 176129198Scognet { 0x0de00000, 0x01600000, "cmn", "Dn2" }, 177129198Scognet { 0x0de00000, 0x01800000, "orr", "Sdn2" }, 178129198Scognet { 0x0de00000, 0x01a00000, "mov", "Sd2" }, 179129198Scognet { 0x0de00000, 0x01c00000, "bic", "Sdn2" }, 180129198Scognet { 0x0de00000, 0x01e00000, "mvn", "Sd2" }, 181129198Scognet { 0x0ff08f10, 0x0e000100, "adf", "PRfgh" }, 182129198Scognet { 0x0ff08f10, 0x0e100100, "muf", "PRfgh" }, 183129198Scognet { 0x0ff08f10, 0x0e200100, "suf", "PRfgh" }, 184129198Scognet { 0x0ff08f10, 0x0e300100, "rsf", "PRfgh" }, 185129198Scognet { 0x0ff08f10, 0x0e400100, "dvf", "PRfgh" }, 186129198Scognet { 0x0ff08f10, 0x0e500100, "rdf", "PRfgh" }, 187129198Scognet { 0x0ff08f10, 0x0e600100, "pow", "PRfgh" }, 188129198Scognet { 0x0ff08f10, 0x0e700100, "rpw", "PRfgh" }, 189129198Scognet { 0x0ff08f10, 0x0e800100, "rmf", "PRfgh" }, 190129198Scognet { 0x0ff08f10, 0x0e900100, "fml", "PRfgh" }, 191129198Scognet { 0x0ff08f10, 0x0ea00100, "fdv", "PRfgh" }, 192129198Scognet { 0x0ff08f10, 0x0eb00100, "frd", "PRfgh" }, 193129198Scognet { 0x0ff08f10, 0x0ec00100, "pol", "PRfgh" }, 194129198Scognet { 0x0f008f10, 0x0e000100, "fpbop", "PRfgh" }, 195129198Scognet { 0x0ff08f10, 0x0e008100, "mvf", "PRfh" }, 196129198Scognet { 0x0ff08f10, 0x0e108100, "mnf", "PRfh" }, 197129198Scognet { 0x0ff08f10, 0x0e208100, "abs", "PRfh" }, 198129198Scognet { 0x0ff08f10, 0x0e308100, "rnd", "PRfh" }, 199129198Scognet { 0x0ff08f10, 0x0e408100, "sqt", "PRfh" }, 200129198Scognet { 0x0ff08f10, 0x0e508100, "log", "PRfh" }, 201129198Scognet { 0x0ff08f10, 0x0e608100, "lgn", "PRfh" }, 202129198Scognet { 0x0ff08f10, 0x0e708100, "exp", "PRfh" }, 203129198Scognet { 0x0ff08f10, 0x0e808100, "sin", "PRfh" }, 204129198Scognet { 0x0ff08f10, 0x0e908100, "cos", "PRfh" }, 205129198Scognet { 0x0ff08f10, 0x0ea08100, "tan", "PRfh" }, 206129198Scognet { 0x0ff08f10, 0x0eb08100, "asn", "PRfh" }, 207129198Scognet { 0x0ff08f10, 0x0ec08100, "acs", "PRfh" }, 208129198Scognet { 0x0ff08f10, 0x0ed08100, "atn", "PRfh" }, 209129198Scognet { 0x0f008f10, 0x0e008100, "fpuop", "PRfh" }, 210129198Scognet { 0x0e100f00, 0x0c000100, "stf", "QLv" }, 211129198Scognet { 0x0e100f00, 0x0c100100, "ldf", "QLv" }, 212129198Scognet { 0x0ff00f10, 0x0e000110, "flt", "PRgd" }, 213129198Scognet { 0x0ff00f10, 0x0e100110, "fix", "PRdh" }, 214129198Scognet { 0x0ff00f10, 0x0e200110, "wfs", "d" }, 215129198Scognet { 0x0ff00f10, 0x0e300110, "rfs", "d" }, 216129198Scognet { 0x0ff00f10, 0x0e400110, "wfc", "d" }, 217129198Scognet { 0x0ff00f10, 0x0e500110, "rfc", "d" }, 218129198Scognet { 0x0ff0ff10, 0x0e90f110, "cmf", "PRgh" }, 219129198Scognet { 0x0ff0ff10, 0x0eb0f110, "cnf", "PRgh" }, 220129198Scognet { 0x0ff0ff10, 0x0ed0f110, "cmfe", "PRgh" }, 221129198Scognet { 0x0ff0ff10, 0x0ef0f110, "cnfe", "PRgh" }, 222129198Scognet { 0xff100010, 0xfe000010, "mcr2", "#z" }, 223129198Scognet { 0x0f100010, 0x0e000010, "mcr", "#z" }, 224129198Scognet { 0xff100010, 0xfe100010, "mrc2", "#z" }, 225129198Scognet { 0x0f100010, 0x0e100010, "mrc", "#z" }, 226129198Scognet { 0xff000010, 0xfe000000, "cdp2", "#y" }, 227129198Scognet { 0x0f000010, 0x0e000000, "cdp", "#y" }, 228129198Scognet { 0xfe100090, 0xfc100000, "ldc2", "L#v" }, 229129198Scognet { 0x0e100090, 0x0c100000, "ldc", "L#v" }, 230129198Scognet { 0xfe100090, 0xfc000000, "stc2", "L#v" }, 231129198Scognet { 0x0e100090, 0x0c000000, "stc", "L#v" }, 232129198Scognet { 0x00000000, 0x00000000, NULL, NULL } 233129198Scognet}; 234129198Scognet 235129198Scognetstatic char const arm32_insn_conditions[][4] = { 236129198Scognet "eq", "ne", "cs", "cc", 237129198Scognet "mi", "pl", "vs", "vc", 238129198Scognet "hi", "ls", "ge", "lt", 239129198Scognet "gt", "le", "", "nv" 240129198Scognet}; 241129198Scognet 242129198Scognetstatic char const insn_block_transfers[][4] = { 243129198Scognet "da", "ia", "db", "ib" 244129198Scognet}; 245129198Scognet 246129198Scognetstatic char const insn_stack_block_transfers[][4] = { 247129198Scognet "ed", "ea", "fd", "fa" 248129198Scognet}; 249129198Scognet 250129198Scognetstatic char const op_shifts[][4] = { 251129198Scognet "lsl", "lsr", "asr", "ror" 252129198Scognet}; 253129198Scognet 254129198Scognetstatic char const insn_fpa_rounding[][2] = { 255129198Scognet "", "p", "m", "z" 256129198Scognet}; 257129198Scognet 258129198Scognetstatic char const insn_fpa_precision[][2] = { 259129198Scognet "s", "d", "e", "p" 260129198Scognet}; 261129198Scognet 262129198Scognetstatic char const insn_fpaconstants[][8] = { 263129198Scognet "0.0", "1.0", "2.0", "3.0", 264129198Scognet "4.0", "5.0", "0.5", "10.0" 265129198Scognet}; 266129198Scognet 267129198Scognet#define insn_condition(x) arm32_insn_conditions[(x >> 28) & 0x0f] 268129198Scognet#define insn_blktrans(x) insn_block_transfers[(x >> 23) & 3] 269129198Scognet#define insn_stkblktrans(x) insn_stack_block_transfers[(x >> 23) & 3] 270129198Scognet#define op2_shift(x) op_shifts[(x >> 5) & 3] 271129198Scognet#define insn_fparnd(x) insn_fpa_rounding[(x >> 5) & 0x03] 272129198Scognet#define insn_fpaprec(x) insn_fpa_precision[(((x >> 18) & 2)|(x >> 7)) & 1] 273129198Scognet#define insn_fpaprect(x) insn_fpa_precision[(((x >> 21) & 2)|(x >> 15)) & 1] 274129198Scognet#define insn_fpaimm(x) insn_fpaconstants[x & 0x07] 275129198Scognet 276129198Scognet/* Local prototypes */ 277129198Scognetstatic void disasm_register_shift(const disasm_interface_t *di, u_int insn); 278129198Scognetstatic void disasm_print_reglist(const disasm_interface_t *di, u_int insn); 279129198Scognetstatic void disasm_insn_ldrstr(const disasm_interface_t *di, u_int insn, 280129198Scognet u_int loc); 281129198Scognetstatic void disasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn, 282129198Scognet u_int loc); 283129198Scognetstatic void disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, 284129198Scognet u_int loc); 285129198Scognetstatic u_int disassemble_readword(u_int address); 286129198Scognetstatic void disassemble_printaddr(u_int address); 287129198Scognet 288129198Scognetvm_offset_t 289129198Scognetdisasm(const disasm_interface_t *di, vm_offset_t loc, int altfmt) 290129198Scognet{ 291279312Sdim const struct arm32_insn *i_ptr = arm32_i; 292129198Scognet 293129198Scognet u_int insn; 294129198Scognet int matchp; 295129198Scognet int branch; 296129198Scognet char* f_ptr; 297129198Scognet int fmt; 298129198Scognet 299129198Scognet fmt = 0; 300129198Scognet matchp = 0; 301129198Scognet insn = di->di_readword(loc); 302129198Scognet 303129198Scognet/* di->di_printf("loc=%08x insn=%08x : ", loc, insn);*/ 304129198Scognet 305129198Scognet while (i_ptr->name) { 306129198Scognet if ((insn & i_ptr->mask) == i_ptr->pattern) { 307129198Scognet matchp = 1; 308129198Scognet break; 309129198Scognet } 310129198Scognet i_ptr++; 311129198Scognet } 312129198Scognet 313129198Scognet if (!matchp) { 314129198Scognet di->di_printf("und%s\t%08x\n", insn_condition(insn), insn); 315129198Scognet return(loc + INSN_SIZE); 316129198Scognet } 317129198Scognet 318129198Scognet /* If instruction forces condition code, don't print it. */ 319129198Scognet if ((i_ptr->mask & 0xf0000000) == 0xf0000000) 320129198Scognet di->di_printf("%s", i_ptr->name); 321129198Scognet else 322129198Scognet di->di_printf("%s%s", i_ptr->name, insn_condition(insn)); 323129198Scognet 324129198Scognet f_ptr = i_ptr->format; 325129198Scognet 326129198Scognet /* Insert tab if there are no instruction modifiers */ 327129198Scognet 328129198Scognet if (*(f_ptr) < 'A' || *(f_ptr) > 'Z') { 329129198Scognet ++fmt; 330129198Scognet di->di_printf("\t"); 331129198Scognet } 332129198Scognet 333129198Scognet while (*f_ptr) { 334129198Scognet switch (*f_ptr) { 335129198Scognet /* 2 - print Operand 2 of a data processing instruction */ 336129198Scognet case '2': 337129198Scognet if (insn & 0x02000000) { 338129198Scognet int rotate= ((insn >> 7) & 0x1e); 339129198Scognet 340129198Scognet di->di_printf("#0x%08x", 341129198Scognet (insn & 0xff) << (32 - rotate) | 342129198Scognet (insn & 0xff) >> rotate); 343236991Simp } else { 344129198Scognet disasm_register_shift(di, insn); 345129198Scognet } 346129198Scognet break; 347129198Scognet /* d - destination register (bits 12-15) */ 348129198Scognet case 'd': 349129198Scognet di->di_printf("r%d", ((insn >> 12) & 0x0f)); 350129198Scognet break; 351129198Scognet /* D - insert 'p' if Rd is R15 */ 352129198Scognet case 'D': 353129198Scognet if (((insn >> 12) & 0x0f) == 15) 354129198Scognet di->di_printf("p"); 355129198Scognet break; 356129198Scognet /* n - n register (bits 16-19) */ 357129198Scognet case 'n': 358129198Scognet di->di_printf("r%d", ((insn >> 16) & 0x0f)); 359129198Scognet break; 360129198Scognet /* s - s register (bits 8-11) */ 361129198Scognet case 's': 362129198Scognet di->di_printf("r%d", ((insn >> 8) & 0x0f)); 363129198Scognet break; 364129198Scognet /* o - indirect register rn (bits 16-19) (used by swap) */ 365129198Scognet case 'o': 366129198Scognet di->di_printf("[r%d]", ((insn >> 16) & 0x0f)); 367129198Scognet break; 368129198Scognet /* m - m register (bits 0-4) */ 369129198Scognet case 'm': 370129198Scognet di->di_printf("r%d", ((insn >> 0) & 0x0f)); 371129198Scognet break; 372129198Scognet /* a - address operand of ldr/str instruction */ 373129198Scognet case 'a': 374129198Scognet disasm_insn_ldrstr(di, insn, loc); 375129198Scognet break; 376129198Scognet /* e - address operand of ldrh/strh instruction */ 377129198Scognet case 'e': 378129198Scognet disasm_insn_ldrhstrh(di, insn, loc); 379129198Scognet break; 380129198Scognet /* l - register list for ldm/stm instruction */ 381129198Scognet case 'l': 382129198Scognet disasm_print_reglist(di, insn); 383129198Scognet break; 384129198Scognet /* f - 1st fp operand (register) (bits 12-14) */ 385129198Scognet case 'f': 386129198Scognet di->di_printf("f%d", (insn >> 12) & 7); 387129198Scognet break; 388129198Scognet /* g - 2nd fp operand (register) (bits 16-18) */ 389129198Scognet case 'g': 390129198Scognet di->di_printf("f%d", (insn >> 16) & 7); 391129198Scognet break; 392129198Scognet /* h - 3rd fp operand (register/immediate) (bits 0-4) */ 393129198Scognet case 'h': 394129198Scognet if (insn & (1 << 3)) 395129198Scognet di->di_printf("#%s", insn_fpaimm(insn)); 396129198Scognet else 397129198Scognet di->di_printf("f%d", insn & 7); 398129198Scognet break; 399129198Scognet /* b - branch address */ 400129198Scognet case 'b': 401129198Scognet branch = ((insn << 2) & 0x03ffffff); 402129198Scognet if (branch & 0x02000000) 403129198Scognet branch |= 0xfc000000; 404129198Scognet di->di_printaddr(loc + 8 + branch); 405129198Scognet break; 406129198Scognet /* t - blx address */ 407129198Scognet case 't': 408129198Scognet branch = ((insn << 2) & 0x03ffffff) | 409129198Scognet (insn >> 23 & 0x00000002); 410129198Scognet if (branch & 0x02000000) 411129198Scognet branch |= 0xfc000000; 412129198Scognet di->di_printaddr(loc + 8 + branch); 413129198Scognet break; 414129198Scognet /* X - block transfer type */ 415129198Scognet case 'X': 416129198Scognet di->di_printf("%s", insn_blktrans(insn)); 417129198Scognet break; 418129198Scognet /* Y - block transfer type (r13 base) */ 419129198Scognet case 'Y': 420129198Scognet di->di_printf("%s", insn_stkblktrans(insn)); 421129198Scognet break; 422129198Scognet /* c - comment field bits(0-23) */ 423129198Scognet case 'c': 424129198Scognet di->di_printf("0x%08x", (insn & 0x00ffffff)); 425129198Scognet break; 426129198Scognet /* k - breakpoint comment (bits 0-3, 8-19) */ 427129198Scognet case 'k': 428129198Scognet di->di_printf("0x%04x", 429129198Scognet (insn & 0x000fff00) >> 4 | (insn & 0x0000000f)); 430129198Scognet break; 431129198Scognet /* p - saved or current status register */ 432129198Scognet case 'p': 433129198Scognet if (insn & 0x00400000) 434129198Scognet di->di_printf("spsr"); 435129198Scognet else 436129198Scognet di->di_printf("cpsr"); 437129198Scognet break; 438129198Scognet /* F - PSR transfer fields */ 439129198Scognet case 'F': 440129198Scognet di->di_printf("_"); 441129198Scognet if (insn & (1 << 16)) 442129198Scognet di->di_printf("c"); 443129198Scognet if (insn & (1 << 17)) 444129198Scognet di->di_printf("x"); 445129198Scognet if (insn & (1 << 18)) 446129198Scognet di->di_printf("s"); 447129198Scognet if (insn & (1 << 19)) 448129198Scognet di->di_printf("f"); 449129198Scognet break; 450129198Scognet /* B - byte transfer flag */ 451129198Scognet case 'B': 452129198Scognet if (insn & 0x00400000) 453129198Scognet di->di_printf("b"); 454129198Scognet break; 455129198Scognet /* L - co-processor transfer size */ 456129198Scognet case 'L': 457129198Scognet if (insn & (1 << 22)) 458129198Scognet di->di_printf("l"); 459129198Scognet break; 460129198Scognet /* S - set status flag */ 461129198Scognet case 'S': 462129198Scognet if (insn & 0x00100000) 463129198Scognet di->di_printf("s"); 464129198Scognet break; 465129198Scognet /* P - fp precision */ 466129198Scognet case 'P': 467129198Scognet di->di_printf("%s", insn_fpaprec(insn)); 468129198Scognet break; 469129198Scognet /* Q - fp precision (for ldf/stf) */ 470129198Scognet case 'Q': 471129198Scognet break; 472129198Scognet /* R - fp rounding */ 473129198Scognet case 'R': 474129198Scognet di->di_printf("%s", insn_fparnd(insn)); 475129198Scognet break; 476129198Scognet /* W - writeback flag */ 477129198Scognet case 'W': 478129198Scognet if (insn & (1 << 21)) 479129198Scognet di->di_printf("!"); 480129198Scognet break; 481129198Scognet /* # - co-processor number */ 482129198Scognet case '#': 483129198Scognet di->di_printf("p%d", (insn >> 8) & 0x0f); 484129198Scognet break; 485129198Scognet /* v - co-processor data transfer registers+addressing mode */ 486129198Scognet case 'v': 487129198Scognet disasm_insn_ldcstc(di, insn, loc); 488129198Scognet break; 489129198Scognet /* x - instruction in hex */ 490129198Scognet case 'x': 491129198Scognet di->di_printf("0x%08x", insn); 492129198Scognet break; 493129198Scognet /* y - co-processor data processing registers */ 494129198Scognet case 'y': 495129198Scognet di->di_printf("%d, ", (insn >> 20) & 0x0f); 496129198Scognet 497129198Scognet di->di_printf("c%d, c%d, c%d", (insn >> 12) & 0x0f, 498129198Scognet (insn >> 16) & 0x0f, insn & 0x0f); 499129198Scognet 500129198Scognet di->di_printf(", %d", (insn >> 5) & 0x07); 501129198Scognet break; 502129198Scognet /* z - co-processor register transfer registers */ 503129198Scognet case 'z': 504129198Scognet di->di_printf("%d, ", (insn >> 21) & 0x07); 505129198Scognet di->di_printf("r%d, c%d, c%d, %d", 506129198Scognet (insn >> 12) & 0x0f, (insn >> 16) & 0x0f, 507129198Scognet insn & 0x0f, (insn >> 5) & 0x07); 508129198Scognet 509129198Scognet/* if (((insn >> 5) & 0x07) != 0) 510129198Scognet di->di_printf(", %d", (insn >> 5) & 0x07);*/ 511129198Scognet break; 512129198Scognet default: 513129198Scognet di->di_printf("[%c - unknown]", *f_ptr); 514129198Scognet break; 515129198Scognet } 516129198Scognet if (*(f_ptr+1) >= 'A' && *(f_ptr+1) <= 'Z') 517129198Scognet ++f_ptr; 518129198Scognet else if (*(++f_ptr)) { 519129198Scognet ++fmt; 520129198Scognet if (fmt == 1) 521129198Scognet di->di_printf("\t"); 522129198Scognet else 523129198Scognet di->di_printf(", "); 524129198Scognet } 525297793Spfg } 526129198Scognet 527129198Scognet di->di_printf("\n"); 528129198Scognet 529129198Scognet return(loc + INSN_SIZE); 530129198Scognet} 531129198Scognet 532129198Scognet 533129198Scognetstatic void 534129198Scognetdisasm_register_shift(const disasm_interface_t *di, u_int insn) 535129198Scognet{ 536129198Scognet di->di_printf("r%d", (insn & 0x0f)); 537129198Scognet if ((insn & 0x00000ff0) == 0) 538129198Scognet ; 539129198Scognet else if ((insn & 0x00000ff0) == 0x00000060) 540129198Scognet di->di_printf(", rrx"); 541129198Scognet else { 542129198Scognet if (insn & 0x10) 543129198Scognet di->di_printf(", %s r%d", op2_shift(insn), 544129198Scognet (insn >> 8) & 0x0f); 545129198Scognet else 546129198Scognet di->di_printf(", %s #%d", op2_shift(insn), 547129198Scognet (insn >> 7) & 0x1f); 548129198Scognet } 549129198Scognet} 550129198Scognet 551129198Scognet 552129198Scognetstatic void 553129198Scognetdisasm_print_reglist(const disasm_interface_t *di, u_int insn) 554129198Scognet{ 555129198Scognet int loop; 556129198Scognet int start; 557129198Scognet int comma; 558129198Scognet 559129198Scognet di->di_printf("{"); 560129198Scognet start = -1; 561129198Scognet comma = 0; 562129198Scognet 563129198Scognet for (loop = 0; loop < 17; ++loop) { 564129198Scognet if (start != -1) { 565129198Scognet if (loop == 16 || !(insn & (1 << loop))) { 566129198Scognet if (comma) 567129198Scognet di->di_printf(", "); 568129198Scognet else 569129198Scognet comma = 1; 570129198Scognet if (start == loop - 1) 571129198Scognet di->di_printf("r%d", start); 572129198Scognet else 573129198Scognet di->di_printf("r%d-r%d", start, loop - 1); 574129198Scognet start = -1; 575129198Scognet } 576129198Scognet } else { 577129198Scognet if (insn & (1 << loop)) 578129198Scognet start = loop; 579129198Scognet } 580129198Scognet } 581129198Scognet di->di_printf("}"); 582129198Scognet 583129198Scognet if (insn & (1 << 22)) 584129198Scognet di->di_printf("^"); 585129198Scognet} 586129198Scognet 587129198Scognetstatic void 588129198Scognetdisasm_insn_ldrstr(const disasm_interface_t *di, u_int insn, u_int loc) 589129198Scognet{ 590129198Scognet int offset; 591129198Scognet 592129198Scognet offset = insn & 0xfff; 593129198Scognet if ((insn & 0x032f0000) == 0x010f0000) { 594129198Scognet /* rA = pc, immediate index */ 595129198Scognet if (insn & 0x00800000) 596129198Scognet loc += offset; 597129198Scognet else 598129198Scognet loc -= offset; 599129198Scognet di->di_printaddr(loc + 8); 600129198Scognet } else { 601129198Scognet di->di_printf("[r%d", (insn >> 16) & 0x0f); 602129198Scognet if ((insn & 0x03000fff) != 0x01000000) { 603129198Scognet di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]"); 604129198Scognet if (!(insn & 0x00800000)) 605129198Scognet di->di_printf("-"); 606129198Scognet if (insn & (1 << 25)) 607129198Scognet disasm_register_shift(di, insn); 608129198Scognet else 609129198Scognet di->di_printf("#0x%03x", offset); 610129198Scognet } 611129198Scognet if (insn & (1 << 24)) 612129198Scognet di->di_printf("]"); 613129198Scognet } 614129198Scognet} 615129198Scognet 616129198Scognetstatic void 617129198Scognetdisasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn, u_int loc) 618129198Scognet{ 619129198Scognet int offset; 620129198Scognet 621129198Scognet offset = ((insn & 0xf00) >> 4) | (insn & 0xf); 622129198Scognet if ((insn & 0x004f0000) == 0x004f0000) { 623129198Scognet /* rA = pc, immediate index */ 624129198Scognet if (insn & 0x00800000) 625129198Scognet loc += offset; 626129198Scognet else 627129198Scognet loc -= offset; 628129198Scognet di->di_printaddr(loc + 8); 629129198Scognet } else { 630129198Scognet di->di_printf("[r%d", (insn >> 16) & 0x0f); 631129198Scognet if ((insn & 0x01400f0f) != 0x01400000) { 632129198Scognet di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]"); 633129198Scognet if (!(insn & 0x00800000)) 634129198Scognet di->di_printf("-"); 635129198Scognet if (insn & (1 << 22)) 636129198Scognet di->di_printf("#0x%02x", offset); 637129198Scognet else 638129198Scognet di->di_printf("r%d", (insn & 0x0f)); 639129198Scognet } 640129198Scognet if (insn & (1 << 24)) 641129198Scognet di->di_printf("]"); 642129198Scognet } 643129198Scognet} 644129198Scognet 645129198Scognetstatic void 646129198Scognetdisasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, u_int loc) 647129198Scognet{ 648129198Scognet if (((insn >> 8) & 0xf) == 1) 649129198Scognet di->di_printf("f%d, ", (insn >> 12) & 0x07); 650129198Scognet else 651129198Scognet di->di_printf("c%d, ", (insn >> 12) & 0x0f); 652129198Scognet 653129198Scognet di->di_printf("[r%d", (insn >> 16) & 0x0f); 654129198Scognet 655129198Scognet di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]"); 656129198Scognet 657129198Scognet if (!(insn & (1 << 23))) 658129198Scognet di->di_printf("-"); 659129198Scognet 660129198Scognet di->di_printf("#0x%03x", (insn & 0xff) << 2); 661129198Scognet 662129198Scognet if (insn & (1 << 24)) 663129198Scognet di->di_printf("]"); 664129198Scognet 665129198Scognet if (insn & (1 << 21)) 666129198Scognet di->di_printf("!"); 667129198Scognet} 668129198Scognet 669129198Scognetstatic u_int 670129198Scognetdisassemble_readword(u_int address) 671129198Scognet{ 672129198Scognet return(*((u_int *)address)); 673129198Scognet} 674129198Scognet 675129198Scognetstatic void 676129198Scognetdisassemble_printaddr(u_int address) 677129198Scognet{ 678129198Scognet printf("0x%08x", address); 679129198Scognet} 680129198Scognet 681129198Scognetstatic const disasm_interface_t disassemble_di = { 682129198Scognet disassemble_readword, disassemble_printaddr, db_printf 683129198Scognet}; 684129198Scognet 685129198Scognetvoid 686129198Scognetdisassemble(u_int address) 687129198Scognet{ 688129198Scognet 689129198Scognet (void)disasm(&disassemble_di, address, 0); 690129198Scognet} 691129198Scognet 692129198Scognet/* End of disassem.c */ 693