diff options
Diffstat (limited to 'contrib/binutils/opcodes/cgen-opc.c')
-rw-r--r-- | contrib/binutils/opcodes/cgen-opc.c | 405 |
1 files changed, 347 insertions, 58 deletions
diff --git a/contrib/binutils/opcodes/cgen-opc.c b/contrib/binutils/opcodes/cgen-opc.c index a9d937b..ede3add 100644 --- a/contrib/binutils/opcodes/cgen-opc.c +++ b/contrib/binutils/opcodes/cgen-opc.c @@ -1,6 +1,6 @@ /* CGEN generic opcode support. - Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. This file is part of the GNU Binutils and GDB, the GNU debugger. @@ -27,51 +27,6 @@ #include "symcat.h" #include "opcode/cgen.h" -/* State variables. - These record the state of the currently selected cpu, machine, endian, etc. - They are set by cgen_set_cpu. */ - -/* Current opcode data. */ -CGEN_OPCODE_DATA *cgen_current_opcode_data; - -/* Current machine (a la BFD machine number). */ -int cgen_current_mach; - -/* Current endian. */ -enum cgen_endian cgen_current_endian = CGEN_ENDIAN_UNKNOWN; - -/* FIXME: To support multiple architectures, we need to return a handle - to the state set up by this function, and pass the handle back to the - other functions. Later. */ - -void -cgen_set_cpu (data, mach, endian) - CGEN_OPCODE_DATA *data; - int mach; - enum cgen_endian endian; -{ - static int init_once_p; - - cgen_current_opcode_data = data; - cgen_current_mach = mach; - cgen_current_endian = endian; - - /* Initialize those things that only need be done once. */ - if (! init_once_p) - { - /* Nothing to do currently. */ - init_once_p = 1; - } - -#if 0 /* This isn't done here because it would put assembler support in the - disassembler, etc. The caller is required to call these after calling - us. */ - /* Reset the hash tables. */ - cgen_asm_init (); - cgen_dis_init (); -#endif -} - static unsigned int hash_keyword_name PARAMS ((const CGEN_KEYWORD *, const char *, int)); static unsigned int hash_keyword_value @@ -299,34 +254,368 @@ build_keyword_hash_tables (kt) /* Hardware support. */ +/* Lookup a hardware element by its name. + Returns NULL if NAME is not supported by the currently selected + mach/isa. */ + const CGEN_HW_ENTRY * -cgen_hw_lookup (name) +cgen_hw_lookup_by_name (cd, name) + CGEN_CPU_DESC cd; const char *name; { - const CGEN_HW_ENTRY *hw = cgen_current_opcode_data->hw_list; + int i; + const CGEN_HW_ENTRY **hw = cd->hw_table.entries; - while (hw != NULL) - { - if (strcmp (name, hw->name) == 0) - return hw; - hw = hw->next; - } + for (i = 0; i < cd->hw_table.num_entries; ++i) + if (hw[i] && strcmp (name, hw[i]->name) == 0) + return hw[i]; + + return NULL; +} + +/* Lookup a hardware element by its number. + Hardware elements are enumerated, however it may be possible to add some + at runtime, thus HWNUM is not an enum type but rather an int. + Returns NULL if HWNUM is not supported by the currently selected mach. */ + +const CGEN_HW_ENTRY * +cgen_hw_lookup_by_num (cd, hwnum) + CGEN_CPU_DESC cd; + int hwnum; +{ + int i; + const CGEN_HW_ENTRY **hw = cd->hw_table.entries; + + /* ??? This can be speeded up. */ + for (i = 0; i < cd->hw_table.num_entries; ++i) + if (hw[i] && hwnum == hw[i]->type) + return hw[i]; return NULL; } +/* Operand support. */ + +/* Lookup an operand by its name. + Returns NULL if NAME is not supported by the currently selected + mach/isa. */ + +const CGEN_OPERAND * +cgen_operand_lookup_by_name (cd, name) + CGEN_CPU_DESC cd; + const char *name; +{ + int i; + const CGEN_OPERAND **op = cd->operand_table.entries; + + for (i = 0; i < cd->operand_table.num_entries; ++i) + if (op[i] && strcmp (name, op[i]->name) == 0) + return op[i]; + + return NULL; +} + +/* Lookup an operand by its number. + Operands are enumerated, however it may be possible to add some + at runtime, thus OPNUM is not an enum type but rather an int. + Returns NULL if OPNUM is not supported by the currently selected + mach/isa. */ + +const CGEN_OPERAND * +cgen_operand_lookup_by_num (cd, opnum) + CGEN_CPU_DESC cd; + int opnum; +{ + return cd->operand_table.entries[opnum]; +} + /* Instruction support. */ /* Return number of instructions. This includes any added at runtime. */ int -cgen_insn_count () +cgen_insn_count (cd) + CGEN_CPU_DESC cd; { - int count = cgen_current_opcode_data->insn_table->num_init_entries; - CGEN_INSN_LIST *insn = cgen_current_opcode_data->insn_table->new_entries; + int count = cd->insn_table.num_init_entries; + CGEN_INSN_LIST *rt_insns = cd->insn_table.new_entries; - for ( ; insn != NULL; insn = insn->next) + for ( ; rt_insns != NULL; rt_insns = rt_insns->next) ++count; return count; } + +/* Return number of macro-instructions. + This includes any added at runtime. */ + +int +cgen_macro_insn_count (cd) + CGEN_CPU_DESC cd; +{ + int count = cd->macro_insn_table.num_init_entries; + CGEN_INSN_LIST *rt_insns = cd->macro_insn_table.new_entries; + + for ( ; rt_insns != NULL; rt_insns = rt_insns->next) + ++count; + + return count; +} + +/* Cover function to read and properly byteswap an insn value. */ + +CGEN_INSN_INT +cgen_get_insn_value (cd, buf, length) + CGEN_CPU_DESC cd; + unsigned char *buf; + int length; +{ + CGEN_INSN_INT value; + + switch (length) + { + case 8: + value = *buf; + break; + case 16: + if (cd->insn_endian == CGEN_ENDIAN_BIG) + value = bfd_getb16 (buf); + else + value = bfd_getl16 (buf); + break; + case 32: + if (cd->insn_endian == 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 (cd, buf, length, value) + CGEN_CPU_DESC cd; + unsigned char *buf; + int length; + CGEN_INSN_INT value; +{ + switch (length) + { + case 8: + buf[0] = value; + break; + case 16: + if (cd->insn_endian == CGEN_ENDIAN_BIG) + bfd_putb16 (value, buf); + else + bfd_putl16 (value, buf); + break; + case 32: + if (cd->insn_endian == 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_INT_VALUE is used if CGEN_INT_INSN_P. + Otherwise INSN_BYTES_VALUE is used. + 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 is a pointer to the insn table entry, or NULL if the instruction + wasn't recognized. */ + +/* ??? Will need to be revisited for VLIW architectures. */ + +const CGEN_INSN * +cgen_lookup_insn (cd, insn, insn_int_value, insn_bytes_value, length, fields, + alias_p) + CGEN_CPU_DESC cd; + const CGEN_INSN *insn; + CGEN_INSN_INT insn_int_value; + /* ??? CGEN_INSN_BYTES would be a nice type name to use here. */ + unsigned char *insn_bytes_value; + int length; + CGEN_FIELDS *fields; + int alias_p; +{ + unsigned char *buf; + CGEN_INSN_INT base_insn; + CGEN_EXTRACT_INFO ex_info; + CGEN_EXTRACT_INFO *info; + + if (cd->int_insn_p) + { + info = NULL; + buf = (unsigned char *) alloca (cd->max_insn_bitsize / 8); + cgen_put_insn_value (cd, buf, length, insn_int_value); + base_insn = insn_int_value; + } + else + { + info = &ex_info; + ex_info.dis_info = NULL; + ex_info.insn_bytes = insn_bytes_value; + ex_info.valid = -1; + buf = insn_bytes_value; + base_insn = cgen_get_insn_value (cd, buf, length); + } + + if (!insn) + { + const CGEN_INSN_LIST *insn_list; + + /* 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 (cd, buf, base_insn); + while (insn_list != NULL) + { + insn = insn_list->insn; + + if (alias_p + /* FIXME: Ensure ALIAS attribute always has same index. */ + || ! CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_ALIAS)) + { + /* Basic bit mask must be correct. */ + /* ??? May wish to allow target to defer this check until the + extract handler. */ + if ((base_insn & CGEN_INSN_BASE_MASK (insn)) + == CGEN_INSN_BASE_VALUE (insn)) + { + /* ??? 0 is passed for `pc' */ + int elength = CGEN_EXTRACT_FN (cd, insn) + (cd, insn, info, base_insn, fields, (bfd_vma) 0); + if (elength > 0) + { + /* sanity check */ + if (length != 0 && length != elength) + abort (); + return insn; + } + } + } + + insn_list = insn_list->next; + } + } + else + { + /* Sanity check: can't pass an alias insn if ! alias_p. */ + if (! alias_p + && CGEN_INSN_ATTR_VALUE (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 (cd, insn) + (cd, insn, info, base_insn, 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 +cgen_get_insn_operands (cd, insn, fields, indices) + CGEN_CPU_DESC cd; + const CGEN_INSN *insn; + const CGEN_FIELDS *fields; + int *indices; +{ + const CGEN_OPINST *opinst; + int i; + + if (insn->opinst == NULL) + abort (); + for (i = 0, opinst = insn->opinst; opinst->type != CGEN_OPINST_END; ++i, ++opinst) + { + enum cgen_operand_type op_type = opinst->op_type; + if (op_type == CGEN_OPERAND_NIL) + indices[i] = opinst->index; + else + indices[i] = (*cd->get_int_operand) (cd, op_type, fields); + } +} + +/* Cover function to cgen_get_insn_operands when either INSN or FIELDS + isn't known. + The INSN, INSN_*_VALUE, and LENGTH arguments are passed to + cgen_lookup_insn unchanged. + INSN_INT_VALUE is used if CGEN_INT_INSN_P. + Otherwise INSN_BYTES_VALUE is used. + + The result is the insn table entry or NULL if the instruction wasn't + recognized. */ + +const CGEN_INSN * +cgen_lookup_get_insn_operands (cd, insn, insn_int_value, insn_bytes_value, + length, indices, fields) + CGEN_CPU_DESC cd; + const CGEN_INSN *insn; + CGEN_INSN_INT insn_int_value; + /* ??? CGEN_INSN_BYTES would be a nice type name to use here. */ + unsigned char *insn_bytes_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 = cgen_lookup_insn (cd, insn, insn_int_value, insn_bytes_value, + length, fields, insn != NULL); + if (! insn) + return NULL; + + cgen_get_insn_operands (cd, insn, fields, indices); + return insn; +} + +/* Allow signed overflow of instruction fields. */ +void +cgen_set_signed_overflow_ok (cd) + CGEN_CPU_DESC cd; +{ + cd->signed_overflow_ok_p = 1; +} + +/* Generate an error message if a signed field in an instruction overflows. */ +void +cgen_clear_signed_overflow_ok (cd) + CGEN_CPU_DESC cd; +{ + cd->signed_overflow_ok_p = 0; +} + +/* Will an error message be generated if a signed field in an instruction overflows ? */ +unsigned int +cgen_signed_overflow_ok_p (cd) + CGEN_CPU_DESC cd; +{ + return cd->signed_overflow_ok_p; +} |