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