[ gas/ChangeLog ]
[platform/upstream/binutils.git] / opcodes / mmix-dis.c
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
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 *
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
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 }