Home | History | Annotate | Download | only in opcodes
      1 /* mmix-dis.c -- Disassemble MMIX instructions.
      2    Copyright (C) 2000-2016 Free Software Foundation, Inc.
      3    Written by Hans-Peter Nilsson (hp (at) bitrange.com)
      4 
      5    This file is part of the GNU opcodes library.
      6 
      7    This library is free software; you can redistribute it and/or modify
      8    it under the terms of the GNU General Public License as published by
      9    the Free Software Foundation; either version 3, or (at your option)
     10    any later version.
     11 
     12    It is distributed in the hope that it will be useful, but WITHOUT
     13    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     14    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
     15    License for more details.
     16 
     17    You should have received a copy of the GNU General Public License
     18    along with this file; see the file COPYING.  If not, write to the Free
     19    Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
     20    MA 02110-1301, USA.  */
     21 
     22 #include "sysdep.h"
     23 #include <stdio.h>
     24 #include "opcode/mmix.h"
     25 #include "dis-asm.h"
     26 #include "libiberty.h"
     27 #include "bfd.h"
     28 #include "opintl.h"
     29 
     30 #define BAD_CASE(x)				\
     31  do						\
     32    {						\
     33      fprintf (stderr,				\
     34 	      _("Bad case %d (%s) in %s:%d\n"),	\
     35 	      x, #x, __FILE__, __LINE__);	\
     36      abort ();					\
     37    }						\
     38  while (0)
     39 
     40 #define FATAL_DEBUG							\
     41  do									\
     42    {									\
     43      fprintf (stderr,							\
     44 	      _("Internal: Non-debugged code (test-case missing): %s:%d"),\
     45 	      __FILE__, __LINE__);					\
     46      abort ();								\
     47    }									\
     48  while (0)
     49 
     50 #define ROUND_MODE(n)					\
     51  ((n) == 1 ? "ROUND_OFF" : (n) == 2 ? "ROUND_UP" :	\
     52   (n) == 3 ? "ROUND_DOWN" : (n) == 4 ? "ROUND_NEAR" :	\
     53   _("(unknown)"))
     54 
     55 #define INSN_IMMEDIATE_BIT (IMM_OFFSET_BIT << 24)
     56 #define INSN_BACKWARD_OFFSET_BIT (1 << 24)
     57 
     58 struct mmix_dis_info
     59  {
     60    const char *reg_name[256];
     61    const char *spec_reg_name[32];
     62 
     63    /* Waste a little memory so we don't have to allocate each separately.
     64       We could have an array with static contents for these, but on the
     65       other hand, we don't have to.  */
     66    char basic_reg_name[256][sizeof ("$255")];
     67  };
     68 
     69 /* Initialize a target-specific array in INFO.  */
     70 
     71 static bfd_boolean
     72 initialize_mmix_dis_info (struct disassemble_info *info)
     73 {
     74   struct mmix_dis_info *minfop = malloc (sizeof (struct mmix_dis_info));
     75   long i;
     76 
     77   if (minfop == NULL)
     78     return FALSE;
     79 
     80   memset (minfop, 0, sizeof (*minfop));
     81 
     82   /* Initialize register names from register symbols.  If there's no
     83      register section, then there are no register symbols.  */
     84   if ((info->section != NULL && info->section->owner != NULL)
     85       || (info->symbols != NULL
     86 	  && info->symbols[0] != NULL
     87 	  && bfd_asymbol_bfd (info->symbols[0]) != NULL))
     88     {
     89       bfd *abfd = info->section && info->section->owner != NULL
     90 	? info->section->owner
     91 	: bfd_asymbol_bfd (info->symbols[0]);
     92       asection *reg_section = bfd_get_section_by_name (abfd, "*REG*");
     93 
     94       if (reg_section != NULL)
     95 	{
     96 	  /* The returned symcount *does* include the ending NULL.  */
     97 	  long symsize = bfd_get_symtab_upper_bound (abfd);
     98 	  asymbol **syms = malloc (symsize);
     99 	  long nsyms;
    100 
    101 	  if (syms == NULL)
    102 	    {
    103 	      FATAL_DEBUG;
    104 	      free (minfop);
    105 	      return FALSE;
    106 	    }
    107 	  nsyms = bfd_canonicalize_symtab (abfd, syms);
    108 
    109 	  /* We use the first name for a register.  If this is MMO, then
    110 	     it's the name with the first sequence number, presumably the
    111 	     first in the source.  */
    112 	  for (i = 0; i < nsyms && syms[i] != NULL; i++)
    113 	    {
    114 	      if (syms[i]->section == reg_section
    115 		  && syms[i]->value < 256
    116 		  && minfop->reg_name[syms[i]->value] == NULL)
    117 		minfop->reg_name[syms[i]->value] = syms[i]->name;
    118 	    }
    119 	}
    120     }
    121 
    122   /* Fill in the rest with the canonical names.  */
    123   for (i = 0; i < 256; i++)
    124     if (minfop->reg_name[i] == NULL)
    125       {
    126 	sprintf (minfop->basic_reg_name[i], "$%ld", i);
    127 	minfop->reg_name[i] = minfop->basic_reg_name[i];
    128       }
    129 
    130   /* We assume it's actually a one-to-one mapping of number-to-name.  */
    131   for (i = 0; mmix_spec_regs[i].name != NULL; i++)
    132     minfop->spec_reg_name[mmix_spec_regs[i].number] = mmix_spec_regs[i].name;
    133 
    134   info->private_data = (void *) minfop;
    135   return TRUE;
    136 }
    137 
    138 /* A table indexed by the first byte is constructed as we disassemble each
    139    tetrabyte.  The contents is a pointer into mmix_insns reflecting the
    140    first found entry with matching match-bits and lose-bits.  Further
    141    entries are considered one after one until the operand constraints
    142    match or the match-bits and lose-bits do not match.  Normally a
    143    "further entry" will just show that there was no other match.  */
    144 
    145 static const struct mmix_opcode *
    146 get_opcode (unsigned long insn)
    147 {
    148   static const struct mmix_opcode **opcodes = NULL;
    149   const struct mmix_opcode *opcodep = mmix_opcodes;
    150   unsigned int opcode_part = (insn >> 24) & 255;
    151 
    152   if (opcodes == NULL)
    153     opcodes = xcalloc (256, sizeof (struct mmix_opcode *));
    154 
    155   opcodep = opcodes[opcode_part];
    156   if (opcodep == NULL
    157       || (opcodep->match & insn) != opcodep->match
    158       || (opcodep->lose & insn) != 0)
    159     {
    160       /* Search through the table.  */
    161       for (opcodep = mmix_opcodes; opcodep->name != NULL; opcodep++)
    162 	{
    163 	  /* FIXME: Break out this into an initialization function.  */
    164 	  if ((opcodep->match & (opcode_part << 24)) == opcode_part
    165 	      && (opcodep->lose & (opcode_part << 24)) == 0)
    166 	    opcodes[opcode_part] = opcodep;
    167 
    168 	  if ((opcodep->match & insn) == opcodep->match
    169 	      && (opcodep->lose & insn) == 0)
    170 	    break;
    171 	}
    172     }
    173 
    174   if (opcodep->name == NULL)
    175     return NULL;
    176 
    177   /* Check constraints.  If they don't match, loop through the next opcode
    178      entries.  */
    179   do
    180     {
    181       switch (opcodep->operands)
    182 	{
    183 	  /* These have no restraint on what can be in the lower three
    184 	     bytes.  */
    185 	case mmix_operands_regs:
    186 	case mmix_operands_reg_yz:
    187 	case mmix_operands_regs_z_opt:
    188 	case mmix_operands_regs_z:
    189 	case mmix_operands_jmp:
    190 	case mmix_operands_pushgo:
    191 	case mmix_operands_pop:
    192 	case mmix_operands_sync:
    193 	case mmix_operands_x_regs_z:
    194 	case mmix_operands_neg:
    195 	case mmix_operands_pushj:
    196 	case mmix_operands_regaddr:
    197 	case mmix_operands_get:
    198 	case mmix_operands_set:
    199 	case mmix_operands_save:
    200 	case mmix_operands_unsave:
    201 	case mmix_operands_xyz_opt:
    202 	  return opcodep;
    203 
    204 	  /* For a ROUND_MODE, the middle byte must be 0..4.  */
    205 	case mmix_operands_roundregs_z:
    206 	case mmix_operands_roundregs:
    207 	  {
    208 	    int midbyte = (insn >> 8) & 255;
    209 
    210 	    if (midbyte <= 4)
    211 	      return opcodep;
    212 	  }
    213 	break;
    214 
    215 	case mmix_operands_put:
    216 	  /* A "PUT".  If it is "immediate", then no restrictions,
    217 	     otherwise we have to make sure the register number is < 32.  */
    218 	  if ((insn & INSN_IMMEDIATE_BIT)
    219 	      || ((insn >> 16) & 255) < 32)
    220 	    return opcodep;
    221 	  break;
    222 
    223 	case mmix_operands_resume:
    224 	  /* Middle bytes must be zero.  */
    225 	  if ((insn & 0x00ffff00) == 0)
    226 	    return opcodep;
    227 	  break;
    228 
    229 	default:
    230 	  BAD_CASE (opcodep->operands);
    231 	}
    232 
    233       opcodep++;
    234     }
    235   while ((opcodep->match & insn) == opcodep->match
    236 	 && (opcodep->lose & insn) == 0);
    237 
    238   /* If we got here, we had no match.  */
    239   return NULL;
    240 }
    241 
    242 /* The main disassembly function.  */
    243 
    244 int
    245 print_insn_mmix (bfd_vma memaddr, struct disassemble_info *info)
    246 {
    247   unsigned char buffer[4];
    248   unsigned long insn;
    249   unsigned int x, y, z;
    250   const struct mmix_opcode *opcodep;
    251   int status = (*info->read_memory_func) (memaddr, buffer, 4, info);
    252   struct mmix_dis_info *minfop;
    253 
    254   if (status != 0)
    255     {
    256       (*info->memory_error_func) (status, memaddr, info);
    257       return -1;
    258     }
    259 
    260   /* FIXME: Is -1 suitable?  */
    261   if (info->private_data == NULL
    262       && ! initialize_mmix_dis_info (info))
    263     return -1;
    264 
    265   minfop = (struct mmix_dis_info *) info->private_data;
    266   x = buffer[1];
    267   y = buffer[2];
    268   z = buffer[3];
    269 
    270   insn = bfd_getb32 (buffer);
    271 
    272   opcodep = get_opcode (insn);
    273 
    274   if (opcodep == NULL)
    275     {
    276       (*info->fprintf_func) (info->stream, _("*unknown*"));
    277       return 4;
    278     }
    279 
    280   (*info->fprintf_func) (info->stream, "%s ", opcodep->name);
    281 
    282   /* Present bytes in the order they are laid out in memory.  */
    283   info->display_endian = BFD_ENDIAN_BIG;
    284 
    285   info->insn_info_valid = 1;
    286   info->bytes_per_chunk = 4;
    287   info->branch_delay_insns = 0;
    288   info->target = 0;
    289   switch (opcodep->type)
    290     {
    291     case mmix_type_normal:
    292     case mmix_type_memaccess_block:
    293       info->insn_type = dis_nonbranch;
    294       break;
    295 
    296     case mmix_type_branch:
    297       info->insn_type = dis_branch;
    298       break;
    299 
    300     case mmix_type_condbranch:
    301       info->insn_type = dis_condbranch;
    302       break;
    303 
    304     case mmix_type_memaccess_octa:
    305       info->insn_type = dis_dref;
    306       info->data_size = 8;
    307       break;
    308 
    309     case mmix_type_memaccess_tetra:
    310       info->insn_type = dis_dref;
    311       info->data_size = 4;
    312       break;
    313 
    314     case mmix_type_memaccess_wyde:
    315       info->insn_type = dis_dref;
    316       info->data_size = 2;
    317       break;
    318 
    319     case mmix_type_memaccess_byte:
    320       info->insn_type = dis_dref;
    321       info->data_size = 1;
    322       break;
    323 
    324     case mmix_type_jsr:
    325       info->insn_type = dis_jsr;
    326       break;
    327 
    328     default:
    329       BAD_CASE(opcodep->type);
    330     }
    331 
    332   switch (opcodep->operands)
    333     {
    334     case mmix_operands_regs:
    335       /*  All registers: "$X,$Y,$Z".  */
    336       (*info->fprintf_func) (info->stream, "%s,%s,%s",
    337 			     minfop->reg_name[x],
    338 			     minfop->reg_name[y],
    339 			     minfop->reg_name[z]);
    340       break;
    341 
    342     case mmix_operands_reg_yz:
    343       /* Like SETH - "$X,YZ".  */
    344       (*info->fprintf_func) (info->stream, "%s,0x%x",
    345 			     minfop->reg_name[x], y * 256 + z);
    346       break;
    347 
    348     case mmix_operands_regs_z_opt:
    349     case mmix_operands_regs_z:
    350     case mmix_operands_pushgo:
    351       /* The regular "$X,$Y,$Z|Z".  */
    352       if (insn & INSN_IMMEDIATE_BIT)
    353 	(*info->fprintf_func) (info->stream, "%s,%s,%d",
    354 			       minfop->reg_name[x], minfop->reg_name[y], z);
    355       else
    356 	(*info->fprintf_func) (info->stream, "%s,%s,%s",
    357 			       minfop->reg_name[x],
    358 			       minfop->reg_name[y],
    359 			       minfop->reg_name[z]);
    360       break;
    361 
    362     case mmix_operands_jmp:
    363       /* Address; only JMP.  */
    364       {
    365 	bfd_signed_vma offset = (x * 65536 + y * 256 + z) * 4;
    366 
    367 	if (insn & INSN_BACKWARD_OFFSET_BIT)
    368 	  offset -= (256 * 65536) * 4;
    369 
    370 	info->target = memaddr + offset;
    371 	(*info->print_address_func) (memaddr + offset, info);
    372       }
    373       break;
    374 
    375     case mmix_operands_roundregs_z:
    376       /* Two registers, like FLOT, possibly with rounding: "$X,$Z|Z"
    377 	 "$X,ROUND_MODE,$Z|Z".  */
    378       if (y != 0)
    379 	{
    380 	  if (insn & INSN_IMMEDIATE_BIT)
    381 	    (*info->fprintf_func) (info->stream, "%s,%s,%d",
    382 				   minfop->reg_name[x],
    383 				   ROUND_MODE (y), z);
    384 	  else
    385 	    (*info->fprintf_func) (info->stream, "%s,%s,%s",
    386 				   minfop->reg_name[x],
    387 				   ROUND_MODE (y),
    388 				   minfop->reg_name[z]);
    389 	}
    390       else
    391 	{
    392 	  if (insn & INSN_IMMEDIATE_BIT)
    393 	    (*info->fprintf_func) (info->stream, "%s,%d",
    394 				   minfop->reg_name[x], z);
    395 	  else
    396 	    (*info->fprintf_func) (info->stream, "%s,%s",
    397 				   minfop->reg_name[x],
    398 				   minfop->reg_name[z]);
    399 	}
    400       break;
    401 
    402     case mmix_operands_pop:
    403       /* Like POP - "X,YZ".  */
    404       (*info->fprintf_func) (info->stream, "%d,%d", x, y*256 + z);
    405       break;
    406 
    407     case mmix_operands_roundregs:
    408       /* Two registers, possibly with rounding: "$X,$Z" or
    409 	 "$X,ROUND_MODE,$Z".  */
    410       if (y != 0)
    411 	(*info->fprintf_func) (info->stream, "%s,%s,%s",
    412 			       minfop->reg_name[x],
    413 			       ROUND_MODE (y),
    414 			       minfop->reg_name[z]);
    415       else
    416 	(*info->fprintf_func) (info->stream, "%s,%s",
    417 			       minfop->reg_name[x],
    418 			       minfop->reg_name[z]);
    419       break;
    420 
    421     case mmix_operands_sync:
    422 	/* Like SYNC - "XYZ".  */
    423       (*info->fprintf_func) (info->stream, "%u",
    424 			     x * 65536 + y * 256 + z);
    425       break;
    426 
    427     case mmix_operands_x_regs_z:
    428       /* Like SYNCD - "X,$Y,$Z|Z".  */
    429       if (insn & INSN_IMMEDIATE_BIT)
    430 	(*info->fprintf_func) (info->stream, "%d,%s,%d",
    431 			       x, minfop->reg_name[y], z);
    432       else
    433 	(*info->fprintf_func) (info->stream, "%d,%s,%s",
    434 			       x, minfop->reg_name[y],
    435 			       minfop->reg_name[z]);
    436       break;
    437 
    438     case mmix_operands_neg:
    439       /* Like NEG and NEGU - "$X,Y,$Z|Z".  */
    440       if (insn & INSN_IMMEDIATE_BIT)
    441 	(*info->fprintf_func) (info->stream, "%s,%d,%d",
    442 			       minfop->reg_name[x], y, z);
    443       else
    444 	(*info->fprintf_func) (info->stream, "%s,%d,%s",
    445 			       minfop->reg_name[x], y,
    446 			       minfop->reg_name[z]);
    447       break;
    448 
    449     case mmix_operands_pushj:
    450     case mmix_operands_regaddr:
    451       /* Like GETA or branches - "$X,Address".  */
    452       {
    453 	bfd_signed_vma offset = (y * 256 + z) * 4;
    454 
    455 	if (insn & INSN_BACKWARD_OFFSET_BIT)
    456 	  offset -= 65536 * 4;
    457 
    458 	info->target = memaddr + offset;
    459 
    460 	(*info->fprintf_func) (info->stream, "%s,", minfop->reg_name[x]);
    461 	(*info->print_address_func) (memaddr + offset, info);
    462       }
    463       break;
    464 
    465     case mmix_operands_get:
    466       /* GET - "X,spec_reg".  */
    467       (*info->fprintf_func) (info->stream, "%s,%s",
    468 			     minfop->reg_name[x],
    469 			     minfop->spec_reg_name[z]);
    470       break;
    471 
    472     case mmix_operands_put:
    473       /* PUT - "spec_reg,$Z|Z".  */
    474       if (insn & INSN_IMMEDIATE_BIT)
    475 	(*info->fprintf_func) (info->stream, "%s,%d",
    476 			       minfop->spec_reg_name[x], z);
    477       else
    478 	(*info->fprintf_func) (info->stream, "%s,%s",
    479 			       minfop->spec_reg_name[x],
    480 			       minfop->reg_name[z]);
    481       break;
    482 
    483     case mmix_operands_set:
    484       /*  Two registers, "$X,$Y".  */
    485       (*info->fprintf_func) (info->stream, "%s,%s",
    486 			     minfop->reg_name[x],
    487 			     minfop->reg_name[y]);
    488       break;
    489 
    490     case mmix_operands_save:
    491       /* SAVE - "$X,0".  */
    492       (*info->fprintf_func) (info->stream, "%s,0", minfop->reg_name[x]);
    493       break;
    494 
    495     case mmix_operands_unsave:
    496       /* UNSAVE - "0,$Z".  */
    497       (*info->fprintf_func) (info->stream, "0,%s", minfop->reg_name[z]);
    498       break;
    499 
    500     case mmix_operands_xyz_opt:
    501       /* Like SWYM or TRAP - "X,Y,Z".  */
    502       (*info->fprintf_func) (info->stream, "%d,%d,%d", x, y, z);
    503       break;
    504 
    505     case mmix_operands_resume:
    506       /* Just "Z", like RESUME.  */
    507       (*info->fprintf_func) (info->stream, "%d", z);
    508       break;
    509 
    510     default:
    511       (*info->fprintf_func) (info->stream, _("*unknown operands type: %d*"),
    512 			     opcodep->operands);
    513       break;
    514     }
    515 
    516   return 4;
    517 }
    518