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-2016 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 "ip2k-desc.h"
36 #include "ip2k-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 
42 static void print_normal
43   (CGEN_CPU_DESC, void *, long, unsigned int, bfd_vma, int);
44 static void print_address
45   (CGEN_CPU_DESC, void *, bfd_vma, unsigned int, bfd_vma, int) ATTRIBUTE_UNUSED;
46 static void print_keyword
47   (CGEN_CPU_DESC, void *, CGEN_KEYWORD *, long, unsigned int) ATTRIBUTE_UNUSED;
48 static void print_insn_normal
49   (CGEN_CPU_DESC, void *, const CGEN_INSN *, CGEN_FIELDS *, bfd_vma, int);
50 static int print_insn
51   (CGEN_CPU_DESC, bfd_vma,  disassemble_info *, bfd_byte *, unsigned);
52 static int default_print_insn
53   (CGEN_CPU_DESC, bfd_vma, disassemble_info *) ATTRIBUTE_UNUSED;
54 static 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 /* -- dis.c */
61 
62 static void
print_fr(CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,void * dis_info,long value,unsigned int attrs ATTRIBUTE_UNUSED,bfd_vma pc ATTRIBUTE_UNUSED,int length ATTRIBUTE_UNUSED)63 print_fr (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
64 	  void * dis_info,
65 	  long value,
66 	  unsigned int attrs ATTRIBUTE_UNUSED,
67 	  bfd_vma pc ATTRIBUTE_UNUSED,
68 	  int length ATTRIBUTE_UNUSED)
69 {
70   disassemble_info *info = (disassemble_info *) dis_info;
71   const CGEN_KEYWORD_ENTRY *ke;
72   extern CGEN_KEYWORD ip2k_cgen_opval_register_names;
73   long offsettest;
74   long offsetvalue;
75 
76   if (value == 0) /* This is (IP).  */
77     {
78       (*info->fprintf_func) (info->stream, "%s", "(IP)");
79       return;
80     }
81 
82   offsettest = value >> 7;
83   offsetvalue = value & 0x7F;
84 
85   /* Check to see if first two bits are 10 -> (DP).  */
86   if (offsettest == 2)
87     {
88       if (offsetvalue == 0)
89 	(*info->fprintf_func) (info->stream, "%s","(DP)");
90       else
91 	(*info->fprintf_func) (info->stream, "$%lx%s", offsetvalue, "(DP)");
92       return;
93     }
94 
95   /* Check to see if first two bits are 11 -> (SP).  */
96   if (offsettest == 3)
97     {
98       if (offsetvalue == 0)
99 	(*info->fprintf_func) (info->stream, "%s", "(SP)");
100       else
101 	(*info->fprintf_func) (info->stream, "$%lx%s", offsetvalue,"(SP)");
102       return;
103     }
104 
105   /* Attempt to print as a register keyword.  */
106   ke = cgen_keyword_lookup_value (& ip2k_cgen_opval_register_names, value);
107 
108   if (ke != NULL)
109     (*info->fprintf_func) (info->stream, "%s", ke->name);
110   else
111     /* Print as an address literal.  */
112     (*info->fprintf_func) (info->stream, "$%02lx", value);
113 }
114 
115 static void
print_dollarhex(CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,void * dis_info,long value,unsigned int attrs ATTRIBUTE_UNUSED,bfd_vma pc ATTRIBUTE_UNUSED,int length ATTRIBUTE_UNUSED)116 print_dollarhex (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
117 		 void * dis_info,
118 		 long value,
119 		 unsigned int attrs ATTRIBUTE_UNUSED,
120 		 bfd_vma pc ATTRIBUTE_UNUSED,
121 		 int length ATTRIBUTE_UNUSED)
122 {
123   disassemble_info *info = (disassemble_info *) dis_info;
124 
125   (*info->fprintf_func) (info->stream, "$%lx", value);
126 }
127 
128 static void
print_dollarhex8(CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,void * dis_info,long value,unsigned int attrs ATTRIBUTE_UNUSED,bfd_vma pc ATTRIBUTE_UNUSED,int length ATTRIBUTE_UNUSED)129 print_dollarhex8 (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
130 		  void * dis_info,
131 		  long value,
132 		  unsigned int attrs ATTRIBUTE_UNUSED,
133 		  bfd_vma pc ATTRIBUTE_UNUSED,
134 		  int length ATTRIBUTE_UNUSED)
135 {
136   disassemble_info *info = (disassemble_info *) dis_info;
137 
138   (*info->fprintf_func) (info->stream, "$%02lx", value);
139 }
140 
141 static void
print_dollarhex_addr16h(CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,void * dis_info,long value,unsigned int attrs ATTRIBUTE_UNUSED,bfd_vma pc ATTRIBUTE_UNUSED,int length ATTRIBUTE_UNUSED)142 print_dollarhex_addr16h (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
143 			 void * dis_info,
144 			 long value,
145 			 unsigned int attrs ATTRIBUTE_UNUSED,
146 			 bfd_vma pc ATTRIBUTE_UNUSED,
147 			 int length ATTRIBUTE_UNUSED)
148 {
149   disassemble_info *info = (disassemble_info *) dis_info;
150 
151   /* This is a loadh instruction. Shift the value to the left
152      by 8 bits so that disassembled code will reassemble properly.  */
153   value = ((value << 8) & 0xFF00);
154 
155   (*info->fprintf_func) (info->stream, "$%04lx", value);
156 }
157 
158 static void
print_dollarhex_addr16l(CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,void * dis_info,long value,unsigned int attrs ATTRIBUTE_UNUSED,bfd_vma pc ATTRIBUTE_UNUSED,int length ATTRIBUTE_UNUSED)159 print_dollarhex_addr16l (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
160 			 void * dis_info,
161 			 long value,
162 			 unsigned int attrs ATTRIBUTE_UNUSED,
163 			 bfd_vma pc ATTRIBUTE_UNUSED,
164 			 int length ATTRIBUTE_UNUSED)
165 {
166   disassemble_info *info = (disassemble_info *) dis_info;
167 
168   (*info->fprintf_func) (info->stream, "$%04lx", value);
169 }
170 
171 static void
print_dollarhex_p(CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,void * dis_info,long value,unsigned int attrs ATTRIBUTE_UNUSED,bfd_vma pc ATTRIBUTE_UNUSED,int length ATTRIBUTE_UNUSED)172 print_dollarhex_p (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
173 		   void * dis_info,
174 		   long value,
175 		   unsigned int attrs ATTRIBUTE_UNUSED,
176 		   bfd_vma pc ATTRIBUTE_UNUSED,
177 		   int length ATTRIBUTE_UNUSED)
178 {
179   disassemble_info *info = (disassemble_info *) dis_info;
180 
181   value = ((value << 14) & 0x1C000);
182   ;value = (value  & 0x1FFFF);
183   (*info->fprintf_func) (info->stream, "$%05lx", value);
184 }
185 
186 static void
print_dollarhex_cj(CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,void * dis_info,long value,unsigned int attrs ATTRIBUTE_UNUSED,bfd_vma pc ATTRIBUTE_UNUSED,int length ATTRIBUTE_UNUSED)187 print_dollarhex_cj (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
188 		    void * dis_info,
189 		    long value,
190 		    unsigned int attrs ATTRIBUTE_UNUSED,
191 		    bfd_vma pc ATTRIBUTE_UNUSED,
192 		    int length ATTRIBUTE_UNUSED)
193 {
194   disassemble_info *info = (disassemble_info *) dis_info;
195 
196   value = ((value << 1) & 0x1FFFF);
197   (*info->fprintf_func) (info->stream, "$%05lx", value);
198 }
199 
200 static void
print_decimal(CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,void * dis_info,long value,unsigned int attrs ATTRIBUTE_UNUSED,bfd_vma pc ATTRIBUTE_UNUSED,int length ATTRIBUTE_UNUSED)201 print_decimal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
202 	       void * dis_info,
203 	       long value,
204 	       unsigned int attrs ATTRIBUTE_UNUSED,
205 	       bfd_vma pc ATTRIBUTE_UNUSED,
206 	       int length ATTRIBUTE_UNUSED)
207 {
208   disassemble_info *info = (disassemble_info *) dis_info;
209 
210   (*info->fprintf_func) (info->stream, "%ld", value);
211 }
212 
213 
214 
215 /* -- */
216 
217 void ip2k_cgen_print_operand
218   (CGEN_CPU_DESC, int, PTR, CGEN_FIELDS *, void const *, bfd_vma, int);
219 
220 /* Main entry point for printing operands.
221    XINFO is a `void *' and not a `disassemble_info *' to not put a requirement
222    of dis-asm.h on cgen.h.
223 
224    This function is basically just a big switch statement.  Earlier versions
225    used tables to look up the function to use, but
226    - if the table contains both assembler and disassembler functions then
227      the disassembler contains much of the assembler and vice-versa,
228    - there's a lot of inlining possibilities as things grow,
229    - using a switch statement avoids the function call overhead.
230 
231    This function could be moved into `print_insn_normal', but keeping it
232    separate makes clear the interface between `print_insn_normal' and each of
233    the handlers.  */
234 
235 void
ip2k_cgen_print_operand(CGEN_CPU_DESC cd,int opindex,void * xinfo,CGEN_FIELDS * fields,void const * attrs ATTRIBUTE_UNUSED,bfd_vma pc,int length)236 ip2k_cgen_print_operand (CGEN_CPU_DESC cd,
237 			   int opindex,
238 			   void * xinfo,
239 			   CGEN_FIELDS *fields,
240 			   void const *attrs ATTRIBUTE_UNUSED,
241 			   bfd_vma pc,
242 			   int length)
243 {
244   disassemble_info *info = (disassemble_info *) xinfo;
245 
246   switch (opindex)
247     {
248     case IP2K_OPERAND_ADDR16CJP :
249       print_dollarhex_cj (cd, info, fields->f_addr16cjp, 0|(1<<CGEN_OPERAND_ABS_ADDR), pc, length);
250       break;
251     case IP2K_OPERAND_ADDR16H :
252       print_dollarhex_addr16h (cd, info, fields->f_imm8, 0, pc, length);
253       break;
254     case IP2K_OPERAND_ADDR16L :
255       print_dollarhex_addr16l (cd, info, fields->f_imm8, 0, pc, length);
256       break;
257     case IP2K_OPERAND_ADDR16P :
258       print_dollarhex_p (cd, info, fields->f_page3, 0, pc, length);
259       break;
260     case IP2K_OPERAND_BITNO :
261       print_decimal (cd, info, fields->f_bitno, 0, pc, length);
262       break;
263     case IP2K_OPERAND_CBIT :
264       print_normal (cd, info, 0, 0, pc, length);
265       break;
266     case IP2K_OPERAND_DCBIT :
267       print_normal (cd, info, 0, 0, pc, length);
268       break;
269     case IP2K_OPERAND_FR :
270       print_fr (cd, info, fields->f_reg, 0|(1<<CGEN_OPERAND_ABS_ADDR), pc, length);
271       break;
272     case IP2K_OPERAND_LIT8 :
273       print_dollarhex8 (cd, info, fields->f_imm8, 0|(1<<CGEN_OPERAND_SIGNED), pc, length);
274       break;
275     case IP2K_OPERAND_PABITS :
276       print_normal (cd, info, 0, 0, pc, length);
277       break;
278     case IP2K_OPERAND_RETI3 :
279       print_dollarhex (cd, info, fields->f_reti3, 0, pc, length);
280       break;
281     case IP2K_OPERAND_ZBIT :
282       print_normal (cd, info, 0, 0, pc, length);
283       break;
284 
285     default :
286       /* xgettext:c-format */
287       fprintf (stderr, _("Unrecognized field %d while printing insn.\n"),
288 	       opindex);
289     abort ();
290   }
291 }
292 
293 cgen_print_fn * const ip2k_cgen_print_handlers[] =
294 {
295   print_insn_normal,
296 };
297 
298 
299 void
ip2k_cgen_init_dis(CGEN_CPU_DESC cd)300 ip2k_cgen_init_dis (CGEN_CPU_DESC cd)
301 {
302   ip2k_cgen_init_opcode_table (cd);
303   ip2k_cgen_init_ibld_table (cd);
304   cd->print_handlers = & ip2k_cgen_print_handlers[0];
305   cd->print_operand = ip2k_cgen_print_operand;
306 }
307 
308 
309 /* Default print handler.  */
310 
311 static void
print_normal(CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,void * dis_info,long value,unsigned int attrs,bfd_vma pc ATTRIBUTE_UNUSED,int length ATTRIBUTE_UNUSED)312 print_normal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
313 	      void *dis_info,
314 	      long value,
315 	      unsigned int attrs,
316 	      bfd_vma pc ATTRIBUTE_UNUSED,
317 	      int length ATTRIBUTE_UNUSED)
318 {
319   disassemble_info *info = (disassemble_info *) dis_info;
320 
321   /* Print the operand as directed by the attributes.  */
322   if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
323     ; /* nothing to do */
324   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
325     (*info->fprintf_func) (info->stream, "%ld", value);
326   else
327     (*info->fprintf_func) (info->stream, "0x%lx", value);
328 }
329 
330 /* Default address handler.  */
331 
332 static void
print_address(CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,void * dis_info,bfd_vma value,unsigned int attrs,bfd_vma pc ATTRIBUTE_UNUSED,int length ATTRIBUTE_UNUSED)333 print_address (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
334 	       void *dis_info,
335 	       bfd_vma value,
336 	       unsigned int attrs,
337 	       bfd_vma pc ATTRIBUTE_UNUSED,
338 	       int length ATTRIBUTE_UNUSED)
339 {
340   disassemble_info *info = (disassemble_info *) dis_info;
341 
342   /* Print the operand as directed by the attributes.  */
343   if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
344     ; /* Nothing to do.  */
345   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_PCREL_ADDR))
346     (*info->print_address_func) (value, info);
347   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_ABS_ADDR))
348     (*info->print_address_func) (value, info);
349   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
350     (*info->fprintf_func) (info->stream, "%ld", (long) value);
351   else
352     (*info->fprintf_func) (info->stream, "0x%lx", (long) value);
353 }
354 
355 /* Keyword print handler.  */
356 
357 static void
print_keyword(CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,void * dis_info,CGEN_KEYWORD * keyword_table,long value,unsigned int attrs ATTRIBUTE_UNUSED)358 print_keyword (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
359 	       void *dis_info,
360 	       CGEN_KEYWORD *keyword_table,
361 	       long value,
362 	       unsigned int attrs ATTRIBUTE_UNUSED)
363 {
364   disassemble_info *info = (disassemble_info *) dis_info;
365   const CGEN_KEYWORD_ENTRY *ke;
366 
367   ke = cgen_keyword_lookup_value (keyword_table, value);
368   if (ke != NULL)
369     (*info->fprintf_func) (info->stream, "%s", ke->name);
370   else
371     (*info->fprintf_func) (info->stream, "???");
372 }
373 
374 /* Default insn printer.
375 
376    DIS_INFO is defined as `void *' so the disassembler needn't know anything
377    about disassemble_info.  */
378 
379 static void
print_insn_normal(CGEN_CPU_DESC cd,void * dis_info,const CGEN_INSN * insn,CGEN_FIELDS * fields,bfd_vma pc,int length)380 print_insn_normal (CGEN_CPU_DESC cd,
381 		   void *dis_info,
382 		   const CGEN_INSN *insn,
383 		   CGEN_FIELDS *fields,
384 		   bfd_vma pc,
385 		   int length)
386 {
387   const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
388   disassemble_info *info = (disassemble_info *) dis_info;
389   const CGEN_SYNTAX_CHAR_TYPE *syn;
390 
391   CGEN_INIT_PRINT (cd);
392 
393   for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
394     {
395       if (CGEN_SYNTAX_MNEMONIC_P (*syn))
396 	{
397 	  (*info->fprintf_func) (info->stream, "%s", CGEN_INSN_MNEMONIC (insn));
398 	  continue;
399 	}
400       if (CGEN_SYNTAX_CHAR_P (*syn))
401 	{
402 	  (*info->fprintf_func) (info->stream, "%c", CGEN_SYNTAX_CHAR (*syn));
403 	  continue;
404 	}
405 
406       /* We have an operand.  */
407       ip2k_cgen_print_operand (cd, CGEN_SYNTAX_FIELD (*syn), info,
408 				 fields, CGEN_INSN_ATTRS (insn), pc, length);
409     }
410 }
411 
412 /* Subroutine of print_insn. Reads an insn into the given buffers and updates
413    the extract info.
414    Returns 0 if all is well, non-zero otherwise.  */
415 
416 static int
read_insn(CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,bfd_vma pc,disassemble_info * info,bfd_byte * buf,int buflen,CGEN_EXTRACT_INFO * ex_info,unsigned long * insn_value)417 read_insn (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
418 	   bfd_vma pc,
419 	   disassemble_info *info,
420 	   bfd_byte *buf,
421 	   int buflen,
422 	   CGEN_EXTRACT_INFO *ex_info,
423 	   unsigned long *insn_value)
424 {
425   int status = (*info->read_memory_func) (pc, buf, buflen, info);
426 
427   if (status != 0)
428     {
429       (*info->memory_error_func) (status, pc, info);
430       return -1;
431     }
432 
433   ex_info->dis_info = info;
434   ex_info->valid = (1 << buflen) - 1;
435   ex_info->insn_bytes = buf;
436 
437   *insn_value = bfd_get_bits (buf, buflen * 8, info->endian == BFD_ENDIAN_BIG);
438   return 0;
439 }
440 
441 /* Utility to print an insn.
442    BUF is the base part of the insn, target byte order, BUFLEN bytes long.
443    The result is the size of the insn in bytes or zero for an unknown insn
444    or -1 if an error occurs fetching data (memory_error_func will have
445    been called).  */
446 
447 static int
print_insn(CGEN_CPU_DESC cd,bfd_vma pc,disassemble_info * info,bfd_byte * buf,unsigned int buflen)448 print_insn (CGEN_CPU_DESC cd,
449 	    bfd_vma pc,
450 	    disassemble_info *info,
451 	    bfd_byte *buf,
452 	    unsigned int buflen)
453 {
454   CGEN_INSN_INT insn_value;
455   const CGEN_INSN_LIST *insn_list;
456   CGEN_EXTRACT_INFO ex_info;
457   int basesize;
458 
459   /* Extract base part of instruction, just in case CGEN_DIS_* uses it. */
460   basesize = cd->base_insn_bitsize < buflen * 8 ?
461                                      cd->base_insn_bitsize : buflen * 8;
462   insn_value = cgen_get_insn_value (cd, buf, basesize);
463 
464 
465   /* Fill in ex_info fields like read_insn would.  Don't actually call
466      read_insn, since the incoming buffer is already read (and possibly
467      modified a la m32r).  */
468   ex_info.valid = (1 << buflen) - 1;
469   ex_info.dis_info = info;
470   ex_info.insn_bytes = buf;
471 
472   /* The instructions are stored in hash lists.
473      Pick the first one and keep trying until we find the right one.  */
474 
475   insn_list = CGEN_DIS_LOOKUP_INSN (cd, (char *) buf, insn_value);
476   while (insn_list != NULL)
477     {
478       const CGEN_INSN *insn = insn_list->insn;
479       CGEN_FIELDS fields;
480       int length;
481       unsigned long insn_value_cropped;
482 
483 #ifdef CGEN_VALIDATE_INSN_SUPPORTED
484       /* Not needed as insn shouldn't be in hash lists if not supported.  */
485       /* Supported by this cpu?  */
486       if (! ip2k_cgen_insn_supported (cd, insn))
487         {
488           insn_list = CGEN_DIS_NEXT_INSN (insn_list);
489 	  continue;
490         }
491 #endif
492 
493       /* Basic bit mask must be correct.  */
494       /* ??? May wish to allow target to defer this check until the extract
495 	 handler.  */
496 
497       /* Base size may exceed this instruction's size.  Extract the
498          relevant part from the buffer. */
499       if ((unsigned) (CGEN_INSN_BITSIZE (insn) / 8) < buflen &&
500 	  (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
501 	insn_value_cropped = bfd_get_bits (buf, CGEN_INSN_BITSIZE (insn),
502 					   info->endian == BFD_ENDIAN_BIG);
503       else
504 	insn_value_cropped = insn_value;
505 
506       if ((insn_value_cropped & CGEN_INSN_BASE_MASK (insn))
507 	  == CGEN_INSN_BASE_VALUE (insn))
508 	{
509 	  /* Printing is handled in two passes.  The first pass parses the
510 	     machine insn and extracts the fields.  The second pass prints
511 	     them.  */
512 
513 	  /* Make sure the entire insn is loaded into insn_value, if it
514 	     can fit.  */
515 	  if (((unsigned) CGEN_INSN_BITSIZE (insn) > cd->base_insn_bitsize) &&
516 	      (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
517 	    {
518 	      unsigned long full_insn_value;
519 	      int rc = read_insn (cd, pc, info, buf,
520 				  CGEN_INSN_BITSIZE (insn) / 8,
521 				  & ex_info, & full_insn_value);
522 	      if (rc != 0)
523 		return rc;
524 	      length = CGEN_EXTRACT_FN (cd, insn)
525 		(cd, insn, &ex_info, full_insn_value, &fields, pc);
526 	    }
527 	  else
528 	    length = CGEN_EXTRACT_FN (cd, insn)
529 	      (cd, insn, &ex_info, insn_value_cropped, &fields, pc);
530 
531 	  /* Length < 0 -> error.  */
532 	  if (length < 0)
533 	    return length;
534 	  if (length > 0)
535 	    {
536 	      CGEN_PRINT_FN (cd, insn) (cd, info, insn, &fields, pc, length);
537 	      /* Length is in bits, result is in bytes.  */
538 	      return length / 8;
539 	    }
540 	}
541 
542       insn_list = CGEN_DIS_NEXT_INSN (insn_list);
543     }
544 
545   return 0;
546 }
547 
548 /* Default value for CGEN_PRINT_INSN.
549    The result is the size of the insn in bytes or zero for an unknown insn
550    or -1 if an error occured fetching bytes.  */
551 
552 #ifndef CGEN_PRINT_INSN
553 #define CGEN_PRINT_INSN default_print_insn
554 #endif
555 
556 static int
default_print_insn(CGEN_CPU_DESC cd,bfd_vma pc,disassemble_info * info)557 default_print_insn (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info)
558 {
559   bfd_byte buf[CGEN_MAX_INSN_SIZE];
560   int buflen;
561   int status;
562 
563   /* Attempt to read the base part of the insn.  */
564   buflen = cd->base_insn_bitsize / 8;
565   status = (*info->read_memory_func) (pc, buf, buflen, info);
566 
567   /* Try again with the minimum part, if min < base.  */
568   if (status != 0 && (cd->min_insn_bitsize < cd->base_insn_bitsize))
569     {
570       buflen = cd->min_insn_bitsize / 8;
571       status = (*info->read_memory_func) (pc, buf, buflen, info);
572     }
573 
574   if (status != 0)
575     {
576       (*info->memory_error_func) (status, pc, info);
577       return -1;
578     }
579 
580   return print_insn (cd, pc, info, buf, buflen);
581 }
582 
583 /* Main entry point.
584    Print one instruction from PC on INFO->STREAM.
585    Return the size of the instruction (in bytes).  */
586 
587 typedef struct cpu_desc_list
588 {
589   struct cpu_desc_list *next;
590   CGEN_BITSET *isa;
591   int mach;
592   int endian;
593   CGEN_CPU_DESC cd;
594 } cpu_desc_list;
595 
596 int
print_insn_ip2k(bfd_vma pc,disassemble_info * info)597 print_insn_ip2k (bfd_vma pc, disassemble_info *info)
598 {
599   static cpu_desc_list *cd_list = 0;
600   cpu_desc_list *cl = 0;
601   static CGEN_CPU_DESC cd = 0;
602   static CGEN_BITSET *prev_isa;
603   static int prev_mach;
604   static int prev_endian;
605   int length;
606   CGEN_BITSET *isa;
607   int mach;
608   int endian = (info->endian == BFD_ENDIAN_BIG
609 		? CGEN_ENDIAN_BIG
610 		: CGEN_ENDIAN_LITTLE);
611   enum bfd_architecture arch;
612 
613   /* ??? gdb will set mach but leave the architecture as "unknown" */
614 #ifndef CGEN_BFD_ARCH
615 #define CGEN_BFD_ARCH bfd_arch_ip2k
616 #endif
617   arch = info->arch;
618   if (arch == bfd_arch_unknown)
619     arch = CGEN_BFD_ARCH;
620 
621   /* There's no standard way to compute the machine or isa number
622      so we leave it to the target.  */
623 #ifdef CGEN_COMPUTE_MACH
624   mach = CGEN_COMPUTE_MACH (info);
625 #else
626   mach = info->mach;
627 #endif
628 
629 #ifdef CGEN_COMPUTE_ISA
630   {
631     static CGEN_BITSET *permanent_isa;
632 
633     if (!permanent_isa)
634       permanent_isa = cgen_bitset_create (MAX_ISAS);
635     isa = permanent_isa;
636     cgen_bitset_clear (isa);
637     cgen_bitset_add (isa, CGEN_COMPUTE_ISA (info));
638   }
639 #else
640   isa = info->insn_sets;
641 #endif
642 
643   /* If we've switched cpu's, try to find a handle we've used before */
644   if (cd
645       && (cgen_bitset_compare (isa, prev_isa) != 0
646 	  || mach != prev_mach
647 	  || endian != prev_endian))
648     {
649       cd = 0;
650       for (cl = cd_list; cl; cl = cl->next)
651 	{
652 	  if (cgen_bitset_compare (cl->isa, isa) == 0 &&
653 	      cl->mach == mach &&
654 	      cl->endian == endian)
655 	    {
656 	      cd = cl->cd;
657  	      prev_isa = cd->isas;
658 	      break;
659 	    }
660 	}
661     }
662 
663   /* If we haven't initialized yet, initialize the opcode table.  */
664   if (! cd)
665     {
666       const bfd_arch_info_type *arch_type = bfd_lookup_arch (arch, mach);
667       const char *mach_name;
668 
669       if (!arch_type)
670 	abort ();
671       mach_name = arch_type->printable_name;
672 
673       prev_isa = cgen_bitset_copy (isa);
674       prev_mach = mach;
675       prev_endian = endian;
676       cd = ip2k_cgen_cpu_open (CGEN_CPU_OPEN_ISAS, prev_isa,
677 				 CGEN_CPU_OPEN_BFDMACH, mach_name,
678 				 CGEN_CPU_OPEN_ENDIAN, prev_endian,
679 				 CGEN_CPU_OPEN_END);
680       if (!cd)
681 	abort ();
682 
683       /* Save this away for future reference.  */
684       cl = xmalloc (sizeof (struct cpu_desc_list));
685       cl->cd = cd;
686       cl->isa = prev_isa;
687       cl->mach = mach;
688       cl->endian = endian;
689       cl->next = cd_list;
690       cd_list = cl;
691 
692       ip2k_cgen_init_dis (cd);
693     }
694 
695   /* We try to have as much common code as possible.
696      But at this point some targets need to take over.  */
697   /* ??? Some targets may need a hook elsewhere.  Try to avoid this,
698      but if not possible try to move this hook elsewhere rather than
699      have two hooks.  */
700   length = CGEN_PRINT_INSN (cd, pc, info);
701   if (length > 0)
702     return length;
703   if (length < 0)
704     return -1;
705 
706   (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG);
707   return cd->default_insn_bitsize / 8;
708 }
709