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