1/* Disassembler interface for targets using CGEN. -*- C -*-
2   CGEN: Cpu tools GENerator
3
4   THIS FILE IS MACHINE GENERATED WITH CGEN.
5   - the resultant file is machine generated, cgen-dis.in isn't
6
7   Copyright (C) 1996-2017 Free Software Foundation, Inc.
8
9   This file is part of libopcodes.
10
11   This library is free software; you can redistribute it and/or modify
12   it under the terms of the GNU General Public License as published by
13   the Free Software Foundation; either version 3, or (at your option)
14   any later version.
15
16   It is distributed in the hope that it will be useful, but WITHOUT
17   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
19   License for more details.
20
21   You should have received a copy of the GNU General Public License
22   along with this program; if not, write to the Free Software Foundation, Inc.,
23   51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
24
25/* ??? Eventually more and more of this stuff can go to cpu-independent files.
26   Keep that in mind.  */
27
28#include "sysdep.h"
29#include <stdio.h>
30#include "ansidecl.h"
31#include "dis-asm.h"
32#include "bfd.h"
33#include "symcat.h"
34#include "libiberty.h"
35#include "lm32-desc.h"
36#include "lm32-opc.h"
37#include "opintl.h"
38
39/* Default text to print if an instruction isn't recognized.  */
40#define UNKNOWN_INSN_MSG _("*unknown*")
41
42static void print_normal
43  (CGEN_CPU_DESC, void *, long, unsigned int, bfd_vma, int);
44static void print_address
45  (CGEN_CPU_DESC, void *, bfd_vma, unsigned int, bfd_vma, int) ATTRIBUTE_UNUSED;
46static void print_keyword
47  (CGEN_CPU_DESC, void *, CGEN_KEYWORD *, long, unsigned int) ATTRIBUTE_UNUSED;
48static void print_insn_normal
49  (CGEN_CPU_DESC, void *, const CGEN_INSN *, CGEN_FIELDS *, bfd_vma, int);
50static int print_insn
51  (CGEN_CPU_DESC, bfd_vma,  disassemble_info *, bfd_byte *, unsigned);
52static int default_print_insn
53  (CGEN_CPU_DESC, bfd_vma, disassemble_info *) ATTRIBUTE_UNUSED;
54static int read_insn
55  (CGEN_CPU_DESC, bfd_vma, disassemble_info *, bfd_byte *, int, CGEN_EXTRACT_INFO *,
56   unsigned long *);
57
58/* -- disassembler routines inserted here.  */
59
60
61void lm32_cgen_print_operand
62  (CGEN_CPU_DESC, int, PTR, CGEN_FIELDS *, void const *, bfd_vma, int);
63
64/* Main entry point for printing operands.
65   XINFO is a `void *' and not a `disassemble_info *' to not put a requirement
66   of dis-asm.h on cgen.h.
67
68   This function is basically just a big switch statement.  Earlier versions
69   used tables to look up the function to use, but
70   - if the table contains both assembler and disassembler functions then
71     the disassembler contains much of the assembler and vice-versa,
72   - there's a lot of inlining possibilities as things grow,
73   - using a switch statement avoids the function call overhead.
74
75   This function could be moved into `print_insn_normal', but keeping it
76   separate makes clear the interface between `print_insn_normal' and each of
77   the handlers.  */
78
79void
80lm32_cgen_print_operand (CGEN_CPU_DESC cd,
81			   int opindex,
82			   void * xinfo,
83			   CGEN_FIELDS *fields,
84			   void const *attrs ATTRIBUTE_UNUSED,
85			   bfd_vma pc,
86			   int length)
87{
88  disassemble_info *info = (disassemble_info *) xinfo;
89
90  switch (opindex)
91    {
92    case LM32_OPERAND_BRANCH :
93      print_address (cd, info, fields->f_branch, 0|(1<<CGEN_OPERAND_PCREL_ADDR), pc, length);
94      break;
95    case LM32_OPERAND_CALL :
96      print_address (cd, info, fields->f_call, 0|(1<<CGEN_OPERAND_PCREL_ADDR), pc, length);
97      break;
98    case LM32_OPERAND_CSR :
99      print_keyword (cd, info, & lm32_cgen_opval_h_csr, fields->f_csr, 0);
100      break;
101    case LM32_OPERAND_EXCEPTION :
102      print_normal (cd, info, fields->f_exception, 0, pc, length);
103      break;
104    case LM32_OPERAND_GOT16 :
105      print_normal (cd, info, fields->f_imm, 0|(1<<CGEN_OPERAND_SIGNED), pc, length);
106      break;
107    case LM32_OPERAND_GOTOFFHI16 :
108      print_normal (cd, info, fields->f_imm, 0|(1<<CGEN_OPERAND_SIGNED), pc, length);
109      break;
110    case LM32_OPERAND_GOTOFFLO16 :
111      print_normal (cd, info, fields->f_imm, 0|(1<<CGEN_OPERAND_SIGNED), pc, length);
112      break;
113    case LM32_OPERAND_GP16 :
114      print_normal (cd, info, fields->f_imm, 0|(1<<CGEN_OPERAND_SIGNED), pc, length);
115      break;
116    case LM32_OPERAND_HI16 :
117      print_normal (cd, info, fields->f_uimm, 0, pc, length);
118      break;
119    case LM32_OPERAND_IMM :
120      print_normal (cd, info, fields->f_imm, 0|(1<<CGEN_OPERAND_SIGNED), pc, length);
121      break;
122    case LM32_OPERAND_LO16 :
123      print_normal (cd, info, fields->f_uimm, 0, pc, length);
124      break;
125    case LM32_OPERAND_R0 :
126      print_keyword (cd, info, & lm32_cgen_opval_h_gr, fields->f_r0, 0);
127      break;
128    case LM32_OPERAND_R1 :
129      print_keyword (cd, info, & lm32_cgen_opval_h_gr, fields->f_r1, 0);
130      break;
131    case LM32_OPERAND_R2 :
132      print_keyword (cd, info, & lm32_cgen_opval_h_gr, fields->f_r2, 0);
133      break;
134    case LM32_OPERAND_SHIFT :
135      print_normal (cd, info, fields->f_shift, 0, pc, length);
136      break;
137    case LM32_OPERAND_UIMM :
138      print_normal (cd, info, fields->f_uimm, 0, pc, length);
139      break;
140    case LM32_OPERAND_USER :
141      print_normal (cd, info, fields->f_user, 0, pc, length);
142      break;
143
144    default :
145      /* xgettext:c-format */
146      fprintf (stderr, _("Unrecognized field %d while printing insn.\n"),
147	       opindex);
148    abort ();
149  }
150}
151
152cgen_print_fn * const lm32_cgen_print_handlers[] =
153{
154  print_insn_normal,
155};
156
157
158void
159lm32_cgen_init_dis (CGEN_CPU_DESC cd)
160{
161  lm32_cgen_init_opcode_table (cd);
162  lm32_cgen_init_ibld_table (cd);
163  cd->print_handlers = & lm32_cgen_print_handlers[0];
164  cd->print_operand = lm32_cgen_print_operand;
165}
166
167
168/* Default print handler.  */
169
170static void
171print_normal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
172	      void *dis_info,
173	      long value,
174	      unsigned int attrs,
175	      bfd_vma pc ATTRIBUTE_UNUSED,
176	      int length ATTRIBUTE_UNUSED)
177{
178  disassemble_info *info = (disassemble_info *) dis_info;
179
180  /* Print the operand as directed by the attributes.  */
181  if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
182    ; /* nothing to do */
183  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
184    (*info->fprintf_func) (info->stream, "%ld", value);
185  else
186    (*info->fprintf_func) (info->stream, "0x%lx", value);
187}
188
189/* Default address handler.  */
190
191static void
192print_address (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
193	       void *dis_info,
194	       bfd_vma value,
195	       unsigned int attrs,
196	       bfd_vma pc ATTRIBUTE_UNUSED,
197	       int length ATTRIBUTE_UNUSED)
198{
199  disassemble_info *info = (disassemble_info *) dis_info;
200
201  /* Print the operand as directed by the attributes.  */
202  if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
203    ; /* Nothing to do.  */
204  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_PCREL_ADDR))
205    (*info->print_address_func) (value, info);
206  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_ABS_ADDR))
207    (*info->print_address_func) (value, info);
208  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
209    (*info->fprintf_func) (info->stream, "%ld", (long) value);
210  else
211    (*info->fprintf_func) (info->stream, "0x%lx", (long) value);
212}
213
214/* Keyword print handler.  */
215
216static void
217print_keyword (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
218	       void *dis_info,
219	       CGEN_KEYWORD *keyword_table,
220	       long value,
221	       unsigned int attrs ATTRIBUTE_UNUSED)
222{
223  disassemble_info *info = (disassemble_info *) dis_info;
224  const CGEN_KEYWORD_ENTRY *ke;
225
226  ke = cgen_keyword_lookup_value (keyword_table, value);
227  if (ke != NULL)
228    (*info->fprintf_func) (info->stream, "%s", ke->name);
229  else
230    (*info->fprintf_func) (info->stream, "???");
231}
232
233/* Default insn printer.
234
235   DIS_INFO is defined as `void *' so the disassembler needn't know anything
236   about disassemble_info.  */
237
238static void
239print_insn_normal (CGEN_CPU_DESC cd,
240		   void *dis_info,
241		   const CGEN_INSN *insn,
242		   CGEN_FIELDS *fields,
243		   bfd_vma pc,
244		   int length)
245{
246  const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
247  disassemble_info *info = (disassemble_info *) dis_info;
248  const CGEN_SYNTAX_CHAR_TYPE *syn;
249
250  CGEN_INIT_PRINT (cd);
251
252  for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
253    {
254      if (CGEN_SYNTAX_MNEMONIC_P (*syn))
255	{
256	  (*info->fprintf_func) (info->stream, "%s", CGEN_INSN_MNEMONIC (insn));
257	  continue;
258	}
259      if (CGEN_SYNTAX_CHAR_P (*syn))
260	{
261	  (*info->fprintf_func) (info->stream, "%c", CGEN_SYNTAX_CHAR (*syn));
262	  continue;
263	}
264
265      /* We have an operand.  */
266      lm32_cgen_print_operand (cd, CGEN_SYNTAX_FIELD (*syn), info,
267				 fields, CGEN_INSN_ATTRS (insn), pc, length);
268    }
269}
270
271/* Subroutine of print_insn. Reads an insn into the given buffers and updates
272   the extract info.
273   Returns 0 if all is well, non-zero otherwise.  */
274
275static int
276read_insn (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
277	   bfd_vma pc,
278	   disassemble_info *info,
279	   bfd_byte *buf,
280	   int buflen,
281	   CGEN_EXTRACT_INFO *ex_info,
282	   unsigned long *insn_value)
283{
284  int status = (*info->read_memory_func) (pc, buf, buflen, info);
285
286  if (status != 0)
287    {
288      (*info->memory_error_func) (status, pc, info);
289      return -1;
290    }
291
292  ex_info->dis_info = info;
293  ex_info->valid = (1 << buflen) - 1;
294  ex_info->insn_bytes = buf;
295
296  *insn_value = bfd_get_bits (buf, buflen * 8, info->endian == BFD_ENDIAN_BIG);
297  return 0;
298}
299
300/* Utility to print an insn.
301   BUF is the base part of the insn, target byte order, BUFLEN bytes long.
302   The result is the size of the insn in bytes or zero for an unknown insn
303   or -1 if an error occurs fetching data (memory_error_func will have
304   been called).  */
305
306static int
307print_insn (CGEN_CPU_DESC cd,
308	    bfd_vma pc,
309	    disassemble_info *info,
310	    bfd_byte *buf,
311	    unsigned int buflen)
312{
313  CGEN_INSN_INT insn_value;
314  const CGEN_INSN_LIST *insn_list;
315  CGEN_EXTRACT_INFO ex_info;
316  int basesize;
317
318  /* Extract base part of instruction, just in case CGEN_DIS_* uses it. */
319  basesize = cd->base_insn_bitsize < buflen * 8 ?
320                                     cd->base_insn_bitsize : buflen * 8;
321  insn_value = cgen_get_insn_value (cd, buf, basesize);
322
323
324  /* Fill in ex_info fields like read_insn would.  Don't actually call
325     read_insn, since the incoming buffer is already read (and possibly
326     modified a la m32r).  */
327  ex_info.valid = (1 << buflen) - 1;
328  ex_info.dis_info = info;
329  ex_info.insn_bytes = buf;
330
331  /* The instructions are stored in hash lists.
332     Pick the first one and keep trying until we find the right one.  */
333
334  insn_list = CGEN_DIS_LOOKUP_INSN (cd, (char *) buf, insn_value);
335  while (insn_list != NULL)
336    {
337      const CGEN_INSN *insn = insn_list->insn;
338      CGEN_FIELDS fields;
339      int length;
340      unsigned long insn_value_cropped;
341
342#ifdef CGEN_VALIDATE_INSN_SUPPORTED
343      /* Not needed as insn shouldn't be in hash lists if not supported.  */
344      /* Supported by this cpu?  */
345      if (! lm32_cgen_insn_supported (cd, insn))
346        {
347          insn_list = CGEN_DIS_NEXT_INSN (insn_list);
348	  continue;
349        }
350#endif
351
352      /* Basic bit mask must be correct.  */
353      /* ??? May wish to allow target to defer this check until the extract
354	 handler.  */
355
356      /* Base size may exceed this instruction's size.  Extract the
357         relevant part from the buffer. */
358      if ((unsigned) (CGEN_INSN_BITSIZE (insn) / 8) < buflen &&
359	  (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
360	insn_value_cropped = bfd_get_bits (buf, CGEN_INSN_BITSIZE (insn),
361					   info->endian == BFD_ENDIAN_BIG);
362      else
363	insn_value_cropped = insn_value;
364
365      if ((insn_value_cropped & CGEN_INSN_BASE_MASK (insn))
366	  == CGEN_INSN_BASE_VALUE (insn))
367	{
368	  /* Printing is handled in two passes.  The first pass parses the
369	     machine insn and extracts the fields.  The second pass prints
370	     them.  */
371
372	  /* Make sure the entire insn is loaded into insn_value, if it
373	     can fit.  */
374	  if (((unsigned) CGEN_INSN_BITSIZE (insn) > cd->base_insn_bitsize) &&
375	      (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
376	    {
377	      unsigned long full_insn_value;
378	      int rc = read_insn (cd, pc, info, buf,
379				  CGEN_INSN_BITSIZE (insn) / 8,
380				  & ex_info, & full_insn_value);
381	      if (rc != 0)
382		return rc;
383	      length = CGEN_EXTRACT_FN (cd, insn)
384		(cd, insn, &ex_info, full_insn_value, &fields, pc);
385	    }
386	  else
387	    length = CGEN_EXTRACT_FN (cd, insn)
388	      (cd, insn, &ex_info, insn_value_cropped, &fields, pc);
389
390	  /* Length < 0 -> error.  */
391	  if (length < 0)
392	    return length;
393	  if (length > 0)
394	    {
395	      CGEN_PRINT_FN (cd, insn) (cd, info, insn, &fields, pc, length);
396	      /* Length is in bits, result is in bytes.  */
397	      return length / 8;
398	    }
399	}
400
401      insn_list = CGEN_DIS_NEXT_INSN (insn_list);
402    }
403
404  return 0;
405}
406
407/* Default value for CGEN_PRINT_INSN.
408   The result is the size of the insn in bytes or zero for an unknown insn
409   or -1 if an error occured fetching bytes.  */
410
411#ifndef CGEN_PRINT_INSN
412#define CGEN_PRINT_INSN default_print_insn
413#endif
414
415static int
416default_print_insn (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info)
417{
418  bfd_byte buf[CGEN_MAX_INSN_SIZE];
419  int buflen;
420  int status;
421
422  /* Attempt to read the base part of the insn.  */
423  buflen = cd->base_insn_bitsize / 8;
424  status = (*info->read_memory_func) (pc, buf, buflen, info);
425
426  /* Try again with the minimum part, if min < base.  */
427  if (status != 0 && (cd->min_insn_bitsize < cd->base_insn_bitsize))
428    {
429      buflen = cd->min_insn_bitsize / 8;
430      status = (*info->read_memory_func) (pc, buf, buflen, info);
431    }
432
433  if (status != 0)
434    {
435      (*info->memory_error_func) (status, pc, info);
436      return -1;
437    }
438
439  return print_insn (cd, pc, info, buf, buflen);
440}
441
442/* Main entry point.
443   Print one instruction from PC on INFO->STREAM.
444   Return the size of the instruction (in bytes).  */
445
446typedef struct cpu_desc_list
447{
448  struct cpu_desc_list *next;
449  CGEN_BITSET *isa;
450  int mach;
451  int endian;
452  CGEN_CPU_DESC cd;
453} cpu_desc_list;
454
455int
456print_insn_lm32 (bfd_vma pc, disassemble_info *info)
457{
458  static cpu_desc_list *cd_list = 0;
459  cpu_desc_list *cl = 0;
460  static CGEN_CPU_DESC cd = 0;
461  static CGEN_BITSET *prev_isa;
462  static int prev_mach;
463  static int prev_endian;
464  int length;
465  CGEN_BITSET *isa;
466  int mach;
467  int endian = (info->endian == BFD_ENDIAN_BIG
468		? CGEN_ENDIAN_BIG
469		: CGEN_ENDIAN_LITTLE);
470  enum bfd_architecture arch;
471
472  /* ??? gdb will set mach but leave the architecture as "unknown" */
473#ifndef CGEN_BFD_ARCH
474#define CGEN_BFD_ARCH bfd_arch_lm32
475#endif
476  arch = info->arch;
477  if (arch == bfd_arch_unknown)
478    arch = CGEN_BFD_ARCH;
479
480  /* There's no standard way to compute the machine or isa number
481     so we leave it to the target.  */
482#ifdef CGEN_COMPUTE_MACH
483  mach = CGEN_COMPUTE_MACH (info);
484#else
485  mach = info->mach;
486#endif
487
488#ifdef CGEN_COMPUTE_ISA
489  {
490    static CGEN_BITSET *permanent_isa;
491
492    if (!permanent_isa)
493      permanent_isa = cgen_bitset_create (MAX_ISAS);
494    isa = permanent_isa;
495    cgen_bitset_clear (isa);
496    cgen_bitset_add (isa, CGEN_COMPUTE_ISA (info));
497  }
498#else
499  isa = info->insn_sets;
500#endif
501
502  /* If we've switched cpu's, try to find a handle we've used before */
503  if (cd
504      && (cgen_bitset_compare (isa, prev_isa) != 0
505	  || mach != prev_mach
506	  || endian != prev_endian))
507    {
508      cd = 0;
509      for (cl = cd_list; cl; cl = cl->next)
510	{
511	  if (cgen_bitset_compare (cl->isa, isa) == 0 &&
512	      cl->mach == mach &&
513	      cl->endian == endian)
514	    {
515	      cd = cl->cd;
516 	      prev_isa = cd->isas;
517	      break;
518	    }
519	}
520    }
521
522  /* If we haven't initialized yet, initialize the opcode table.  */
523  if (! cd)
524    {
525      const bfd_arch_info_type *arch_type = bfd_lookup_arch (arch, mach);
526      const char *mach_name;
527
528      if (!arch_type)
529	abort ();
530      mach_name = arch_type->printable_name;
531
532      prev_isa = cgen_bitset_copy (isa);
533      prev_mach = mach;
534      prev_endian = endian;
535      cd = lm32_cgen_cpu_open (CGEN_CPU_OPEN_ISAS, prev_isa,
536				 CGEN_CPU_OPEN_BFDMACH, mach_name,
537				 CGEN_CPU_OPEN_ENDIAN, prev_endian,
538				 CGEN_CPU_OPEN_END);
539      if (!cd)
540	abort ();
541
542      /* Save this away for future reference.  */
543      cl = xmalloc (sizeof (struct cpu_desc_list));
544      cl->cd = cd;
545      cl->isa = prev_isa;
546      cl->mach = mach;
547      cl->endian = endian;
548      cl->next = cd_list;
549      cd_list = cl;
550
551      lm32_cgen_init_dis (cd);
552    }
553
554  /* We try to have as much common code as possible.
555     But at this point some targets need to take over.  */
556  /* ??? Some targets may need a hook elsewhere.  Try to avoid this,
557     but if not possible try to move this hook elsewhere rather than
558     have two hooks.  */
559  length = CGEN_PRINT_INSN (cd, pc, info);
560  if (length > 0)
561    return length;
562  if (length < 0)
563    return -1;
564
565  (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG);
566  return cd->default_insn_bitsize / 8;
567}
568