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