| 1 | /* Generic opcode table support for targets using CGEN. -*- C -*- |
| 2 | CGEN: Cpu tools GENerator |
| 3 | |
| 4 | This file is used to generate @arch@-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 |
| 22 | Foundation, Inc., 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 "@arch@-opc.h" |
| 31 | #include "opintl.h" |
| 32 | |
| 33 | /* Look up instruction INSN_VALUE and extract its fields. |
| 34 | INSN, if non-null, is the insn table entry. |
| 35 | Otherwise INSN_VALUE is examined to compute it. |
| 36 | LENGTH is the bit length of INSN_VALUE if known, otherwise 0. |
| 37 | 0 is only valid if `insn == NULL && ! defined (CGEN_INT_INSN)'. |
| 38 | If INSN != NULL, LENGTH must be valid. |
| 39 | ALIAS_P is non-zero if alias insns are to be included in the search. |
| 40 | |
| 41 | The result a pointer to the insn table entry, or NULL if the instruction |
| 42 | wasn't recognized. */ |
| 43 | |
| 44 | const CGEN_INSN * |
| 45 | @arch@_cgen_lookup_insn (insn, insn_value, length, fields, alias_p) |
| 46 | const CGEN_INSN *insn; |
| 47 | cgen_insn_t insn_value; |
| 48 | int length; |
| 49 | CGEN_FIELDS *fields; |
| 50 | int alias_p; |
| 51 | { |
| 52 | char buf[16]; |
| 53 | |
| 54 | if (!insn) |
| 55 | { |
| 56 | const CGEN_INSN_LIST *insn_list; |
| 57 | |
| 58 | #ifdef CGEN_INT_INSN |
| 59 | switch (length) |
| 60 | { |
| 61 | case 8: |
| 62 | buf[0] = insn_value; |
| 63 | break; |
| 64 | case 16: |
| 65 | if (cgen_current_endian == CGEN_ENDIAN_BIG) |
| 66 | bfd_putb16 (insn_value, buf); |
| 67 | else |
| 68 | bfd_putl16 (insn_value, buf); |
| 69 | break; |
| 70 | case 32: |
| 71 | if (cgen_current_endian == CGEN_ENDIAN_BIG) |
| 72 | bfd_putb32 (insn_value, buf); |
| 73 | else |
| 74 | bfd_putl32 (insn_value, buf); |
| 75 | break; |
| 76 | default: |
| 77 | abort (); |
| 78 | } |
| 79 | #else |
| 80 | abort (); /* FIXME: unfinished */ |
| 81 | #endif |
| 82 | |
| 83 | /* The instructions are stored in hash lists. |
| 84 | Pick the first one and keep trying until we find the right one. */ |
| 85 | |
| 86 | insn_list = CGEN_DIS_LOOKUP_INSN (buf, insn_value); |
| 87 | while (insn_list != NULL) |
| 88 | { |
| 89 | insn = insn_list->insn; |
| 90 | |
| 91 | if (alias_p |
| 92 | || ! CGEN_INSN_ATTR (insn, CGEN_INSN_ALIAS)) |
| 93 | { |
| 94 | /* Basic bit mask must be correct. */ |
| 95 | /* ??? May wish to allow target to defer this check until the |
| 96 | extract handler. */ |
| 97 | if ((insn_value & CGEN_INSN_MASK (insn)) == CGEN_INSN_VALUE (insn)) |
| 98 | { |
| 99 | int elength = (*CGEN_EXTRACT_FN (insn)) (insn, NULL, |
| 100 | insn_value, fields); |
| 101 | if (elength > 0) |
| 102 | { |
| 103 | /* sanity check */ |
| 104 | if (length != 0 && length != elength) |
| 105 | abort (); |
| 106 | return insn; |
| 107 | } |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | insn_list = CGEN_DIS_NEXT_INSN (insn_list); |
| 112 | } |
| 113 | } |
| 114 | else |
| 115 | { |
| 116 | /* Sanity check: can't pass an alias insn if ! alias_p. */ |
| 117 | if (! alias_p |
| 118 | && CGEN_INSN_ATTR (insn, CGEN_INSN_ALIAS)) |
| 119 | abort (); |
| 120 | /* Sanity check: length must be correct. */ |
| 121 | if (length != CGEN_INSN_BITSIZE (insn)) |
| 122 | abort (); |
| 123 | |
| 124 | length = (*CGEN_EXTRACT_FN (insn)) (insn, NULL, insn_value, fields); |
| 125 | /* Sanity check: must succeed. |
| 126 | Could relax this later if it ever proves useful. */ |
| 127 | if (length == 0) |
| 128 | abort (); |
| 129 | return insn; |
| 130 | } |
| 131 | |
| 132 | return NULL; |
| 133 | } |
| 134 | |
| 135 | /* Fill in the operand instances used by INSN whose operands are FIELDS. |
| 136 | INDICES is a pointer to a buffer of MAX_OPERAND_INSTANCES ints to be filled |
| 137 | in. */ |
| 138 | |
| 139 | void |
| 140 | @arch@_cgen_get_insn_operands (insn, fields, indices) |
| 141 | const CGEN_INSN * insn; |
| 142 | const CGEN_FIELDS * fields; |
| 143 | int *indices; |
| 144 | { |
| 145 | const CGEN_OPERAND_INSTANCE *opinst; |
| 146 | int i; |
| 147 | |
| 148 | for (i = 0, opinst = CGEN_INSN_OPERANDS (insn); |
| 149 | opinst != NULL |
| 150 | && CGEN_OPERAND_INSTANCE_TYPE (opinst) != CGEN_OPERAND_INSTANCE_END; |
| 151 | ++i, ++opinst) |
| 152 | { |
| 153 | const CGEN_OPERAND *op = CGEN_OPERAND_INSTANCE_OPERAND (opinst); |
| 154 | if (op == NULL) |
| 155 | indices[i] = CGEN_OPERAND_INSTANCE_INDEX (opinst); |
| 156 | else |
| 157 | indices[i] = @arch@_cgen_get_operand (CGEN_OPERAND_INDEX (op), fields); |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | /* Cover function to @arch@_cgen_get_insn_operands when either INSN or FIELDS |
| 162 | isn't known. |
| 163 | The INSN, INSN_VALUE, and LENGTH arguments are passed to |
| 164 | @arch@_cgen_lookup_insn unchanged. |
| 165 | |
| 166 | The result is the insn table entry or NULL if the instruction wasn't |
| 167 | recognized. */ |
| 168 | |
| 169 | const CGEN_INSN * |
| 170 | @arch@_cgen_lookup_get_insn_operands (insn, insn_value, length, indices) |
| 171 | const CGEN_INSN *insn; |
| 172 | cgen_insn_t insn_value; |
| 173 | int length; |
| 174 | int *indices; |
| 175 | { |
| 176 | CGEN_FIELDS fields; |
| 177 | |
| 178 | /* Pass non-zero for ALIAS_P only if INSN != NULL. |
| 179 | If INSN == NULL, we want a real insn. */ |
| 180 | insn = @arch@_cgen_lookup_insn (insn, insn_value, length, &fields, |
| 181 | insn != NULL); |
| 182 | if (! insn) |
| 183 | return NULL; |
| 184 | |
| 185 | @arch@_cgen_get_insn_operands (insn, &fields, indices); |
| 186 | return insn; |
| 187 | } |