diff options
Diffstat (limited to 'opcodes/fr30-opc.c')
-rw-r--r-- | opcodes/fr30-opc.c | 660 |
1 files changed, 660 insertions, 0 deletions
diff --git a/opcodes/fr30-opc.c b/opcodes/fr30-opc.c new file mode 100644 index 0000000..8becb0b --- /dev/null +++ b/opcodes/fr30-opc.c @@ -0,0 +1,660 @@ +/* Generic opcode table support for targets using CGEN. -*- C -*- + CGEN: Cpu tools GENerator + +THIS FILE IS USED TO GENERATE fr30-opc.c. + +Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of the GNU Binutils and GDB, the GNU debugger. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "sysdep.h" +#include <stdio.h> +#include "ansidecl.h" +#include "libiberty.h" +#include "bfd.h" +#include "symcat.h" +#include "fr30-opc.h" +#include "opintl.h" + +/* The hash functions are recorded here to help keep assembler code out of + the disassembler and vice versa. */ + +static int asm_hash_insn_p PARAMS ((const CGEN_INSN *)); +static unsigned int asm_hash_insn PARAMS ((const char *)); +static int dis_hash_insn_p PARAMS ((const CGEN_INSN *)); +static unsigned int dis_hash_insn PARAMS ((const char *, unsigned long)); + +/* Cover function to read and properly byteswap an insn value. */ + +CGEN_INSN_INT +cgen_get_insn_value (od, buf, length) + CGEN_OPCODE_DESC od; + unsigned char *buf; + int length; +{ + CGEN_INSN_INT value; + + switch (length) + { + case 8: + value = *buf; + break; + case 16: + if (CGEN_OPCODE_INSN_ENDIAN (od) == CGEN_ENDIAN_BIG) + value = bfd_getb16 (buf); + else + value = bfd_getl16 (buf); + break; + case 32: + if (CGEN_OPCODE_INSN_ENDIAN (od) == CGEN_ENDIAN_BIG) + value = bfd_getb32 (buf); + else + value = bfd_getl32 (buf); + break; + default: + abort (); + } + + return value; +} + +/* Cover function to store an insn value properly byteswapped. */ + +void +cgen_put_insn_value (od, buf, length, value) + CGEN_OPCODE_DESC od; + unsigned char *buf; + int length; + CGEN_INSN_INT value; +{ + switch (length) + { + case 8: + buf[0] = value; + break; + case 16: + if (CGEN_OPCODE_INSN_ENDIAN (od) == CGEN_ENDIAN_BIG) + bfd_putb16 (value, buf); + else + bfd_putl16 (value, buf); + break; + case 32: + if (CGEN_OPCODE_INSN_ENDIAN (od) == CGEN_ENDIAN_BIG) + bfd_putb32 (value, buf); + else + bfd_putl32 (value, buf); + break; + default: + abort (); + } +} + +/* Look up instruction INSN_VALUE and extract its fields. + INSN, if non-null, is the insn table entry. + Otherwise INSN_VALUE is examined to compute it. + LENGTH is the bit length of INSN_VALUE if known, otherwise 0. + 0 is only valid if `insn == NULL && ! CGEN_INT_INSN_P'. + If INSN != NULL, LENGTH must be valid. + ALIAS_P is non-zero if alias insns are to be included in the search. + + The result a pointer to the insn table entry, or NULL if the instruction + wasn't recognized. */ + +const CGEN_INSN * +fr30_cgen_lookup_insn (od, insn, insn_value, length, fields, alias_p) + CGEN_OPCODE_DESC od; + const CGEN_INSN *insn; + CGEN_INSN_BYTES insn_value; + int length; + CGEN_FIELDS *fields; + int alias_p; +{ + unsigned char buf[16]; + unsigned char *bufp; + unsigned int base_insn; +#if CGEN_INT_INSN_P + CGEN_EXTRACT_INFO *info = NULL; +#else + CGEN_EXTRACT_INFO ex_info; + CGEN_EXTRACT_INFO *info = &ex_info; +#endif + +#if ! CGEN_INT_INSN_P + ex_info.dis_info = NULL; + ex_info.bytes = insn_value; + ex_info.valid = -1; +#endif + + if (!insn) + { + const CGEN_INSN_LIST *insn_list; + +#if CGEN_INT_INSN_P + cgen_put_insn_value (od, buf, length, insn_value); + bufp = buf; + base_insn = insn_value; /*???*/ +#else + base_insn = cgen_get_insn_value (od, buf, length); + bufp = insn_value; +#endif + + /* The instructions are stored in hash lists. + Pick the first one and keep trying until we find the right one. */ + + insn_list = CGEN_DIS_LOOKUP_INSN (od, bufp, base_insn); + while (insn_list != NULL) + { + insn = insn_list->insn; + + if (alias_p + || ! CGEN_INSN_ATTR (insn, CGEN_INSN_ALIAS)) + { + /* Basic bit mask must be correct. */ + /* ??? May wish to allow target to defer this check until the + extract handler. */ + if ((insn_value & CGEN_INSN_MASK (insn)) == CGEN_INSN_VALUE (insn)) + { + /* ??? 0 is passed for `pc' */ + int elength = (*CGEN_EXTRACT_FN (insn)) (od, insn, info, + insn_value, fields, + (bfd_vma) 0); + if (elength > 0) + { + /* sanity check */ + if (length != 0 && length != elength) + abort (); + return insn; + } + } + } + + insn_list = CGEN_DIS_NEXT_INSN (insn_list); + } + } + else + { + /* Sanity check: can't pass an alias insn if ! alias_p. */ + if (! alias_p + && CGEN_INSN_ATTR (insn, CGEN_INSN_ALIAS)) + abort (); + /* Sanity check: length must be correct. */ + if (length != CGEN_INSN_BITSIZE (insn)) + abort (); + + /* ??? 0 is passed for `pc' */ + length = (*CGEN_EXTRACT_FN (insn)) (od, insn, info, insn_value, fields, + (bfd_vma) 0); + /* Sanity check: must succeed. + Could relax this later if it ever proves useful. */ + if (length == 0) + abort (); + return insn; + } + + return NULL; +} + +/* Fill in the operand instances used by INSN whose operands are FIELDS. + INDICES is a pointer to a buffer of MAX_OPERAND_INSTANCES ints to be filled + in. */ + +void +fr30_cgen_get_insn_operands (od, insn, fields, indices) + CGEN_OPCODE_DESC od; + const CGEN_INSN * insn; + const CGEN_FIELDS * fields; + int *indices; +{ + const CGEN_OPERAND_INSTANCE *opinst; + int i; + + for (i = 0, opinst = CGEN_INSN_OPERANDS (insn); + opinst != NULL + && CGEN_OPERAND_INSTANCE_TYPE (opinst) != CGEN_OPERAND_INSTANCE_END; + ++i, ++opinst) + { + const CGEN_OPERAND *op = CGEN_OPERAND_INSTANCE_OPERAND (opinst); + if (op == NULL) + indices[i] = CGEN_OPERAND_INSTANCE_INDEX (opinst); + else + indices[i] = fr30_cgen_get_int_operand (CGEN_OPERAND_INDEX (op), + fields); + } +} + +/* Cover function to fr30_cgen_get_insn_operands when either INSN or FIELDS + isn't known. + The INSN, INSN_VALUE, and LENGTH arguments are passed to + fr30_cgen_lookup_insn unchanged. + + The result is the insn table entry or NULL if the instruction wasn't + recognized. */ + +const CGEN_INSN * +fr30_cgen_lookup_get_insn_operands (od, insn, insn_value, length, indices) + CGEN_OPCODE_DESC od; + const CGEN_INSN *insn; + CGEN_INSN_BYTES insn_value; + int length; + int *indices; +{ + CGEN_FIELDS fields; + + /* Pass non-zero for ALIAS_P only if INSN != NULL. + If INSN == NULL, we want a real insn. */ + insn = fr30_cgen_lookup_insn (od, insn, insn_value, length, &fields, + insn != NULL); + if (! insn) + return NULL; + + fr30_cgen_get_insn_operands (od, insn, &fields, indices); + return insn; +} +/* Attributes. */ + +static const CGEN_ATTR_ENTRY MACH_attr[] = +{ + { "base", MACH_BASE }, + { "fr30", MACH_FR30 }, + { "max", MACH_MAX }, + { 0, 0 } +}; + +const CGEN_ATTR_TABLE fr30_cgen_hardware_attr_table[] = +{ + { "CACHE-ADDR", NULL }, + { "PC", NULL }, + { "PROFILE", NULL }, + { 0, 0 } +}; + +const CGEN_ATTR_TABLE fr30_cgen_operand_attr_table[] = +{ + { "ABS-ADDR", NULL }, + { "FAKE", NULL }, + { "NEGATIVE", NULL }, + { "PCREL-ADDR", NULL }, + { "RELAX", NULL }, + { "SIGN-OPT", NULL }, + { "UNSIGNED", NULL }, + { 0, 0 } +}; + +const CGEN_ATTR_TABLE fr30_cgen_insn_attr_table[] = +{ + { "ALIAS", NULL }, + { "COND-CTI", NULL }, + { "NO-DIS", NULL }, + { "RELAX", NULL }, + { "RELAXABLE", NULL }, + { "SKIP-CTI", NULL }, + { "UNCOND-CTI", NULL }, + { "VIRTUAL", NULL }, + { 0, 0 } +}; + +CGEN_KEYWORD_ENTRY fr30_cgen_opval_h_gr_entries[] = +{ + { "ac", 13 }, + { "fp", 14 }, + { "sp", 15 }, + { "r0", 0 }, + { "r1", 1 }, + { "r2", 2 }, + { "r3", 3 }, + { "r4", 4 }, + { "r5", 5 }, + { "r6", 6 }, + { "r7", 7 }, + { "r8", 8 }, + { "r9", 9 }, + { "r10", 10 }, + { "r11", 11 }, + { "r12", 12 }, + { "r13", 13 }, + { "r14", 14 }, + { "r15", 15 } +}; + +CGEN_KEYWORD fr30_cgen_opval_h_gr = +{ + & fr30_cgen_opval_h_gr_entries[0], + 19 +}; + + +/* The hardware table. */ + +#define HW_ENT(n) fr30_cgen_hw_entries[n] +static const CGEN_HW_ENTRY fr30_cgen_hw_entries[] = +{ + { 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 } } }, + { HW_H_MEMORY, & HW_ENT (HW_H_MEMORY + 1), "h-memory", CGEN_ASM_KEYWORD, (PTR) 0, { 0, 0, { 0 } } }, + { HW_H_SINT, & HW_ENT (HW_H_SINT + 1), "h-sint", CGEN_ASM_KEYWORD, (PTR) 0, { 0, 0, { 0 } } }, + { HW_H_UINT, & HW_ENT (HW_H_UINT + 1), "h-uint", CGEN_ASM_KEYWORD, (PTR) 0, { 0, 0, { 0 } } }, + { HW_H_ADDR, & HW_ENT (HW_H_ADDR + 1), "h-addr", CGEN_ASM_KEYWORD, (PTR) 0, { 0, 0, { 0 } } }, + { HW_H_IADDR, & HW_ENT (HW_H_IADDR + 1), "h-iaddr", CGEN_ASM_KEYWORD, (PTR) 0, { 0, 0, { 0 } } }, + { 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 } } }, + { 0 } +}; + +/* The operand table. */ + +#define OPERAND(op) CONCAT2 (FR30_OPERAND_,op) +#define OP_ENT(op) fr30_cgen_operand_table[OPERAND (op)] + +const CGEN_OPERAND fr30_cgen_operand_table[MAX_OPERANDS] = +{ +/* pc: program counter */ + { "pc", & HW_ENT (HW_H_PC), 0, 0, + { 0, 0|(1<<CGEN_OPERAND_FAKE), { 0 } } }, +/* Ri: destination register */ + { "Ri", & HW_ENT (HW_H_GR), 12, 4, + { 0, 0|(1<<CGEN_OPERAND_UNSIGNED), { 0 } } }, +/* Rj: source register */ + { "Rj", & HW_ENT (HW_H_GR), 8, 4, + { 0, 0|(1<<CGEN_OPERAND_UNSIGNED), { 0 } } }, +}; + +/* Operand references. */ + +#define INPUT CGEN_OPERAND_INSTANCE_INPUT +#define OUTPUT CGEN_OPERAND_INSTANCE_OUTPUT + +static const CGEN_OPERAND_INSTANCE fmt_ADD_ops[] = { + { INPUT, "Rj", & HW_ENT (HW_H_GR), CGEN_MODE_SI, & OP_ENT (RJ), 0 }, + { INPUT, "Ri", & HW_ENT (HW_H_GR), CGEN_MODE_SI, & OP_ENT (RI), 0 }, + { OUTPUT, "Ri", & HW_ENT (HW_H_GR), CGEN_MODE_SI, & OP_ENT (RI), 0 }, + { 0 } +}; + +#undef INPUT +#undef OUTPUT + +#define A(a) (1 << CONCAT2 (CGEN_INSN_,a)) +#define MNEM CGEN_SYNTAX_MNEMONIC /* syntax value for mnemonic */ +#define OP(field) CGEN_SYNTAX_MAKE_FIELD (OPERAND (field)) + +/* The instruction table. + This is currently non-static because the simulator accesses it + directly. */ + +const CGEN_INSN fr30_cgen_insn_table_entries[MAX_INSNS] = +{ + /* Special null first entry. + A `num' value of zero is thus invalid. + Also, the special `invalid' insn resides here. */ + { { 0 }, 0 }, +/* ADD $Rj,$Ri */ + { + { 1, 1, 1, 1 }, + FR30_INSN_ADD, "ADD", "ADD", + { { MNEM, ' ', OP (RJ), ',', OP (RI), 0 } }, + { 16, 16, 0xff00 }, 0xa600, + (PTR) & fmt_ADD_ops[0], + { 0, 0, { 0 } } + }, +}; + +#undef A +#undef MNEM +#undef OP + +static const CGEN_INSN_TABLE insn_table = +{ + & fr30_cgen_insn_table_entries[0], + sizeof (CGEN_INSN), + MAX_INSNS, + NULL +}; + +/* Each non-simple macro entry points to an array of expansion possibilities. */ + +#define A(a) (1 << CONCAT2 (CGEN_INSN_,a)) +#define MNEM CGEN_SYNTAX_MNEMONIC /* syntax value for mnemonic */ +#define OP(field) CGEN_SYNTAX_MAKE_FIELD (OPERAND (field)) + +/* The macro instruction table. */ + +static const CGEN_INSN macro_insn_table_entries[] = +{ +}; + +#undef A +#undef MNEM +#undef OP + +static const CGEN_INSN_TABLE macro_insn_table = +{ + & macro_insn_table_entries[0], + sizeof (CGEN_INSN), + (sizeof (macro_insn_table_entries) / + sizeof (macro_insn_table_entries[0])), + NULL +}; + +static void +init_tables () +{ +} + +/* Return non-zero if INSN is to be added to the hash table. + Targets are free to override CGEN_{ASM,DIS}_HASH_P in the .opc file. */ + +static int +asm_hash_insn_p (insn) + const CGEN_INSN * insn; +{ + return CGEN_ASM_HASH_P (insn); +} + +static int +dis_hash_insn_p (insn) + const CGEN_INSN * insn; +{ + /* If building the hash table and the NO-DIS attribute is present, + ignore. */ + if (CGEN_INSN_ATTR (insn, CGEN_INSN_NO_DIS)) + return 0; + return CGEN_DIS_HASH_P (insn); +} + +/* The result is the hash value of the insn. + Targets are free to override CGEN_{ASM,DIS}_HASH in the .opc file. */ + +static unsigned int +asm_hash_insn (mnem) + const char * mnem; +{ + return CGEN_ASM_HASH (mnem); +} + +/* BUF is a pointer to the insn's bytes in target order. + VALUE is an integer of the first CGEN_BASE_INSN_BITSIZE bits, + host order. */ + +static unsigned int +dis_hash_insn (buf, value) + const char * buf; + unsigned long value; +{ + return CGEN_DIS_HASH (buf, value); +} + +/* Initialize an opcode table and return a descriptor. + It's much like opening a file, and must be the first function called. */ + +CGEN_OPCODE_DESC +fr30_cgen_opcode_open (mach, endian) + int mach; + enum cgen_endian endian; +{ + CGEN_OPCODE_TABLE * table = (CGEN_OPCODE_TABLE *) xmalloc (sizeof (CGEN_OPCODE_TABLE)); + static int init_p; + + if (! init_p) + { + init_tables (); + init_p = 1; + } + + memset (table, 0, sizeof (*table)); + + CGEN_OPCODE_MACH (table) = mach; + CGEN_OPCODE_ENDIAN (table) = endian; + /* FIXME: for the sparc case we can determine insn-endianness statically. + The worry here is where both data and insn endian can be independently + chosen, in which case this function will need another argument. + Actually, will want to allow for more arguments in the future anyway. */ + CGEN_OPCODE_INSN_ENDIAN (table) = endian; + + CGEN_OPCODE_HW_LIST (table) = & fr30_cgen_hw_entries[0]; + + CGEN_OPCODE_OPERAND_TABLE (table) = & fr30_cgen_operand_table[0]; + + * CGEN_OPCODE_INSN_TABLE (table) = insn_table; + + * CGEN_OPCODE_MACRO_INSN_TABLE (table) = macro_insn_table; + + CGEN_OPCODE_ASM_HASH_P (table) = asm_hash_insn_p; + CGEN_OPCODE_ASM_HASH (table) = asm_hash_insn; + CGEN_OPCODE_ASM_HASH_SIZE (table) = CGEN_ASM_HASH_SIZE; + + CGEN_OPCODE_DIS_HASH_P (table) = dis_hash_insn_p; + CGEN_OPCODE_DIS_HASH (table) = dis_hash_insn; + CGEN_OPCODE_DIS_HASH_SIZE (table) = CGEN_DIS_HASH_SIZE; + + return (CGEN_OPCODE_DESC) table; +} + +/* Close an opcode table. */ + +void +fr30_cgen_opcode_close (desc) + CGEN_OPCODE_DESC desc; +{ + free (desc); +} + +/* Getting values from cgen_fields is handled by a collection of functions. + They are distinguished by the type of the VALUE argument they return. + TODO: floating point, inlining support, remove cases where result type + not appropriate. */ + +int +fr30_cgen_get_int_operand (opindex, fields) + int opindex; + const CGEN_FIELDS * fields; +{ + int value; + + switch (opindex) + { + case FR30_OPERAND_RI : + value = fields->f_Ri; + break; + case FR30_OPERAND_RJ : + value = fields->f_Rj; + break; + + default : + /* xgettext:c-format */ + fprintf (stderr, _("Unrecognized field %d while getting int operand.\n"), + opindex); + abort (); + } + + return value; +} + +bfd_vma +fr30_cgen_get_vma_operand (opindex, fields) + int opindex; + const CGEN_FIELDS * fields; +{ + bfd_vma value; + + switch (opindex) + { + case FR30_OPERAND_RI : + value = fields->f_Ri; + break; + case FR30_OPERAND_RJ : + value = fields->f_Rj; + break; + + default : + /* xgettext:c-format */ + fprintf (stderr, _("Unrecognized field %d while getting vma operand.\n"), + opindex); + abort (); + } + + return value; +} + +/* Stuffing values in cgen_fields is handled by a collection of functions. + They are distinguished by the type of the VALUE argument they accept. + TODO: floating point, inlining support, remove cases where argument type + not appropriate. */ + +void +fr30_cgen_set_int_operand (opindex, fields, value) + int opindex; + CGEN_FIELDS * fields; + int value; +{ + switch (opindex) + { + case FR30_OPERAND_RI : + fields->f_Ri = value; + break; + case FR30_OPERAND_RJ : + fields->f_Rj = value; + break; + + default : + /* xgettext:c-format */ + fprintf (stderr, _("Unrecognized field %d while setting int operand.\n"), + opindex); + abort (); + } +} + +void +fr30_cgen_set_vma_operand (opindex, fields, value) + int opindex; + CGEN_FIELDS * fields; + bfd_vma value; +{ + switch (opindex) + { + case FR30_OPERAND_RI : + fields->f_Ri = value; + break; + case FR30_OPERAND_RJ : + fields->f_Rj = value; + break; + + default : + /* xgettext:c-format */ + fprintf (stderr, _("Unrecognized field %d while setting vma operand.\n"), + opindex); + abort (); + } +} + |