]>
Commit | Line | Data |
---|---|---|
ab0bd049 DE |
1 | /* Generic opcode table support for targets using CGEN. -*- C -*- |
2 | CGEN: Cpu tools GENerator | |
3 | ||
0499462e | 4 | THIS FILE IS USED TO GENERATE @[email protected]. |
ab0bd049 DE |
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 | |
0499462e DE |
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. */ | |
ab0bd049 DE |
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" | |
0499462e | 30 | #include "@[email protected]" |
1e74d15c | 31 | #include "opintl.h" |
ab0bd049 | 32 | |
c2009f4a DE |
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 | ||
fcea6f20 DE |
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 | ||
ab0bd049 | 106 | /* Look up instruction INSN_VALUE and extract its fields. |
2613b5e6 | 107 | INSN, if non-null, is the insn table entry. |
ab0bd049 DE |
108 | Otherwise INSN_VALUE is examined to compute it. |
109 | LENGTH is the bit length of INSN_VALUE if known, otherwise 0. | |
fcea6f20 | 110 | 0 is only valid if `insn == NULL && ! CGEN_INT_INSN_P'. |
2613b5e6 | 111 | If INSN != NULL, LENGTH must be valid. |
390bd87d | 112 | ALIAS_P is non-zero if alias insns are to be included in the search. |
2613b5e6 | 113 | |
ab0bd049 DE |
114 | The result a pointer to the insn table entry, or NULL if the instruction |
115 | wasn't recognized. */ | |
116 | ||
117 | const CGEN_INSN * | |
c2009f4a DE |
118 | @arch@_cgen_lookup_insn (od, insn, insn_value, length, fields, alias_p) |
119 | CGEN_OPCODE_DESC od; | |
ab0bd049 | 120 | const CGEN_INSN *insn; |
fcea6f20 | 121 | CGEN_INSN_BYTES insn_value; |
ab0bd049 DE |
122 | int length; |
123 | CGEN_FIELDS *fields; | |
2613b5e6 | 124 | int alias_p; |
ab0bd049 | 125 | { |
fcea6f20 DE |
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 | |
ab0bd049 DE |
141 | |
142 | if (!insn) | |
143 | { | |
144 | const CGEN_INSN_LIST *insn_list; | |
145 | ||
fcea6f20 DE |
146 | #if CGEN_INT_INSN_P |
147 | cgen_put_insn_value (od, buf, length, insn_value); | |
148 | bufp = buf; | |
149 | base_insn = insn_value; /*???*/ | |
ab0bd049 | 150 | #else |
fcea6f20 DE |
151 | base_insn = cgen_get_insn_value (od, buf, length); |
152 | bufp = insn_value; | |
ab0bd049 DE |
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 | ||
fcea6f20 | 158 | insn_list = CGEN_DIS_LOOKUP_INSN (od, bufp, base_insn); |
ab0bd049 DE |
159 | while (insn_list != NULL) |
160 | { | |
161 | insn = insn_list->insn; | |
162 | ||
390bd87d DE |
163 | if (alias_p |
164 | || ! CGEN_INSN_ATTR (insn, CGEN_INSN_ALIAS)) | |
ab0bd049 | 165 | { |
390bd87d DE |
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 | { | |
fbc8134d | 171 | /* ??? 0 is passed for `pc' */ |
fcea6f20 | 172 | int elength = (*CGEN_EXTRACT_FN (insn)) (od, insn, info, |
fbc8134d DE |
173 | insn_value, fields, |
174 | (bfd_vma) 0); | |
2613b5e6 DE |
175 | if (elength > 0) |
176 | { | |
177 | /* sanity check */ | |
178 | if (length != 0 && length != elength) | |
179 | abort (); | |
180 | return insn; | |
181 | } | |
390bd87d | 182 | } |
ab0bd049 DE |
183 | } |
184 | ||
185 | insn_list = CGEN_DIS_NEXT_INSN (insn_list); | |
186 | } | |
187 | } | |
188 | else | |
189 | { | |
390bd87d DE |
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 (); | |
2613b5e6 DE |
194 | /* Sanity check: length must be correct. */ |
195 | if (length != CGEN_INSN_BITSIZE (insn)) | |
196 | abort (); | |
390bd87d | 197 | |
fbc8134d | 198 | /* ??? 0 is passed for `pc' */ |
fcea6f20 | 199 | length = (*CGEN_EXTRACT_FN (insn)) (od, insn, info, insn_value, fields, |
fbc8134d | 200 | (bfd_vma) 0); |
2613b5e6 DE |
201 | /* Sanity check: must succeed. |
202 | Could relax this later if it ever proves useful. */ | |
203 | if (length == 0) | |
204 | abort (); | |
205 | return insn; | |
ab0bd049 DE |
206 | } |
207 | ||
208 | return NULL; | |
209 | } | |
210 | ||
2613b5e6 | 211 | /* Fill in the operand instances used by INSN whose operands are FIELDS. |
bed9a23c | 212 | INDICES is a pointer to a buffer of MAX_OPERAND_INSTANCES ints to be filled |
2613b5e6 | 213 | in. */ |
ab0bd049 | 214 | |
2613b5e6 | 215 | void |
c2009f4a DE |
216 | @arch@_cgen_get_insn_operands (od, insn, fields, indices) |
217 | CGEN_OPCODE_DESC od; | |
2613b5e6 DE |
218 | const CGEN_INSN * insn; |
219 | const CGEN_FIELDS * fields; | |
ab0bd049 DE |
220 | int *indices; |
221 | { | |
ab0bd049 DE |
222 | const CGEN_OPERAND_INSTANCE *opinst; |
223 | int i; | |
224 | ||
ab0bd049 | 225 | for (i = 0, opinst = CGEN_INSN_OPERANDS (insn); |
b2f18612 DE |
226 | opinst != NULL |
227 | && CGEN_OPERAND_INSTANCE_TYPE (opinst) != CGEN_OPERAND_INSTANCE_END; | |
ab0bd049 DE |
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 | |
fbc8134d DE |
234 | indices[i] = @arch@_cgen_get_int_operand (CGEN_OPERAND_INDEX (op), |
235 | fields); | |
ab0bd049 | 236 | } |
2613b5e6 DE |
237 | } |
238 | ||
239 | /* Cover function to @arch@_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 | @arch@_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 * | |
c2009f4a DE |
248 | @arch@_cgen_lookup_get_insn_operands (od, insn, insn_value, length, indices) |
249 | CGEN_OPCODE_DESC od; | |
2613b5e6 | 250 | const CGEN_INSN *insn; |
fcea6f20 | 251 | CGEN_INSN_BYTES insn_value; |
2613b5e6 DE |
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. */ | |
c2009f4a | 259 | insn = @arch@_cgen_lookup_insn (od, insn, insn_value, length, &fields, |
2613b5e6 DE |
260 | insn != NULL); |
261 | if (! insn) | |
262 | return NULL; | |
ab0bd049 | 263 | |
c2009f4a | 264 | @arch@_cgen_get_insn_operands (od, insn, &fields, indices); |
ab0bd049 DE |
265 | return insn; |
266 | } |