1  /* mmix-dis.c -- Disassemble MMIX instructions.
       2     Copyright (C) 2000-2023 Free Software Foundation, Inc.
       3     Written by Hans-Peter Nilsson (hp@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  }