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