Commit | Line | Data |
---|---|---|
9c03036a DE |
1 | /* Assembler interface for targets using CGEN. -*- C -*- |
2 | CGEN: Cpu tools GENerator | |
3 | ||
4 | This file is used to generate m32r-asm.c. | |
5 | ||
6 | Copyright (C) 1996, 1997 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 | ||
23cf992f | 24 | #include "sysdep.h" |
9c03036a DE |
25 | #include <ctype.h> |
26 | #include <stdio.h> | |
27 | #include "ansidecl.h" | |
28 | #include "bfd.h" | |
29 | #include "m32r-opc.h" | |
30 | ||
31 | /* ??? The layout of this stuff is still work in progress. | |
32 | For speed in assembly/disassembly, we use inline functions. That of course | |
33 | will only work for GCC. When this stuff is finished, we can decide whether | |
34 | to keep the inline functions (and only get the performance increase when | |
35 | compiled with GCC), or switch to macros, or use something else. | |
36 | */ | |
37 | ||
23cf992f NC |
38 | static const char * parse_insn_normal |
39 | PARAMS ((const CGEN_INSN *, const char **, CGEN_FIELDS *)); | |
9c03036a | 40 | static void insert_insn_normal |
23cf992f | 41 | PARAMS ((const CGEN_INSN *, CGEN_FIELDS *, cgen_insn_t *)); |
9c03036a DE |
42 | \f |
43 | /* Default insertion routine. | |
44 | ||
45 | SHIFT is negative for left shifts, positive for right shifts. | |
46 | All bits of VALUE to be inserted must be valid as we don't handle | |
47 | signed vs unsigned shifts. | |
48 | ||
49 | ATTRS is a mask of the boolean attributes. We don't need any at the | |
50 | moment, but for consistency with extract_normal we have them. */ | |
51 | ||
52 | /* FIXME: This duplicates functionality with bfd's howto table and | |
53 | bfd_install_relocation. */ | |
54 | /* FIXME: For architectures where insns can be representable as ints, | |
55 | store insn in `field' struct and add registers, etc. while parsing. */ | |
56 | ||
57 | static CGEN_INLINE void | |
58 | insert_normal (value, attrs, start, length, shift, total_length, buffer) | |
59 | long value; | |
60 | unsigned int attrs; | |
61 | int start, length, shift, total_length; | |
62 | char *buffer; | |
63 | { | |
64 | bfd_vma x; | |
65 | ||
66 | #if 0 /*def CGEN_INT_INSN*/ | |
67 | *buffer |= ((value & ((1 << length) - 1)) | |
68 | << (total_length - (start + length))); | |
69 | #else | |
70 | switch (total_length) | |
71 | { | |
72 | case 8: | |
73 | x = *(unsigned char *) buffer; | |
74 | break; | |
75 | case 16: | |
76 | if (CGEN_CURRENT_ENDIAN == CGEN_ENDIAN_BIG) | |
77 | x = bfd_getb16 (buffer); | |
78 | else | |
79 | x = bfd_getl16 (buffer); | |
80 | break; | |
81 | case 32: | |
82 | if (CGEN_CURRENT_ENDIAN == CGEN_ENDIAN_BIG) | |
83 | x = bfd_getb32 (buffer); | |
84 | else | |
85 | x = bfd_getl32 (buffer); | |
86 | break; | |
87 | default : | |
88 | abort (); | |
89 | } | |
90 | ||
91 | if (shift < 0) | |
92 | value <<= -shift; | |
93 | else | |
94 | value >>= shift; | |
95 | ||
96 | x |= ((value & ((1 << length) - 1)) | |
97 | << (total_length - (start + length))); | |
98 | ||
99 | switch (total_length) | |
100 | { | |
101 | case 8: | |
102 | *buffer = value; | |
103 | break; | |
104 | case 16: | |
105 | if (CGEN_CURRENT_ENDIAN == CGEN_ENDIAN_BIG) | |
106 | bfd_putb16 (x, buffer); | |
107 | else | |
108 | bfd_putl16 (x, buffer); | |
109 | break; | |
110 | case 32: | |
111 | if (CGEN_CURRENT_ENDIAN == CGEN_ENDIAN_BIG) | |
112 | bfd_putb32 (x, buffer); | |
113 | else | |
114 | bfd_putl32 (x, buffer); | |
115 | break; | |
116 | default : | |
117 | abort (); | |
118 | } | |
119 | #endif | |
120 | } | |
121 | \f | |
122 | /* -- assembler routines inserted here */ | |
9c03036a DE |
123 | \f |
124 | /* Default insn parser. | |
125 | ||
126 | The syntax string is scanned and operands are parsed and stored in FIELDS. | |
127 | Relocs are queued as we go via other callbacks. | |
128 | ||
129 | ??? Note that this is currently an all-or-nothing parser. If we fail to | |
130 | parse the instruction, we return 0 and the caller will start over from | |
131 | the beginning. Backtracking will be necessary in parsing subexpressions, | |
132 | but that can be handled there. Not handling backtracking here may get | |
133 | expensive in the case of the m68k. Deal with later. | |
134 | ||
135 | Returns NULL for success, an error message for failure. | |
136 | */ | |
137 | ||
138 | static const char * | |
139 | parse_insn_normal (insn, strp, fields) | |
23cf992f | 140 | const CGEN_INSN *insn; |
9c03036a | 141 | const char **strp; |
23cf992f | 142 | CGEN_FIELDS *fields; |
9c03036a | 143 | { |
23cf992f | 144 | const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn); |
9c03036a DE |
145 | const char *str = *strp; |
146 | const char *errmsg; | |
23cf992f | 147 | const char *p; |
9c03036a DE |
148 | const unsigned char *syn; |
149 | #ifdef CGEN_MNEMONIC_OPERANDS | |
150 | int past_opcode_p; | |
151 | #endif | |
152 | ||
23cf992f NC |
153 | /* For now we assume the mnemonic is first (there are no leading operands). |
154 | We can parse it without needing to set up operand parsing. */ | |
155 | p = CGEN_INSN_MNEMONIC (insn); | |
156 | while (*p && *p == *str) | |
157 | ++p, ++str; | |
158 | if (*p || (*str && !isspace (*str))) | |
159 | return "unrecognized instruction"; | |
9c03036a DE |
160 | |
161 | CGEN_INIT_PARSE (); | |
a394e326 | 162 | cgen_init_parse_operand (); |
9c03036a DE |
163 | #ifdef CGEN_MNEMONIC_OPERANDS |
164 | past_opcode_p = 0; | |
165 | #endif | |
166 | ||
167 | /* We don't check for (*str != '\0') here because we want to parse | |
168 | any trailing fake arguments in the syntax string. */ | |
23cf992f NC |
169 | syn = CGEN_SYNTAX_STRING (CGEN_INSN_SYNTAX (insn)); |
170 | /* Mnemonics come first for now, ensure valid string. */ | |
171 | if (! CGEN_SYNTAX_MNEMONIC_P (*syn)) | |
172 | abort (); | |
173 | ++syn; | |
174 | while (*syn != 0) | |
9c03036a DE |
175 | { |
176 | /* Non operand chars must match exactly. */ | |
177 | /* FIXME: Need to better handle whitespace. */ | |
178 | if (CGEN_SYNTAX_CHAR_P (*syn)) | |
179 | { | |
180 | if (*str == CGEN_SYNTAX_CHAR (*syn)) | |
181 | { | |
182 | #ifdef CGEN_MNEMONIC_OPERANDS | |
183 | if (*syn == ' ') | |
184 | past_opcode_p = 1; | |
185 | #endif | |
186 | ++syn; | |
187 | ++str; | |
188 | } | |
189 | else | |
190 | { | |
191 | /* Syntax char didn't match. Can't be this insn. */ | |
192 | /* FIXME: would like to return "expected char `c'" */ | |
193 | return "syntax error"; | |
194 | } | |
195 | continue; | |
196 | } | |
197 | ||
198 | /* We have an operand of some sort. */ | |
199 | errmsg = m32r_cgen_parse_operand (CGEN_SYNTAX_FIELD (*syn), | |
200 | &str, fields); | |
201 | if (errmsg) | |
202 | return errmsg; | |
203 | ||
204 | /* Done with this operand, continue with next one. */ | |
205 | ++syn; | |
206 | } | |
207 | ||
208 | /* If we're at the end of the syntax string, we're done. */ | |
209 | if (*syn == '\0') | |
210 | { | |
211 | /* FIXME: For the moment we assume a valid `str' can only contain | |
212 | blanks now. IE: We needn't try again with a longer version of | |
213 | the insn and it is assumed that longer versions of insns appear | |
214 | before shorter ones (eg: lsr r2,r3,1 vs lsr r2,r3). */ | |
215 | while (isspace (*str)) | |
216 | ++str; | |
217 | ||
218 | if (*str != '\0') | |
219 | return "junk at end of line"; /* FIXME: would like to include `str' */ | |
220 | ||
221 | return NULL; | |
222 | } | |
223 | ||
224 | /* We couldn't parse it. */ | |
225 | return "unrecognized instruction"; | |
226 | } | |
227 | ||
228 | /* Default insn builder (insert handler). | |
229 | The instruction is recorded in target byte order. */ | |
230 | ||
231 | static void | |
232 | insert_insn_normal (insn, fields, buffer) | |
23cf992f NC |
233 | const CGEN_INSN *insn; |
234 | CGEN_FIELDS *fields; | |
9c03036a DE |
235 | cgen_insn_t *buffer; |
236 | { | |
23cf992f | 237 | const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn); |
9c03036a DE |
238 | bfd_vma value; |
239 | const unsigned char *syn; | |
240 | ||
241 | CGEN_INIT_INSERT (); | |
23cf992f | 242 | value = CGEN_INSN_VALUE (insn); |
9c03036a DE |
243 | |
244 | /* If we're recording insns as numbers (rather than a string of bytes), | |
245 | target byte order handling is deferred until later. */ | |
246 | #undef min | |
247 | #define min(a,b) ((a) < (b) ? (a) : (b)) | |
248 | #if 0 /*def CGEN_INT_INSN*/ | |
249 | *buffer = value; | |
250 | #else | |
251 | switch (min (CGEN_BASE_INSN_BITSIZE, CGEN_FIELDS_BITSIZE (fields))) | |
252 | { | |
253 | case 8: | |
254 | *buffer = value; | |
255 | break; | |
256 | case 16: | |
257 | if (CGEN_CURRENT_ENDIAN == CGEN_ENDIAN_BIG) | |
258 | bfd_putb16 (value, (char *) buffer); | |
259 | else | |
260 | bfd_putl16 (value, (char *) buffer); | |
261 | break; | |
262 | case 32: | |
263 | if (CGEN_CURRENT_ENDIAN == CGEN_ENDIAN_BIG) | |
264 | bfd_putb32 (value, (char *) buffer); | |
265 | else | |
266 | bfd_putl32 (value, (char *) buffer); | |
267 | break; | |
268 | default: | |
269 | abort (); | |
270 | } | |
271 | #endif | |
272 | ||
273 | /* ??? Rather than scanning the syntax string again, we could store | |
274 | in `fields' a null terminated list of the fields that are present. */ | |
275 | ||
23cf992f | 276 | for (syn = CGEN_SYNTAX_STRING (syntax); *syn != '\0'; ++syn) |
9c03036a DE |
277 | { |
278 | if (CGEN_SYNTAX_CHAR_P (*syn)) | |
279 | continue; | |
280 | ||
281 | m32r_cgen_insert_operand (CGEN_SYNTAX_FIELD (*syn), fields, buffer); | |
282 | } | |
283 | } | |
284 | \f | |
285 | /* Main entry point. | |
286 | This routine is called for each instruction to be assembled. | |
287 | STR points to the insn to be assembled. | |
288 | We assume all necessary tables have been initialized. | |
289 | The result is a pointer to the insn's entry in the opcode table, | |
290 | or NULL if an error occured (an error message will have already been | |
291 | printed). */ | |
292 | ||
23cf992f | 293 | const CGEN_INSN * |
5b3b8cb0 | 294 | m32r_cgen_assemble_insn (str, fields, buf, errmsg) |
9c03036a | 295 | const char *str; |
23cf992f | 296 | CGEN_FIELDS *fields; |
9c03036a | 297 | cgen_insn_t *buf; |
5b3b8cb0 | 298 | char **errmsg; |
9c03036a DE |
299 | { |
300 | const char *start; | |
301 | CGEN_INSN_LIST *ilist; | |
302 | ||
303 | /* Skip leading white space. */ | |
304 | while (isspace (*str)) | |
305 | ++str; | |
306 | ||
307 | /* The instructions are stored in hashed lists. | |
308 | Get the first in the list. */ | |
309 | ilist = CGEN_ASM_LOOKUP_INSN (str); | |
310 | ||
311 | /* Keep looking until we find a match. */ | |
312 | ||
313 | start = str; | |
314 | for ( ; ilist != NULL ; ilist = CGEN_ASM_NEXT_INSN (ilist)) | |
315 | { | |
23cf992f | 316 | const CGEN_INSN *insn = ilist->insn; |
9c03036a DE |
317 | |
318 | #if 0 /* not needed as unsupported opcodes shouldn't be in the hash lists */ | |
319 | /* Is this insn supported by the selected cpu? */ | |
320 | if (! m32r_cgen_insn_supported (insn)) | |
321 | continue; | |
322 | #endif | |
323 | ||
324 | #if 1 /* FIXME: wip */ | |
325 | /* If the RELAX attribute is set, this is an insn that shouldn't be | |
326 | chosen immediately. Instead, it is used during assembler/linker | |
327 | relaxation if possible. */ | |
328 | if (CGEN_INSN_ATTR (insn, CGEN_INSN_RELAX) != 0) | |
329 | continue; | |
330 | #endif | |
331 | ||
332 | str = start; | |
333 | ||
334 | /* Record a default length for the insn. This will get set to the | |
335 | correct value while parsing. */ | |
336 | /* FIXME: wip */ | |
337 | CGEN_FIELDS_BITSIZE (fields) = CGEN_INSN_BITSIZE (insn); | |
338 | ||
339 | /* ??? The extent to which moving the parse and insert handlers into | |
340 | this function (thus removing the function call) will speed things up | |
341 | is unclear. The simplicity and flexibility of the current scheme is | |
342 | appropriate for now. One could have the best of both worlds with | |
343 | inline functions but of course that would only work for gcc. Since | |
344 | we're machine generating some code we could do that here too. Maybe | |
345 | later. */ | |
346 | if (! (*CGEN_PARSE_FN (insn)) (insn, &str, fields)) | |
347 | { | |
348 | (*CGEN_INSERT_FN (insn)) (insn, fields, buf); | |
349 | /* It is up to the caller to actually output the insn and any | |
350 | queued relocs. */ | |
351 | return insn; | |
352 | } | |
353 | ||
354 | /* Try the next entry. */ | |
355 | } | |
356 | ||
5b3b8cb0 DE |
357 | /* FIXME: We can return a better error message than this. |
358 | Need to track why it failed and pick the right one. */ | |
359 | { | |
360 | static char errbuf[100]; | |
361 | sprintf (errbuf, "bad instruction `%.50s%s'", | |
362 | start, strlen (start) > 50 ? "..." : ""); | |
363 | *errmsg = errbuf; | |
364 | return NULL; | |
365 | } | |
9c03036a DE |
366 | } |
367 | \f | |
5b3b8cb0 DE |
368 | #if 0 /* This calls back to GAS which we can't do without care. */ |
369 | ||
9c03036a | 370 | /* Record each member of OPVALS in the assembler's symbol table. |
5b3b8cb0 DE |
371 | This lets GAS parse registers for us. |
372 | ??? Interesting idea but not currently used. */ | |
9c03036a | 373 | |
23cf992f NC |
374 | /* Record each member of OPVALS in the assembler's symbol table. |
375 | FIXME: Not currently used. */ | |
376 | ||
9c03036a DE |
377 | void |
378 | m32r_cgen_asm_hash_keywords (opvals) | |
23cf992f | 379 | CGEN_KEYWORD *opvals; |
9c03036a | 380 | { |
23cf992f NC |
381 | CGEN_KEYWORD_SEARCH search = cgen_keyword_search_init (opvals, NULL); |
382 | const CGEN_KEYWORD_ENTRY *ke; | |
9c03036a DE |
383 | |
384 | while ((ke = cgen_keyword_search_next (&search)) != NULL) | |
385 | { | |
386 | #if 0 /* Unnecessary, should be done in the search routine. */ | |
387 | if (! m32r_cgen_opval_supported (ke)) | |
388 | continue; | |
389 | #endif | |
390 | cgen_asm_record_register (ke->name, ke->value); | |
391 | } | |
392 | } | |
5b3b8cb0 DE |
393 | |
394 | #endif /* 0 */ |