Wed Nov 4 18:46:47 1998 Dave Brolley <brolley@cygnus.com>
[deliverable/binutils-gdb.git] / opcodes / fr30-opc.c
1 /* Generic opcode table support for targets using CGEN. -*- C -*-
2 CGEN: Cpu tools GENerator
3
4 THIS FILE IS USED TO GENERATE fr30-opc.c.
5
6 Copyright (C) 1998 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 Foundation, Inc.,
22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23
24 #include "sysdep.h"
25 #include <stdio.h>
26 #include "ansidecl.h"
27 #include "libiberty.h"
28 #include "bfd.h"
29 #include "symcat.h"
30 #include "fr30-opc.h"
31 #include "opintl.h"
32
33 /* The hash functions are recorded here to help keep assembler code out of
34 the disassembler and vice versa. */
35
36 static int asm_hash_insn_p PARAMS ((const CGEN_INSN *));
37 static unsigned int asm_hash_insn PARAMS ((const char *));
38 static int dis_hash_insn_p PARAMS ((const CGEN_INSN *));
39 static unsigned int dis_hash_insn PARAMS ((const char *, unsigned long));
40
41 /* Cover function to read and properly byteswap an insn value. */
42
43 CGEN_INSN_INT
44 cgen_get_insn_value (od, buf, length)
45 CGEN_OPCODE_DESC od;
46 unsigned char *buf;
47 int length;
48 {
49 CGEN_INSN_INT value;
50
51 switch (length)
52 {
53 case 8:
54 value = *buf;
55 break;
56 case 16:
57 if (CGEN_OPCODE_INSN_ENDIAN (od) == CGEN_ENDIAN_BIG)
58 value = bfd_getb16 (buf);
59 else
60 value = bfd_getl16 (buf);
61 break;
62 case 32:
63 if (CGEN_OPCODE_INSN_ENDIAN (od) == CGEN_ENDIAN_BIG)
64 value = bfd_getb32 (buf);
65 else
66 value = bfd_getl32 (buf);
67 break;
68 default:
69 abort ();
70 }
71
72 return value;
73 }
74
75 /* Cover function to store an insn value properly byteswapped. */
76
77 void
78 cgen_put_insn_value (od, buf, length, value)
79 CGEN_OPCODE_DESC od;
80 unsigned char *buf;
81 int length;
82 CGEN_INSN_INT value;
83 {
84 switch (length)
85 {
86 case 8:
87 buf[0] = value;
88 break;
89 case 16:
90 if (CGEN_OPCODE_INSN_ENDIAN (od) == CGEN_ENDIAN_BIG)
91 bfd_putb16 (value, buf);
92 else
93 bfd_putl16 (value, buf);
94 break;
95 case 32:
96 if (CGEN_OPCODE_INSN_ENDIAN (od) == CGEN_ENDIAN_BIG)
97 bfd_putb32 (value, buf);
98 else
99 bfd_putl32 (value, buf);
100 break;
101 default:
102 abort ();
103 }
104 }
105
106 /* Look up instruction INSN_VALUE and extract its fields.
107 INSN, if non-null, is the insn table entry.
108 Otherwise INSN_VALUE is examined to compute it.
109 LENGTH is the bit length of INSN_VALUE if known, otherwise 0.
110 0 is only valid if `insn == NULL && ! CGEN_INT_INSN_P'.
111 If INSN != NULL, LENGTH must be valid.
112 ALIAS_P is non-zero if alias insns are to be included in the search.
113
114 The result a pointer to the insn table entry, or NULL if the instruction
115 wasn't recognized. */
116
117 const CGEN_INSN *
118 fr30_cgen_lookup_insn (od, insn, insn_value, length, fields, alias_p)
119 CGEN_OPCODE_DESC od;
120 const CGEN_INSN *insn;
121 CGEN_INSN_BYTES insn_value;
122 int length;
123 CGEN_FIELDS *fields;
124 int alias_p;
125 {
126 unsigned char buf[16];
127 unsigned char *bufp;
128 unsigned int base_insn;
129 #if CGEN_INT_INSN_P
130 CGEN_EXTRACT_INFO *info = NULL;
131 #else
132 CGEN_EXTRACT_INFO ex_info;
133 CGEN_EXTRACT_INFO *info = &ex_info;
134 #endif
135
136 #if ! CGEN_INT_INSN_P
137 ex_info.dis_info = NULL;
138 ex_info.bytes = insn_value;
139 ex_info.valid = -1;
140 #endif
141
142 if (!insn)
143 {
144 const CGEN_INSN_LIST *insn_list;
145
146 #if CGEN_INT_INSN_P
147 cgen_put_insn_value (od, buf, length, insn_value);
148 bufp = buf;
149 base_insn = insn_value; /*???*/
150 #else
151 base_insn = cgen_get_insn_value (od, buf, length);
152 bufp = insn_value;
153 #endif
154
155 /* The instructions are stored in hash lists.
156 Pick the first one and keep trying until we find the right one. */
157
158 insn_list = CGEN_DIS_LOOKUP_INSN (od, bufp, base_insn);
159 while (insn_list != NULL)
160 {
161 insn = insn_list->insn;
162
163 if (alias_p
164 || ! CGEN_INSN_ATTR (insn, CGEN_INSN_ALIAS))
165 {
166 /* Basic bit mask must be correct. */
167 /* ??? May wish to allow target to defer this check until the
168 extract handler. */
169 if ((insn_value & CGEN_INSN_MASK (insn)) == CGEN_INSN_VALUE (insn))
170 {
171 /* ??? 0 is passed for `pc' */
172 int elength = (*CGEN_EXTRACT_FN (insn)) (od, insn, info,
173 insn_value, fields,
174 (bfd_vma) 0);
175 if (elength > 0)
176 {
177 /* sanity check */
178 if (length != 0 && length != elength)
179 abort ();
180 return insn;
181 }
182 }
183 }
184
185 insn_list = CGEN_DIS_NEXT_INSN (insn_list);
186 }
187 }
188 else
189 {
190 /* Sanity check: can't pass an alias insn if ! alias_p. */
191 if (! alias_p
192 && CGEN_INSN_ATTR (insn, CGEN_INSN_ALIAS))
193 abort ();
194 /* Sanity check: length must be correct. */
195 if (length != CGEN_INSN_BITSIZE (insn))
196 abort ();
197
198 /* ??? 0 is passed for `pc' */
199 length = (*CGEN_EXTRACT_FN (insn)) (od, insn, info, insn_value, fields,
200 (bfd_vma) 0);
201 /* Sanity check: must succeed.
202 Could relax this later if it ever proves useful. */
203 if (length == 0)
204 abort ();
205 return insn;
206 }
207
208 return NULL;
209 }
210
211 /* Fill in the operand instances used by INSN whose operands are FIELDS.
212 INDICES is a pointer to a buffer of MAX_OPERAND_INSTANCES ints to be filled
213 in. */
214
215 void
216 fr30_cgen_get_insn_operands (od, insn, fields, indices)
217 CGEN_OPCODE_DESC od;
218 const CGEN_INSN * insn;
219 const CGEN_FIELDS * fields;
220 int *indices;
221 {
222 const CGEN_OPERAND_INSTANCE *opinst;
223 int i;
224
225 for (i = 0, opinst = CGEN_INSN_OPERANDS (insn);
226 opinst != NULL
227 && CGEN_OPERAND_INSTANCE_TYPE (opinst) != CGEN_OPERAND_INSTANCE_END;
228 ++i, ++opinst)
229 {
230 const CGEN_OPERAND *op = CGEN_OPERAND_INSTANCE_OPERAND (opinst);
231 if (op == NULL)
232 indices[i] = CGEN_OPERAND_INSTANCE_INDEX (opinst);
233 else
234 indices[i] = fr30_cgen_get_int_operand (CGEN_OPERAND_INDEX (op),
235 fields);
236 }
237 }
238
239 /* Cover function to fr30_cgen_get_insn_operands when either INSN or FIELDS
240 isn't known.
241 The INSN, INSN_VALUE, and LENGTH arguments are passed to
242 fr30_cgen_lookup_insn unchanged.
243
244 The result is the insn table entry or NULL if the instruction wasn't
245 recognized. */
246
247 const CGEN_INSN *
248 fr30_cgen_lookup_get_insn_operands (od, insn, insn_value, length, indices)
249 CGEN_OPCODE_DESC od;
250 const CGEN_INSN *insn;
251 CGEN_INSN_BYTES insn_value;
252 int length;
253 int *indices;
254 {
255 CGEN_FIELDS fields;
256
257 /* Pass non-zero for ALIAS_P only if INSN != NULL.
258 If INSN == NULL, we want a real insn. */
259 insn = fr30_cgen_lookup_insn (od, insn, insn_value, length, &fields,
260 insn != NULL);
261 if (! insn)
262 return NULL;
263
264 fr30_cgen_get_insn_operands (od, insn, &fields, indices);
265 return insn;
266 }
267 /* Attributes. */
268
269 static const CGEN_ATTR_ENTRY MACH_attr[] =
270 {
271 { "base", MACH_BASE },
272 { "fr30", MACH_FR30 },
273 { "max", MACH_MAX },
274 { 0, 0 }
275 };
276
277 const CGEN_ATTR_TABLE fr30_cgen_hardware_attr_table[] =
278 {
279 { "CACHE-ADDR", NULL },
280 { "PC", NULL },
281 { "PROFILE", NULL },
282 { 0, 0 }
283 };
284
285 const CGEN_ATTR_TABLE fr30_cgen_operand_attr_table[] =
286 {
287 { "ABS-ADDR", NULL },
288 { "FAKE", NULL },
289 { "NEGATIVE", NULL },
290 { "PCREL-ADDR", NULL },
291 { "RELAX", NULL },
292 { "SIGN-OPT", NULL },
293 { "UNSIGNED", NULL },
294 { 0, 0 }
295 };
296
297 const CGEN_ATTR_TABLE fr30_cgen_insn_attr_table[] =
298 {
299 { "ALIAS", NULL },
300 { "COND-CTI", NULL },
301 { "NO-DIS", NULL },
302 { "RELAX", NULL },
303 { "RELAXABLE", NULL },
304 { "SKIP-CTI", NULL },
305 { "UNCOND-CTI", NULL },
306 { "VIRTUAL", NULL },
307 { 0, 0 }
308 };
309
310 CGEN_KEYWORD_ENTRY fr30_cgen_opval_h_gr_entries[] =
311 {
312 { "ac", 13 },
313 { "fp", 14 },
314 { "sp", 15 },
315 { "r0", 0 },
316 { "r1", 1 },
317 { "r2", 2 },
318 { "r3", 3 },
319 { "r4", 4 },
320 { "r5", 5 },
321 { "r6", 6 },
322 { "r7", 7 },
323 { "r8", 8 },
324 { "r9", 9 },
325 { "r10", 10 },
326 { "r11", 11 },
327 { "r12", 12 },
328 { "r13", 13 },
329 { "r14", 14 },
330 { "r15", 15 }
331 };
332
333 CGEN_KEYWORD fr30_cgen_opval_h_gr =
334 {
335 & fr30_cgen_opval_h_gr_entries[0],
336 19
337 };
338
339
340 /* The hardware table. */
341
342 #define HW_ENT(n) fr30_cgen_hw_entries[n]
343 static const CGEN_HW_ENTRY fr30_cgen_hw_entries[] =
344 {
345 { HW_H_PC, & HW_ENT (HW_H_PC + 1), "h-pc", CGEN_ASM_KEYWORD, (PTR) 0, { 0, 0|(1<<CGEN_HW_PROFILE)|(1<<CGEN_HW_PC), { 0 } } },
346 { HW_H_MEMORY, & HW_ENT (HW_H_MEMORY + 1), "h-memory", CGEN_ASM_KEYWORD, (PTR) 0, { 0, 0, { 0 } } },
347 { HW_H_SINT, & HW_ENT (HW_H_SINT + 1), "h-sint", CGEN_ASM_KEYWORD, (PTR) 0, { 0, 0, { 0 } } },
348 { HW_H_UINT, & HW_ENT (HW_H_UINT + 1), "h-uint", CGEN_ASM_KEYWORD, (PTR) 0, { 0, 0, { 0 } } },
349 { HW_H_ADDR, & HW_ENT (HW_H_ADDR + 1), "h-addr", CGEN_ASM_KEYWORD, (PTR) 0, { 0, 0, { 0 } } },
350 { HW_H_IADDR, & HW_ENT (HW_H_IADDR + 1), "h-iaddr", CGEN_ASM_KEYWORD, (PTR) 0, { 0, 0, { 0 } } },
351 { HW_H_GR, & HW_ENT (HW_H_GR + 1), "h-gr", CGEN_ASM_KEYWORD, (PTR) & fr30_cgen_opval_h_gr, { 0, 0|(1<<CGEN_HW_CACHE_ADDR)|(1<<CGEN_HW_PROFILE), { 0 } } },
352 { 0 }
353 };
354
355 /* The operand table. */
356
357 #define OPERAND(op) CONCAT2 (FR30_OPERAND_,op)
358 #define OP_ENT(op) fr30_cgen_operand_table[OPERAND (op)]
359
360 const CGEN_OPERAND fr30_cgen_operand_table[MAX_OPERANDS] =
361 {
362 /* pc: program counter */
363 { "pc", & HW_ENT (HW_H_PC), 0, 0,
364 { 0, 0|(1<<CGEN_OPERAND_FAKE), { 0 } } },
365 /* Ri: destination register */
366 { "Ri", & HW_ENT (HW_H_GR), 12, 4,
367 { 0, 0|(1<<CGEN_OPERAND_UNSIGNED), { 0 } } },
368 /* Rj: source register */
369 { "Rj", & HW_ENT (HW_H_GR), 8, 4,
370 { 0, 0|(1<<CGEN_OPERAND_UNSIGNED), { 0 } } },
371 };
372
373 /* Operand references. */
374
375 #define INPUT CGEN_OPERAND_INSTANCE_INPUT
376 #define OUTPUT CGEN_OPERAND_INSTANCE_OUTPUT
377
378 static const CGEN_OPERAND_INSTANCE fmt_ADD_ops[] = {
379 { INPUT, "Rj", & HW_ENT (HW_H_GR), CGEN_MODE_SI, & OP_ENT (RJ), 0 },
380 { INPUT, "Ri", & HW_ENT (HW_H_GR), CGEN_MODE_SI, & OP_ENT (RI), 0 },
381 { OUTPUT, "Ri", & HW_ENT (HW_H_GR), CGEN_MODE_SI, & OP_ENT (RI), 0 },
382 { 0 }
383 };
384
385 #undef INPUT
386 #undef OUTPUT
387
388 #define A(a) (1 << CONCAT2 (CGEN_INSN_,a))
389 #define MNEM CGEN_SYNTAX_MNEMONIC /* syntax value for mnemonic */
390 #define OP(field) CGEN_SYNTAX_MAKE_FIELD (OPERAND (field))
391
392 /* The instruction table.
393 This is currently non-static because the simulator accesses it
394 directly. */
395
396 const CGEN_INSN fr30_cgen_insn_table_entries[MAX_INSNS] =
397 {
398 /* Special null first entry.
399 A `num' value of zero is thus invalid.
400 Also, the special `invalid' insn resides here. */
401 { { 0 }, 0 },
402 /* ADD $Rj,$Ri */
403 {
404 { 1, 1, 1, 1 },
405 FR30_INSN_ADD, "ADD", "ADD",
406 { { MNEM, ' ', OP (RJ), ',', OP (RI), 0 } },
407 { 16, 16, 0xff00 }, 0xa600,
408 (PTR) & fmt_ADD_ops[0],
409 { 0, 0, { 0 } }
410 },
411 };
412
413 #undef A
414 #undef MNEM
415 #undef OP
416
417 static const CGEN_INSN_TABLE insn_table =
418 {
419 & fr30_cgen_insn_table_entries[0],
420 sizeof (CGEN_INSN),
421 MAX_INSNS,
422 NULL
423 };
424
425 /* Each non-simple macro entry points to an array of expansion possibilities. */
426
427 #define A(a) (1 << CONCAT2 (CGEN_INSN_,a))
428 #define MNEM CGEN_SYNTAX_MNEMONIC /* syntax value for mnemonic */
429 #define OP(field) CGEN_SYNTAX_MAKE_FIELD (OPERAND (field))
430
431 /* The macro instruction table. */
432
433 static const CGEN_INSN macro_insn_table_entries[] =
434 {
435 };
436
437 #undef A
438 #undef MNEM
439 #undef OP
440
441 static const CGEN_INSN_TABLE macro_insn_table =
442 {
443 & macro_insn_table_entries[0],
444 sizeof (CGEN_INSN),
445 (sizeof (macro_insn_table_entries) /
446 sizeof (macro_insn_table_entries[0])),
447 NULL
448 };
449
450 static void
451 init_tables ()
452 {
453 }
454
455 /* Return non-zero if INSN is to be added to the hash table.
456 Targets are free to override CGEN_{ASM,DIS}_HASH_P in the .opc file. */
457
458 static int
459 asm_hash_insn_p (insn)
460 const CGEN_INSN * insn;
461 {
462 return CGEN_ASM_HASH_P (insn);
463 }
464
465 static int
466 dis_hash_insn_p (insn)
467 const CGEN_INSN * insn;
468 {
469 /* If building the hash table and the NO-DIS attribute is present,
470 ignore. */
471 if (CGEN_INSN_ATTR (insn, CGEN_INSN_NO_DIS))
472 return 0;
473 return CGEN_DIS_HASH_P (insn);
474 }
475
476 /* The result is the hash value of the insn.
477 Targets are free to override CGEN_{ASM,DIS}_HASH in the .opc file. */
478
479 static unsigned int
480 asm_hash_insn (mnem)
481 const char * mnem;
482 {
483 return CGEN_ASM_HASH (mnem);
484 }
485
486 /* BUF is a pointer to the insn's bytes in target order.
487 VALUE is an integer of the first CGEN_BASE_INSN_BITSIZE bits,
488 host order. */
489
490 static unsigned int
491 dis_hash_insn (buf, value)
492 const char * buf;
493 unsigned long value;
494 {
495 return CGEN_DIS_HASH (buf, value);
496 }
497
498 /* Initialize an opcode table and return a descriptor.
499 It's much like opening a file, and must be the first function called. */
500
501 CGEN_OPCODE_DESC
502 fr30_cgen_opcode_open (mach, endian)
503 int mach;
504 enum cgen_endian endian;
505 {
506 CGEN_OPCODE_TABLE * table = (CGEN_OPCODE_TABLE *) xmalloc (sizeof (CGEN_OPCODE_TABLE));
507 static int init_p;
508
509 if (! init_p)
510 {
511 init_tables ();
512 init_p = 1;
513 }
514
515 memset (table, 0, sizeof (*table));
516
517 CGEN_OPCODE_MACH (table) = mach;
518 CGEN_OPCODE_ENDIAN (table) = endian;
519 /* FIXME: for the sparc case we can determine insn-endianness statically.
520 The worry here is where both data and insn endian can be independently
521 chosen, in which case this function will need another argument.
522 Actually, will want to allow for more arguments in the future anyway. */
523 CGEN_OPCODE_INSN_ENDIAN (table) = endian;
524
525 CGEN_OPCODE_HW_LIST (table) = & fr30_cgen_hw_entries[0];
526
527 CGEN_OPCODE_OPERAND_TABLE (table) = & fr30_cgen_operand_table[0];
528
529 * CGEN_OPCODE_INSN_TABLE (table) = insn_table;
530
531 * CGEN_OPCODE_MACRO_INSN_TABLE (table) = macro_insn_table;
532
533 CGEN_OPCODE_ASM_HASH_P (table) = asm_hash_insn_p;
534 CGEN_OPCODE_ASM_HASH (table) = asm_hash_insn;
535 CGEN_OPCODE_ASM_HASH_SIZE (table) = CGEN_ASM_HASH_SIZE;
536
537 CGEN_OPCODE_DIS_HASH_P (table) = dis_hash_insn_p;
538 CGEN_OPCODE_DIS_HASH (table) = dis_hash_insn;
539 CGEN_OPCODE_DIS_HASH_SIZE (table) = CGEN_DIS_HASH_SIZE;
540
541 return (CGEN_OPCODE_DESC) table;
542 }
543
544 /* Close an opcode table. */
545
546 void
547 fr30_cgen_opcode_close (desc)
548 CGEN_OPCODE_DESC desc;
549 {
550 free (desc);
551 }
552
553 /* Getting values from cgen_fields is handled by a collection of functions.
554 They are distinguished by the type of the VALUE argument they return.
555 TODO: floating point, inlining support, remove cases where result type
556 not appropriate. */
557
558 int
559 fr30_cgen_get_int_operand (opindex, fields)
560 int opindex;
561 const CGEN_FIELDS * fields;
562 {
563 int value;
564
565 switch (opindex)
566 {
567 case FR30_OPERAND_RI :
568 value = fields->f_Ri;
569 break;
570 case FR30_OPERAND_RJ :
571 value = fields->f_Rj;
572 break;
573
574 default :
575 /* xgettext:c-format */
576 fprintf (stderr, _("Unrecognized field %d while getting int operand.\n"),
577 opindex);
578 abort ();
579 }
580
581 return value;
582 }
583
584 bfd_vma
585 fr30_cgen_get_vma_operand (opindex, fields)
586 int opindex;
587 const CGEN_FIELDS * fields;
588 {
589 bfd_vma value;
590
591 switch (opindex)
592 {
593 case FR30_OPERAND_RI :
594 value = fields->f_Ri;
595 break;
596 case FR30_OPERAND_RJ :
597 value = fields->f_Rj;
598 break;
599
600 default :
601 /* xgettext:c-format */
602 fprintf (stderr, _("Unrecognized field %d while getting vma operand.\n"),
603 opindex);
604 abort ();
605 }
606
607 return value;
608 }
609
610 /* Stuffing values in cgen_fields is handled by a collection of functions.
611 They are distinguished by the type of the VALUE argument they accept.
612 TODO: floating point, inlining support, remove cases where argument type
613 not appropriate. */
614
615 void
616 fr30_cgen_set_int_operand (opindex, fields, value)
617 int opindex;
618 CGEN_FIELDS * fields;
619 int value;
620 {
621 switch (opindex)
622 {
623 case FR30_OPERAND_RI :
624 fields->f_Ri = value;
625 break;
626 case FR30_OPERAND_RJ :
627 fields->f_Rj = value;
628 break;
629
630 default :
631 /* xgettext:c-format */
632 fprintf (stderr, _("Unrecognized field %d while setting int operand.\n"),
633 opindex);
634 abort ();
635 }
636 }
637
638 void
639 fr30_cgen_set_vma_operand (opindex, fields, value)
640 int opindex;
641 CGEN_FIELDS * fields;
642 bfd_vma value;
643 {
644 switch (opindex)
645 {
646 case FR30_OPERAND_RI :
647 fields->f_Ri = value;
648 break;
649 case FR30_OPERAND_RJ :
650 fields->f_Rj = value;
651 break;
652
653 default :
654 /* xgettext:c-format */
655 fprintf (stderr, _("Unrecognized field %d while setting vma operand.\n"),
656 opindex);
657 abort ();
658 }
659 }
660
This page took 0.044108 seconds and 5 git commands to generate.