diff options
Diffstat (limited to 'opcodes/arc-dis.c')
-rw-r--r-- | opcodes/arc-dis.c | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/opcodes/arc-dis.c b/opcodes/arc-dis.c new file mode 100644 index 0000000..802b07b --- /dev/null +++ b/opcodes/arc-dis.c @@ -0,0 +1,267 @@ +/* Instruction printing code for the ARC. + Copyright (C) 1994, 1995, 1997, 1998 Free Software Foundation, Inc. + Contributed by Doug Evans (dje@cygnus.com). + +This program 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 2 of the License, or +(at your option) any later version. + +This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "dis-asm.h" +#include "opcode/arc.h" +#include "elf-bfd.h" +#include "elf/arc.h" +#include "opintl.h" + +static int print_insn_arc_base_little PARAMS ((bfd_vma, disassemble_info *)); +static int print_insn_arc_base_big PARAMS ((bfd_vma, disassemble_info *)); + +static int print_insn PARAMS ((bfd_vma, disassemble_info *, int, int)); + +/* Print one instruction from PC on INFO->STREAM. + Return the size of the instruction (4 or 8 for the ARC). */ + +static int +print_insn (pc, info, mach, big_p) + bfd_vma pc; + disassemble_info *info; + int mach; + int big_p; +{ + const struct arc_opcode *opcode; + bfd_byte buffer[4]; + void *stream = info->stream; + fprintf_ftype func = info->fprintf_func; + int status; + /* First element is insn, second element is limm (if present). */ + arc_insn insn[2]; + int got_limm_p = 0; + static int initialized = 0; + static int current_mach = 0; + + if (!initialized || mach != current_mach) + { + initialized = 1; + current_mach = arc_get_opcode_mach (mach, big_p); + arc_opcode_init_tables (current_mach); + } + + status = (*info->read_memory_func) (pc, buffer, 4, info); + if (status != 0) + { + (*info->memory_error_func) (status, pc, info); + return -1; + } + if (big_p) + insn[0] = bfd_getb32 (buffer); + else + insn[0] = bfd_getl32 (buffer); + + (*func) (stream, "%08lx\t", insn[0]); + + /* The instructions are stored in lists hashed by the insn code + (though we needn't care how they're hashed). */ + + opcode = arc_opcode_lookup_dis (insn[0]); + for ( ; opcode != NULL; opcode = ARC_OPCODE_NEXT_DIS (opcode)) + { + char *syn; + int mods,invalid; + long value; + const struct arc_operand *operand; + const struct arc_operand_value *opval; + + /* Basic bit mask must be correct. */ + if ((insn[0] & opcode->mask) != opcode->value) + continue; + + /* Supported by this cpu? */ + if (! arc_opcode_supported (opcode)) + continue; + + /* Make two passes over the operands. First see if any of them + have extraction functions, and, if they do, make sure the + instruction is valid. */ + + arc_opcode_init_extract (); + invalid = 0; + + /* ??? Granted, this is slower than the `ppc' way. Maybe when this is + done it'll be clear what the right way to do this is. */ + /* Instructions like "add.f r0,r1,1" are tricky because the ".f" gets + printed first, but we don't know how to print it until we've processed + the regs. Since we're scanning all the args before printing the insn + anyways, it's actually quite easy. */ + + for (syn = opcode->syntax; *syn; ++syn) + { + int c; + + if (*syn != '%' || *++syn == '%') + continue; + mods = 0; + c = *syn; + while (ARC_MOD_P (arc_operands[arc_operand_map[c]].flags)) + { + mods |= arc_operands[arc_operand_map[c]].flags & ARC_MOD_BITS; + ++syn; + c = *syn; + } + operand = arc_operands + arc_operand_map[c]; + if (operand->extract) + (*operand->extract) (insn, operand, mods, + (const struct arc_operand_value **) NULL, + &invalid); + } + if (invalid) + continue; + + /* The instruction is valid. */ + + /* If we have an insn with a limm, fetch it now. Scanning the insns + twice lets us do this. */ + if (arc_opcode_limm_p (NULL)) + { + status = (*info->read_memory_func) (pc + 4, buffer, 4, info); + if (status != 0) + { + (*info->memory_error_func) (status, pc, info); + return -1; + } + if (big_p) + insn[1] = bfd_getb32 (buffer); + else + insn[1] = bfd_getl32 (buffer); + got_limm_p = 1; + } + + for (syn = opcode->syntax; *syn; ++syn) + { + int c; + + if (*syn != '%' || *++syn == '%') + { + (*func) (stream, "%c", *syn); + continue; + } + + /* We have an operand. Fetch any special modifiers. */ + mods = 0; + c = *syn; + while (ARC_MOD_P (arc_operands[arc_operand_map[c]].flags)) + { + mods |= arc_operands[arc_operand_map[c]].flags & ARC_MOD_BITS; + ++syn; + c = *syn; + } + operand = arc_operands + arc_operand_map[c]; + + /* Extract the value from the instruction. */ + opval = NULL; + if (operand->extract) + { + value = (*operand->extract) (insn, operand, mods, + &opval, (int *) NULL); + } + else + { + value = (insn[0] >> operand->shift) & ((1 << operand->bits) - 1); + if ((operand->flags & ARC_OPERAND_SIGNED) + && (value & (1 << (operand->bits - 1)))) + value -= 1 << operand->bits; + + /* If this is a suffix operand, set `opval'. */ + if (operand->flags & ARC_OPERAND_SUFFIX) + opval = arc_opcode_lookup_suffix (operand, value); + } + + /* Print the operand as directed by the flags. */ + if (operand->flags & ARC_OPERAND_FAKE) + ; /* nothing to do (??? at least not yet) */ + else if (operand->flags & ARC_OPERAND_SUFFIX) + { + /* Default suffixes aren't printed. Fortunately, they all have + zero values. Also, zero values for boolean suffixes are + represented by the absence of text. */ + + if (value != 0) + { + /* ??? OPVAL should have a value. If it doesn't just cope + as we want disassembly to be reasonably robust. + Also remember that several condition code values (16-31) + aren't defined yet. For these cases just print the + number suitably decorated. */ + if (opval) + (*func) (stream, "%s%s", + mods & ARC_MOD_DOT ? "." : "", + opval->name); + else + (*func) (stream, "%s%c%d", + mods & ARC_MOD_DOT ? "." : "", + operand->fmt, value); + } + } + else if (operand->flags & ARC_OPERAND_RELATIVE_BRANCH) + (*info->print_address_func) (pc + 4 + value, info); + /* ??? Not all cases of this are currently caught. */ + else if (operand->flags & ARC_OPERAND_ABSOLUTE_BRANCH) + (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info); + else if (operand->flags & ARC_OPERAND_ADDRESS) + (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info); + else if (opval) + /* Note that this case catches both normal and auxiliary regs. */ + (*func) (stream, "%s", opval->name); + else + (*func) (stream, "%ld", value); + } + + /* We have found and printed an instruction; return. */ + return got_limm_p ? 8 : 4; + } + + (*func) (stream, _("*unknown*")); + return 4; +} + +/* Given MACH, one of bfd_mach_arc_xxx, return the print_insn function to use. + This does things a non-standard way (the "standard" way would be to copy + this code into disassemble.c). Since there are more than a couple of + variants, hiding all this crud here seems cleaner. */ + +disassembler_ftype +arc_get_disassembler (mach, big_p) + int mach; + int big_p; +{ + switch (mach) + { + case bfd_mach_arc_base: + return big_p ? print_insn_arc_base_big : print_insn_arc_base_little; + } + return print_insn_arc_base_little; +} + +static int +print_insn_arc_base_little (pc, info) + bfd_vma pc; + disassemble_info *info; +{ + return print_insn (pc, info, bfd_mach_arc_base, 0); +} + +static int +print_insn_arc_base_big (pc, info) + bfd_vma pc; + disassemble_info *info; +{ + return print_insn (pc, info, bfd_mach_arc_base, 1); +} |