* cgen-asm.c (cgen_current_opcode_table): Renamed from ..._data.
[external/binutils.git] / opcodes / cgen-asm.c
1 /* CGEN generic assembler support code.
2
3    Copyright (C) 1996, 1997, 1998 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 "sysdep.h"
22 #include <stdio.h>
23 #include <ctype.h>
24 #include "ansidecl.h"
25 #include "libiberty.h"
26 #include "bfd.h"
27 #include "symcat.h"
28 #include "opcode/cgen.h"
29
30 /* Operand parsing callback.  */
31 const char * (*cgen_parse_operand_fn)
32      PARAMS ((enum cgen_parse_operand_type, const char **, int, int,
33               enum cgen_parse_operand_result *, bfd_vma *));
34
35 /* This is not published as part of the public interface so we don't
36    declare this in cgen.h.  */
37 extern CGEN_OPCODE_TABLE *cgen_current_opcode_table;
38
39 /* Assembler instruction hash table.  */
40 static CGEN_INSN_LIST **asm_hash_table;
41 static CGEN_INSN_LIST *asm_hash_table_entries;
42
43 /* Called once at startup and whenever machine/endian change.  */
44
45 void
46 cgen_asm_init ()
47 {
48   if (asm_hash_table)
49     {
50       free (asm_hash_table);
51       free (asm_hash_table_entries);
52       asm_hash_table = NULL;
53       asm_hash_table_entries = NULL;
54     }
55 }
56
57 /* Called whenever starting to parse an insn.  */
58
59 void
60 cgen_init_parse_operand ()
61 {
62   /* This tells the callback to re-initialize.  */
63   (void) (*cgen_parse_operand_fn) (CGEN_PARSE_OPERAND_INIT, NULL, 0, 0,
64                                    NULL, NULL);
65 }
66
67 /* Subroutine of build_asm_hash_table to add INSNS to the hash table.
68
69    COUNT is the number of elements in INSNS.
70    ENTSIZE is sizeof (CGEN_INSN) for the target.
71    This is a bit tricky as the size of the attribute member of CGEN_INSN
72    is variable among architectures.  This code could be moved to
73    cgen-asm.in, but I prefer to keep it here for now.
74    OTABLE is the opcode table.
75    HTABLE points to the hash table.
76    HENTBUF is a pointer to sufficiently large buffer of hash entries.
77    The result is a pointer to the next entry to use.
78
79    The table is scanned backwards as additions are made to the front of the
80    list and we want earlier ones to be prefered.  */
81
82 static CGEN_INSN_LIST *
83 hash_insn_array (insns, count, entsize, otable, htable, hentbuf)
84      const CGEN_INSN * insns;
85      int count;
86      int entsize;
87      const CGEN_OPCODE_TABLE * otable;
88      CGEN_INSN_LIST ** htable;
89      CGEN_INSN_LIST * hentbuf;
90 {
91   const CGEN_INSN * insn;
92
93   for (insn = (CGEN_INSN *) ((char *) insns + entsize * (count - 1));
94        insn >= insns;
95        insn = (CGEN_INSN *) ((char *) insn - entsize), ++ hentbuf)
96     {
97       unsigned int hash;
98
99       if (! (*otable->asm_hash_p) (insn))
100         continue;
101       hash = (*otable->asm_hash) (CGEN_INSN_MNEMONIC (insn));
102       hentbuf->next = htable[hash];
103       hentbuf->insn = insn;
104       htable[hash] = hentbuf;
105     }
106
107   return hentbuf;
108 }
109
110 /* Subroutine of build_asm_hash_table to add INSNS to the hash table.
111    This function is identical to hash_insn_array except the insns are
112    in a list.  */
113
114 static CGEN_INSN_LIST *
115 hash_insn_list (insns, otable, htable, hentbuf)
116      const CGEN_INSN_LIST * insns;
117      const CGEN_OPCODE_TABLE * otable;
118      CGEN_INSN_LIST ** htable;
119      CGEN_INSN_LIST * hentbuf;
120 {
121   const CGEN_INSN_LIST * ilist;
122
123   for (ilist = insns; ilist != NULL; ilist = ilist->next, ++ hentbuf)
124     {
125       unsigned int hash;
126
127       if (! (*otable->asm_hash_p) (ilist->insn))
128         continue;
129       hash = (*otable->asm_hash) (CGEN_INSN_MNEMONIC (ilist->insn));
130       hentbuf->next = htable[hash];
131       hentbuf->insn = ilist->insn;
132       asm_hash_table[hash] = hentbuf;
133     }
134
135   return hentbuf;
136 }
137
138 /* Build the assembler instruction hash table.  */
139
140 static void
141 build_asm_hash_table ()
142 {
143   int count = cgen_insn_count () + cgen_macro_insn_count ();
144   CGEN_OPCODE_TABLE *opcode_table = cgen_current_opcode_table;
145   CGEN_INSN_TABLE *insn_table = opcode_table->insn_table;
146   CGEN_INSN_TABLE *macro_insn_table = opcode_table->macro_insn_table;
147   unsigned int hash_size = opcode_table->asm_hash_table_size;
148   CGEN_INSN_LIST *hash_entry_buf;
149
150   /* The space allocated for the hash table consists of two parts:
151      the hash table and the hash lists.  */
152
153   asm_hash_table = (CGEN_INSN_LIST **)
154     xmalloc (hash_size * sizeof (CGEN_INSN_LIST *));
155   memset (asm_hash_table, 0, hash_size * sizeof (CGEN_INSN_LIST *));
156   asm_hash_table_entries = hash_entry_buf = (CGEN_INSN_LIST *)
157     xmalloc (count * sizeof (CGEN_INSN_LIST));
158
159   /* Add compiled in insns.
160      Don't include the first one as it is a reserved entry.  */
161   /* ??? It was the end of all hash chains, and also the special
162      "illegal insn" marker.  May be able to do it differently now.  */
163
164   hash_entry_buf = hash_insn_array ((CGEN_INSN *) ((char *) insn_table->init_entries
165                                                    + insn_table->entry_size),
166                                     insn_table->num_init_entries - 1,
167                                     insn_table->entry_size,
168                                     opcode_table, asm_hash_table, hash_entry_buf);
169
170   /* Add compiled in macro-insns.  */
171
172   hash_entry_buf = hash_insn_array (macro_insn_table->init_entries,
173                                     macro_insn_table->num_init_entries,
174                                     macro_insn_table->entry_size,
175                                     opcode_table, asm_hash_table, hash_entry_buf);
176
177   /* Add runtime added insns.
178      Later added insns will be prefered over earlier ones.  */
179
180   hash_entry_buf = hash_insn_list (insn_table->new_entries, opcode_table,
181                                    asm_hash_table, hash_entry_buf);
182
183   /* Add runtime added macro-insns.  */
184
185   hash_insn_list (macro_insn_table->new_entries,
186                   opcode_table, asm_hash_table, hash_entry_buf);
187 }
188
189 /* Return the first entry in the hash list for INSN.
190    ??? Of course it would be better to pass in a pointer to the
191    opcode data structure, rather than reference a global.  Later.  */
192
193 CGEN_INSN_LIST *
194 cgen_asm_lookup_insn (insn)
195      const char *insn;
196 {
197   unsigned int hash;
198
199   if (asm_hash_table == NULL)
200     build_asm_hash_table ();
201
202   hash = (*cgen_current_opcode_table->asm_hash) (insn);
203   return asm_hash_table[hash];
204 }
205 \f
206 /* Keyword parser.
207    The result is NULL upon success or an error message.
208    If successful, *STRP is updated to point passed the keyword.
209
210    ??? At present we have a static notion of how to pick out a keyword.
211    Later we can allow a target to customize this if necessary [say by
212    recording something in the keyword table].  */
213
214 const char *
215 cgen_parse_keyword (strp, keyword_table, valuep)
216      const char **strp;
217      CGEN_KEYWORD *keyword_table;
218      long *valuep;
219 {
220   const CGEN_KEYWORD_ENTRY *ke;
221   char buf[256];
222   const char *p,*start;
223
224   p = start = *strp;
225
226   /* Allow any first character.
227      Note that this allows recognizing ",a" for the annul flag in sparc
228      even though "," is subsequently not a valid keyword char.  */
229   if (*p)
230     ++p;
231
232   /* Now allow letters, digits, and _.  */
233   while (((p - start) < (int) sizeof (buf))
234          && (isalnum ((unsigned char) *p) || *p == '_'))
235     ++p;
236
237   if (p - start >= (int) sizeof (buf))
238     return "unrecognized keyword/register name";
239
240   memcpy (buf, start, p - start);
241   buf[p - start] = 0;
242
243   ke = cgen_keyword_lookup_name (keyword_table, buf);
244
245   if (ke != NULL)
246     {
247       *valuep = ke->value;
248       /* Don't advance pointer if we recognized the null keyword.  */
249       if (ke->name[0] != 0)
250         *strp = p;
251       return NULL;
252     }
253
254   return "unrecognized keyword/register name";
255 }
256
257 /* Signed integer parser.  */
258
259 const char *
260 cgen_parse_signed_integer (strp, opindex, valuep)
261      const char **strp;
262      int opindex;
263      long *valuep;
264 {
265   bfd_vma value;
266   enum cgen_parse_operand_result result;
267   const char *errmsg;
268
269   errmsg = (*cgen_parse_operand_fn) (CGEN_PARSE_OPERAND_INTEGER, strp,
270                                      opindex, BFD_RELOC_NONE,
271                                      &result, &value);
272   /* FIXME: Examine `result'.  */
273   if (!errmsg)
274     *valuep = value;
275   return errmsg;
276 }
277
278 /* Unsigned integer parser.  */
279
280 const char *
281 cgen_parse_unsigned_integer (strp, opindex, valuep)
282      const char **strp;
283      int opindex;
284      unsigned long *valuep;
285 {
286   bfd_vma value;
287   enum cgen_parse_operand_result result;
288   const char *errmsg;
289
290   errmsg = (*cgen_parse_operand_fn) (CGEN_PARSE_OPERAND_INTEGER, strp,
291                                      opindex, BFD_RELOC_NONE,
292                                      &result, &value);
293   /* FIXME: Examine `result'.  */
294   if (!errmsg)
295     *valuep = value;
296   return errmsg;
297 }
298
299 /* Address parser.  */
300
301 const char *
302 cgen_parse_address (strp, opindex, opinfo, resultp, valuep)
303      const char **strp;
304      int opindex;
305      int opinfo;
306      enum cgen_parse_operand_result *resultp;
307      long *valuep;
308 {
309   bfd_vma value;
310   enum cgen_parse_operand_result result_type;
311   const char *errmsg;
312
313   errmsg = (*cgen_parse_operand_fn) (CGEN_PARSE_OPERAND_ADDRESS, strp,
314                                      opindex, opinfo,
315                                      &result_type, &value);
316   /* FIXME: Examine `result'.  */
317   if (!errmsg)
318     {
319       if (resultp != NULL)
320         *resultp = result_type;
321       *valuep = value;
322     }
323   return errmsg;
324 }
325 \f
326 /* Signed integer validation routine.  */
327
328 const char *
329 cgen_validate_signed_integer (value, min, max)
330      long value, min, max;
331 {
332   if (value < min || value > max)
333     {
334       const char *err =
335         "operand out of range (%ld not between %ld and %ld)";
336       static char buf[100];
337
338       sprintf (buf, err, value, min, max);
339       return buf;
340     }
341
342   return NULL;
343 }
344
345 /* Unsigned integer validation routine.
346    Supplying `min' here may seem unnecessary, but we also want to handle
347    cases where min != 0 (and max > LONG_MAX).  */
348
349 const char *
350 cgen_validate_unsigned_integer (value, min, max)
351      unsigned long value, min, max;
352 {
353   if (value < min || value > max)
354     {
355       const char *err =
356         "operand out of range (%lu not between %lu and %lu)";
357       static char buf[100];
358
359       sprintf (buf, err, value, min, max);
360       return buf;
361     }
362
363   return NULL;
364 }