Commit | Line | Data |
---|---|---|
9c03036a DE |
1 | /* Disassembler interface for targets using CGEN. -*- C -*- |
2 | CGEN: Cpu tools GENerator | |
3 | ||
4 | This file is used to generate m32r-dis.c. | |
5 | ||
6 | Copyright (C) 1996, 1997 Free Software Foundation, Inc. | |
7 | ||
8 | This file is part of the GNU Binutils and GDB, the GNU debugger. | |
9 | ||
10 | This program is free software; you can redistribute it and/or modify | |
11 | it under the terms of the GNU General Public License as published by | |
12 | the Free Software Foundation; either version 2, or (at your option) | |
13 | any later version. | |
14 | ||
15 | This program is distributed in the hope that it will be useful, | |
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | GNU General Public License for more details. | |
19 | ||
20 | You should have received a copy of the GNU General Public License | |
21 | along with this program; if not, write to the Free Software | |
22 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |
23 | ||
23cf992f | 24 | #include "sysdep.h" |
9c03036a DE |
25 | #include <stdio.h> |
26 | #include "ansidecl.h" | |
27 | #include "dis-asm.h" | |
9c03036a | 28 | #include "bfd.h" |
23cf992f | 29 | #include "m32r-opc.h" |
9c03036a DE |
30 | |
31 | /* ??? The layout of this stuff is still work in progress. | |
32 | For speed in assembly/disassembly, we use inline functions. That of course | |
33 | will only work for GCC. When this stuff is finished, we can decide whether | |
34 | to keep the inline functions (and only get the performance increase when | |
35 | compiled with GCC), or switch to macros, or use something else. | |
36 | */ | |
37 | ||
38 | /* Default text to print if an instruction isn't recognized. */ | |
39 | #define UNKNOWN_INSN_MSG "*unknown*" | |
40 | ||
41 | /* FIXME: Machine generate. */ | |
42 | #ifndef CGEN_PCREL_OFFSET | |
43 | #define CGEN_PCREL_OFFSET 0 | |
44 | #endif | |
45 | ||
46 | static int print_insn PARAMS ((bfd_vma, disassemble_info *, char *, int)); | |
47 | ||
48 | static int extract_insn_normal | |
23cf992f | 49 | PARAMS ((const CGEN_INSN *, void *, cgen_insn_t, CGEN_FIELDS *)); |
9c03036a | 50 | static void print_insn_normal |
23cf992f NC |
51 | PARAMS ((void *, const CGEN_INSN *, CGEN_FIELDS *, bfd_vma, int)); |
52 | ||
53 | CGEN_INLINE void | |
54 | m32r_cgen_print_operand | |
55 | PARAMS ((int opindex, disassemble_info * info, CGEN_FIELDS * fields, void const * attrs, bfd_vma pc, int length)); | |
56 | ||
9c03036a DE |
57 | \f |
58 | /* Default extraction routine. | |
59 | ||
60 | ATTRS is a mask of the boolean attributes. We only need `unsigned', | |
61 | but for generality we take a bitmask of all of them. */ | |
62 | ||
63 | static int | |
64 | extract_normal (buf_ctrl, insn_value, attrs, start, length, shift, total_length, valuep) | |
23cf992f NC |
65 | void * buf_ctrl; |
66 | cgen_insn_t insn_value; | |
9c03036a | 67 | unsigned int attrs; |
23cf992f NC |
68 | int start; |
69 | int length; | |
70 | int shift; | |
71 | int total_length; | |
72 | long * valuep; | |
9c03036a DE |
73 | { |
74 | long value; | |
75 | ||
76 | #ifdef CGEN_INT_INSN | |
77 | #if 0 | |
78 | value = ((insn_value >> (CGEN_BASE_INSN_BITSIZE - (start + length))) | |
79 | & ((1 << length) - 1)); | |
80 | #else | |
81 | value = ((insn_value >> (total_length - (start + length))) | |
82 | & ((1 << length) - 1)); | |
83 | #endif | |
84 | if (! (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_UNSIGNED)) | |
85 | && (value & (1 << (length - 1)))) | |
86 | value -= 1 << length; | |
87 | #else | |
88 | /* FIXME: unfinished */ | |
89 | #endif | |
90 | ||
91 | /* This is backwards as we undo the effects of insert_normal. */ | |
92 | if (shift < 0) | |
93 | value >>= -shift; | |
94 | else | |
95 | value <<= shift; | |
96 | ||
23cf992f | 97 | * valuep = value; |
9c03036a DE |
98 | return 1; |
99 | } | |
100 | ||
101 | /* Default print handler. */ | |
102 | ||
103 | static void | |
104 | print_normal (dis_info, value, attrs, pc, length) | |
23cf992f NC |
105 | void * dis_info; |
106 | long value; | |
107 | unsigned int attrs; | |
9c03036a | 108 | unsigned long pc; /* FIXME: should be bfd_vma */ |
23cf992f | 109 | int length; |
9c03036a | 110 | { |
23cf992f | 111 | disassemble_info * info = dis_info; |
9c03036a DE |
112 | |
113 | /* Print the operand as directed by the attributes. */ | |
114 | if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_FAKE)) | |
115 | ; /* nothing to do (??? at least not yet) */ | |
116 | else if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_PCREL_ADDR)) | |
117 | (*info->print_address_func) (pc + CGEN_PCREL_OFFSET + value, info); | |
118 | /* ??? Not all cases of this are currently caught. */ | |
119 | else if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_ABS_ADDR)) | |
120 | /* FIXME: Why & 0xffffffff? */ | |
121 | (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info); | |
122 | else if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_UNSIGNED)) | |
123 | (*info->fprintf_func) (info->stream, "0x%lx", value); | |
124 | else | |
125 | (*info->fprintf_func) (info->stream, "%ld", value); | |
126 | } | |
127 | ||
128 | /* Keyword print handler. */ | |
129 | ||
130 | static void | |
131 | print_keyword (dis_info, keyword_table, value, attrs) | |
23cf992f NC |
132 | void * dis_info; |
133 | CGEN_KEYWORD * keyword_table; | |
134 | long value; | |
135 | CGEN_ATTR * attrs; | |
9c03036a | 136 | { |
23cf992f NC |
137 | disassemble_info * info = dis_info; |
138 | const CGEN_KEYWORD_ENTRY * ke; | |
9c03036a DE |
139 | |
140 | ke = cgen_keyword_lookup_value (keyword_table, value); | |
23cf992f | 141 | info->fprintf_func (info->stream, "%s", ke == NULL ? "???" : ke->name); |
9c03036a DE |
142 | } |
143 | \f | |
144 | /* -- disassembler routines inserted here */ | |
9c03036a DE |
145 | \f |
146 | /* Default insn extractor. | |
147 | ||
148 | The extracted fields are stored in DIS_FLDS. | |
149 | BUF_CTRL is used to handle reading variable length insns (FIXME: not done). | |
150 | Return the length of the insn in bits, or 0 if no match. */ | |
151 | ||
152 | static int | |
153 | extract_insn_normal (insn, buf_ctrl, insn_value, fields) | |
23cf992f NC |
154 | const CGEN_INSN * insn; |
155 | void * buf_ctrl; | |
156 | cgen_insn_t insn_value; | |
157 | CGEN_FIELDS * fields; | |
9c03036a | 158 | { |
23cf992f NC |
159 | const CGEN_SYNTAX * syntax = CGEN_INSN_SYNTAX (insn); |
160 | const unsigned char * syn; | |
9c03036a | 161 | |
9c03036a DE |
162 | CGEN_FIELDS_BITSIZE (fields) = CGEN_INSN_BITSIZE (insn); |
163 | ||
164 | CGEN_INIT_EXTRACT (); | |
165 | ||
23cf992f | 166 | for (syn = CGEN_SYNTAX_STRING (syntax); * syn; ++ syn) |
9c03036a DE |
167 | { |
168 | int length; | |
169 | ||
23cf992f | 170 | if (CGEN_SYNTAX_CHAR_P (* syn)) |
9c03036a DE |
171 | continue; |
172 | ||
23cf992f | 173 | length = m32r_cgen_extract_operand (CGEN_SYNTAX_FIELD (* syn), |
9c03036a DE |
174 | buf_ctrl, insn_value, fields); |
175 | if (length == 0) | |
176 | return 0; | |
177 | } | |
178 | ||
23cf992f NC |
179 | /* We recognized and successfully extracted this insn. */ |
180 | return CGEN_INSN_BITSIZE (insn); | |
9c03036a DE |
181 | } |
182 | ||
183 | /* Default insn printer. | |
184 | ||
185 | DIS_INFO is defined as `void *' so the disassembler needn't know anything | |
186 | about disassemble_info. | |
187 | */ | |
188 | ||
189 | static void | |
190 | print_insn_normal (dis_info, insn, fields, pc, length) | |
23cf992f NC |
191 | void * dis_info; |
192 | const CGEN_INSN * insn; | |
193 | CGEN_FIELDS * fields; | |
194 | bfd_vma pc; | |
195 | int length; | |
9c03036a | 196 | { |
23cf992f NC |
197 | const CGEN_SYNTAX * syntax = CGEN_INSN_SYNTAX (insn); |
198 | disassemble_info * info = dis_info; | |
199 | const unsigned char * syn; | |
9c03036a DE |
200 | |
201 | CGEN_INIT_PRINT (); | |
202 | ||
23cf992f | 203 | for (syn = CGEN_SYNTAX_STRING (syntax); * syn; ++ syn) |
9c03036a | 204 | { |
23cf992f NC |
205 | if (CGEN_SYNTAX_MNEMONIC_P (* syn)) |
206 | { | |
207 | info->fprintf_func (info->stream, "%s", CGEN_INSN_MNEMONIC (insn)); | |
208 | continue; | |
209 | } | |
210 | if (CGEN_SYNTAX_CHAR_P (* syn)) | |
9c03036a | 211 | { |
23cf992f | 212 | info->fprintf_func (info->stream, "%c", CGEN_SYNTAX_CHAR (* syn)); |
9c03036a DE |
213 | continue; |
214 | } | |
215 | ||
216 | /* We have an operand. */ | |
23cf992f | 217 | m32r_cgen_print_operand (CGEN_SYNTAX_FIELD (* syn), info, |
9c03036a DE |
218 | fields, CGEN_INSN_ATTRS (insn), pc, length); |
219 | } | |
220 | } | |
221 | \f | |
222 | /* Default value for CGEN_PRINT_INSN. | |
223 | Given BUFLEN bytes (target byte order) read into BUF, look up the | |
224 | insn in the instruction table and disassemble it. | |
225 | ||
226 | The result is the size of the insn in bytes. */ | |
227 | ||
228 | #ifndef CGEN_PRINT_INSN | |
229 | #define CGEN_PRINT_INSN print_insn | |
230 | #endif | |
231 | ||
232 | static int | |
233 | print_insn (pc, info, buf, buflen) | |
23cf992f NC |
234 | bfd_vma pc; |
235 | disassemble_info * info; | |
236 | char * buf; | |
237 | int buflen; | |
9c03036a | 238 | { |
23cf992f NC |
239 | int i; |
240 | unsigned long insn_value; | |
241 | const CGEN_INSN_LIST * insn_list; | |
242 | int extra_bytes; | |
243 | ||
9c03036a DE |
244 | switch (buflen) |
245 | { | |
246 | case 8: | |
247 | insn_value = buf[0]; | |
248 | break; | |
249 | case 16: | |
250 | insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb16 (buf) : bfd_getl16 (buf); | |
251 | break; | |
252 | case 32: | |
253 | insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb32 (buf) : bfd_getl32 (buf); | |
254 | break; | |
255 | default: | |
256 | abort (); | |
257 | } | |
258 | ||
23cf992f NC |
259 | /* Special case - a 32 bit instruction which is actually two 16 bit instructions |
260 | being executed in parallel. */ | |
261 | if (buflen == 32 | |
0d0bb914 | 262 | && (pc & 0x3) == 0 |
23cf992f NC |
263 | && ((insn_value & 0x80008000) == 0x00008000)) |
264 | { | |
265 | if (info->endian == BFD_ENDIAN_BIG) | |
266 | { | |
267 | static char buf2 [4]; | |
268 | ||
269 | print_insn (pc, info, buf, 16); | |
270 | ||
271 | info->fprintf_func (info->stream, " || "); | |
272 | ||
273 | buf2 [0] = buf [2] & ~ 0x80; | |
274 | buf2 [1] = buf [3]; | |
275 | buf2 [2] = 0; | |
276 | buf2 [3] = 0; | |
277 | buf = buf2; | |
278 | ||
279 | insn_value <<= 17; | |
280 | insn_value >>= 1; | |
281 | } | |
282 | else | |
283 | { | |
284 | print_insn (pc, info, buf + 2, 16); | |
285 | ||
286 | info->fprintf_func (info->stream, " || "); | |
287 | ||
288 | insn_value &= 0x7fff; | |
289 | } | |
290 | ||
291 | pc += 2; | |
292 | extra_bytes = 2; | |
293 | } | |
294 | else | |
295 | extra_bytes = 0; | |
296 | ||
9c03036a DE |
297 | /* The instructions are stored in hash lists. |
298 | Pick the first one and keep trying until we find the right one. */ | |
299 | ||
300 | insn_list = CGEN_DIS_LOOKUP_INSN (buf, insn_value); | |
23cf992f | 301 | |
9c03036a DE |
302 | while (insn_list != NULL) |
303 | { | |
23cf992f NC |
304 | const CGEN_INSN * insn = insn_list->insn; |
305 | unsigned long value; | |
9c03036a DE |
306 | |
307 | #if 0 /* not needed as insn shouldn't be in hash lists if not supported */ | |
308 | /* Supported by this cpu? */ | |
309 | if (! m32r_cgen_insn_supported (insn)) | |
310 | continue; | |
311 | #endif | |
312 | ||
23cf992f NC |
313 | /* If we are looking at a 16 bit insn we may have to adjust the value being examined. */ |
314 | value = insn_value; | |
315 | if (CGEN_INSN_BITSIZE (insn) == 16) | |
316 | { | |
317 | /* If this is a big endian target, | |
318 | and we have read 32 bits for the instruction value, | |
319 | then we must examine the top 16 bits, not the bottom. */ | |
320 | if (buflen == 32 && info->endian == BFD_ENDIAN_BIG) | |
321 | value >>= 16; | |
322 | } | |
323 | ||
9c03036a DE |
324 | /* Basic bit mask must be correct. */ |
325 | /* ??? May wish to allow target to defer this check until the extract | |
326 | handler. */ | |
23cf992f | 327 | if ((value & CGEN_INSN_MASK (insn)) == CGEN_INSN_VALUE (insn)) |
9c03036a | 328 | { |
23cf992f NC |
329 | CGEN_FIELDS fields; |
330 | int length; | |
331 | ||
9c03036a DE |
332 | /* Printing is handled in two passes. The first pass parses the |
333 | machine insn and extracts the fields. The second pass prints | |
334 | them. */ | |
335 | ||
23cf992f | 336 | length = CGEN_EXTRACT_FN (insn) (insn, NULL, value, & fields); |
9c03036a DE |
337 | if (length > 0) |
338 | { | |
23cf992f NC |
339 | CGEN_PRINT_FN (insn) (info, insn, & fields, pc, length); |
340 | ||
9c03036a | 341 | /* length is in bits, result is in bytes */ |
23cf992f | 342 | return (length / 8) + extra_bytes; |
9c03036a DE |
343 | } |
344 | } | |
23cf992f | 345 | |
9c03036a DE |
346 | insn_list = CGEN_DIS_NEXT_INSN (insn_list); |
347 | } | |
348 | ||
23cf992f | 349 | return extra_bytes; |
9c03036a DE |
350 | } |
351 | ||
352 | /* Main entry point. | |
353 | Print one instruction from PC on INFO->STREAM. | |
354 | Return the size of the instruction (in bytes). */ | |
355 | ||
356 | int | |
357 | print_insn_m32r (pc, info) | |
23cf992f NC |
358 | bfd_vma pc; |
359 | disassemble_info * info; | |
9c03036a | 360 | { |
23cf992f NC |
361 | char buffer [CGEN_MAX_INSN_SIZE]; |
362 | int status; | |
363 | int length; | |
364 | static int initialized = 0; | |
365 | static int current_mach = 0; | |
366 | static int current_bigend = 0; | |
367 | int mach = info->mach; | |
368 | int bigend = info->endian == BFD_ENDIAN_BIG; | |
9c03036a DE |
369 | |
370 | /* If we haven't initialized yet, or if we've switched cpu's, initialize. */ | |
23cf992f | 371 | if (!initialized || mach != current_mach || bigend != current_bigend) |
9c03036a | 372 | { |
23cf992f NC |
373 | initialized = 1; |
374 | current_mach = mach; | |
375 | current_bigend = bigend; | |
376 | ||
377 | m32r_cgen_init_dis (mach, bigend ? CGEN_ENDIAN_BIG : CGEN_ENDIAN_LITTLE); | |
9c03036a DE |
378 | } |
379 | ||
380 | /* Read enough of the insn so we can look it up in the hash lists. */ | |
381 | ||
23cf992f | 382 | status = info->read_memory_func (pc, buffer, CGEN_BASE_INSN_SIZE, info); |
9c03036a DE |
383 | if (status != 0) |
384 | { | |
23cf992f NC |
385 | /* Try reading a 16 bit instruction. */ |
386 | info->bytes_per_chunk = 2; | |
387 | status = info->read_memory_func (pc, buffer, CGEN_BASE_INSN_SIZE / 2, info); | |
388 | buffer [2] = buffer [3] = 0; | |
389 | } | |
390 | if (status != 0) | |
391 | { | |
392 | info->memory_error_func (status, pc, info); | |
9c03036a DE |
393 | return -1; |
394 | } | |
395 | ||
396 | /* We try to have as much common code as possible. | |
397 | But at this point some targets need to take over. */ | |
398 | /* ??? Some targets may need a hook elsewhere. Try to avoid this, | |
23cf992f | 399 | but if not possible try to move this hook elsewhere rather than |
9c03036a DE |
400 | have two hooks. */ |
401 | length = CGEN_PRINT_INSN (pc, info, buffer, CGEN_BASE_INSN_BITSIZE); | |
23cf992f | 402 | |
9c03036a DE |
403 | if (length) |
404 | return length; | |
405 | ||
23cf992f NC |
406 | info->fprintf_func (info->stream, UNKNOWN_INSN_MSG); |
407 | ||
9c03036a DE |
408 | return CGEN_DEFAULT_INSN_SIZE; |
409 | } | |
23cf992f NC |
410 | |
411 | /* Get the generate machine specific code. */ | |
412 | #include "m32r-dis.in" |