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