| Index: opcodes/nios2-dis.c
|
| diff --git a/opcodes/nios2-dis.c b/opcodes/nios2-dis.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ef7a5f96109b89b825a9a0356dfadd9de05e820c
|
| --- /dev/null
|
| +++ b/opcodes/nios2-dis.c
|
| @@ -0,0 +1,423 @@
|
| +/* Altera Nios II disassemble routines
|
| + Copyright (C) 2012, 2013 Free Software Foundation, Inc.
|
| + Contributed by Nigel Gray (ngray@altera.com).
|
| + Contributed by Mentor Graphics, Inc.
|
| +
|
| + 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/nios2.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/nios2.h"
|
| +#endif
|
| +
|
| +/* Length of Nios II instruction in bytes. */
|
| +#define INSNLEN 4
|
| +
|
| +/* Data structures used by the opcode hash table. */
|
| +typedef struct _nios2_opcode_hash
|
| +{
|
| + const struct nios2_opcode *opcode;
|
| + struct _nios2_opcode_hash *next;
|
| +} nios2_opcode_hash;
|
| +
|
| +static bfd_boolean nios2_hash_init = 0;
|
| +static nios2_opcode_hash *nios2_hash[(OP_MASK_OP) + 1];
|
| +
|
| +/* Separate hash table for pseudo-ops. */
|
| +static nios2_opcode_hash *nios2_ps_hash[(OP_MASK_OP) + 1];
|
| +
|
| +/* Function to initialize the opcode hash table. */
|
| +static void
|
| +nios2_init_opcode_hash (void)
|
| +{
|
| + unsigned int i;
|
| + register const struct nios2_opcode *op;
|
| +
|
| + for (i = 0; i <= OP_MASK_OP; ++i)
|
| + nios2_hash[0] = NULL;
|
| + for (i = 0; i <= OP_MASK_OP; i++)
|
| + for (op = nios2_opcodes; op < &nios2_opcodes[NUMOPCODES]; op++)
|
| + {
|
| + nios2_opcode_hash *new_hash;
|
| + nios2_opcode_hash **bucket = NULL;
|
| +
|
| + if ((op->pinfo & NIOS2_INSN_MACRO) == NIOS2_INSN_MACRO)
|
| + {
|
| + if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP)
|
| + && (op->pinfo & (NIOS2_INSN_MACRO_MOV | NIOS2_INSN_MACRO_MOVI)
|
| + & 0x7fffffff))
|
| + bucket = &(nios2_ps_hash[i]);
|
| + }
|
| + else if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP))
|
| + bucket = &(nios2_hash[i]);
|
| +
|
| + if (bucket)
|
| + {
|
| + new_hash =
|
| + (nios2_opcode_hash *) malloc (sizeof (nios2_opcode_hash));
|
| + if (new_hash == NULL)
|
| + {
|
| + fprintf (stderr,
|
| + "error allocating memory...broken disassembler\n");
|
| + abort ();
|
| + }
|
| + new_hash->opcode = op;
|
| + new_hash->next = NULL;
|
| + while (*bucket)
|
| + bucket = &((*bucket)->next);
|
| + *bucket = new_hash;
|
| + }
|
| + }
|
| + nios2_hash_init = 1;
|
| +#ifdef DEBUG_HASHTABLE
|
| + for (i = 0; i <= OP_MASK_OP; ++i)
|
| + {
|
| + nios2_opcode_hash *tmp_hash = nios2_hash[i];
|
| + printf ("index: 0x%02X ops: ", i);
|
| + while (tmp_hash != NULL)
|
| + {
|
| + printf ("%s ", tmp_hash->opcode->name);
|
| + tmp_hash = tmp_hash->next;
|
| + }
|
| + printf ("\n");
|
| + }
|
| +
|
| + for (i = 0; i <= OP_MASK_OP; ++i)
|
| + {
|
| + nios2_opcode_hash *tmp_hash = nios2_ps_hash[i];
|
| + printf ("index: 0x%02X ops: ", i);
|
| + while (tmp_hash != NULL)
|
| + {
|
| + printf ("%s ", tmp_hash->opcode->name);
|
| + tmp_hash = tmp_hash->next;
|
| + }
|
| + printf ("\n");
|
| + }
|
| +#endif /* DEBUG_HASHTABLE */
|
| +}
|
| +
|
| +/* Return a pointer to an nios2_opcode struct for a given instruction
|
| + opcode, or NULL if there is an error. */
|
| +const struct nios2_opcode *
|
| +nios2_find_opcode_hash (unsigned long opcode)
|
| +{
|
| + nios2_opcode_hash *entry;
|
| +
|
| + /* Build a hash table to shorten the search time. */
|
| + if (!nios2_hash_init)
|
| + nios2_init_opcode_hash ();
|
| +
|
| + /* First look in the pseudo-op hashtable. */
|
| + for (entry = nios2_ps_hash[(opcode >> OP_SH_OP) & OP_MASK_OP];
|
| + entry; entry = entry->next)
|
| + if (entry->opcode->match == (opcode & entry->opcode->mask))
|
| + return entry->opcode;
|
| +
|
| + /* Otherwise look in the main hashtable. */
|
| + for (entry = nios2_hash[(opcode >> OP_SH_OP) & OP_MASK_OP];
|
| + entry; entry = entry->next)
|
| + if (entry->opcode->match == (opcode & entry->opcode->mask))
|
| + return entry->opcode;
|
| +
|
| + return NULL;
|
| +}
|
| +
|
| +/* There are 32 regular registers, 32 coprocessor registers,
|
| + and 32 control registers. */
|
| +#define NUMREGNAMES 32
|
| +
|
| +/* Return a pointer to the base of the coprocessor register name array. */
|
| +static struct nios2_reg *
|
| +nios2_coprocessor_regs (void)
|
| +{
|
| + static struct nios2_reg *cached = NULL;
|
| +
|
| + if (!cached)
|
| + {
|
| + int i;
|
| + for (i = NUMREGNAMES; i < nios2_num_regs; i++)
|
| + if (!strcmp (nios2_regs[i].name, "c0"))
|
| + {
|
| + cached = nios2_regs + i;
|
| + break;
|
| + }
|
| + assert (cached);
|
| + }
|
| + return cached;
|
| +}
|
| +
|
| +/* Return a pointer to the base of the control register name array. */
|
| +static struct nios2_reg *
|
| +nios2_control_regs (void)
|
| +{
|
| + static struct nios2_reg *cached = NULL;
|
| +
|
| + if (!cached)
|
| + {
|
| + int i;
|
| + for (i = NUMREGNAMES; i < nios2_num_regs; i++)
|
| + if (!strcmp (nios2_regs[i].name, "status"))
|
| + {
|
| + cached = nios2_regs + i;
|
| + break;
|
| + }
|
| + assert (cached);
|
| + }
|
| + return cached;
|
| +}
|
| +
|
| +/* The function nios2_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
|
| +nios2_print_insn_arg (const char *argptr,
|
| + unsigned long opcode, bfd_vma address,
|
| + disassemble_info *info)
|
| +{
|
| + unsigned long i = 0;
|
| + struct nios2_reg *reg_base;
|
| +
|
| + switch (*argptr)
|
| + {
|
| + case ',':
|
| + case '(':
|
| + case ')':
|
| + (*info->fprintf_func) (info->stream, "%c", *argptr);
|
| + break;
|
| + case 'd':
|
| + i = GET_INSN_FIELD (RRD, opcode);
|
| +
|
| + if (GET_INSN_FIELD (OP, opcode) == OP_MATCH_CUSTOM
|
| + && GET_INSN_FIELD (CUSTOM_C, opcode) == 0)
|
| + reg_base = nios2_coprocessor_regs ();
|
| + else
|
| + reg_base = nios2_regs;
|
| +
|
| + if (i < NUMREGNAMES)
|
| + (*info->fprintf_func) (info->stream, "%s", reg_base[i].name);
|
| + else
|
| + (*info->fprintf_func) (info->stream, "unknown");
|
| + break;
|
| + case 's':
|
| + i = GET_INSN_FIELD (RRS, opcode);
|
| +
|
| + if (GET_INSN_FIELD (OP, opcode) == OP_MATCH_CUSTOM
|
| + && GET_INSN_FIELD (CUSTOM_A, opcode) == 0)
|
| + reg_base = nios2_coprocessor_regs ();
|
| + else
|
| + reg_base = nios2_regs;
|
| +
|
| + if (i < NUMREGNAMES)
|
| + (*info->fprintf_func) (info->stream, "%s", reg_base[i].name);
|
| + else
|
| + (*info->fprintf_func) (info->stream, "unknown");
|
| + break;
|
| + case 't':
|
| + i = GET_INSN_FIELD (RRT, opcode);
|
| +
|
| + if (GET_INSN_FIELD (OP, opcode) == OP_MATCH_CUSTOM
|
| + && GET_INSN_FIELD (CUSTOM_B, opcode) == 0)
|
| + reg_base = nios2_coprocessor_regs ();
|
| + else
|
| + reg_base = nios2_regs;
|
| +
|
| + if (i < NUMREGNAMES)
|
| + (*info->fprintf_func) (info->stream, "%s", reg_base[i].name);
|
| + else
|
| + (*info->fprintf_func) (info->stream, "unknown");
|
| + break;
|
| + case 'i':
|
| + /* 16-bit signed immediate. */
|
| + i = (signed) (GET_INSN_FIELD (IMM16, opcode) << 16) >> 16;
|
| + (*info->fprintf_func) (info->stream, "%ld", i);
|
| + break;
|
| + case 'u':
|
| + /* 16-bit unsigned immediate. */
|
| + i = GET_INSN_FIELD (IMM16, opcode);
|
| + (*info->fprintf_func) (info->stream, "%ld", i);
|
| + break;
|
| + case 'o':
|
| + /* 16-bit signed immediate address offset. */
|
| + i = (signed) (GET_INSN_FIELD (IMM16, opcode) << 16) >> 16;
|
| + address = address + 4 + i;
|
| + (*info->print_address_func) (address, info);
|
| + break;
|
| + case 'p':
|
| + /* 5-bit unsigned immediate. */
|
| + i = GET_INSN_FIELD (CACHE_OPX, opcode);
|
| + (*info->fprintf_func) (info->stream, "%ld", i);
|
| + break;
|
| + case 'j':
|
| + /* 5-bit unsigned immediate. */
|
| + i = GET_INSN_FIELD (IMM5, opcode);
|
| + (*info->fprintf_func) (info->stream, "%ld", i);
|
| + break;
|
| + case 'l':
|
| + /* 8-bit unsigned immediate. */
|
| + /* FIXME - not yet implemented */
|
| + i = GET_INSN_FIELD (CUSTOM_N, opcode);
|
| + (*info->fprintf_func) (info->stream, "%lu", i);
|
| + break;
|
| + case 'm':
|
| + /* 26-bit unsigned immediate. */
|
| + i = GET_INSN_FIELD (IMM26, opcode);
|
| + /* This translates to an address because it's only used in call
|
| + instructions. */
|
| + address = (address & 0xf0000000) | (i << 2);
|
| + (*info->print_address_func) (address, info);
|
| + break;
|
| + case 'c':
|
| + /* Control register index. */
|
| + i = GET_INSN_FIELD (IMM5, opcode);
|
| + reg_base = nios2_control_regs ();
|
| + (*info->fprintf_func) (info->stream, "%s", reg_base[i].name);
|
| + break;
|
| + case 'b':
|
| + i = GET_INSN_FIELD (IMM5, opcode);
|
| + (*info->fprintf_func) (info->stream, "%ld", i);
|
| + break;
|
| + default:
|
| + (*info->fprintf_func) (info->stream, "unknown");
|
| + break;
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +/* nios2_disassemble does all the work of disassembling a Nios II
|
| + instruction opcode. */
|
| +static int
|
| +nios2_disassemble (bfd_vma address, unsigned long opcode,
|
| + disassemble_info *info)
|
| +{
|
| + const struct nios2_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 = nios2_find_opcode_hash (opcode);
|
| +
|
| + if (op != NULL)
|
| + {
|
| + bfd_boolean is_nop = FALSE;
|
| + if (op->pinfo == NIOS2_INSN_MACRO_MOV)
|
| + {
|
| + /* Check for mov r0, r0 and change to nop. */
|
| + int dst, src;
|
| + dst = GET_INSN_FIELD (RRD, opcode);
|
| + src = GET_INSN_FIELD (RRS, opcode);
|
| + if (dst == 0 && src == 0)
|
| + {
|
| + (*info->fprintf_func) (info->stream, "nop");
|
| + is_nop = TRUE;
|
| + }
|
| + else
|
| + (*info->fprintf_func) (info->stream, "%s", op->name);
|
| + }
|
| + else
|
| + (*info->fprintf_func) (info->stream, "%s", op->name);
|
| +
|
| + if (!is_nop)
|
| + {
|
| + const char *argstr = op->args;
|
| + if (argstr != NULL && *argstr != '\0')
|
| + {
|
| + (*info->fprintf_func) (info->stream, "\t");
|
| + while (*argstr != '\0')
|
| + {
|
| + nios2_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_nios2 is the main disassemble function for Nios II.
|
| + The function diassembler(abfd) (source in disassemble.c) returns a
|
| + pointer to this either print_insn_big_nios2 or
|
| + print_insn_little_nios2, which in turn call this function when the
|
| + bfd machine type is Nios II. print_insn_nios2 reads the
|
| + instruction word at the address given, and prints the disassembled
|
| + instruction on the stream info->stream using info->fprintf_func. */
|
| +
|
| +static int
|
| +print_insn_nios2 (bfd_vma address, disassemble_info *info,
|
| + enum bfd_endian endianness)
|
| +{
|
| + bfd_byte buffer[INSNLEN];
|
| + int status;
|
| +
|
| + status = (*info->read_memory_func) (address, buffer, INSNLEN, info);
|
| + if (status == 0)
|
| + {
|
| + unsigned long insn;
|
| + if (endianness == BFD_ENDIAN_BIG)
|
| + insn = (unsigned long) bfd_getb32 (buffer);
|
| + else
|
| + insn = (unsigned long) bfd_getl32 (buffer);
|
| + status = nios2_disassemble (address, insn, info);
|
| + }
|
| + else
|
| + {
|
| + (*info->memory_error_func) (status, address, info);
|
| + status = -1;
|
| + }
|
| + return status;
|
| +}
|
| +
|
| +/* These two functions are the main entry points, accessed from
|
| + disassemble.c. */
|
| +int
|
| +print_insn_big_nios2 (bfd_vma address, disassemble_info *info)
|
| +{
|
| + return print_insn_nios2 (address, info, BFD_ENDIAN_BIG);
|
| +}
|
| +
|
| +int
|
| +print_insn_little_nios2 (bfd_vma address, disassemble_info *info)
|
| +{
|
| + return print_insn_nios2 (address, info, BFD_ENDIAN_LITTLE);
|
| +}
|
|
|