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