summaryrefslogtreecommitdiff
path: root/opcodes/microblaze-dis.c
diff options
context:
space:
mode:
authorNick Clifton <nickc@redhat.com>2009-08-06 17:38:04 +0000
committerNick Clifton <nickc@redhat.com>2009-08-06 17:38:04 +0000
commitad627b386b6942f5ca67d6374afaa9853f74cdbf (patch)
treedc720185e27e125c1198439422ae8c2569c8cf63 /opcodes/microblaze-dis.c
parent560c595224d5a41856e15532ebcc2e09682bb5fd (diff)
downloadbinutils-redhat-ad627b386b6942f5ca67d6374afaa9853f74cdbf.tar.gz
Add support for Xilinx MicroBlaze processor.
* bfd/Makefile.am: Add cpu-microblaze.{lo,c}, elf32-microblaze.{lo,c}. * bfd/Makefile.in: Same. * bfd/archures.c: Add bfd_arch_microblaze. * bfd/bfd-in2.h: Regenerate. * bfd/config.bfd: Add microblaze target. * bfd/configure: Add bfd_elf32_microblaze_vec target. * bfd/configure.in: Same. * bfd/cpu-microblaze.c: New. * bfd/elf32-microblaze.c: New. * bfd/libbfd-in.h: Add prototype _bfd_dwarf2_fixup_section_debug_loc(). * bfd/libbfd.h: Regenerate. * bfd/reloc.c: Add MICROBLAZE relocations. * bfd/section.c: Add struct relax_table and relax_count to section. * bfd/targets.c: Add bfd_elf32_microblaze_vec. * binutils/MAINTAINERS: Add self as maintainer. * binutils/readelf.c: Include elf/microblaze.h, add EM_MICROBLAZE & EM_MICROBLAZE_OLD to guess_is_rela(), dump_relocations(), get_machine_name(). * config.sub: Add microblaze target. * configure: Same. * configure.ac: Same. * gas/Makefile.am: add microblaze to CPU_TYPES, config/tc-microblaze.c to TARGET_CPU_CFILES, config/tc-microblaze.h to TARGET_CPU_HFILES, add DEP_microblaze_elf target. * gas/Makefile.in: Same. * gas/config/tc-microblaze.c: Add MicroBlaze assembler. * gas/config/tc-microblaze.h: Add header for tc-microblaze.c. * gas/configure: Add microblaze target. * gas/configure.in: Same. * gas/configure.tgt: Same. * gas/doc/Makefile.am: Add c-microblaze.texi to CPU_DOCS. * gas/doc/Makefile.in: Same. * gas/doc/all.texi: Set MICROBLAZE. * gas/doc/as.texinfo: Add MicroBlaze doc links. * gas/doc/c-microblaze.texi: New MicroBlaze docs. * include/dis-asm.h: Decl print_insn_microblaze(). * include/elf/common.h: Define EM_MICROBLAZE & EM_MICROBLAZE_OLD. * include/elf/microblaze.h: New reloc definitions. * ld/Makefile.am: Add eelf32mb_linux.o, eelf32microblaze.o to ALL_EMULATIONS, targets. * ld/Makefile.in: Same. * ld/configure.tgt: Add microblaze*-linux*, microblaze* targets. * ld/emulparams/elf32mb_linux.sh: New. * ld/emulparams/elf32microblaze.sh. New. * ld/scripttempl/elfmicroblaze.sc: New. * opcodes/Makefile.am: Add microblaze-opc.h to HFILES, microblaze-dis.c to CFILES, microblaze-dis.lo to ALL_MACHINES, targets. * opcodes/Makefile.in: Same. * opcodes/configure: Add bfd_microblaze_arch target. * opcodes/configure.in: Same. * opcodes/disassemble.c: Define ARCH_microblaze, return print_insn_microblaze(). * opcodes/microblaze-dis.c: New MicroBlaze disassembler. * opcodes/microblaze-opc.h: New MicroBlaze opcode definitions. * opcodes/microblaze-opcm.h: New MicroBlaze opcode types.
Diffstat (limited to 'opcodes/microblaze-dis.c')
-rw-r--r--opcodes/microblaze-dis.c527
1 files changed, 527 insertions, 0 deletions
diff --git a/opcodes/microblaze-dis.c b/opcodes/microblaze-dis.c
new file mode 100644
index 0000000000..178415e99a
--- /dev/null
+++ b/opcodes/microblaze-dis.c
@@ -0,0 +1,527 @@
+/* Disassemble Xilinx microblaze instructions.
+
+ Copyright 2009 Free Software Foundation, 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"
+#define STATIC_TABLE
+#define DEFINE_TABLE
+
+#include "microblaze-opc.h"
+#include "dis-asm.h"
+#include <strings.h>
+
+#define get_field_rd(instr) get_field (instr, RD_MASK, RD_LOW)
+#define get_field_r1(instr) get_field (instr, RA_MASK, RA_LOW)
+#define get_field_r2(instr) get_field (instr, RB_MASK, RB_LOW)
+#define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW)
+#define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW)
+
+static char *
+get_field (long instr, long mask, unsigned short low)
+{
+ char tmpstr[25];
+
+ sprintf (tmpstr, "%s%d", register_prefix, (int)((instr & mask) >> low));
+ return (strdup (tmpstr));
+}
+
+static char *
+get_field_imm (long instr)
+{
+ char tmpstr[25];
+
+ sprintf (tmpstr, "%d", (short)((instr & IMM_MASK) >> IMM_LOW));
+ return (strdup (tmpstr));
+}
+
+static char *
+get_field_imm5 (long instr)
+{
+ char tmpstr[25];
+
+ sprintf (tmpstr, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW));
+ return (strdup (tmpstr));
+}
+
+static char *
+get_field_rfsl (long instr)
+{
+ char tmpstr[25];
+
+ sprintf (tmpstr, "%s%d", fsl_register_prefix,
+ (short)((instr & RFSL_MASK) >> IMM_LOW));
+ return (strdup (tmpstr));
+}
+
+static char *
+get_field_imm15 (long instr)
+{
+ char tmpstr[25];
+
+ sprintf (tmpstr, "%d", (short)((instr & IMM15_MASK) >> IMM_LOW));
+ return (strdup (tmpstr));
+}
+
+static char *
+get_field_special (long instr, struct op_code_struct * op)
+{
+ char tmpstr[25];
+ char spr[6];
+
+ switch ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask))
+ {
+ case REG_MSR_MASK :
+ strcpy (spr, "msr");
+ break;
+ case REG_PC_MASK :
+ strcpy (spr, "pc");
+ break;
+ case REG_EAR_MASK :
+ strcpy (spr, "ear");
+ break;
+ case REG_ESR_MASK :
+ strcpy (spr, "esr");
+ break;
+ case REG_FSR_MASK :
+ strcpy (spr, "fsr");
+ break;
+ case REG_BTR_MASK :
+ strcpy (spr, "btr");
+ break;
+ case REG_EDR_MASK :
+ strcpy (spr, "edr");
+ break;
+ case REG_PID_MASK :
+ strcpy (spr, "pid");
+ break;
+ case REG_ZPR_MASK :
+ strcpy (spr, "zpr");
+ break;
+ case REG_TLBX_MASK :
+ strcpy (spr, "tlbx");
+ break;
+ case REG_TLBLO_MASK :
+ strcpy (spr, "tlblo");
+ break;
+ case REG_TLBHI_MASK :
+ strcpy (spr, "tlbhi");
+ break;
+ case REG_TLBSX_MASK :
+ strcpy (spr, "tlbsx");
+ break;
+ default :
+ if (((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000)
+ == REG_PVR_MASK)
+ {
+ sprintf (tmpstr, "%spvr%d", register_prefix,
+ (unsigned short)(((instr & IMM_MASK) >> IMM_LOW)
+ ^ op->immval_mask) ^ REG_PVR_MASK);
+ return (strdup (tmpstr));
+ }
+ else
+ strcpy (spr, "pc");
+ break;
+ }
+
+ sprintf (tmpstr, "%s%s", register_prefix, spr);
+ return (strdup (tmpstr));
+}
+
+static unsigned long
+read_insn_microblaze (bfd_vma memaddr,
+ struct disassemble_info *info,
+ struct op_code_struct **opr)
+{
+ unsigned char ibytes[4];
+ int status;
+ struct op_code_struct * op;
+ unsigned long inst;
+
+ status = info->read_memory_func (memaddr, ibytes, 4, info);
+
+ if (status != 0)
+ {
+ info->memory_error_func (status, memaddr, info);
+ return 0;
+ }
+
+ if (info->endian == BFD_ENDIAN_BIG)
+ inst = (ibytes[0] << 24) | (ibytes[1] << 16) | (ibytes[2] << 8) | ibytes[3];
+ else if (info->endian == BFD_ENDIAN_LITTLE)
+ inst = (ibytes[3] << 24) | (ibytes[2] << 16) | (ibytes[1] << 8) | ibytes[0];
+ else
+ abort ();
+
+ /* Just a linear search of the table. */
+ for (op = opcodes; op->name != 0; op ++)
+ if (op->bit_sequence == (inst & op->opcode_mask))
+ break;
+
+ *opr = op;
+ return inst;
+}
+
+
+int
+print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info)
+{
+ fprintf_ftype fprintf = info->fprintf_func;
+ void * stream = info->stream;
+ unsigned long inst, prev_inst;
+ struct op_code_struct * op, *pop;
+ int immval = 0;
+ bfd_boolean immfound = FALSE;
+ static bfd_vma prev_insn_addr = -1; /* Init the prev insn addr. */
+ static int prev_insn_vma = -1; /* Init the prev insn vma. */
+ int curr_insn_vma = info->buffer_vma;
+
+ info->bytes_per_chunk = 4;
+
+ inst = read_insn_microblaze (memaddr, info, &op);
+ if (inst == 0)
+ return -1;
+
+ if (prev_insn_vma == curr_insn_vma)
+ {
+ if (memaddr-(info->bytes_per_chunk) == prev_insn_addr)
+ {
+ prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop);
+ if (prev_inst == 0)
+ return -1;
+ if (pop->instr == imm)
+ {
+ immval = (get_int_field_imm (prev_inst) << 16) & 0xffff0000;
+ immfound = TRUE;
+ }
+ else
+ {
+ immval = 0;
+ immfound = FALSE;
+ }
+ }
+ }
+
+ /* Make curr insn as prev insn. */
+ prev_insn_addr = memaddr;
+ prev_insn_vma = curr_insn_vma;
+
+ if (op->name == NULL)
+ fprintf (stream, ".short 0x%04x", inst);
+ else
+ {
+ fprintf (stream, "%s", op->name);
+
+ switch (op->inst_type)
+ {
+ case INST_TYPE_RD_R1_R2:
+ fprintf (stream, "\t%s, %s, %s", get_field_rd (inst),
+ get_field_r1(inst), get_field_r2 (inst));
+ break;
+ case INST_TYPE_RD_R1_IMM:
+ fprintf (stream, "\t%s, %s, %s", get_field_rd (inst),
+ get_field_r1(inst), get_field_imm (inst));
+ if (info->print_address_func && get_int_field_r1 (inst) == 0
+ && info->symbol_at_address_func)
+ {
+ if (immfound)
+ immval |= (get_int_field_imm (inst) & 0x0000ffff);
+ else
+ {
+ immval = get_int_field_imm (inst);
+ if (immval & 0x8000)
+ immval |= 0xFFFF0000;
+ }
+ if (immval > 0 && info->symbol_at_address_func (immval, info))
+ {
+ fprintf (stream, "\t// ");
+ info->print_address_func (immval, info);
+ }
+ }
+ break;
+ case INST_TYPE_RD_R1_IMM5:
+ fprintf (stream, "\t%s, %s, %s", get_field_rd (inst),
+ get_field_r1(inst), get_field_imm5 (inst));
+ break;
+ case INST_TYPE_RD_RFSL:
+ fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_rfsl (inst));
+ break;
+ case INST_TYPE_R1_RFSL:
+ fprintf (stream, "\t%s, %s", get_field_r1 (inst), get_field_rfsl (inst));
+ break;
+ case INST_TYPE_RD_SPECIAL:
+ fprintf (stream, "\t%s, %s", get_field_rd (inst),
+ get_field_special (inst, op));
+ break;
+ case INST_TYPE_SPECIAL_R1:
+ fprintf (stream, "\t%s, %s", get_field_special (inst, op),
+ get_field_r1(inst));
+ break;
+ case INST_TYPE_RD_R1:
+ fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_r1 (inst));
+ break;
+ case INST_TYPE_R1_R2:
+ fprintf (stream, "\t%s, %s", get_field_r1 (inst), get_field_r2 (inst));
+ break;
+ case INST_TYPE_R1_IMM:
+ fprintf (stream, "\t%s, %s", get_field_r1 (inst), get_field_imm (inst));
+ /* The non-pc relative instructions are returns, which shouldn't
+ have a label printed. */
+ if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET
+ && info->symbol_at_address_func)
+ {
+ if (immfound)
+ immval |= (get_int_field_imm (inst) & 0x0000ffff);
+ else
+ {
+ immval = get_int_field_imm (inst);
+ if (immval & 0x8000)
+ immval |= 0xFFFF0000;
+ }
+ immval += memaddr;
+ if (immval > 0 && info->symbol_at_address_func (immval, info))
+ {
+ fprintf (stream, "\t// ");
+ info->print_address_func (immval, info);
+ }
+ else
+ {
+ fprintf (stream, "\t\t// ");
+ fprintf (stream, "%x", immval);
+ }
+ }
+ break;
+ case INST_TYPE_RD_IMM:
+ fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_imm (inst));
+ if (info->print_address_func && info->symbol_at_address_func)
+ {
+ if (immfound)
+ immval |= (get_int_field_imm (inst) & 0x0000ffff);
+ else
+ {
+ immval = get_int_field_imm (inst);
+ if (immval & 0x8000)
+ immval |= 0xFFFF0000;
+ }
+ if (op->inst_offset_type == INST_PC_OFFSET)
+ immval += (int) memaddr;
+ if (info->symbol_at_address_func (immval, info))
+ {
+ fprintf (stream, "\t// ");
+ info->print_address_func (immval, info);
+ }
+ }
+ break;
+ case INST_TYPE_IMM:
+ fprintf (stream, "\t%s", get_field_imm (inst));
+ if (info->print_address_func && info->symbol_at_address_func
+ && op->instr != imm)
+ {
+ if (immfound)
+ immval |= (get_int_field_imm (inst) & 0x0000ffff);
+ else
+ {
+ immval = get_int_field_imm (inst);
+ if (immval & 0x8000)
+ immval |= 0xFFFF0000;
+ }
+ if (op->inst_offset_type == INST_PC_OFFSET)
+ immval += (int) memaddr;
+ if (immval > 0 && info->symbol_at_address_func (immval, info))
+ {
+ fprintf (stream, "\t// ");
+ info->print_address_func (immval, info);
+ }
+ else if (op->inst_offset_type == INST_PC_OFFSET)
+ {
+ fprintf (stream, "\t\t// ");
+ fprintf (stream, "%x", immval);
+ }
+ }
+ break;
+ case INST_TYPE_RD_R2:
+ fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_r2 (inst));
+ break;
+ case INST_TYPE_R2:
+ fprintf (stream, "\t%s", get_field_r2 (inst));
+ break;
+ case INST_TYPE_R1:
+ fprintf (stream, "\t%s", get_field_r1 (inst));
+ break;
+ case INST_TYPE_RD_R1_SPECIAL:
+ fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_r2 (inst));
+ break;
+ case INST_TYPE_RD_IMM15:
+ fprintf (stream, "\t%s, %s", get_field_rd (inst), get_field_imm15 (inst));
+ break;
+ /* For tuqula instruction */
+ case INST_TYPE_RD:
+ fprintf (stream, "\t%s", get_field_rd (inst));
+ break;
+ case INST_TYPE_RFSL:
+ fprintf (stream, "\t%s", get_field_rfsl (inst));
+ break;
+ default:
+ /* If the disassembler lags the instruction set. */
+ fprintf (stream, "\tundecoded operands, inst is 0x%04x", inst);
+ break;
+ }
+ }
+
+ /* Say how many bytes we consumed. */
+ return 4;
+}
+#if 0
+static enum microblaze_instr
+get_insn_microblaze (long inst,
+ bfd_boolean *isunsignedimm,
+ enum microblaze_instr_type *insn_type,
+ short *delay_slots)
+{
+ struct op_code_struct * op;
+ *isunsignedimm = FALSE;
+
+ /* Just a linear search of the table. */
+ for (op = opcodes; op->name != 0; op ++)
+ if (op->bit_sequence == (inst & op->opcode_mask))
+ break;
+
+ if (op->name == 0)
+ return invalid_inst;
+ else
+ {
+ *isunsignedimm = (op->inst_type == INST_TYPE_RD_R1_UNSIGNED_IMM);
+ *insn_type = op->instr_type;
+ *delay_slots = op->delay_slots;
+ return op->instr;
+ }
+}
+
+short
+get_delay_slots_microblaze (long inst)
+{
+ bfd_boolean isunsignedimm;
+ enum microblaze_instr_type insn_type;
+ enum microblaze_instr op;
+ short delay_slots;
+
+ op = get_insn_microblaze (inst, &isunsignedimm, &insn_type, &delay_slots);
+ if (op == invalid_inst)
+ return 0;
+ else
+ return delay_slots;
+}
+
+enum microblaze_instr
+microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *imm)
+{
+ enum microblaze_instr op;
+ bfd_boolean t1;
+ enum microblaze_instr_type t2;
+ short t3;
+
+ op = get_insn_microblaze (insn, &t1, &t2, &t3);
+ *rd = (insn & RD_MASK) >> RD_LOW;
+ *ra = (insn & RA_MASK) >> RA_LOW;
+ *rb = (insn & RB_MASK) >> RB_LOW;
+ t3 = (insn & IMM_MASK) >> IMM_LOW;
+ *imm = (int) t3;
+ return (op);
+}
+
+unsigned long
+microblaze_get_target_address (long inst, bfd_boolean immfound, int immval,
+ long pcval, long r1val, long r2val,
+ bfd_boolean *targetvalid,
+ bfd_boolean *unconditionalbranch)
+{
+ struct op_code_struct * op;
+ long targetaddr = 0;
+
+ *unconditionalbranch = FALSE;
+ /* Just a linear search of the table. */
+ for (op = opcodes; op->name != 0; op ++)
+ if (op->bit_sequence == (inst & op->opcode_mask))
+ break;
+
+ if (op->name == 0)
+ {
+ *targetvalid = FALSE;
+ }
+ else if (op->instr_type == branch_inst)
+ {
+ switch (op->inst_type)
+ {
+ case INST_TYPE_R2:
+ *unconditionalbranch = TRUE;
+ /* Fall through. */
+ case INST_TYPE_RD_R2:
+ case INST_TYPE_R1_R2:
+ targetaddr = r2val;
+ *targetvalid = TRUE;
+ if (op->inst_offset_type == INST_PC_OFFSET)
+ targetaddr += pcval;
+ break;
+ case INST_TYPE_IMM:
+ *unconditionalbranch = TRUE;
+ /* Fall through. */
+ case INST_TYPE_RD_IMM:
+ case INST_TYPE_R1_IMM:
+ if (immfound)
+ {
+ targetaddr = (immval << 16) & 0xffff0000;
+ targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
+ }
+ else
+ {
+ targetaddr = get_int_field_imm (inst);
+ if (targetaddr & 0x8000)
+ targetaddr |= 0xFFFF0000;
+ }
+ if (op->inst_offset_type == INST_PC_OFFSET)
+ targetaddr += pcval;
+ *targetvalid = TRUE;
+ break;
+ default:
+ *targetvalid = FALSE;
+ break;
+ }
+ }
+ else if (op->instr_type == return_inst)
+ {
+ if (immfound)
+ {
+ targetaddr = (immval << 16) & 0xffff0000;
+ targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
+ }
+ else
+ {
+ targetaddr = get_int_field_imm (inst);
+ if (targetaddr & 0x8000)
+ targetaddr |= 0xFFFF0000;
+ }
+ targetaddr += r1val;
+ *targetvalid = TRUE;
+ }
+ else
+ *targetvalid = FALSE;
+ return targetaddr;
+}
+#endif