diff options
author | Dimitar Dimitrov <dimitar@dinux.eu> | 2016-12-30 12:39:50 +0200 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2016-12-31 12:02:50 +1030 |
commit | 111468496477e97c9414d2d54f97bfdaa380f794 (patch) | |
tree | 31a04a1497296d7b1b485e8ee77e747b3f3c971d /opcodes/pru-dis.c | |
parent | 889294f6ffb380eb37b1f1f3bd22807fa9204c14 (diff) | |
download | gdb-111468496477e97c9414d2d54f97bfdaa380f794.zip gdb-111468496477e97c9414d2d54f97bfdaa380f794.tar.gz gdb-111468496477e97c9414d2d54f97bfdaa380f794.tar.bz2 |
PRU Opcode Port
opcodes/
* Makefile.am: Add PRU source files.
* configure.ac: Add PRU target.
* disassemble.c (disassembler): Register PRU arch.
* pru-dis.c: New file.
* pru-opc.c: New file.
* Makefile.in: Regenerate.
* configure: Regenerate.
Signed-off-by: Dimitar Dimitrov <dimitar@dinux.eu>
Diffstat (limited to 'opcodes/pru-dis.c')
-rw-r--r-- | opcodes/pru-dis.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/opcodes/pru-dis.c b/opcodes/pru-dis.c new file mode 100644 index 0000000..dd150c5 --- /dev/null +++ b/opcodes/pru-dis.c @@ -0,0 +1,286 @@ +/* TI PRU disassemble routines + Copyright (C) 2014-2016 Free Software Foundation, Inc. + Contributed by Dimitar Dimitrov <dimitar@dinux.eu> + + This file is part of the GNU opcodes library. + + This library 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 3, or (at your option) + any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with this file; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "sysdep.h" +#include "dis-asm.h" +#include "opcode/pru.h" +#include "libiberty.h" +#include <string.h> +#include <assert.h> + +/* No symbol table is available when this code runs out in an embedded + system as when it is used for disassembler support in a monitor. */ +#if !defined (EMBEDDED_ENV) +#define SYMTAB_AVAILABLE 1 +#include "elf-bfd.h" +#include "elf/pru.h" +#endif + +/* Length of PRU instruction in bytes. */ +#define INSNLEN 4 + +/* Return a pointer to an pru_opcode struct for a given instruction + opcode, or NULL if there is an error. */ +const struct pru_opcode * +pru_find_opcode (unsigned long opcode) +{ + const struct pru_opcode *p; + const struct pru_opcode *op = NULL; + const struct pru_opcode *pseudo_op = NULL; + + for (p = pru_opcodes; p < &pru_opcodes[NUMOPCODES]; p++) + { + if ((p->mask & opcode) == p->match) + { + if ((p->pinfo & PRU_INSN_MACRO) == PRU_INSN_MACRO) + pseudo_op = p; + else if ((p->pinfo & PRU_INSN_LDI32) == PRU_INSN_LDI32) + /* ignore - should be caught with regular patterns */; + else + op = p; + } + } + + return pseudo_op ? pseudo_op : op; +} + +/* There are 32 regular registers, each with 8 possible subfield selectors. */ +#define NUMREGNAMES (32 * 8) + +static void +pru_print_insn_arg_reg (unsigned int r, unsigned int sel, + disassemble_info *info) +{ + unsigned int i = r * RSEL_NUM_ITEMS + sel; + assert (i < (unsigned int)pru_num_regs); + assert (i < NUMREGNAMES); + (*info->fprintf_func) (info->stream, "%s", pru_regs[i].name); +} + +/* The function pru_print_insn_arg uses the character pointed + to by ARGPTR to determine how it print the next token or separator + character in the arguments to an instruction. */ +static int +pru_print_insn_arg (const char *argptr, + unsigned long opcode, bfd_vma address, + disassemble_info *info) +{ + long offs = 0; + unsigned long i = 0; + unsigned long io = 0; + + switch (*argptr) + { + case ',': + (*info->fprintf_func) (info->stream, "%c ", *argptr); + break; + case 'd': + pru_print_insn_arg_reg (GET_INSN_FIELD (RD, opcode), + GET_INSN_FIELD (RDSEL, opcode), + info); + break; + case 'D': + /* The first 4 values for RDB and RSEL are the same, so we + can reuse some code. */ + pru_print_insn_arg_reg (GET_INSN_FIELD (RD, opcode), + GET_INSN_FIELD (RDB, opcode), + info); + break; + case 's': + pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode), + GET_INSN_FIELD (RS1SEL, opcode), + info); + break; + case 'S': + pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode), + RSEL_31_0, + info); + break; + case 'b': + io = GET_INSN_FIELD (IO, opcode); + + if (io) + { + i = GET_INSN_FIELD (IMM8, opcode); + (*info->fprintf_func) (info->stream, "%ld", i); + } + else + { + pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode), + GET_INSN_FIELD (RS2SEL, opcode), + info); + } + break; + case 'B': + io = GET_INSN_FIELD (IO, opcode); + + if (io) + { + i = GET_INSN_FIELD (IMM8, opcode) + 1; + (*info->fprintf_func) (info->stream, "%ld", i); + } + else + { + pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode), + GET_INSN_FIELD (RS2SEL, opcode), + info); + } + break; + case 'j': + io = GET_INSN_FIELD (IO, opcode); + + if (io) + { + /* For the sake of pretty-printing, dump text addresses with + their "virtual" offset that we use for distinguishing + PMEM vs DMEM. This is needed for printing the correct text + labels. */ + bfd_vma text_offset = address & ~0x3fffff; + i = GET_INSN_FIELD (IMM16, opcode) * 4; + (*info->print_address_func) (i + text_offset, info); + } + else + { + pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode), + GET_INSN_FIELD (RS2SEL, opcode), + info); + } + break; + case 'W': + i = GET_INSN_FIELD (IMM16, opcode); + (*info->fprintf_func) (info->stream, "%ld", i); + break; + case 'o': + offs = GET_BROFF_SIGNED (opcode) * 4; + (*info->print_address_func) (address + offs, info); + break; + case 'O': + offs = GET_INSN_FIELD (LOOP_JMPOFFS, opcode) * 4; + (*info->print_address_func) (address + offs, info); + break; + case 'l': + i = GET_BURSTLEN (opcode); + if (i < LSSBBO_BYTECOUNT_R0_BITS7_0) + (*info->fprintf_func) (info->stream, "%ld", i + 1); + else + { + i -= LSSBBO_BYTECOUNT_R0_BITS7_0; + (*info->fprintf_func) (info->stream, "r0.b%ld", i); + } + break; + case 'n': + i = GET_INSN_FIELD (XFR_LENGTH, opcode); + if (i < LSSBBO_BYTECOUNT_R0_BITS7_0) + (*info->fprintf_func) (info->stream, "%ld", i + 1); + else + { + i -= LSSBBO_BYTECOUNT_R0_BITS7_0; + (*info->fprintf_func) (info->stream, "r0.b%ld", i); + } + break; + case 'c': + i = GET_INSN_FIELD (CB, opcode); + (*info->fprintf_func) (info->stream, "%ld", i); + break; + case 'w': + i = GET_INSN_FIELD (WAKEONSTATUS, opcode); + (*info->fprintf_func) (info->stream, "%ld", i); + break; + case 'x': + i = GET_INSN_FIELD (XFR_WBA, opcode); + (*info->fprintf_func) (info->stream, "%ld", i); + break; + default: + (*info->fprintf_func) (info->stream, "unknown"); + break; + } + return 0; +} + +/* pru_disassemble does all the work of disassembling a PRU + instruction opcode. */ +static int +pru_disassemble (bfd_vma address, unsigned long opcode, + disassemble_info *info) +{ + const struct pru_opcode *op; + + info->bytes_per_line = INSNLEN; + info->bytes_per_chunk = INSNLEN; + info->display_endian = info->endian; + info->insn_info_valid = 1; + info->branch_delay_insns = 0; + info->data_size = 0; + info->insn_type = dis_nonbranch; + info->target = 0; + info->target2 = 0; + + /* Find the major opcode and use this to disassemble + the instruction and its arguments. */ + op = pru_find_opcode (opcode); + + if (op != NULL) + { + (*info->fprintf_func) (info->stream, "%s", op->name); + + const char *argstr = op->args; + if (argstr != NULL && *argstr != '\0') + { + (*info->fprintf_func) (info->stream, "\t"); + while (*argstr != '\0') + { + pru_print_insn_arg (argstr, opcode, address, info); + ++argstr; + } + } + } + else + { + /* Handle undefined instructions. */ + info->insn_type = dis_noninsn; + (*info->fprintf_func) (info->stream, "0x%lx", opcode); + } + /* Tell the caller how far to advance the program counter. */ + return INSNLEN; +} + + +/* print_insn_pru is the main disassemble function for PRU. */ +int +print_insn_pru (bfd_vma address, disassemble_info *info) +{ + bfd_byte buffer[INSNLEN]; + int status; + + status = (*info->read_memory_func) (address, buffer, INSNLEN, info); + if (status == 0) + { + unsigned long insn; + insn = (unsigned long) bfd_getl32 (buffer); + status = pru_disassemble (address, insn, info); + } + else + { + (*info->memory_error_func) (status, address, info); + status = -1; + } + return status; +} |