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