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, 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 
58 struct 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 
69 static bfd_boolean initialize_mmix_dis_info
70   PARAMS ((struct disassemble_info *));
71 static const struct mmix_opcode *get_opcode
72   PARAMS ((unsigned long));
73 
74 
75 /* Initialize a target-specific array in INFO.  */
76 
77 static bfd_boolean
initialize_mmix_dis_info(info)78 initialize_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 
152 static const struct mmix_opcode *
get_opcode(insn)153 get_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 
250 int
print_insn_mmix(memaddr,info)251 print_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