123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- /* mmix-dis.c -- Disassemble MMIX instructions.
- Copyright (C) 2000-2015 Free Software Foundation, Inc.
- Written by Hans-Peter Nilsson (hp@bitrange.com)
- 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 <stdio.h>
- #include "opcode/mmix.h"
- #include "dis-asm.h"
- #include "libiberty.h"
- #include "bfd.h"
- #include "opintl.h"
- #define BAD_CASE(x) \
- do \
- { \
- fprintf (stderr, \
- _("Bad case %d (%s) in %s:%d\n"), \
- x, #x, __FILE__, __LINE__); \
- abort (); \
- } \
- while (0)
- #define FATAL_DEBUG \
- do \
- { \
- fprintf (stderr, \
- _("Internal: Non-debugged code (test-case missing): %s:%d"),\
- __FILE__, __LINE__); \
- abort (); \
- } \
- while (0)
- #define ROUND_MODE(n) \
- ((n) == 1 ? "ROUND_OFF" : (n) == 2 ? "ROUND_UP" : \
- (n) == 3 ? "ROUND_DOWN" : (n) == 4 ? "ROUND_NEAR" : \
- _("(unknown)"))
- #define INSN_IMMEDIATE_BIT (IMM_OFFSET_BIT << 24)
- #define INSN_BACKWARD_OFFSET_BIT (1 << 24)
- struct mmix_dis_info
- {
- const char *reg_name[256];
- const char *spec_reg_name[32];
- /* Waste a little memory so we don't have to allocate each separately.
- We could have an array with static contents for these, but on the
- other hand, we don't have to. */
- char basic_reg_name[256][sizeof ("$255")];
- };
- /* Initialize a target-specific array in INFO. */
- static bfd_boolean
- initialize_mmix_dis_info (struct disassemble_info *info)
- {
- struct mmix_dis_info *minfop = malloc (sizeof (struct mmix_dis_info));
- long i;
- if (minfop == NULL)
- return FALSE;
- memset (minfop, 0, sizeof (*minfop));
- /* Initialize register names from register symbols. If there's no
- register section, then there are no register symbols. */
- if ((info->section != NULL && info->section->owner != NULL)
- || (info->symbols != NULL
- && info->symbols[0] != NULL
- && bfd_asymbol_bfd (info->symbols[0]) != NULL))
- {
- bfd *abfd = info->section && info->section->owner != NULL
- ? info->section->owner
- : bfd_asymbol_bfd (info->symbols[0]);
- asection *reg_section = bfd_get_section_by_name (abfd, "*REG*");
- if (reg_section != NULL)
- {
- /* The returned symcount *does* include the ending NULL. */
- long symsize = bfd_get_symtab_upper_bound (abfd);
- asymbol **syms = malloc (symsize);
- long nsyms;
- if (syms == NULL)
- {
- FATAL_DEBUG;
- free (minfop);
- return FALSE;
- }
- nsyms = bfd_canonicalize_symtab (abfd, syms);
- /* We use the first name for a register. If this is MMO, then
- it's the name with the first sequence number, presumably the
- first in the source. */
- for (i = 0; i < nsyms && syms[i] != NULL; i++)
- {
- if (syms[i]->section == reg_section
- && syms[i]->value < 256
- && minfop->reg_name[syms[i]->value] == NULL)
- minfop->reg_name[syms[i]->value] = syms[i]->name;
- }
- }
- }
- /* Fill in the rest with the canonical names. */
- for (i = 0; i < 256; i++)
- if (minfop->reg_name[i] == NULL)
- {
- sprintf (minfop->basic_reg_name[i], "$%ld", i);
- minfop->reg_name[i] = minfop->basic_reg_name[i];
- }
- /* We assume it's actually a one-to-one mapping of number-to-name. */
- for (i = 0; mmix_spec_regs[i].name != NULL; i++)
- minfop->spec_reg_name[mmix_spec_regs[i].number] = mmix_spec_regs[i].name;
- info->private_data = (void *) minfop;
- return TRUE;
- }
- /* A table indexed by the first byte is constructed as we disassemble each
- tetrabyte. The contents is a pointer into mmix_insns reflecting the
- first found entry with matching match-bits and lose-bits. Further
- entries are considered one after one until the operand constraints
- match or the match-bits and lose-bits do not match. Normally a
- "further entry" will just show that there was no other match. */
- static const struct mmix_opcode *
- get_opcode (unsigned long insn)
- {
- static const struct mmix_opcode **opcodes = NULL;
- const struct mmix_opcode *opcodep = mmix_opcodes;
- unsigned int opcode_part = (insn >> 24) & 255;
- if (opcodes == NULL)
- opcodes = xcalloc (256, sizeof (struct mmix_opcode *));
- opcodep = opcodes[opcode_part];
- if (opcodep == NULL
- || (opcodep->match & insn) != opcodep->match
- || (opcodep->lose & insn) != 0)
- {
- /* Search through the table. */
- for (opcodep = mmix_opcodes; opcodep->name != NULL; opcodep++)
- {
- /* FIXME: Break out this into an initialization function. */
- if ((opcodep->match & (opcode_part << 24)) == opcode_part
- && (opcodep->lose & (opcode_part << 24)) == 0)
- opcodes[opcode_part] = opcodep;
- if ((opcodep->match & insn) == opcodep->match
- && (opcodep->lose & insn) == 0)
- break;
- }
- }
- if (opcodep->name == NULL)
- return NULL;
- /* Check constraints. If they don't match, loop through the next opcode
- entries. */
- do
- {
- switch (opcodep->operands)
- {
- /* These have no restraint on what can be in the lower three
- bytes. */
- case mmix_operands_regs:
- case mmix_operands_reg_yz:
- case mmix_operands_regs_z_opt:
- case mmix_operands_regs_z:
- case mmix_operands_jmp:
- case mmix_operands_pushgo:
- case mmix_operands_pop:
- case mmix_operands_sync:
- case mmix_operands_x_regs_z:
- case mmix_operands_neg:
- case mmix_operands_pushj:
- case mmix_operands_regaddr:
- case mmix_operands_get:
- case mmix_operands_set:
- case mmix_operands_save:
- case mmix_operands_unsave:
- case mmix_operands_xyz_opt:
- return opcodep;
- /* For a ROUND_MODE, the middle byte must be 0..4. */
- case mmix_operands_roundregs_z:
- case mmix_operands_roundregs:
- {
- int midbyte = (insn >> 8) & 255;
- if (midbyte <= 4)
- return opcodep;
- }
- break;
- case mmix_operands_put:
- /* A "PUT". If it is "immediate", then no restrictions,
- otherwise we have to make sure the register number is < 32. */
- if ((insn & INSN_IMMEDIATE_BIT)
- || ((insn >> 16) & 255) < 32)
- return opcodep;
- break;
- case mmix_operands_resume:
- /* Middle bytes must be zero. */
- if ((insn & 0x00ffff00) == 0)
- return opcodep;
- break;
- default:
- BAD_CASE (opcodep->operands);
- }
- opcodep++;
- }
- while ((opcodep->match & insn) == opcodep->match
- && (opcodep->lose & insn) == 0);
- /* If we got here, we had no match. */
- return NULL;
- }
- /* The main disassembly function. */
- int
- print_insn_mmix (bfd_vma memaddr, struct disassemble_info *info)
- {
- unsigned char buffer[4];
- unsigned long insn;
- unsigned int x, y, z;
- const struct mmix_opcode *opcodep;
- int status = (*info->read_memory_func) (memaddr, buffer, 4, info);
- struct mmix_dis_info *minfop;
- if (status != 0)
- {
- (*info->memory_error_func) (status, memaddr, info);
- return -1;
- }
- /* FIXME: Is -1 suitable? */
- if (info->private_data == NULL
- && ! initialize_mmix_dis_info (info))
- return -1;
- minfop = (struct mmix_dis_info *) info->private_data;
- x = buffer[1];
- y = buffer[2];
- z = buffer[3];
- insn = bfd_getb32 (buffer);
- opcodep = get_opcode (insn);
- if (opcodep == NULL)
- {
- (*info->fprintf_func) (info->stream, _("*unknown*"));
- return 4;
- }
- (*info->fprintf_func) (info->stream, "%s ", opcodep->name);
- /* Present bytes in the order they are laid out in memory. */
- info->display_endian = BFD_ENDIAN_BIG;
- info->insn_info_valid = 1;
- info->bytes_per_chunk = 4;
- info->branch_delay_insns = 0;
- info->target = 0;
- switch (opcodep->type)
- {
- case mmix_type_normal:
- case mmix_type_memaccess_block:
- info->insn_type = dis_nonbranch;
- break;
- case mmix_type_branch:
- info->insn_type = dis_branch;
- break;
- case mmix_type_condbranch:
- info->insn_type = dis_condbranch;
- break;
- case mmix_type_memaccess_octa:
- info->insn_type = dis_dref;
- info->data_size = 8;
- break;
- case mmix_type_memaccess_tetra:
- info->insn_type = dis_dref;
- info->data_size = 4;
- break;
- case mmix_type_memaccess_wyde:
- info->insn_type = dis_dref;
- info->data_size = 2;
- break;
- case mmix_type_memaccess_byte:
- info->insn_type = dis_dref;
- info->data_size = 1;
- break;
- case mmix_type_jsr:
- info->insn_type = dis_jsr;
- break;
- default:
- BAD_CASE(opcodep->type);
- }
- switch (opcodep->operands)
- {
- case mmix_operands_regs:
- /* All registers: "$X,$Y,$Z". */
- (*info->fprintf_func) (info->stream, "%s,%s,%s",
- minfop->reg_name[x],
- minfop->reg_name[y],
- minfop->reg_name[z]);
- break;
- case mmix_operands_reg_yz:
- /* Like SETH - "$X,YZ". */
- (*info->fprintf_func) (info->stream, "%s,0x%x",
- minfop->reg_name[x], y * 256 + z);
- break;
- case mmix_operands_regs_z_opt:
- case mmix_operands_regs_z:
- case mmix_operands_pushgo:
- /* The regular "$X,$Y,$Z|Z". */
- if (insn & INSN_IMMEDIATE_BIT)
- (*info->fprintf_func) (info->stream, "%s,%s,%d",
- minfop->reg_name[x], minfop->reg_name[y], z);
- else
- (*info->fprintf_func) (info->stream, "%s,%s,%s",
- minfop->reg_name[x],
- minfop->reg_name[y],
- minfop->reg_name[z]);
- break;
- case mmix_operands_jmp:
- /* Address; only JMP. */
- {
- bfd_signed_vma offset = (x * 65536 + y * 256 + z) * 4;
- if (insn & INSN_BACKWARD_OFFSET_BIT)
- offset -= (256 * 65536) * 4;
- info->target = memaddr + offset;
- (*info->print_address_func) (memaddr + offset, info);
- }
- break;
- case mmix_operands_roundregs_z:
- /* Two registers, like FLOT, possibly with rounding: "$X,$Z|Z"
- "$X,ROUND_MODE,$Z|Z". */
- if (y != 0)
- {
- if (insn & INSN_IMMEDIATE_BIT)
- (*info->fprintf_func) (info->stream, "%s,%s,%d",
- minfop->reg_name[x],
- ROUND_MODE (y), z);
- else
- (*info->fprintf_func) (info->stream, "%s,%s,%s",
- minfop->reg_name[x],
- ROUND_MODE (y),
- minfop->reg_name[z]);
- }
- else
- {
- if (insn & INSN_IMMEDIATE_BIT)
- (*info->fprintf_func) (info->stream, "%s,%d",
- minfop->reg_name[x], z);
- else
- (*info->fprintf_func) (info->stream, "%s,%s",
- minfop->reg_name[x],
- minfop->reg_name[z]);
- }
- break;
- case mmix_operands_pop:
- /* Like POP - "X,YZ". */
- (*info->fprintf_func) (info->stream, "%d,%d", x, y*256 + z);
- break;
- case mmix_operands_roundregs:
- /* Two registers, possibly with rounding: "$X,$Z" or
- "$X,ROUND_MODE,$Z". */
- if (y != 0)
- (*info->fprintf_func) (info->stream, "%s,%s,%s",
- minfop->reg_name[x],
- ROUND_MODE (y),
- minfop->reg_name[z]);
- else
- (*info->fprintf_func) (info->stream, "%s,%s",
- minfop->reg_name[x],
- minfop->reg_name[z]);
- break;
- case mmix_operands_sync:
- /* Like SYNC - "XYZ". */
- (*info->fprintf_func) (info->stream, "%u",
- x * 65536 + y * 256 + z);
- break;
- case mmix_operands_x_regs_z:
- /* Like SYNCD - "X,$Y,$Z|Z". */
- if (insn & INSN_IMMEDIATE_BIT)
- (*info->fprintf_func) (info->stream, "%d,%s,%d",
- x, minfop->reg_name[y], z);
- else
- (*info->fprintf_func) (info->stream, "%d,%s,%s",
- x, minfop->reg_name[y],
- minfop->reg_name[z]);
- break;
- case mmix_operands_neg:
- /* Like NEG and NEGU - "$X,Y,$Z|Z". */
- if (insn & INSN_IMMEDIATE_BIT)
- (*info->fprintf_func) (info->stream, "%s,%d,%d",
- minfop->reg_name[x], y, z);
- else
- (*info->fprintf_func) (info->stream, "%s,%d,%s",
- minfop->reg_name[x], y,
- minfop->reg_name[z]);
- break;
- case mmix_operands_pushj:
- case mmix_operands_regaddr:
- /* Like GETA or branches - "$X,Address". */
- {
- bfd_signed_vma offset = (y * 256 + z) * 4;
- if (insn & INSN_BACKWARD_OFFSET_BIT)
- offset -= 65536 * 4;
- info->target = memaddr + offset;
- (*info->fprintf_func) (info->stream, "%s,", minfop->reg_name[x]);
- (*info->print_address_func) (memaddr + offset, info);
- }
- break;
- case mmix_operands_get:
- /* GET - "X,spec_reg". */
- (*info->fprintf_func) (info->stream, "%s,%s",
- minfop->reg_name[x],
- minfop->spec_reg_name[z]);
- break;
- case mmix_operands_put:
- /* PUT - "spec_reg,$Z|Z". */
- if (insn & INSN_IMMEDIATE_BIT)
- (*info->fprintf_func) (info->stream, "%s,%d",
- minfop->spec_reg_name[x], z);
- else
- (*info->fprintf_func) (info->stream, "%s,%s",
- minfop->spec_reg_name[x],
- minfop->reg_name[z]);
- break;
- case mmix_operands_set:
- /* Two registers, "$X,$Y". */
- (*info->fprintf_func) (info->stream, "%s,%s",
- minfop->reg_name[x],
- minfop->reg_name[y]);
- break;
- case mmix_operands_save:
- /* SAVE - "$X,0". */
- (*info->fprintf_func) (info->stream, "%s,0", minfop->reg_name[x]);
- break;
- case mmix_operands_unsave:
- /* UNSAVE - "0,$Z". */
- (*info->fprintf_func) (info->stream, "0,%s", minfop->reg_name[z]);
- break;
- case mmix_operands_xyz_opt:
- /* Like SWYM or TRAP - "X,Y,Z". */
- (*info->fprintf_func) (info->stream, "%d,%d,%d", x, y, z);
- break;
- case mmix_operands_resume:
- /* Just "Z", like RESUME. */
- (*info->fprintf_func) (info->stream, "%d", z);
- break;
- default:
- (*info->fprintf_func) (info->stream, _("*unknown operands type: %d*"),
- opcodep->operands);
- break;
- }
- return 4;
- }
|