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