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