cgen-asm.c revision 38889
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.  */
31const 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.  */
37extern CGEN_OPCODE_DATA *cgen_current_opcode_data;
38
39/* Assembler instruction hash table.  */
40static CGEN_INSN_LIST **asm_hash_table;
41
42/* Called once at startup and whenever machine/endian change.  */
43
44void
45cgen_asm_init ()
46{
47  if (asm_hash_table)
48    {
49      free (asm_hash_table);
50      asm_hash_table = NULL;
51    }
52}
53
54/* Called whenever starting to parse an insn.  */
55
56void
57cgen_init_parse_operand ()
58{
59  /* This tells the callback to re-initialize.  */
60  (void) (*cgen_parse_operand_fn) (CGEN_PARSE_OPERAND_INIT, NULL, 0, 0,
61				   NULL, NULL);
62}
63
64/* Build the assembler instruction hash table.  */
65
66static void
67build_asm_hash_table ()
68{
69  unsigned int hash;
70  int count = cgen_insn_count ();
71  CGEN_OPCODE_DATA *data = cgen_current_opcode_data;
72  CGEN_INSN_TABLE *insn_table = data->insn_table;
73  unsigned int entry_size = insn_table->entry_size;
74  unsigned int hash_size = insn_table->asm_hash_table_size;
75  const CGEN_INSN *insn;
76  CGEN_INSN_LIST *insn_lists,*new_insns;
77
78  /* The space allocated for the hash table consists of two parts:
79     the hash table and the hash lists.  */
80
81  asm_hash_table = (CGEN_INSN_LIST **)
82    xmalloc (hash_size * sizeof (CGEN_INSN_LIST *)
83	     + count * sizeof (CGEN_INSN_LIST));
84  memset (asm_hash_table, 0,
85	  hash_size * sizeof (CGEN_INSN_LIST *)
86	  + count * sizeof (CGEN_INSN_LIST));
87  insn_lists = (CGEN_INSN_LIST *) (asm_hash_table + hash_size);
88
89  /* Add compiled in insns.
90     The table is scanned backwards as later additions are inserted in
91     front of earlier ones and we want earlier ones to be prefered.
92     We stop at the first one as it is a reserved entry.
93     This is a bit tricky as the attribute member of CGEN_INSN is variable
94     among architectures.  This code could be moved to cgen-asm.in, but
95     I prefer to keep it here for now.  */
96
97  for (insn = (CGEN_INSN *)
98       ((char *) insn_table->init_entries
99	+ entry_size * (insn_table->num_init_entries - 1));
100       insn > insn_table->init_entries;
101       insn = (CGEN_INSN *) ((char *) insn - entry_size), ++insn_lists)
102    {
103      hash = (*insn_table->asm_hash) CGEN_INSN_MNEMONIC (insn);
104      insn_lists->next = asm_hash_table[hash];
105      insn_lists->insn = insn;
106      asm_hash_table[hash] = insn_lists;
107    }
108
109  /* Add runtime added insns.
110     ??? Currently later added insns will be prefered over earlier ones.
111     Not sure this is a bug or not.  */
112  for (new_insns = insn_table->new_entries;
113       new_insns != NULL;
114       new_insns = new_insns->next, ++insn_lists)
115    {
116      hash = (*insn_table->asm_hash) CGEN_INSN_MNEMONIC (new_insns->insn);
117      insn_lists->next = asm_hash_table[hash];
118      insn_lists->insn = new_insns->insn;
119      asm_hash_table[hash] = insn_lists;
120    }
121}
122
123/* Return the first entry in the hash list for INSN.  */
124
125CGEN_INSN_LIST *
126cgen_asm_lookup_insn (insn)
127     const char *insn;
128{
129  unsigned int hash;
130
131  if (asm_hash_table == NULL)
132    build_asm_hash_table ();
133
134  hash = (*cgen_current_opcode_data->insn_table->asm_hash) (insn);
135  return asm_hash_table[hash];
136}
137
138/* Keyword parser.
139   The result is NULL upon success or an error message.
140   If successful, *STRP is updated to point passed the keyword.
141
142   ??? At present we have a static notion of how to pick out a keyword.
143   Later we can allow a target to customize this if necessary [say by
144   recording something in the keyword table].  */
145
146const char *
147cgen_parse_keyword (strp, keyword_table, valuep)
148     const char **strp;
149     CGEN_KEYWORD *keyword_table;
150     long *valuep;
151{
152  const CGEN_KEYWORD_ENTRY *ke;
153  char buf[256];
154  const char *p,*start;
155
156  p = start = *strp;
157
158  /* Allow any first character.
159     Note that this allows recognizing ",a" for the annul flag in sparc
160     even though "," is subsequently not a valid keyword char.  */
161  if (*p)
162    ++p;
163
164  /* Now allow letters, digits, and _.  */
165  while (((p - start) < (int) sizeof (buf))
166	 && (isalnum ((unsigned char) *p) || *p == '_'))
167    ++p;
168
169  if (p - start >= (int) sizeof (buf))
170    return "unrecognized keyword/register name";
171
172  memcpy (buf, start, p - start);
173  buf[p - start] = 0;
174
175  ke = cgen_keyword_lookup_name (keyword_table, buf);
176
177  if (ke != NULL)
178    {
179      *valuep = ke->value;
180      /* Don't advance pointer if we recognized the null keyword.  */
181      if (ke->name[0] != 0)
182	*strp = p;
183      return NULL;
184    }
185
186  return "unrecognized keyword/register name";
187}
188
189/* Signed integer parser.  */
190
191const char *
192cgen_parse_signed_integer (strp, opindex, valuep)
193     const char **strp;
194     int opindex;
195     long *valuep;
196{
197  bfd_vma value;
198  enum cgen_parse_operand_result result;
199  const char *errmsg;
200
201  errmsg = (*cgen_parse_operand_fn) (CGEN_PARSE_OPERAND_INTEGER, strp,
202				     opindex, BFD_RELOC_NONE,
203				     &result, &value);
204  /* FIXME: Examine `result'.  */
205  if (!errmsg)
206    *valuep = value;
207  return errmsg;
208}
209
210/* Unsigned integer parser.  */
211
212const char *
213cgen_parse_unsigned_integer (strp, opindex, valuep)
214     const char **strp;
215     int opindex;
216     unsigned long *valuep;
217{
218  bfd_vma value;
219  enum cgen_parse_operand_result result;
220  const char *errmsg;
221
222  errmsg = (*cgen_parse_operand_fn) (CGEN_PARSE_OPERAND_INTEGER, strp,
223				     opindex, BFD_RELOC_NONE,
224				     &result, &value);
225  /* FIXME: Examine `result'.  */
226  if (!errmsg)
227    *valuep = value;
228  return errmsg;
229}
230
231/* Address parser.  */
232
233const char *
234cgen_parse_address (strp, opindex, opinfo, resultp, valuep)
235     const char **strp;
236     int opindex;
237     int opinfo;
238     enum cgen_parse_operand_result *resultp;
239     long *valuep;
240{
241  bfd_vma value;
242  enum cgen_parse_operand_result result_type;
243  const char *errmsg;
244
245  errmsg = (*cgen_parse_operand_fn) (CGEN_PARSE_OPERAND_ADDRESS, strp,
246				     opindex, opinfo,
247				     &result_type, &value);
248  /* FIXME: Examine `result'.  */
249  if (!errmsg)
250    {
251      if (resultp != NULL)
252	*resultp = result_type;
253      *valuep = value;
254    }
255  return errmsg;
256}
257
258/* Signed integer validation routine.  */
259
260const char *
261cgen_validate_signed_integer (value, min, max)
262     long value, min, max;
263{
264  if (value < min || value > max)
265    {
266      const char *err =
267	"operand out of range (%ld not between %ld and %ld)";
268      static char buf[100];
269
270      sprintf (buf, err, value, min, max);
271      return buf;
272    }
273
274  return NULL;
275}
276
277/* Unsigned integer validation routine.
278   Supplying `min' here may seem unnecessary, but we also want to handle
279   cases where min != 0 (and max > LONG_MAX).  */
280
281const char *
282cgen_validate_unsigned_integer (value, min, max)
283     unsigned long value, min, max;
284{
285  if (value < min || value > max)
286    {
287      const char *err =
288	"operand out of range (%lu not between %lu and %lu)";
289      static char buf[100];
290
291      sprintf (buf, err, value, min, max);
292      return buf;
293    }
294
295  return NULL;
296}
297