| /* itbl-ops.c |
| Copyright (C) 1997-2024 Free Software Foundation, Inc. |
| |
| This file is part of GAS, the GNU Assembler. |
| |
| GAS is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GAS is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GAS; see the file COPYING. If not, write to the Free |
| Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
| 02110-1301, USA. */ |
| |
| /*======================================================================*/ |
| /* |
| * Herein lies the support for dynamic specification of processor |
| * instructions and registers. Mnemonics, values, and formats for each |
| * instruction and register are specified in an ascii file consisting of |
| * table entries. The grammar for the table is defined in the document |
| * "Processor instruction table specification". |
| * |
| * Instructions use the gnu assembler syntax, with the addition of |
| * allowing mnemonics for register. |
| * Eg. "func $2,reg3,0x100,symbol ; comment" |
| * func - opcode name |
| * $n - register n |
| * reg3 - mnemonic for processor's register defined in table |
| * 0xddd..d - immediate value |
| * symbol - address of label or external symbol |
| * |
| * First, itbl_parse reads in the table of register and instruction |
| * names and formats, and builds a list of entries for each |
| * processor/type combination. lex and yacc are used to parse |
| * the entries in the table and call functions defined here to |
| * add each entry to our list. |
| * |
| * Then, when assembling or disassembling, these functions are called to |
| * 1) get information on a processor's registers and |
| * 2) assemble/disassemble an instruction. |
| * To assemble(disassemble) an instruction, the function |
| * itbl_assemble(itbl_disassemble) is called to search the list of |
| * instruction entries, and if a match is found, uses the format |
| * described in the instruction entry structure to complete the action. |
| * |
| * Eg. Suppose we have a Mips coprocessor "cop3" with data register "d2" |
| * and we want to define function "pig" which takes two operands. |
| * |
| * Given the table entries: |
| * "p3 insn pig 0x1:24-21 dreg:20-16 immed:15-0" |
| * "p3 dreg d2 0x2" |
| * and that the instruction encoding for coprocessor pz has encoding: |
| * #define MIPS_ENCODE_COP_NUM(z) ((0x21|(z<<1))<<25) |
| * #define ITBL_ENCODE_PNUM(pnum) MIPS_ENCODE_COP_NUM(pnum) |
| * |
| * a structure to describe the instruction might look something like: |
| * struct itbl_entry = { |
| * e_processor processor = e_p3 |
| * e_type type = e_insn |
| * char *name = "pig" |
| * uint value = 0x1 |
| * uint flags = 0 |
| * struct itbl_range range = 24-21 |
| * struct itbl_field *field = { |
| * e_type type = e_dreg |
| * struct itbl_range range = 20-16 |
| * struct itbl_field *next = { |
| * e_type type = e_immed |
| * struct itbl_range range = 15-0 |
| * struct itbl_field *next = 0 |
| * }; |
| * }; |
| * struct itbl_entry *next = 0 |
| * }; |
| * |
| * And the assembler instructions: |
| * "pig d2,0x100" |
| * "pig $2,0x100" |
| * |
| * would both assemble to the hex value: |
| * "0x4e220100" |
| * |
| */ |
| |
| #include "as.h" |
| #include "itbl-ops.h" |
| #include <itbl-parse.h> |
| |
| /* #define DEBUG */ |
| |
| #ifdef DEBUG |
| #include <assert.h> |
| #define ASSERT(x) gas_assert (x) |
| #define DBG(x) printf x |
| #else |
| #define ASSERT(x) |
| #define DBG(x) |
| #endif |
| |
| #ifndef min |
| #define min(a,b) (a<b?a:b) |
| #endif |
| |
| int itbl_have_entries = 0; |
| |
| /*======================================================================*/ |
| /* structures for keeping itbl format entries */ |
| |
| struct itbl_range { |
| int sbit; /* mask starting bit position */ |
| int ebit; /* mask ending bit position */ |
| }; |
| |
| struct itbl_field { |
| e_type type; /* dreg/creg/greg/immed/symb */ |
| struct itbl_range range; /* field's bitfield range within instruction */ |
| unsigned long flags; /* field flags */ |
| struct itbl_field *next; /* next field in list */ |
| }; |
| |
| /* These structures define the instructions and registers for a processor. |
| * If the type is an instruction, the structure defines the format of an |
| * instruction where the fields are the list of operands. |
| * The flags field below uses the same values as those defined in the |
| * gnu assembler and are machine specific. */ |
| struct itbl_entry { |
| e_processor processor; /* processor number */ |
| e_type type; /* dreg/creg/greg/insn */ |
| char *name; /* mnemonic name for insn/register */ |
| unsigned long value; /* opcode/instruction mask/register number */ |
| unsigned long flags; /* effects of the instruction */ |
| struct itbl_range range; /* bit range within instruction for value */ |
| struct itbl_field *fields; /* list of operand definitions (if any) */ |
| struct itbl_entry *next; /* next entry */ |
| }; |
| |
| /* local data and structures */ |
| |
| static int itbl_num_opcodes = 0; |
| /* Array of entries for each processor and entry type */ |
| static struct itbl_entry *entries[e_nprocs][e_ntypes]; |
| |
| /* local prototypes */ |
| static unsigned long build_opcode (struct itbl_entry *e); |
| static e_type get_type (int yytype); |
| static e_processor get_processor (int yyproc); |
| static struct itbl_entry **get_entries (e_processor processor, |
| e_type type); |
| static struct itbl_entry *find_entry_byname (e_processor processor, |
| e_type type, char *name); |
| static struct itbl_entry *find_entry_byval (e_processor processor, |
| e_type type, unsigned long val, struct itbl_range *r); |
| static struct itbl_entry *alloc_entry (e_processor processor, |
| e_type type, char *name, unsigned long value); |
| static unsigned long apply_range (unsigned long value, struct itbl_range r); |
| static unsigned long extract_range (unsigned long value, struct itbl_range r); |
| static struct itbl_field *alloc_field (e_type type, int sbit, |
| int ebit, unsigned long flags); |
| |
| /*======================================================================*/ |
| /* Interfaces to the parser */ |
| |
| /* Open the table and use lex and yacc to parse the entries. |
| * Return 1 for failure; 0 for success. */ |
| |
| int |
| itbl_parse (char *insntbl) |
| { |
| extern FILE *yyin; |
| extern int yyparse (void); |
| |
| yyin = fopen (insntbl, FOPEN_RT); |
| if (yyin == 0) |
| { |
| printf ("Can't open processor instruction specification file \"%s\"\n", |
| insntbl); |
| return 1; |
| } |
| |
| while (yyparse ()) |
| ; |
| |
| fclose (yyin); |
| itbl_have_entries = 1; |
| return 0; |
| } |
| |
| /* Add a register entry */ |
| |
| struct itbl_entry * |
| itbl_add_reg (int yyprocessor, int yytype, char *regname, |
| int regnum) |
| { |
| return alloc_entry (get_processor (yyprocessor), get_type (yytype), regname, |
| (unsigned long) regnum); |
| } |
| |
| /* Add an instruction entry */ |
| |
| struct itbl_entry * |
| itbl_add_insn (int yyprocessor, char *name, unsigned long value, |
| int sbit, int ebit, unsigned long flags) |
| { |
| struct itbl_entry *e; |
| e = alloc_entry (get_processor (yyprocessor), e_insn, name, value); |
| if (e) |
| { |
| e->range.sbit = sbit; |
| e->range.ebit = ebit; |
| e->flags = flags; |
| itbl_num_opcodes++; |
| } |
| return e; |
| } |
| |
| /* Add an operand to an instruction entry */ |
| |
| struct itbl_field * |
| itbl_add_operand (struct itbl_entry *e, int yytype, int sbit, |
| int ebit, unsigned long flags) |
| { |
| struct itbl_field *f, **last_f; |
| if (!e) |
| return 0; |
| /* Add to end of fields' list. */ |
| f = alloc_field (get_type (yytype), sbit, ebit, flags); |
| if (f) |
| { |
| last_f = &e->fields; |
| while (*last_f) |
| last_f = &(*last_f)->next; |
| *last_f = f; |
| f->next = 0; |
| } |
| return f; |
| } |
| |
| /*======================================================================*/ |
| /* Interfaces for assembler and disassembler */ |
| |
| #ifndef STAND_ALONE |
| static void append_insns_as_macros (void); |
| |
| /* Initialize for gas. */ |
| |
| void |
| itbl_init (void) |
| { |
| struct itbl_entry *e, **es; |
| e_processor procn; |
| e_type type; |
| |
| if (!itbl_have_entries) |
| return; |
| |
| /* Since register names don't have a prefix, put them in the symbol table so |
| they can't be used as symbols. This simplifies argument parsing as |
| we can let gas parse registers for us. */ |
| /* Use symbol_create instead of symbol_new so we don't try to |
| output registers into the object file's symbol table. */ |
| |
| for (type = e_regtype0; type < e_nregtypes; type++) |
| for (procn = e_p0; procn < e_nprocs; procn++) |
| { |
| es = get_entries (procn, type); |
| for (e = *es; e; e = e->next) |
| { |
| symbol_table_insert (symbol_create (e->name, reg_section, |
| &zero_address_frag, e->value)); |
| } |
| } |
| append_insns_as_macros (); |
| } |
| |
| /* Append insns to opcodes table and increase number of opcodes |
| * Structure of opcodes table: |
| * struct itbl_opcode |
| * { |
| * const char *name; |
| * const char *args; - string describing the arguments. |
| * unsigned long match; - opcode, or ISA level if pinfo=INSN_MACRO |
| * unsigned long mask; - opcode mask, or macro id if pinfo=INSN_MACRO |
| * unsigned long pinfo; - insn flags, or INSN_MACRO |
| * }; |
| * examples: |
| * {"li", "t,i", 0x34000000, 0xffe00000, WR_t }, |
| * {"li", "t,I", 0, (int) M_LI, INSN_MACRO }, |
| */ |
| |
| static char *form_args (struct itbl_entry *e); |
| static void |
| append_insns_as_macros (void) |
| { |
| struct ITBL_OPCODE_STRUCT *new_opcodes, *o; |
| struct itbl_entry *e, **es; |
| int n, size, new_num_opcodes; |
| #ifdef USE_MACROS |
| int id; |
| #endif |
| |
| if (!itbl_have_entries) |
| return; |
| |
| if (!itbl_num_opcodes) /* no new instructions to add! */ |
| { |
| return; |
| } |
| DBG (("previous num_opcodes=%d\n", ITBL_NUM_OPCODES)); |
| |
| new_num_opcodes = ITBL_NUM_OPCODES + itbl_num_opcodes; |
| ASSERT (new_num_opcodes >= itbl_num_opcodes); |
| |
| size = sizeof (struct ITBL_OPCODE_STRUCT) * ITBL_NUM_OPCODES; |
| ASSERT (size >= 0); |
| DBG (("I get=%d\n", size / sizeof (ITBL_OPCODES[0]))); |
| |
| /* FIXME since ITBL_OPCODES could be a static table, |
| we can't realloc or delete the old memory. */ |
| new_opcodes = XNEWVEC (struct ITBL_OPCODE_STRUCT, new_num_opcodes); |
| if (!new_opcodes) |
| { |
| printf (_("Unable to allocate memory for new instructions\n")); |
| return; |
| } |
| if (size) /* copy preexisting opcodes table */ |
| memcpy (new_opcodes, ITBL_OPCODES, size); |
| |
| /* FIXME! some NUMOPCODES are calculated expressions. |
| These need to be changed before itbls can be supported. */ |
| |
| #ifdef USE_MACROS |
| id = ITBL_NUM_MACROS; /* begin the next macro id after the last */ |
| #endif |
| o = &new_opcodes[ITBL_NUM_OPCODES]; /* append macro to opcodes list */ |
| for (n = e_p0; n < e_nprocs; n++) |
| { |
| es = get_entries (n, e_insn); |
| for (e = *es; e; e = e->next) |
| { |
| /* name, args, mask, match, pinfo |
| * {"li", "t,i", 0x34000000, 0xffe00000, WR_t }, |
| * {"li", "t,I", 0, (int) M_LI, INSN_MACRO }, |
| * Construct args from itbl_fields. |
| */ |
| o->name = e->name; |
| o->args = strdup (form_args (e)); |
| o->mask = apply_range (e->value, e->range); |
| /* FIXME how to catch during assembly? */ |
| /* mask to identify this insn */ |
| o->match = apply_range (e->value, e->range); |
| o->pinfo = 0; |
| |
| #ifdef USE_MACROS |
| o->mask = id++; /* FIXME how to catch during assembly? */ |
| o->match = 0; /* for macros, the insn_isa number */ |
| o->pinfo = INSN_MACRO; |
| #endif |
| |
| /* Don't add instructions which caused an error */ |
| if (o->args) |
| o++; |
| else |
| new_num_opcodes--; |
| } |
| } |
| ITBL_OPCODES = new_opcodes; |
| ITBL_NUM_OPCODES = new_num_opcodes; |
| |
| /* FIXME |
| At this point, we can free the entries, as they should have |
| been added to the assembler's tables. |
| Don't free name though, since name is being used by the new |
| opcodes table. |
| |
| Eventually, we should also free the new opcodes table itself |
| on exit. |
| */ |
| } |
| |
| static char * |
| form_args (struct itbl_entry *e) |
| { |
| static char s[31]; |
| char c = 0, *p = s; |
| struct itbl_field *f; |
| |
| ASSERT (e); |
| for (f = e->fields; f; f = f->next) |
| { |
| switch (f->type) |
| { |
| case e_dreg: |
| c = 'd'; |
| break; |
| case e_creg: |
| c = 't'; |
| break; |
| case e_greg: |
| c = 's'; |
| break; |
| case e_immed: |
| c = 'i'; |
| break; |
| case e_addr: |
| c = 'a'; |
| break; |
| default: |
| c = 0; /* ignore; unknown field type */ |
| } |
| if (c) |
| { |
| if (p != s) |
| *p++ = ','; |
| *p++ = c; |
| } |
| } |
| *p = 0; |
| return s; |
| } |
| #endif /* !STAND_ALONE */ |
| |
| /* Get processor's register name from val */ |
| |
| int |
| itbl_get_reg_val (char *name, unsigned long *pval) |
| { |
| e_type t; |
| e_processor p; |
| |
| for (p = e_p0; p < e_nprocs; p++) |
| { |
| for (t = e_regtype0; t < e_nregtypes; t++) |
| { |
| if (itbl_get_val (p, t, name, pval)) |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| char * |
| itbl_get_name (e_processor processor, e_type type, unsigned long val) |
| { |
| struct itbl_entry *r; |
| /* type depends on instruction passed */ |
| r = find_entry_byval (processor, type, val, 0); |
| if (r) |
| return r->name; |
| else |
| return 0; /* error; invalid operand */ |
| } |
| |
| /* Get processor's register value from name */ |
| |
| int |
| itbl_get_val (e_processor processor, e_type type, char *name, |
| unsigned long *pval) |
| { |
| struct itbl_entry *r; |
| /* type depends on instruction passed */ |
| r = find_entry_byname (processor, type, name); |
| if (r == NULL) |
| return 0; |
| *pval = r->value; |
| return 1; |
| } |
| |
| /* Assemble instruction "name" with operands "s". |
| * name - name of instruction |
| * s - operands |
| * returns - long word for assembled instruction */ |
| |
| unsigned long |
| itbl_assemble (char *name, char *s) |
| { |
| unsigned long opcode; |
| struct itbl_entry *e = NULL; |
| struct itbl_field *f; |
| char *n; |
| int processor; |
| |
| if (!name || !*name) |
| return 0; /* error! must have an opcode name/expr */ |
| |
| /* find entry in list of instructions for all processors */ |
| for (processor = 0; processor < e_nprocs; processor++) |
| { |
| e = find_entry_byname (processor, e_insn, name); |
| if (e) |
| break; |
| } |
| if (!e) |
| return 0; /* opcode not in table; invalid instruction */ |
| opcode = build_opcode (e); |
| |
| /* parse opcode's args (if any) */ |
| for (f = e->fields; f; f = f->next) /* for each arg, ... */ |
| { |
| struct itbl_entry *r; |
| unsigned long value; |
| if (!s || !*s) |
| return 0; /* error - not enough operands */ |
| n = itbl_get_field (&s); |
| /* n should be in form $n or 0xhhh (are symbol names valid?? */ |
| switch (f->type) |
| { |
| case e_dreg: |
| case e_creg: |
| case e_greg: |
| /* Accept either a string name |
| * or '$' followed by the register number */ |
| if (*n == '$') |
| { |
| n++; |
| value = strtol (n, 0, 10); |
| /* FIXME! could have "0l"... then what?? */ |
| if (value == 0 && *n != '0') |
| return 0; /* error; invalid operand */ |
| } |
| else |
| { |
| r = find_entry_byname (e->processor, f->type, n); |
| if (r) |
| value = r->value; |
| else |
| return 0; /* error; invalid operand */ |
| } |
| break; |
| case e_addr: |
| /* use assembler's symbol table to find symbol */ |
| /* FIXME!! Do we need this? |
| if so, what about relocs?? |
| my_getExpression (&imm_expr, s); |
| return 0; /-* error; invalid operand *-/ |
| break; |
| */ |
| /* If not a symbol, fallthru to IMMED */ |
| case e_immed: |
| if (*n == '0' && *(n + 1) == 'x') /* hex begins 0x... */ |
| { |
| n += 2; |
| value = strtol (n, 0, 16); |
| /* FIXME! could have "0xl"... then what?? */ |
| } |
| else |
| { |
| value = strtol (n, 0, 10); |
| /* FIXME! could have "0l"... then what?? */ |
| if (value == 0 && *n != '0') |
| return 0; /* error; invalid operand */ |
| } |
| break; |
| default: |
| return 0; /* error; invalid field spec */ |
| } |
| opcode |= apply_range (value, f->range); |
| } |
| if (s && *s) |
| return 0; /* error - too many operands */ |
| return opcode; /* done! */ |
| } |
| |
| /* Disassemble instruction "insn". |
| * insn - instruction |
| * s - buffer to hold disassembled instruction |
| * returns - 1 if succeeded; 0 if failed |
| */ |
| |
| int |
| itbl_disassemble (char *s, unsigned long insn) |
| { |
| e_processor processor; |
| struct itbl_entry *e; |
| struct itbl_field *f; |
| |
| if (!ITBL_IS_INSN (insn)) |
| return 0; /* error */ |
| processor = get_processor (ITBL_DECODE_PNUM (insn)); |
| |
| /* find entry in list */ |
| e = find_entry_byval (processor, e_insn, insn, 0); |
| if (!e) |
| return 0; /* opcode not in table; invalid instruction */ |
| strcpy (s, e->name); |
| |
| /* Parse insn's args (if any). */ |
| for (f = e->fields; f; f = f->next) /* for each arg, ... */ |
| { |
| struct itbl_entry *r; |
| unsigned long value; |
| char s_value[20]; |
| |
| if (f == e->fields) /* First operand is preceded by tab. */ |
| strcat (s, "\t"); |
| else /* ','s separate following operands. */ |
| strcat (s, ","); |
| value = extract_range (insn, f->range); |
| /* n should be in form $n or 0xhhh (are symbol names valid?? */ |
| switch (f->type) |
| { |
| case e_dreg: |
| case e_creg: |
| case e_greg: |
| /* Accept either a string name |
| or '$' followed by the register number. */ |
| r = find_entry_byval (e->processor, f->type, value, &f->range); |
| if (r) |
| strcat (s, r->name); |
| else |
| { |
| sprintf (s_value, "$%lu", value); |
| strcat (s, s_value); |
| } |
| break; |
| case e_addr: |
| /* Use assembler's symbol table to find symbol. */ |
| /* FIXME!! Do we need this? If so, what about relocs?? */ |
| /* If not a symbol, fall through to IMMED. */ |
| case e_immed: |
| sprintf (s_value, "0x%lx", value); |
| strcat (s, s_value); |
| break; |
| default: |
| return 0; /* error; invalid field spec */ |
| } |
| } |
| return 1; /* Done! */ |
| } |
| |
| /*======================================================================*/ |
| /* |
| * Local functions for manipulating private structures containing |
| * the names and format for the new instructions and registers |
| * for each processor. |
| */ |
| |
| /* Calculate instruction's opcode and function values from entry */ |
| |
| static unsigned long |
| build_opcode (struct itbl_entry *e) |
| { |
| unsigned long opcode; |
| |
| opcode = apply_range (e->value, e->range); |
| opcode |= ITBL_ENCODE_PNUM (e->processor); |
| return opcode; |
| } |
| |
| /* Calculate absolute value given the relative value and bit position range |
| * within the instruction. |
| * The range is inclusive where 0 is least significant bit. |
| * A range of { 24, 20 } will have a mask of |
| * bit 3 2 1 |
| * pos: 1098 7654 3210 9876 5432 1098 7654 3210 |
| * bin: 0000 0001 1111 0000 0000 0000 0000 0000 |
| * hex: 0 1 f 0 0 0 0 0 |
| * mask: 0x01f00000. |
| */ |
| |
| static unsigned long |
| apply_range (unsigned long rval, struct itbl_range r) |
| { |
| unsigned long mask; |
| unsigned long aval; |
| int len = MAX_BITPOS - r.sbit; |
| |
| ASSERT (r.sbit >= r.ebit); |
| ASSERT (MAX_BITPOS >= r.sbit); |
| ASSERT (r.ebit >= 0); |
| |
| /* create mask by truncating 1s by shifting */ |
| mask = 0xffffffff << len; |
| mask = mask >> len; |
| mask = mask >> r.ebit; |
| mask = mask << r.ebit; |
| |
| aval = (rval << r.ebit) & mask; |
| return aval; |
| } |
| |
| /* Calculate relative value given the absolute value and bit position range |
| * within the instruction. */ |
| |
| static unsigned long |
| extract_range (unsigned long aval, struct itbl_range r) |
| { |
| unsigned long mask; |
| unsigned long rval; |
| int len = MAX_BITPOS - r.sbit; |
| |
| /* create mask by truncating 1s by shifting */ |
| mask = 0xffffffff << len; |
| mask = mask >> len; |
| mask = mask >> r.ebit; |
| mask = mask << r.ebit; |
| |
| rval = (aval & mask) >> r.ebit; |
| return rval; |
| } |
| |
| /* Extract processor's assembly instruction field name from s; |
| * forms are "n args" "n,args" or "n" */ |
| /* Return next argument from string pointer "s" and advance s. |
| * delimiters are " ,()" */ |
| |
| char * |
| itbl_get_field (char **S) |
| { |
| static char n[128]; |
| char *s; |
| int len; |
| |
| s = *S; |
| if (!s || !*s) |
| return 0; |
| /* FIXME: This is a weird set of delimiters. */ |
| len = strcspn (s, " \t,()"); |
| ASSERT (128 > len + 1); |
| strncpy (n, s, len); |
| n[len] = 0; |
| if (s[len] == '\0') |
| s = 0; /* no more args */ |
| else |
| s += len + 1; /* advance to next arg */ |
| |
| *S = s; |
| return n; |
| } |
| |
| /* Search entries for a given processor and type |
| * to find one matching the name "n". |
| * Return a pointer to the entry */ |
| |
| static struct itbl_entry * |
| find_entry_byname (e_processor processor, |
| e_type type, char *n) |
| { |
| struct itbl_entry *e, **es; |
| |
| es = get_entries (processor, type); |
| for (e = *es; e; e = e->next) /* for each entry, ... */ |
| { |
| if (!strcmp (e->name, n)) |
| return e; |
| } |
| return 0; |
| } |
| |
| /* Search entries for a given processor and type |
| * to find one matching the value "val" for the range "r". |
| * Return a pointer to the entry. |
| * This function is used for disassembling fields of an instruction. |
| */ |
| |
| static struct itbl_entry * |
| find_entry_byval (e_processor processor, e_type type, |
| unsigned long val, struct itbl_range *r) |
| { |
| struct itbl_entry *e, **es; |
| unsigned long eval; |
| |
| es = get_entries (processor, type); |
| for (e = *es; e; e = e->next) /* for each entry, ... */ |
| { |
| if (processor != e->processor) |
| continue; |
| /* For insns, we might not know the range of the opcode, |
| * so a range of 0 will allow this routine to match against |
| * the range of the entry to be compared with. |
| * This could cause ambiguities. |
| * For operands, we get an extracted value and a range. |
| */ |
| /* if range is 0, mask val against the range of the compared entry. */ |
| if (r == 0) /* if no range passed, must be whole 32-bits |
| * so create 32-bit value from entry's range */ |
| { |
| eval = apply_range (e->value, e->range); |
| val &= apply_range (0xffffffff, e->range); |
| } |
| else if ((r->sbit == e->range.sbit && r->ebit == e->range.ebit) |
| || (e->range.sbit == 0 && e->range.ebit == 0)) |
| { |
| eval = apply_range (e->value, *r); |
| val = apply_range (val, *r); |
| } |
| else |
| continue; |
| if (val == eval) |
| return e; |
| } |
| return 0; |
| } |
| |
| /* Return a pointer to the list of entries for a given processor and type. */ |
| |
| static struct itbl_entry ** |
| get_entries (e_processor processor, e_type type) |
| { |
| return &entries[processor][type]; |
| } |
| |
| /* Return an integral value for the processor passed from yyparse. */ |
| |
| static e_processor |
| get_processor (int yyproc) |
| { |
| /* translate from yacc's processor to enum */ |
| if (yyproc >= e_p0 && yyproc < e_nprocs) |
| return (e_processor) yyproc; |
| return e_invproc; /* error; invalid processor */ |
| } |
| |
| /* Return an integral value for the entry type passed from yyparse. */ |
| |
| static e_type |
| get_type (int yytype) |
| { |
| switch (yytype) |
| { |
| /* translate from yacc's type to enum */ |
| case INSN: |
| return e_insn; |
| case DREG: |
| return e_dreg; |
| case CREG: |
| return e_creg; |
| case GREG: |
| return e_greg; |
| case ADDR: |
| return e_addr; |
| case IMMED: |
| return e_immed; |
| default: |
| return e_invtype; /* error; invalid type */ |
| } |
| } |
| |
| /* Allocate and initialize an entry */ |
| |
| static struct itbl_entry * |
| alloc_entry (e_processor processor, e_type type, |
| char *name, unsigned long value) |
| { |
| struct itbl_entry *e, **es; |
| if (!name) |
| return 0; |
| e = XNEW (struct itbl_entry); |
| if (e) |
| { |
| memset (e, 0, sizeof (struct itbl_entry)); |
| e->name = xstrdup (name); |
| e->processor = processor; |
| e->type = type; |
| e->value = value; |
| es = get_entries (e->processor, e->type); |
| e->next = *es; |
| *es = e; |
| } |
| return e; |
| } |
| |
| /* Allocate and initialize an entry's field */ |
| |
| static struct itbl_field * |
| alloc_field (e_type type, int sbit, int ebit, |
| unsigned long flags) |
| { |
| struct itbl_field *f; |
| f = XNEW (struct itbl_field); |
| if (f) |
| { |
| memset (f, 0, sizeof (struct itbl_field)); |
| f->type = type; |
| f->range.sbit = sbit; |
| f->range.ebit = ebit; |
| f->flags = flags; |
| } |
| return f; |
| } |