Commit | Line | Data |
---|---|---|
f6e6b40f BE |
1 | /* Disassembler interface for targets using CGEN. -*- C -*- |
2 | CGEN: Cpu tools GENerator | |
3 | ||
47b0e7ad NC |
4 | THIS FILE IS MACHINE GENERATED WITH CGEN. |
5 | - the resultant file is machine generated, cgen-dis.in isn't | |
f6e6b40f | 6 | |
219d1afa | 7 | Copyright (C) 1996-2018 Free Software Foundation, Inc. |
f6e6b40f | 8 | |
9b201bb5 | 9 | This file is part of libopcodes. |
f6e6b40f | 10 | |
9b201bb5 | 11 | This library is free software; you can redistribute it and/or modify |
47b0e7ad | 12 | it under the terms of the GNU General Public License as published by |
9b201bb5 | 13 | the Free Software Foundation; either version 3, or (at your option) |
47b0e7ad | 14 | any later version. |
f6e6b40f | 15 | |
9b201bb5 NC |
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. | |
f6e6b40f | 20 | |
47b0e7ad NC |
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. */ | |
f6e6b40f BE |
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" | |
7639175c | 31 | #include "disassemble.h" |
f6e6b40f BE |
32 | #include "bfd.h" |
33 | #include "symcat.h" | |
98f70fc4 | 34 | #include "libiberty.h" |
f6e6b40f BE |
35 | #include "@prefix@-desc.h" |
36 | #include "@prefix@-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 | |
10e05405 | 43 | (CGEN_CPU_DESC, void *, long, unsigned int, bfd_vma, int); |
f6e6b40f | 44 | static void print_address |
bf143b25 | 45 | (CGEN_CPU_DESC, void *, bfd_vma, unsigned int, bfd_vma, int) ATTRIBUTE_UNUSED; |
f6e6b40f | 46 | static void print_keyword |
bf143b25 | 47 | (CGEN_CPU_DESC, void *, CGEN_KEYWORD *, long, unsigned int) ATTRIBUTE_UNUSED; |
f6e6b40f | 48 | static void print_insn_normal |
10e05405 | 49 | (CGEN_CPU_DESC, void *, const CGEN_INSN *, CGEN_FIELDS *, bfd_vma, int); |
0e2ee3ca | 50 | static int print_insn |
33b71eeb | 51 | (CGEN_CPU_DESC, bfd_vma, disassemble_info *, bfd_byte *, unsigned); |
f6e6b40f | 52 | static int default_print_insn |
bf143b25 | 53 | (CGEN_CPU_DESC, bfd_vma, disassemble_info *) ATTRIBUTE_UNUSED; |
0e2ee3ca | 54 | static int read_insn |
33b71eeb | 55 | (CGEN_CPU_DESC, bfd_vma, disassemble_info *, bfd_byte *, int, CGEN_EXTRACT_INFO *, |
10e05405 | 56 | unsigned long *); |
f6e6b40f | 57 | \f |
47b0e7ad | 58 | /* -- disassembler routines inserted here. */ |
f6e6b40f BE |
59 | \f |
60 | /* Default print handler. */ | |
61 | ||
62 | static void | |
10e05405 MM |
63 | print_normal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, |
64 | void *dis_info, | |
65 | long value, | |
66 | unsigned int attrs, | |
67 | bfd_vma pc ATTRIBUTE_UNUSED, | |
68 | int length ATTRIBUTE_UNUSED) | |
f6e6b40f BE |
69 | { |
70 | disassemble_info *info = (disassemble_info *) dis_info; | |
71 | ||
f6e6b40f BE |
72 | /* Print the operand as directed by the attributes. */ |
73 | if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY)) | |
74 | ; /* nothing to do */ | |
75 | else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED)) | |
76 | (*info->fprintf_func) (info->stream, "%ld", value); | |
77 | else | |
78 | (*info->fprintf_func) (info->stream, "0x%lx", value); | |
79 | } | |
80 | ||
81 | /* Default address handler. */ | |
82 | ||
83 | static void | |
10e05405 MM |
84 | print_address (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, |
85 | void *dis_info, | |
86 | bfd_vma value, | |
87 | unsigned int attrs, | |
88 | bfd_vma pc ATTRIBUTE_UNUSED, | |
89 | int length ATTRIBUTE_UNUSED) | |
f6e6b40f BE |
90 | { |
91 | disassemble_info *info = (disassemble_info *) dis_info; | |
92 | ||
f6e6b40f BE |
93 | /* Print the operand as directed by the attributes. */ |
94 | if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY)) | |
47b0e7ad | 95 | ; /* Nothing to do. */ |
f6e6b40f BE |
96 | else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_PCREL_ADDR)) |
97 | (*info->print_address_func) (value, info); | |
98 | else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_ABS_ADDR)) | |
99 | (*info->print_address_func) (value, info); | |
100 | else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED)) | |
101 | (*info->fprintf_func) (info->stream, "%ld", (long) value); | |
102 | else | |
103 | (*info->fprintf_func) (info->stream, "0x%lx", (long) value); | |
104 | } | |
105 | ||
106 | /* Keyword print handler. */ | |
107 | ||
108 | static void | |
10e05405 MM |
109 | print_keyword (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, |
110 | void *dis_info, | |
111 | CGEN_KEYWORD *keyword_table, | |
112 | long value, | |
113 | unsigned int attrs ATTRIBUTE_UNUSED) | |
f6e6b40f BE |
114 | { |
115 | disassemble_info *info = (disassemble_info *) dis_info; | |
116 | const CGEN_KEYWORD_ENTRY *ke; | |
117 | ||
118 | ke = cgen_keyword_lookup_value (keyword_table, value); | |
119 | if (ke != NULL) | |
120 | (*info->fprintf_func) (info->stream, "%s", ke->name); | |
121 | else | |
122 | (*info->fprintf_func) (info->stream, "???"); | |
123 | } | |
124 | \f | |
125 | /* Default insn printer. | |
126 | ||
10e05405 | 127 | DIS_INFO is defined as `void *' so the disassembler needn't know anything |
f6e6b40f BE |
128 | about disassemble_info. */ |
129 | ||
130 | static void | |
10e05405 MM |
131 | print_insn_normal (CGEN_CPU_DESC cd, |
132 | void *dis_info, | |
133 | const CGEN_INSN *insn, | |
134 | CGEN_FIELDS *fields, | |
135 | bfd_vma pc, | |
136 | int length) | |
f6e6b40f BE |
137 | { |
138 | const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn); | |
139 | disassemble_info *info = (disassemble_info *) dis_info; | |
4a9f416d | 140 | const CGEN_SYNTAX_CHAR_TYPE *syn; |
f6e6b40f BE |
141 | |
142 | CGEN_INIT_PRINT (cd); | |
143 | ||
144 | for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn) | |
145 | { | |
146 | if (CGEN_SYNTAX_MNEMONIC_P (*syn)) | |
147 | { | |
148 | (*info->fprintf_func) (info->stream, "%s", CGEN_INSN_MNEMONIC (insn)); | |
149 | continue; | |
150 | } | |
151 | if (CGEN_SYNTAX_CHAR_P (*syn)) | |
152 | { | |
153 | (*info->fprintf_func) (info->stream, "%c", CGEN_SYNTAX_CHAR (*syn)); | |
154 | continue; | |
155 | } | |
156 | ||
157 | /* We have an operand. */ | |
158 | @arch@_cgen_print_operand (cd, CGEN_SYNTAX_FIELD (*syn), info, | |
159 | fields, CGEN_INSN_ATTRS (insn), pc, length); | |
160 | } | |
161 | } | |
162 | \f | |
6bb95a0f DB |
163 | /* Subroutine of print_insn. Reads an insn into the given buffers and updates |
164 | the extract info. | |
165 | Returns 0 if all is well, non-zero otherwise. */ | |
0e2ee3ca | 166 | |
f6e6b40f | 167 | static int |
10e05405 MM |
168 | read_insn (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, |
169 | bfd_vma pc, | |
170 | disassemble_info *info, | |
33b71eeb | 171 | bfd_byte *buf, |
10e05405 MM |
172 | int buflen, |
173 | CGEN_EXTRACT_INFO *ex_info, | |
174 | unsigned long *insn_value) | |
f6e6b40f | 175 | { |
6bb95a0f | 176 | int status = (*info->read_memory_func) (pc, buf, buflen, info); |
47b0e7ad | 177 | |
6bb95a0f DB |
178 | if (status != 0) |
179 | { | |
180 | (*info->memory_error_func) (status, pc, info); | |
181 | return -1; | |
182 | } | |
f6e6b40f | 183 | |
6bb95a0f DB |
184 | ex_info->dis_info = info; |
185 | ex_info->valid = (1 << buflen) - 1; | |
186 | ex_info->insn_bytes = buf; | |
f6e6b40f | 187 | |
7e30bc36 | 188 | *insn_value = bfd_get_bits (buf, buflen * 8, info->endian == BFD_ENDIAN_BIG); |
6bb95a0f DB |
189 | return 0; |
190 | } | |
191 | ||
192 | /* Utility to print an insn. | |
193 | BUF is the base part of the insn, target byte order, BUFLEN bytes long. | |
194 | The result is the size of the insn in bytes or zero for an unknown insn | |
195 | or -1 if an error occurs fetching data (memory_error_func will have | |
196 | been called). */ | |
197 | ||
198 | static int | |
10e05405 MM |
199 | print_insn (CGEN_CPU_DESC cd, |
200 | bfd_vma pc, | |
201 | disassemble_info *info, | |
33b71eeb | 202 | bfd_byte *buf, |
10e05405 | 203 | unsigned int buflen) |
6bb95a0f | 204 | { |
81f6038f | 205 | CGEN_INSN_INT insn_value; |
6bb95a0f DB |
206 | const CGEN_INSN_LIST *insn_list; |
207 | CGEN_EXTRACT_INFO ex_info; | |
6e9a3f4d | 208 | int basesize; |
6bb95a0f | 209 | |
52646233 | 210 | /* Extract base part of instruction, just in case CGEN_DIS_* uses it. */ |
6e9a3f4d JH |
211 | basesize = cd->base_insn_bitsize < buflen * 8 ? |
212 | cd->base_insn_bitsize : buflen * 8; | |
213 | insn_value = cgen_get_insn_value (cd, buf, basesize); | |
214 | ||
52646233 FCE |
215 | |
216 | /* Fill in ex_info fields like read_insn would. Don't actually call | |
217 | read_insn, since the incoming buffer is already read (and possibly | |
218 | modified a la m32r). */ | |
219 | ex_info.valid = (1 << buflen) - 1; | |
220 | ex_info.dis_info = info; | |
221 | ex_info.insn_bytes = buf; | |
6bb95a0f | 222 | |
f6e6b40f BE |
223 | /* The instructions are stored in hash lists. |
224 | Pick the first one and keep trying until we find the right one. */ | |
225 | ||
33b71eeb | 226 | insn_list = CGEN_DIS_LOOKUP_INSN (cd, (char *) buf, insn_value); |
f6e6b40f BE |
227 | while (insn_list != NULL) |
228 | { | |
229 | const CGEN_INSN *insn = insn_list->insn; | |
230 | CGEN_FIELDS fields; | |
231 | int length; | |
52646233 | 232 | unsigned long insn_value_cropped; |
f6e6b40f | 233 | |
43e65147 | 234 | #ifdef CGEN_VALIDATE_INSN_SUPPORTED |
0e2ee3ca | 235 | /* Not needed as insn shouldn't be in hash lists if not supported. */ |
f6e6b40f BE |
236 | /* Supported by this cpu? */ |
237 | if (! @arch@_cgen_insn_supported (cd, insn)) | |
238 | { | |
239 | insn_list = CGEN_DIS_NEXT_INSN (insn_list); | |
240 | continue; | |
241 | } | |
242 | #endif | |
243 | ||
244 | /* Basic bit mask must be correct. */ | |
245 | /* ??? May wish to allow target to defer this check until the extract | |
246 | handler. */ | |
52646233 FCE |
247 | |
248 | /* Base size may exceed this instruction's size. Extract the | |
249 | relevant part from the buffer. */ | |
0e2ee3ca NC |
250 | if ((unsigned) (CGEN_INSN_BITSIZE (insn) / 8) < buflen && |
251 | (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long)) | |
43e65147 | 252 | insn_value_cropped = bfd_get_bits (buf, CGEN_INSN_BITSIZE (insn), |
52646233 FCE |
253 | info->endian == BFD_ENDIAN_BIG); |
254 | else | |
255 | insn_value_cropped = insn_value; | |
256 | ||
257 | if ((insn_value_cropped & CGEN_INSN_BASE_MASK (insn)) | |
f6e6b40f BE |
258 | == CGEN_INSN_BASE_VALUE (insn)) |
259 | { | |
260 | /* Printing is handled in two passes. The first pass parses the | |
261 | machine insn and extracts the fields. The second pass prints | |
262 | them. */ | |
263 | ||
1ffd7d02 DB |
264 | /* Make sure the entire insn is loaded into insn_value, if it |
265 | can fit. */ | |
0e2ee3ca NC |
266 | if (((unsigned) CGEN_INSN_BITSIZE (insn) > cd->base_insn_bitsize) && |
267 | (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long)) | |
6bb95a0f DB |
268 | { |
269 | unsigned long full_insn_value; | |
270 | int rc = read_insn (cd, pc, info, buf, | |
271 | CGEN_INSN_BITSIZE (insn) / 8, | |
272 | & ex_info, & full_insn_value); | |
273 | if (rc != 0) | |
274 | return rc; | |
275 | length = CGEN_EXTRACT_FN (cd, insn) | |
276 | (cd, insn, &ex_info, full_insn_value, &fields, pc); | |
277 | } | |
278 | else | |
1ffd7d02 | 279 | length = CGEN_EXTRACT_FN (cd, insn) |
84fd0d38 | 280 | (cd, insn, &ex_info, insn_value_cropped, &fields, pc); |
6bb95a0f | 281 | |
47b0e7ad | 282 | /* Length < 0 -> error. */ |
f6e6b40f BE |
283 | if (length < 0) |
284 | return length; | |
285 | if (length > 0) | |
286 | { | |
287 | CGEN_PRINT_FN (cd, insn) (cd, info, insn, &fields, pc, length); | |
47b0e7ad | 288 | /* Length is in bits, result is in bytes. */ |
f6e6b40f BE |
289 | return length / 8; |
290 | } | |
291 | } | |
292 | ||
293 | insn_list = CGEN_DIS_NEXT_INSN (insn_list); | |
294 | } | |
295 | ||
296 | return 0; | |
297 | } | |
298 | ||
299 | /* Default value for CGEN_PRINT_INSN. | |
300 | The result is the size of the insn in bytes or zero for an unknown insn | |
301 | or -1 if an error occured fetching bytes. */ | |
302 | ||
303 | #ifndef CGEN_PRINT_INSN | |
304 | #define CGEN_PRINT_INSN default_print_insn | |
305 | #endif | |
306 | ||
307 | static int | |
10e05405 | 308 | default_print_insn (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info) |
f6e6b40f | 309 | { |
33b71eeb | 310 | bfd_byte buf[CGEN_MAX_INSN_SIZE]; |
a00ad97d | 311 | int buflen; |
f6e6b40f BE |
312 | int status; |
313 | ||
a00ad97d FCE |
314 | /* Attempt to read the base part of the insn. */ |
315 | buflen = cd->base_insn_bitsize / 8; | |
316 | status = (*info->read_memory_func) (pc, buf, buflen, info); | |
317 | ||
318 | /* Try again with the minimum part, if min < base. */ | |
319 | if (status != 0 && (cd->min_insn_bitsize < cd->base_insn_bitsize)) | |
320 | { | |
321 | buflen = cd->min_insn_bitsize / 8; | |
322 | status = (*info->read_memory_func) (pc, buf, buflen, info); | |
323 | } | |
f6e6b40f | 324 | |
f6e6b40f BE |
325 | if (status != 0) |
326 | { | |
327 | (*info->memory_error_func) (status, pc, info); | |
328 | return -1; | |
329 | } | |
330 | ||
a00ad97d | 331 | return print_insn (cd, pc, info, buf, buflen); |
f6e6b40f BE |
332 | } |
333 | ||
334 | /* Main entry point. | |
335 | Print one instruction from PC on INFO->STREAM. | |
336 | Return the size of the instruction (in bytes). */ | |
337 | ||
47b0e7ad NC |
338 | typedef struct cpu_desc_list |
339 | { | |
748f46d4 | 340 | struct cpu_desc_list *next; |
fb53f5a8 | 341 | CGEN_BITSET *isa; |
748f46d4 GH |
342 | int mach; |
343 | int endian; | |
344 | CGEN_CPU_DESC cd; | |
345 | } cpu_desc_list; | |
346 | ||
f6e6b40f | 347 | int |
10e05405 | 348 | print_insn_@arch@ (bfd_vma pc, disassemble_info *info) |
f6e6b40f | 349 | { |
748f46d4 GH |
350 | static cpu_desc_list *cd_list = 0; |
351 | cpu_desc_list *cl = 0; | |
f6e6b40f | 352 | static CGEN_CPU_DESC cd = 0; |
fb53f5a8 | 353 | static CGEN_BITSET *prev_isa; |
f6e6b40f BE |
354 | static int prev_mach; |
355 | static int prev_endian; | |
356 | int length; | |
fb53f5a8 DB |
357 | CGEN_BITSET *isa; |
358 | int mach; | |
f6e6b40f BE |
359 | int endian = (info->endian == BFD_ENDIAN_BIG |
360 | ? CGEN_ENDIAN_BIG | |
361 | : CGEN_ENDIAN_LITTLE); | |
362 | enum bfd_architecture arch; | |
363 | ||
364 | /* ??? gdb will set mach but leave the architecture as "unknown" */ | |
365 | #ifndef CGEN_BFD_ARCH | |
366 | #define CGEN_BFD_ARCH bfd_arch_@arch@ | |
367 | #endif | |
368 | arch = info->arch; | |
369 | if (arch == bfd_arch_unknown) | |
370 | arch = CGEN_BFD_ARCH; | |
43e65147 | 371 | |
27fca2d8 | 372 | /* There's no standard way to compute the machine or isa number |
f6e6b40f | 373 | so we leave it to the target. */ |
27fca2d8 PM |
374 | #ifdef CGEN_COMPUTE_MACH |
375 | mach = CGEN_COMPUTE_MACH (info); | |
376 | #else | |
377 | mach = info->mach; | |
378 | #endif | |
379 | ||
f6e6b40f | 380 | #ifdef CGEN_COMPUTE_ISA |
fb53f5a8 DB |
381 | { |
382 | static CGEN_BITSET *permanent_isa; | |
383 | ||
384 | if (!permanent_isa) | |
385 | permanent_isa = cgen_bitset_create (MAX_ISAS); | |
386 | isa = permanent_isa; | |
387 | cgen_bitset_clear (isa); | |
388 | cgen_bitset_add (isa, CGEN_COMPUTE_ISA (info)); | |
389 | } | |
f6e6b40f | 390 | #else |
cadaa522 | 391 | isa = info->insn_sets; |
f6e6b40f BE |
392 | #endif |
393 | ||
748f46d4 | 394 | /* If we've switched cpu's, try to find a handle we've used before */ |
f6e6b40f | 395 | if (cd |
fb53f5a8 | 396 | && (cgen_bitset_compare (isa, prev_isa) != 0 |
f6e6b40f BE |
397 | || mach != prev_mach |
398 | || endian != prev_endian)) | |
399 | { | |
f6e6b40f | 400 | cd = 0; |
748f46d4 GH |
401 | for (cl = cd_list; cl; cl = cl->next) |
402 | { | |
fb53f5a8 | 403 | if (cgen_bitset_compare (cl->isa, isa) == 0 && |
748f46d4 GH |
404 | cl->mach == mach && |
405 | cl->endian == endian) | |
406 | { | |
407 | cd = cl->cd; | |
fb53f5a8 | 408 | prev_isa = cd->isas; |
748f46d4 GH |
409 | break; |
410 | } | |
411 | } | |
43e65147 | 412 | } |
f6e6b40f BE |
413 | |
414 | /* If we haven't initialized yet, initialize the opcode table. */ | |
415 | if (! cd) | |
416 | { | |
417 | const bfd_arch_info_type *arch_type = bfd_lookup_arch (arch, mach); | |
418 | const char *mach_name; | |
419 | ||
420 | if (!arch_type) | |
421 | abort (); | |
422 | mach_name = arch_type->printable_name; | |
423 | ||
fb53f5a8 | 424 | prev_isa = cgen_bitset_copy (isa); |
f6e6b40f BE |
425 | prev_mach = mach; |
426 | prev_endian = endian; | |
427 | cd = @arch@_cgen_cpu_open (CGEN_CPU_OPEN_ISAS, prev_isa, | |
428 | CGEN_CPU_OPEN_BFDMACH, mach_name, | |
429 | CGEN_CPU_OPEN_ENDIAN, prev_endian, | |
430 | CGEN_CPU_OPEN_END); | |
431 | if (!cd) | |
432 | abort (); | |
748f46d4 | 433 | |
47b0e7ad | 434 | /* Save this away for future reference. */ |
748f46d4 GH |
435 | cl = xmalloc (sizeof (struct cpu_desc_list)); |
436 | cl->cd = cd; | |
fb53f5a8 | 437 | cl->isa = prev_isa; |
748f46d4 GH |
438 | cl->mach = mach; |
439 | cl->endian = endian; | |
440 | cl->next = cd_list; | |
441 | cd_list = cl; | |
442 | ||
f6e6b40f BE |
443 | @arch@_cgen_init_dis (cd); |
444 | } | |
445 | ||
446 | /* We try to have as much common code as possible. | |
447 | But at this point some targets need to take over. */ | |
448 | /* ??? Some targets may need a hook elsewhere. Try to avoid this, | |
449 | but if not possible try to move this hook elsewhere rather than | |
450 | have two hooks. */ | |
451 | length = CGEN_PRINT_INSN (cd, pc, info); | |
452 | if (length > 0) | |
453 | return length; | |
454 | if (length < 0) | |
455 | return -1; | |
456 | ||
457 | (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG); | |
458 | return cd->default_insn_bitsize / 8; | |
459 | } |