Home | History | Annotate | Line # | Download | only in opcodes
mmix-dis.c revision 1.1.1.2
      1 /* mmix-dis.c -- Disassemble MMIX instructions.
      2    Copyright 2000, 2001, 2002, 2005, 2007 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 <stdio.h>
     23 #include <string.h>
     24 #include <stdlib.h>
     25 #include "opcode/mmix.h"
     26 #include "dis-asm.h"
     27 #include "libiberty.h"
     28 #include "bfd.h"
     29 #include "opintl.h"
     30 
     31 #define BAD_CASE(x)				\
     32  do						\
     33    {						\
     34      fprintf (stderr,				\
     35 	      _("Bad case %d (%s) in %s:%d\n"),	\
     36 	      x, #x, __FILE__, __LINE__);	\
     37      abort ();					\
     38    }						\
     39  while (0)
     40 
     41 #define FATAL_DEBUG							\
     42  do									\
     43    {									\
     44      fprintf (stderr,							\
     45 	      _("Internal: Non-debugged code (test-case missing): %s:%d"),\
     46 	      __FILE__, __LINE__);					\
     47      abort ();								\
     48    }									\
     49  while (0)
     50 
     51 #define ROUND_MODE(n)					\
     52  ((n) == 1 ? "ROUND_OFF" : (n) == 2 ? "ROUND_UP" :	\
     53   (n) == 3 ? "ROUND_DOWN" : (n) == 4 ? "ROUND_NEAR" :	\
     54   _("(unknown)"))
     55 
     56 #define INSN_IMMEDIATE_BIT (IMM_OFFSET_BIT << 24)
     57 #define INSN_BACKWARD_OFFSET_BIT (1 << 24)
     58 
     59 struct mmix_dis_info
     60  {
     61    const char *reg_name[256];
     62    const char *spec_reg_name[32];
     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[256][sizeof ("$255")];
     68  };
     69 
     70 /* Initialize a target-specific array in INFO.  */
     71 
     72 static bfd_boolean
     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 < 256
    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 < 256; 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 /* The main disassembly function.  */
    244 
    245 int
    246 print_insn_mmix (bfd_vma memaddr, struct disassemble_info *info)
    247 {
    248   unsigned char buffer[4];
    249   unsigned long insn;
    250   unsigned int x, y, z;
    251   const struct mmix_opcode *opcodep;
    252   int status = (*info->read_memory_func) (memaddr, buffer, 4, info);
    253   struct mmix_dis_info *minfop;
    254 
    255   if (status != 0)
    256     {
    257       (*info->memory_error_func) (status, memaddr, info);
    258       return -1;
    259     }
    260 
    261   /* FIXME: Is -1 suitable?  */
    262   if (info->private_data == NULL
    263       && ! initialize_mmix_dis_info (info))
    264     return -1;
    265 
    266   minfop = (struct mmix_dis_info *) info->private_data;
    267   x = buffer[1];
    268   y = buffer[2];
    269   z = buffer[3];
    270 
    271   insn = bfd_getb32 (buffer);
    272 
    273   opcodep = get_opcode (insn);
    274 
    275   if (opcodep == NULL)
    276     {
    277       (*info->fprintf_func) (info->stream, _("*unknown*"));
    278       return 4;
    279     }
    280 
    281   (*info->fprintf_func) (info->stream, "%s ", opcodep->name);
    282 
    283   /* Present bytes in the order they are laid out in memory.  */
    284   info->display_endian = BFD_ENDIAN_BIG;
    285 
    286   info->insn_info_valid = 1;
    287   info->bytes_per_chunk = 4;
    288   info->branch_delay_insns = 0;
    289   info->target = 0;
    290   switch (opcodep->type)
    291     {
    292     case mmix_type_normal:
    293     case mmix_type_memaccess_block:
    294       info->insn_type = dis_nonbranch;
    295       break;
    296 
    297     case mmix_type_branch:
    298       info->insn_type = dis_branch;
    299       break;
    300 
    301     case mmix_type_condbranch:
    302       info->insn_type = dis_condbranch;
    303       break;
    304 
    305     case mmix_type_memaccess_octa:
    306       info->insn_type = dis_dref;
    307       info->data_size = 8;
    308       break;
    309 
    310     case mmix_type_memaccess_tetra:
    311       info->insn_type = dis_dref;
    312       info->data_size = 4;
    313       break;
    314 
    315     case mmix_type_memaccess_wyde:
    316       info->insn_type = dis_dref;
    317       info->data_size = 2;
    318       break;
    319 
    320     case mmix_type_memaccess_byte:
    321       info->insn_type = dis_dref;
    322       info->data_size = 1;
    323       break;
    324 
    325     case mmix_type_jsr:
    326       info->insn_type = dis_jsr;
    327       break;
    328 
    329     default:
    330       BAD_CASE(opcodep->type);
    331     }
    332 
    333   switch (opcodep->operands)
    334     {
    335     case mmix_operands_regs:
    336       /*  All registers: "$X,$Y,$Z".  */
    337       (*info->fprintf_func) (info->stream, "%s,%s,%s",
    338 			     minfop->reg_name[x],
    339 			     minfop->reg_name[y],
    340 			     minfop->reg_name[z]);
    341       break;
    342 
    343     case mmix_operands_reg_yz:
    344       /* Like SETH - "$X,YZ".  */
    345       (*info->fprintf_func) (info->stream, "%s,0x%x",
    346 			     minfop->reg_name[x], y * 256 + z);
    347       break;
    348 
    349     case mmix_operands_regs_z_opt:
    350     case mmix_operands_regs_z:
    351     case mmix_operands_pushgo:
    352       /* The regular "$X,$Y,$Z|Z".  */
    353       if (insn & INSN_IMMEDIATE_BIT)
    354 	(*info->fprintf_func) (info->stream, "%s,%s,%d",
    355 			       minfop->reg_name[x], minfop->reg_name[y], z);
    356       else
    357 	(*info->fprintf_func) (info->stream, "%s,%s,%s",
    358 			       minfop->reg_name[x],
    359 			       minfop->reg_name[y],
    360 			       minfop->reg_name[z]);
    361       break;
    362 
    363     case mmix_operands_jmp:
    364       /* Address; only JMP.  */
    365       {
    366 	bfd_signed_vma offset = (x * 65536 + y * 256 + z) * 4;
    367 
    368 	if (insn & INSN_BACKWARD_OFFSET_BIT)
    369 	  offset -= (256 * 65536) * 4;
    370 
    371 	info->target = memaddr + offset;
    372 	(*info->print_address_func) (memaddr + offset, info);
    373       }
    374       break;
    375 
    376     case mmix_operands_roundregs_z:
    377       /* Two registers, like FLOT, possibly with rounding: "$X,$Z|Z"
    378 	 "$X,ROUND_MODE,$Z|Z".  */
    379       if (y != 0)
    380 	{
    381 	  if (insn & INSN_IMMEDIATE_BIT)
    382 	    (*info->fprintf_func) (info->stream, "%s,%s,%d",
    383 				   minfop->reg_name[x],
    384 				   ROUND_MODE (y), z);
    385 	  else
    386 	    (*info->fprintf_func) (info->stream, "%s,%s,%s",
    387 				   minfop->reg_name[x],
    388 				   ROUND_MODE (y),
    389 				   minfop->reg_name[z]);
    390 	}
    391       else
    392 	{
    393 	  if (insn & INSN_IMMEDIATE_BIT)
    394 	    (*info->fprintf_func) (info->stream, "%s,%d",
    395 				   minfop->reg_name[x], z);
    396 	  else
    397 	    (*info->fprintf_func) (info->stream, "%s,%s",
    398 				   minfop->reg_name[x],
    399 				   minfop->reg_name[z]);
    400 	}
    401       break;
    402 
    403     case mmix_operands_pop:
    404       /* Like POP - "X,YZ".  */
    405       (*info->fprintf_func) (info->stream, "%d,%d", x, y*256 + z);
    406       break;
    407 
    408     case mmix_operands_roundregs:
    409       /* Two registers, possibly with rounding: "$X,$Z" or
    410 	 "$X,ROUND_MODE,$Z".  */
    411       if (y != 0)
    412 	(*info->fprintf_func) (info->stream, "%s,%s,%s",
    413 			       minfop->reg_name[x],
    414 			       ROUND_MODE (y),
    415 			       minfop->reg_name[z]);
    416       else
    417 	(*info->fprintf_func) (info->stream, "%s,%s",
    418 			       minfop->reg_name[x],
    419 			       minfop->reg_name[z]);
    420       break;
    421 
    422     case mmix_operands_sync:
    423 	/* Like SYNC - "XYZ".  */
    424       (*info->fprintf_func) (info->stream, "%u",
    425 			     x * 65536 + y * 256 + z);
    426       break;
    427 
    428     case mmix_operands_x_regs_z:
    429       /* Like SYNCD - "X,$Y,$Z|Z".  */
    430       if (insn & INSN_IMMEDIATE_BIT)
    431 	(*info->fprintf_func) (info->stream, "%d,%s,%d",
    432 			       x, minfop->reg_name[y], z);
    433       else
    434 	(*info->fprintf_func) (info->stream, "%d,%s,%s",
    435 			       x, minfop->reg_name[y],
    436 			       minfop->reg_name[z]);
    437       break;
    438 
    439     case mmix_operands_neg:
    440       /* Like NEG and NEGU - "$X,Y,$Z|Z".  */
    441       if (insn & INSN_IMMEDIATE_BIT)
    442 	(*info->fprintf_func) (info->stream, "%s,%d,%d",
    443 			       minfop->reg_name[x], y, z);
    444       else
    445 	(*info->fprintf_func) (info->stream, "%s,%d,%s",
    446 			       minfop->reg_name[x], y,
    447 			       minfop->reg_name[z]);
    448       break;
    449 
    450     case mmix_operands_pushj:
    451     case mmix_operands_regaddr:
    452       /* Like GETA or branches - "$X,Address".  */
    453       {
    454 	bfd_signed_vma offset = (y * 256 + z) * 4;
    455 
    456 	if (insn & INSN_BACKWARD_OFFSET_BIT)
    457 	  offset -= 65536 * 4;
    458 
    459 	info->target = memaddr + offset;
    460 
    461 	(*info->fprintf_func) (info->stream, "%s,", minfop->reg_name[x]);
    462 	(*info->print_address_func) (memaddr + offset, info);
    463       }
    464       break;
    465 
    466     case mmix_operands_get:
    467       /* GET - "X,spec_reg".  */
    468       (*info->fprintf_func) (info->stream, "%s,%s",
    469 			     minfop->reg_name[x],
    470 			     minfop->spec_reg_name[z]);
    471       break;
    472 
    473     case mmix_operands_put:
    474       /* PUT - "spec_reg,$Z|Z".  */
    475       if (insn & INSN_IMMEDIATE_BIT)
    476 	(*info->fprintf_func) (info->stream, "%s,%d",
    477 			       minfop->spec_reg_name[x], z);
    478       else
    479 	(*info->fprintf_func) (info->stream, "%s,%s",
    480 			       minfop->spec_reg_name[x],
    481 			       minfop->reg_name[z]);
    482       break;
    483 
    484     case mmix_operands_set:
    485       /*  Two registers, "$X,$Y".  */
    486       (*info->fprintf_func) (info->stream, "%s,%s",
    487 			     minfop->reg_name[x],
    488 			     minfop->reg_name[y]);
    489       break;
    490 
    491     case mmix_operands_save:
    492       /* SAVE - "$X,0".  */
    493       (*info->fprintf_func) (info->stream, "%s,0", minfop->reg_name[x]);
    494       break;
    495 
    496     case mmix_operands_unsave:
    497       /* UNSAVE - "0,$Z".  */
    498       (*info->fprintf_func) (info->stream, "0,%s", minfop->reg_name[z]);
    499       break;
    500 
    501     case mmix_operands_xyz_opt:
    502       /* Like SWYM or TRAP - "X,Y,Z".  */
    503       (*info->fprintf_func) (info->stream, "%d,%d,%d", x, y, z);
    504       break;
    505 
    506     case mmix_operands_resume:
    507       /* Just "Z", like RESUME.  */
    508       (*info->fprintf_func) (info->stream, "%d", z);
    509       break;
    510 
    511     default:
    512       (*info->fprintf_func) (info->stream, _("*unknown operands type: %d*"),
    513 			     opcodep->operands);
    514       break;
    515     }
    516 
    517   return 4;
    518 }
    519