diff options
author | Andrew Hsieh <andrewhsieh@google.com> | 2014-06-13 12:38:00 -0700 |
---|---|---|
committer | Andrew Hsieh <andrewhsieh@google.com> | 2014-06-13 12:38:00 -0700 |
commit | 54f1b3cf509cd889905287cb8ce6c5ae33911a21 (patch) | |
tree | e39b1a7fa04db86a8215b7f9d4656d74e394aec0 /binutils-2.25/opcodes/crx-dis.c | |
parent | 2a6558a8ecfb81d75215b4ec7dc61113e12cfd5f (diff) | |
download | toolchain_binutils-54f1b3cf509cd889905287cb8ce6c5ae33911a21.tar.gz toolchain_binutils-54f1b3cf509cd889905287cb8ce6c5ae33911a21.tar.bz2 toolchain_binutils-54f1b3cf509cd889905287cb8ce6c5ae33911a21.zip |
Add upstream binutils-2.25 snapshot 4/4 2014
For MIPS -mmsa support
Change-Id: I08c4f002fa7b33dec85ed75956e6ab551bb03c96
Diffstat (limited to 'binutils-2.25/opcodes/crx-dis.c')
-rw-r--r-- | binutils-2.25/opcodes/crx-dis.c | 745 |
1 files changed, 745 insertions, 0 deletions
diff --git a/binutils-2.25/opcodes/crx-dis.c b/binutils-2.25/opcodes/crx-dis.c new file mode 100644 index 00000000..710a96ec --- /dev/null +++ b/binutils-2.25/opcodes/crx-dis.c @@ -0,0 +1,745 @@ +/* Disassembler code for CRX. + Copyright 2004, 2005, 2006, 2007, 2012 Free Software Foundation, Inc. + Contributed by Tomer Levi, NSC, Israel. + Written by Tomer Levi. + + 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 program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "sysdep.h" +#include "dis-asm.h" +#include "opcode/crx.h" + +/* String to print when opcode was not matched. */ +#define ILLEGAL "illegal" + /* Escape to 16-bit immediate. */ +#define ESCAPE_16_BIT 0xE + +/* Extract 'n_bits' from 'a' starting from offset 'offs'. */ +#define EXTRACT(a, offs, n_bits) \ + (n_bits == 32 ? (((a) >> (offs)) & 0xffffffffL) \ + : (((a) >> (offs)) & ((1 << (n_bits)) -1))) + +/* Set Bit Mask - a mask to set all bits starting from offset 'offs'. */ +#define SBM(offs) ((((1 << (32 - offs)) -1) << (offs))) + +typedef unsigned long dwordU; +typedef unsigned short wordU; + +typedef struct +{ + dwordU val; + int nbits; +} parameter; + +/* Structure to hold valid 'cinv' instruction options. */ + +typedef struct + { + /* Cinv printed string. */ + char *str; + /* Value corresponding to the string. */ + unsigned int value; + } +cinv_entry; + +/* CRX 'cinv' options. */ +const cinv_entry crx_cinvs[] = +{ + {"[i]", 2}, {"[i,u]", 3}, {"[d]", 4}, {"[d,u]", 5}, + {"[d,i]", 6}, {"[d,i,u]", 7}, {"[b]", 8}, + {"[b,i]", 10}, {"[b,i,u]", 11}, {"[b,d]", 12}, + {"[b,d,u]", 13}, {"[b,d,i]", 14}, {"[b,d,i,u]", 15} +}; + +/* Enum to distinguish different registers argument types. */ +typedef enum REG_ARG_TYPE + { + /* General purpose register (r<N>). */ + REG_ARG = 0, + /* User register (u<N>). */ + USER_REG_ARG, + /* CO-Processor register (c<N>). */ + COP_ARG, + /* CO-Processor special register (cs<N>). */ + COPS_ARG + } +REG_ARG_TYPE; + +/* Number of valid 'cinv' instruction options. */ +int NUMCINVS = ((sizeof crx_cinvs)/(sizeof crx_cinvs[0])); +/* Current opcode table entry we're disassembling. */ +const inst *instruction; +/* Current instruction we're disassembling. */ +ins currInsn; +/* The current instruction is read into 3 consecutive words. */ +wordU words[3]; +/* Contains all words in appropriate order. */ +ULONGLONG allWords; +/* Holds the current processed argument number. */ +int processing_argument_number; +/* Nonzero means a CST4 instruction. */ +int cst4flag; +/* Nonzero means the instruction's original size is + incremented (escape sequence is used). */ +int size_changed; + +static int get_number_of_operands (void); +static argtype getargtype (operand_type); +static int getbits (operand_type); +static char *getregname (reg); +static char *getcopregname (copreg, reg_type); +static char * getprocregname (int); +static char *gettrapstring (unsigned); +static char *getcinvstring (unsigned); +static void getregliststring (int, char *, enum REG_ARG_TYPE); +static wordU get_word_at_PC (bfd_vma, struct disassemble_info *); +static void get_words_at_PC (bfd_vma, struct disassemble_info *); +static unsigned long build_mask (void); +static int powerof2 (int); +static int match_opcode (void); +static void make_instruction (void); +static void print_arguments (ins *, bfd_vma, struct disassemble_info *); +static void print_arg (argument *, bfd_vma, struct disassemble_info *); + +/* Retrieve the number of operands for the current assembled instruction. */ + +static int +get_number_of_operands (void) +{ + int i; + + for (i = 0; instruction->operands[i].op_type && i < MAX_OPERANDS; i++) + ; + + return i; +} + +/* Return the bit size for a given operand. */ + +static int +getbits (operand_type op) +{ + if (op < MAX_OPRD) + return crx_optab[op].bit_size; + else + return 0; +} + +/* Return the argument type of a given operand. */ + +static argtype +getargtype (operand_type op) +{ + if (op < MAX_OPRD) + return crx_optab[op].arg_type; + else + return nullargs; +} + +/* Given the trap index in dispatch table, return its name. + This routine is used when disassembling the 'excp' instruction. */ + +static char * +gettrapstring (unsigned int trap_index) +{ + const trap_entry *trap; + + for (trap = crx_traps; trap < crx_traps + NUMTRAPS; trap++) + if (trap->entry == trap_index) + return trap->name; + + return ILLEGAL; +} + +/* Given a 'cinv' instruction constant operand, return its corresponding string. + This routine is used when disassembling the 'cinv' instruction. */ + +static char * +getcinvstring (unsigned int num) +{ + const cinv_entry *cinv; + + for (cinv = crx_cinvs; cinv < (crx_cinvs + NUMCINVS); cinv++) + if (cinv->value == num) + return cinv->str; + + return ILLEGAL; +} + +/* Given a register enum value, retrieve its name. */ + +char * +getregname (reg r) +{ + const reg_entry * regentry = &crx_regtab[r]; + + if (regentry->type != CRX_R_REGTYPE) + return ILLEGAL; + else + return regentry->name; +} + +/* Given a coprocessor register enum value, retrieve its name. */ + +char * +getcopregname (copreg r, reg_type type) +{ + const reg_entry * regentry; + + if (type == CRX_C_REGTYPE) + regentry = &crx_copregtab[r]; + else if (type == CRX_CS_REGTYPE) + regentry = &crx_copregtab[r+(cs0-c0)]; + else + return ILLEGAL; + + return regentry->name; +} + + +/* Getting a processor register name. */ + +static char * +getprocregname (int reg_index) +{ + const reg_entry *r; + + for (r = crx_regtab; r < crx_regtab + NUMREGS; r++) + if (r->image == reg_index) + return r->name; + + return "ILLEGAL REGISTER"; +} + +/* Get the power of two for a given integer. */ + +static int +powerof2 (int x) +{ + int product, i; + + for (i = 0, product = 1; i < x; i++) + product *= 2; + + return product; +} + +/* Transform a register bit mask to a register list. */ + +void +getregliststring (int mask, char *string, enum REG_ARG_TYPE core_cop) +{ + char temp_string[5]; + int i; + + string[0] = '{'; + string[1] = '\0'; + + + /* A zero mask means HI/LO registers. */ + if (mask == 0) + { + if (core_cop == USER_REG_ARG) + strcat (string, "ulo,uhi"); + else + strcat (string, "lo,hi"); + } + else + { + for (i = 0; i < 16; i++) + { + if (mask & 0x1) + { + switch (core_cop) + { + case REG_ARG: + sprintf (temp_string, "r%d", i); + break; + case USER_REG_ARG: + sprintf (temp_string, "u%d", i); + break; + case COP_ARG: + sprintf (temp_string, "c%d", i); + break; + case COPS_ARG: + sprintf (temp_string, "cs%d", i); + break; + default: + break; + } + strcat (string, temp_string); + if (mask & 0xfffe) + strcat (string, ","); + } + mask >>= 1; + } + } + + strcat (string, "}"); +} + +/* START and END are relating 'allWords' struct, which is 48 bits size. + + START|--------|END + +---------+---------+---------+---------+ + | | V | A | L | + +---------+---------+---------+---------+ + 0 16 32 48 + words [0] [1] [2] */ + +static parameter +makelongparameter (ULONGLONG val, int start, int end) +{ + parameter p; + + p.val = (dwordU) EXTRACT(val, 48 - end, end - start); + p.nbits = end - start; + return p; +} + +/* Build a mask of the instruction's 'constant' opcode, + based on the instruction's printing flags. */ + +static unsigned long +build_mask (void) +{ + unsigned int print_flags; + unsigned long mask; + + print_flags = instruction->flags & FMT_CRX; + switch (print_flags) + { + case FMT_1: + mask = 0xF0F00000; + break; + case FMT_2: + mask = 0xFFF0FF00; + break; + case FMT_3: + mask = 0xFFF00F00; + break; + case FMT_4: + mask = 0xFFF0F000; + break; + case FMT_5: + mask = 0xFFF0FFF0; + break; + default: + mask = SBM(instruction->match_bits); + break; + } + + return mask; +} + +/* Search for a matching opcode. Return 1 for success, 0 for failure. */ + +static int +match_opcode (void) +{ + unsigned long mask; + + /* The instruction 'constant' opcode doewsn't exceed 32 bits. */ + unsigned long doubleWord = (words[1] + (words[0] << 16)) & 0xffffffff; + + /* Start searching from end of instruction table. */ + instruction = &crx_instruction[NUMOPCODES - 2]; + + /* Loop over instruction table until a full match is found. */ + while (instruction >= crx_instruction) + { + mask = build_mask (); + if ((doubleWord & mask) == BIN(instruction->match, instruction->match_bits)) + return 1; + else + instruction--; + } + return 0; +} + +/* Set the proper parameter value for different type of arguments. */ + +static void +make_argument (argument * a, int start_bits) +{ + int inst_bit_size, total_size; + parameter p; + + if ((instruction->size == 3) && a->size >= 16) + inst_bit_size = 48; + else + inst_bit_size = 32; + + switch (a->type) + { + case arg_copr: + case arg_copsr: + p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size), + inst_bit_size - start_bits); + a->cr = p.val; + break; + + case arg_r: + p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size), + inst_bit_size - start_bits); + a->r = p.val; + break; + + case arg_ic: + p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size), + inst_bit_size - start_bits); + + if ((p.nbits == 4) && cst4flag) + { + if (IS_INSN_TYPE (CMPBR_INS) && (p.val == ESCAPE_16_BIT)) + { + /* A special case, where the value is actually stored + in the last 4 bits. */ + p = makelongparameter (allWords, 44, 48); + /* The size of the instruction should be incremented. */ + size_changed = 1; + } + + if (p.val == 6) + p.val = -1; + else if (p.val == 13) + p.val = 48; + else if (p.val == 5) + p.val = -4; + else if (p.val == 10) + p.val = 32; + else if (p.val == 11) + p.val = 20; + else if (p.val == 9) + p.val = 16; + } + + a->constant = p.val; + break; + + case arg_idxr: + a->scale = 0; + total_size = a->size + 10; /* sizeof(rbase + ridx + scl2) = 10. */ + p = makelongparameter (allWords, inst_bit_size - total_size, + inst_bit_size - (total_size - 4)); + a->r = p.val; + p = makelongparameter (allWords, inst_bit_size - (total_size - 4), + inst_bit_size - (total_size - 8)); + a->i_r = p.val; + p = makelongparameter (allWords, inst_bit_size - (total_size - 8), + inst_bit_size - (total_size - 10)); + a->scale = p.val; + p = makelongparameter (allWords, inst_bit_size - (total_size - 10), + inst_bit_size); + a->constant = p.val; + break; + + case arg_rbase: + p = makelongparameter (allWords, inst_bit_size - (start_bits + 4), + inst_bit_size - start_bits); + a->r = p.val; + break; + + case arg_cr: + if (a->size <= 8) + { + p = makelongparameter (allWords, inst_bit_size - (start_bits + 4), + inst_bit_size - start_bits); + a->r = p.val; + /* Case for opc4 r dispu rbase. */ + p = makelongparameter (allWords, inst_bit_size - (start_bits + 8), + inst_bit_size - (start_bits + 4)); + } + else + { + /* The 'rbase' start_bits is always relative to a 32-bit data type. */ + p = makelongparameter (allWords, 32 - (start_bits + 4), + 32 - start_bits); + a->r = p.val; + p = makelongparameter (allWords, 32 - start_bits, + inst_bit_size); + } + if ((p.nbits == 4) && cst4flag) + { + if (instruction->flags & DISPUW4) + p.val *= 2; + else if (instruction->flags & DISPUD4) + p.val *= 4; + } + a->constant = p.val; + break; + + case arg_c: + p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size), + inst_bit_size - start_bits); + a->constant = p.val; + break; + default: + break; + } +} + +/* Print a single argument. */ + +static void +print_arg (argument *a, bfd_vma memaddr, struct disassemble_info *info) +{ + LONGLONG longdisp, mask; + int sign_flag = 0; + int relative = 0; + bfd_vma number; + int op_index = 0; + char string[200]; + PTR stream = info->stream; + fprintf_ftype func = info->fprintf_func; + + switch (a->type) + { + case arg_copr: + func (stream, "%s", getcopregname (a->cr, CRX_C_REGTYPE)); + break; + + case arg_copsr: + func (stream, "%s", getcopregname (a->cr, CRX_CS_REGTYPE)); + break; + + case arg_r: + if (IS_INSN_MNEMONIC ("mtpr") || IS_INSN_MNEMONIC ("mfpr")) + func (stream, "%s", getprocregname (a->r)); + else + func (stream, "%s", getregname (a->r)); + break; + + case arg_ic: + if (IS_INSN_MNEMONIC ("excp")) + func (stream, "%s", gettrapstring (a->constant)); + + else if (IS_INSN_MNEMONIC ("cinv")) + func (stream, "%s", getcinvstring (a->constant)); + + else if (INST_HAS_REG_LIST) + { + REG_ARG_TYPE reg_arg_type = IS_INSN_TYPE (COP_REG_INS) ? + COP_ARG : IS_INSN_TYPE (COPS_REG_INS) ? + COPS_ARG : (instruction->flags & USER_REG) ? + USER_REG_ARG : REG_ARG; + + if ((reg_arg_type == COP_ARG) || (reg_arg_type == COPS_ARG)) + { + /* Check for proper argument number. */ + if (processing_argument_number == 2) + { + getregliststring (a->constant, string, reg_arg_type); + func (stream, "%s", string); + } + else + func (stream, "$0x%lx", a->constant & 0xffffffff); + } + else + { + getregliststring (a->constant, string, reg_arg_type); + func (stream, "%s", string); + } + } + else + func (stream, "$0x%lx", a->constant & 0xffffffff); + break; + + case arg_idxr: + func (stream, "0x%lx(%s,%s,%d)", a->constant & 0xffffffff, + getregname (a->r), getregname (a->i_r), powerof2 (a->scale)); + break; + + case arg_rbase: + func (stream, "(%s)", getregname (a->r)); + break; + + case arg_cr: + func (stream, "0x%lx(%s)", a->constant & 0xffffffff, getregname (a->r)); + + if (IS_INSN_TYPE (LD_STOR_INS_INC)) + func (stream, "+"); + break; + + case arg_c: + /* Removed the *2 part as because implicit zeros are no more required. + Have to fix this as this needs a bit of extension in terms of branchins. + Have to add support for cmp and branch instructions. */ + if (IS_INSN_TYPE (BRANCH_INS) || IS_INSN_MNEMONIC ("bal") + || IS_INSN_TYPE (CMPBR_INS) || IS_INSN_TYPE (DCR_BRANCH_INS) + || IS_INSN_TYPE (COP_BRANCH_INS)) + { + relative = 1; + longdisp = a->constant; + longdisp <<= 1; + + switch (a->size) + { + case 8: + case 16: + case 24: + case 32: + mask = ((LONGLONG)1 << a->size) - 1; + if (longdisp & ((LONGLONG)1 << a->size)) + { + sign_flag = 1; + longdisp = ~(longdisp) + 1; + } + a->constant = (unsigned long int) (longdisp & mask); + break; + default: + func (stream, + "Wrong offset used in branch/bal instruction"); + break; + } + + } + /* For branch Neq instruction it is 2*offset + 2. */ + else if (IS_INSN_TYPE (BRANCH_NEQ_INS)) + a->constant = 2 * a->constant + 2; + else if (IS_INSN_TYPE (LD_STOR_INS_INC) + || IS_INSN_TYPE (LD_STOR_INS) + || IS_INSN_TYPE (STOR_IMM_INS) + || IS_INSN_TYPE (CSTBIT_INS)) + { + op_index = instruction->flags & REVERSE_MATCH ? 0 : 1; + if (instruction->operands[op_index].op_type == abs16) + a->constant |= 0xFFFF0000; + } + func (stream, "%s", "0x"); + number = (relative ? memaddr : 0) + + (sign_flag ? -a->constant : a->constant); + (*info->print_address_func) (number, info); + break; + default: + break; + } +} + +/* Print all the arguments of CURRINSN instruction. */ + +static void +print_arguments (ins *currentInsn, bfd_vma memaddr, struct disassemble_info *info) +{ + int i; + + for (i = 0; i < currentInsn->nargs; i++) + { + processing_argument_number = i; + + print_arg (¤tInsn->arg[i], memaddr, info); + + if (i != currentInsn->nargs - 1) + info->fprintf_func (info->stream, ", "); + } +} + +/* Build the instruction's arguments. */ + +static void +make_instruction (void) +{ + int i; + unsigned int shift; + + for (i = 0; i < currInsn.nargs; i++) + { + argument a; + + memset (&a, 0, sizeof (a)); + a.type = getargtype (instruction->operands[i].op_type); + if (instruction->operands[i].op_type == cst4 + || instruction->operands[i].op_type == rbase_dispu4) + cst4flag = 1; + a.size = getbits (instruction->operands[i].op_type); + shift = instruction->operands[i].shift; + + make_argument (&a, shift); + currInsn.arg[i] = a; + } + + /* Calculate instruction size (in bytes). */ + currInsn.size = instruction->size + (size_changed ? 1 : 0); + /* Now in bits. */ + currInsn.size *= 2; +} + +/* Retrieve a single word from a given memory address. */ + +static wordU +get_word_at_PC (bfd_vma memaddr, struct disassemble_info *info) +{ + bfd_byte buffer[4]; + int status; + wordU insn = 0; + + status = info->read_memory_func (memaddr, buffer, 2, info); + + if (status == 0) + insn = (wordU) bfd_getl16 (buffer); + + return insn; +} + +/* Retrieve multiple words (3) from a given memory address. */ + +static void +get_words_at_PC (bfd_vma memaddr, struct disassemble_info *info) +{ + int i; + bfd_vma mem; + + for (i = 0, mem = memaddr; i < 3; i++, mem += 2) + words[i] = get_word_at_PC (mem, info); + + allWords = + ((ULONGLONG) words[0] << 32) + ((unsigned long) words[1] << 16) + words[2]; +} + +/* Prints the instruction by calling print_arguments after proper matching. */ + +int +print_insn_crx (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + int is_decoded; /* Nonzero means instruction has a match. */ + + /* Initialize global variables. */ + cst4flag = 0; + size_changed = 0; + + /* Retrieve the encoding from current memory location. */ + get_words_at_PC (memaddr, info); + /* Find a matching opcode in table. */ + is_decoded = match_opcode (); + /* If found, print the instruction's mnemonic and arguments. */ + if (is_decoded > 0 && (words[0] << 16 || words[1]) != 0) + { + info->fprintf_func (info->stream, "%s", instruction->mnemonic); + if ((currInsn.nargs = get_number_of_operands ()) != 0) + info->fprintf_func (info->stream, "\t"); + make_instruction (); + print_arguments (&currInsn, memaddr, info); + return currInsn.size; + } + + /* No match found. */ + info->fprintf_func (info->stream,"%s ",ILLEGAL); + return 2; +} |