Commit | Line | Data |
---|---|---|
9c03036a DE |
1 | /* CGEN generic opcode support. |
2 | ||
3 | Copyright (C) 1996, 1997 Free Software Foundation, Inc. | |
4 | ||
5 | This file is part of the GNU Binutils and GDB, the GNU debugger. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2, or (at your option) | |
10 | any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License along | |
18 | with this program; if not, write to the Free Software Foundation, Inc., | |
19 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |
20 | ||
21 | #include "config.h" | |
22 | #include <stdio.h> | |
23 | #ifdef HAVE_STRING_H | |
24 | #include <string.h> | |
25 | #endif | |
26 | #ifdef HAVE_STRINGS_H | |
27 | #include <strings.h> | |
28 | #endif | |
29 | #include "ansidecl.h" | |
30 | #include "libiberty.h" | |
31 | #include "bfd.h" | |
32 | #include "opcode/cgen.h" | |
33 | ||
34 | /* State variables. | |
35 | These record the state of the currently selected cpu, machine, endian, etc. | |
36 | They are set by cgen_set_cpu. */ | |
37 | ||
38 | /* Current opcode data. */ | |
39 | CGEN_OPCODE_DATA *cgen_current_opcode_data; | |
40 | ||
41 | /* Current machine (a la BFD machine number). */ | |
42 | int cgen_current_mach; | |
43 | ||
44 | /* Current endian. */ | |
45 | enum cgen_endian cgen_current_endian = CGEN_ENDIAN_UNKNOWN; | |
46 | ||
47 | void | |
48 | cgen_set_cpu (data, mach, endian) | |
49 | CGEN_OPCODE_DATA *data; | |
50 | int mach; | |
51 | enum cgen_endian endian; | |
52 | { | |
53 | cgen_current_opcode_data = data; | |
54 | cgen_current_mach = mach; | |
55 | cgen_current_endian = endian; | |
56 | ||
57 | #if 0 /* This isn't done here because it would put assembler support in the | |
58 | disassembler, etc. The caller is required to call these after calling | |
59 | us. */ | |
60 | /* Reset the hash tables. */ | |
61 | cgen_asm_init (); | |
62 | cgen_dis_init (); | |
63 | #endif | |
64 | } | |
65 | \f | |
66 | static unsigned int hash_keyword_name | |
67 | PARAMS ((const struct cgen_keyword *, const char *)); | |
68 | static unsigned int hash_keyword_value | |
69 | PARAMS ((const struct cgen_keyword *, int)); | |
70 | static void build_keyword_hash_tables | |
71 | PARAMS ((struct cgen_keyword *)); | |
72 | ||
73 | /* Return number of hash table entries to use for N elements. */ | |
74 | #define KEYWORD_HASH_SIZE(n) ((n) <= 31 ? 17 : 31) | |
75 | ||
76 | /* Look up *NAMEP in the keyword table KT. | |
77 | The result is the keyword entry or NULL if not found. */ | |
78 | ||
79 | const struct cgen_keyword_entry * | |
80 | cgen_keyword_lookup_name (kt, name) | |
81 | struct cgen_keyword *kt; | |
82 | const char *name; | |
83 | { | |
84 | const struct cgen_keyword_entry *ke; | |
85 | const char *p,*n; | |
86 | ||
87 | if (kt->name_hash_table == NULL) | |
88 | build_keyword_hash_tables (kt); | |
89 | ||
90 | ke = kt->name_hash_table[hash_keyword_name (kt, name)]; | |
91 | ||
92 | /* We do case insensitive comparisons. | |
93 | If that ever becomes a problem, add an attribute that denotes | |
94 | "do case sensitive comparisons". */ | |
95 | ||
96 | while (ke != NULL) | |
97 | { | |
98 | n = name; | |
99 | p = ke->name; | |
100 | ||
101 | while (*p | |
102 | && (*p == *n | |
103 | || (isalpha (*p) && tolower (*p) == tolower (*n)))) | |
104 | ++n, ++p; | |
105 | ||
106 | if (!*p && !*n) | |
107 | return ke; | |
108 | ||
109 | ke = ke->next_name; | |
110 | } | |
111 | ||
112 | return NULL; | |
113 | } | |
114 | ||
115 | /* Look up VALUE in the keyword table KT. | |
116 | The result is the keyword entry or NULL if not found. */ | |
117 | ||
118 | const struct cgen_keyword_entry * | |
119 | cgen_keyword_lookup_value (kt, value) | |
120 | struct cgen_keyword *kt; | |
121 | int value; | |
122 | { | |
123 | const struct cgen_keyword_entry *ke; | |
124 | ||
125 | if (kt->name_hash_table == NULL) | |
126 | build_keyword_hash_tables (kt); | |
127 | ||
128 | ke = kt->value_hash_table[hash_keyword_value (kt, value)]; | |
129 | ||
130 | while (ke != NULL) | |
131 | { | |
132 | if (value == ke->value) | |
133 | return ke; | |
134 | ke = ke->next_value; | |
135 | } | |
136 | ||
137 | return NULL; | |
138 | } | |
139 | ||
140 | /* Add an entry to a keyword table. */ | |
141 | ||
142 | void | |
143 | cgen_keyword_add (kt, ke) | |
144 | struct cgen_keyword *kt; | |
145 | struct cgen_keyword_entry *ke; | |
146 | { | |
147 | unsigned int hash; | |
148 | ||
149 | if (kt->name_hash_table == NULL) | |
150 | build_keyword_hash_tables (kt); | |
151 | ||
152 | hash = hash_keyword_name (kt, ke->name); | |
153 | ke->next_name = kt->name_hash_table[hash]; | |
154 | kt->name_hash_table[hash] = ke; | |
155 | ||
156 | hash = hash_keyword_value (kt, ke->value); | |
157 | ke->next_value = kt->value_hash_table[hash]; | |
158 | kt->value_hash_table[hash] = ke; | |
159 | } | |
160 | ||
161 | /* FIXME: Need function to return count of keywords. */ | |
162 | ||
163 | /* Initialize a keyword table search. | |
164 | SPEC is a specification of what to search for. | |
165 | A value of NULL means to find every keyword. | |
166 | Currently NULL is the only acceptable value [further specification | |
167 | deferred]. | |
168 | The result is an opaque data item used to record the search status. | |
169 | It is passed to each call to cgen_keyword_search_next. */ | |
170 | ||
171 | struct cgen_keyword_search | |
172 | cgen_keyword_search_init (kt, spec) | |
173 | struct cgen_keyword *kt; | |
174 | const char *spec; | |
175 | { | |
176 | struct cgen_keyword_search search; | |
177 | ||
178 | /* FIXME: Need to specify format of PARAMS. */ | |
179 | if (spec != NULL) | |
180 | abort (); | |
181 | ||
182 | if (kt->name_hash_table == NULL) | |
183 | build_keyword_hash_tables (kt); | |
184 | ||
185 | search.table = kt; | |
186 | search.spec = spec; | |
187 | search.current_hash = 0; | |
188 | search.current_entry = NULL; | |
189 | return search; | |
190 | } | |
191 | ||
192 | /* Return the next keyword specified by SEARCH. | |
193 | The result is the next entry or NULL if there are no more. */ | |
194 | ||
195 | const struct cgen_keyword_entry * | |
196 | cgen_keyword_search_next (search) | |
197 | struct cgen_keyword_search *search; | |
198 | { | |
199 | const struct cgen_keyword_entry *ke; | |
200 | ||
201 | /* Has search finished? */ | |
202 | if (search->current_hash == search->table->hash_table_size) | |
203 | return NULL; | |
204 | ||
205 | /* Search in progress? */ | |
206 | if (search->current_entry != NULL | |
207 | /* Anything left on this hash chain? */ | |
208 | && search->current_entry->next_name != NULL) | |
209 | { | |
210 | search->current_entry = search->current_entry->next_name; | |
211 | return search->current_entry; | |
212 | } | |
213 | ||
214 | /* Move to next hash chain [unless we haven't started yet]. */ | |
215 | if (search->current_entry != NULL) | |
216 | ++search->current_hash; | |
217 | ||
218 | while (search->current_hash < search->table->hash_table_size) | |
219 | { | |
220 | search->current_entry = search->table->name_hash_table[search->current_hash]; | |
221 | if (search->current_entry != NULL) | |
222 | return search->current_entry; | |
223 | ++search->current_hash; | |
224 | } | |
225 | ||
226 | return NULL; | |
227 | } | |
228 | ||
229 | /* Return first entry in hash chain for NAME. */ | |
230 | ||
231 | static unsigned int | |
232 | hash_keyword_name (kt, name) | |
233 | const struct cgen_keyword *kt; | |
234 | const char *name; | |
235 | { | |
236 | unsigned int hash; | |
237 | ||
238 | for (hash = 0; *name; ++name) | |
70bb1aa1 | 239 | hash = (hash * 97) + (unsigned char) *name; |
9c03036a DE |
240 | return hash % kt->hash_table_size; |
241 | } | |
242 | ||
243 | /* Return first entry in hash chain for VALUE. */ | |
244 | ||
245 | static unsigned int | |
246 | hash_keyword_value (kt, value) | |
247 | const struct cgen_keyword *kt; | |
248 | int value; | |
249 | { | |
250 | return value % kt->hash_table_size; | |
251 | } | |
252 | ||
253 | /* Build a keyword table's hash tables. | |
254 | We probably needn't build the value hash table for the assembler when | |
255 | we're using the disassembler, but we keep things simple. */ | |
256 | ||
257 | static void | |
258 | build_keyword_hash_tables (kt) | |
259 | struct cgen_keyword *kt; | |
260 | { | |
261 | int i; | |
262 | /* Use the number of compiled in entries as an estimate for the | |
263 | typical sized table [not too many added at runtime]. */ | |
264 | unsigned int size = KEYWORD_HASH_SIZE (kt->num_init_entries); | |
265 | ||
266 | kt->hash_table_size = size; | |
267 | kt->name_hash_table = (struct cgen_keyword_entry **) | |
268 | xmalloc (size * sizeof (struct cgen_keyword_entry *)); | |
269 | memset (kt->name_hash_table, 0, size * sizeof (struct cgen_keyword_entry *)); | |
270 | kt->value_hash_table = (struct cgen_keyword_entry **) | |
271 | xmalloc (size * sizeof (struct cgen_keyword_entry *)); | |
272 | memset (kt->value_hash_table, 0, size * sizeof (struct cgen_keyword_entry *)); | |
273 | ||
274 | /* The table is scanned backwards as we want keywords appearing earlier to | |
275 | be prefered over later ones. */ | |
276 | for (i = kt->num_init_entries - 1; i >= 0; --i) | |
277 | cgen_keyword_add (kt, &kt->init_entries[i]); | |
278 | } | |
279 | \f | |
280 | /* Hardware support. */ | |
281 | ||
282 | CGEN_HW_ENTRY * | |
283 | cgen_hw_lookup (name) | |
284 | const char *name; | |
285 | { | |
286 | CGEN_HW_ENTRY *hw = cgen_current_opcode_data->hw_list; | |
287 | ||
288 | while (hw != NULL) | |
289 | { | |
290 | if (strcmp (name, hw->name) == 0) | |
291 | return hw; | |
292 | hw = hw->next; | |
293 | } | |
294 | ||
295 | return NULL; | |
296 | } | |
297 | \f | |
298 | /* Instruction support. */ | |
299 | ||
300 | /* Return number of instructions. This includes any added at runtime. */ | |
301 | ||
302 | int | |
303 | cgen_insn_count () | |
304 | { | |
305 | int count = cgen_current_opcode_data->insn_table->num_init_entries; | |
306 | CGEN_INSN_LIST *insn = cgen_current_opcode_data->insn_table->new_entries; | |
307 | ||
308 | for ( ; insn != NULL; insn = insn->next) | |
309 | ++count; | |
310 | ||
311 | return count; | |
312 | } |