diff options
author | Dawn Perchik <dawn@cygnus> | 1997-02-11 02:07:57 +0000 |
---|---|---|
committer | Dawn Perchik <dawn@cygnus> | 1997-02-11 02:07:57 +0000 |
commit | 8e5c905e99f422e16eddf45d6718c0931f288a05 (patch) | |
tree | 75ff8dc90b50e814483212922889591ed0f33aea /gas | |
parent | 242ce3d85799ded0e3f725c919c41cb9af1183e0 (diff) | |
download | gdb-8e5c905e99f422e16eddf45d6718c0931f288a05.zip gdb-8e5c905e99f422e16eddf45d6718c0931f288a05.tar.gz gdb-8e5c905e99f422e16eddf45d6718c0931f288a05.tar.bz2 |
* itbl-ops.c: New file. Add support for dynamically read
instruction registers, opcodes and formats. Build internal table
for new instructions and provide callbacks for assembler and
disassembler.
* itbl-lex.l, itbl-parse.y: Lex and yacc parsers for instruction
spec table.
* itbl-ops.h: New file. Header file for itbl support.
* config/itbl-mips.h: New file. Mips specific definitions for
itbl support.
Diffstat (limited to 'gas')
-rw-r--r-- | gas/.Sanitize | 4 | ||||
-rw-r--r-- | gas/ChangeLog | 12 | ||||
-rw-r--r-- | gas/config/.Sanitize | 1 | ||||
-rw-r--r-- | gas/config/itbl-mips.h | 27 | ||||
-rw-r--r-- | gas/itbl-lex.l | 86 | ||||
-rw-r--r-- | gas/itbl-ops.c | 838 | ||||
-rw-r--r-- | gas/itbl-ops.h | 75 | ||||
-rw-r--r-- | gas/itbl-parse.y | 413 |
8 files changed, 1456 insertions, 0 deletions
diff --git a/gas/.Sanitize b/gas/.Sanitize index f250399..eedc78e 100644 --- a/gas/.Sanitize +++ b/gas/.Sanitize @@ -73,6 +73,10 @@ link.cmd listing.c listing.h literal.c +itbl-lex.l +itbl-ops.c +itbl-ops.h +itbl-parse.y mac-as.r macro.c macro.h diff --git a/gas/ChangeLog b/gas/ChangeLog index 56e846b..1af76b2 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,15 @@ +Mon Feb 10 18:09:00 1997 Dawn Perchik (dawn@cygnus.com) + + * itbl-ops.c: New file. Add support for dynamically read + instruction registers, opcodes and formats. Build internal table + for new instructions and provide callbacks for assembler and + disassembler. + * itbl-lex.l, itbl-parse.y: Lex and yacc parsers for instruction + spec table. + * itbl-ops.h: New file. Header file for itbl support. + * config/itbl-mips.h: New file. Mips specific definitions for + itbl support. + Fri Feb 7 09:52:34 1997 Jeffrey A Law (law@cygnus.com) * config/tc-mn10200.c (md_assemble): If a constant operand won't diff --git a/gas/config/.Sanitize b/gas/config/.Sanitize index a83279c..c33246c 100644 --- a/gas/config/.Sanitize +++ b/gas/config/.Sanitize @@ -65,6 +65,7 @@ go32.cfg e-mipsecoff.c e-mipself.c i386coff.mt +itbl-mips.h m68k-parse.h m68k-parse.y m68kcoff.mt diff --git a/gas/config/itbl-mips.h b/gas/config/itbl-mips.h new file mode 100644 index 0000000..75af745 --- /dev/null +++ b/gas/config/itbl-mips.h @@ -0,0 +1,27 @@ + +/* Defines for Mips itbl cop support */ + +#include "opcode/mips.h" + +/* Values for processors will be from 0 to NUMBER_OF_PROCESSORS-1 */ +#define NUMBER_OF_PROCESSORS 4 +#define MAX_BITPOS 31 + +/* Mips specifics */ +#define MIPS_OPCODE_COP0 (0x21) /* COPz+CO, bits 31-25: 0100zz1 */ +#define MIPS_ENCODE_COP_NUM(z) ((MIPS_OPCODE_COP0|z<<1)<<25) +#define MIPS_IS_COP_INSN(insn) ((MIPS_OPCODE_COP0&(insn>>25)) \ + == MIPS_OPCODE_COP0) +#define MIPS_DECODE_COP_NUM(insn) ((~MIPS_OPCODE_COP0&(insn>>25))>>1) +#define MIPS_DECODE_COP_COFUN(insn) ((~MIPS_ENCODE_COP_NUM(3))&(insn)) + +/* definitions required by generic code */ +#define ITBL_IS_INSN(insn) MIPS_IS_COP_INSN(insn) +#define ITBL_DECODE_PNUM(insn) MIPS_DECODE_COP_NUM(insn) +#define ITBL_ENCODE_PNUM(pnum) MIPS_ENCODE_COP_NUM(pnum) + +#define ITBL_OPCODE_STRUCT mips_opcode +#define ITBL_OPCODES mips_opcodes +#define ITBL_NUM_OPCODES NUMOPCODES +#define ITBL_NUM_MACROS M_NUM_MACROS + diff --git a/gas/itbl-lex.l b/gas/itbl-lex.l new file mode 100644 index 0000000..8c1e8e4 --- /dev/null +++ b/gas/itbl-lex.l @@ -0,0 +1,86 @@ +%{ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include "itbl-parse.h" + +#ifdef DEBUG +#define DBG(x) printf x +#define MDBG(x) printf x +#else +#define DBG(x) +#define MDBG(x) +#endif + +int insntbl_line = 1; +%} + +ALNUM [A-Za-z0-9_] +DIGIT [0-9] +ALPHA [A-Za-z_] +HEX [0-9A-Fa-f] + +%% + +"creg"|"CREG" { + return CREG; + } +"dreg"|"DREG" { + return DREG; + } +"greg"|"GREG" { + return GREG; + } +"immed"|"IMMED" { + return IMMED; + } +"addr"|"ADDR" { + return ADDR; + } +"insn"|"INSN" { + return INSN; + } +"p"{DIGIT} { + yytext[yyleng]=0; + yylval.processor = strtoul(yytext+1,0,0); + return PNUM; + } +{DIGIT}+ { + yytext[yyleng]=0; + yylval.num = strtoul(yytext,0,0); + return NUM; + } +"0x"{HEX}+ { + yytext[yyleng]=0; + yylval.num = strtoul(yytext,0,0); + return NUM; + } +{ALPHA}{ALNUM}* { + yytext[yyleng]=0; + yylval.str = strdup(yytext); + return ID; + } +";"|"#" { + int c; + while ((c = input()) != EOF) { + if (c == '\n') + { + unput(c); + break; + } + } + } +"\n" { + insntbl_line++; + MDBG(("in lex, NL=%d (x%x)\n",NL,NL)); + return NL; + } +" "|"\t" { } +. { + MDBG(("char=%x,%d\n",yytext[0],yytext[0])); + return yytext[0]; + } +%% + +int yywrap() { return 1; } diff --git a/gas/itbl-ops.c b/gas/itbl-ops.c new file mode 100644 index 0000000..a348f91 --- /dev/null +++ b/gas/itbl-ops.c @@ -0,0 +1,838 @@ + +/*======================================================================*/ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "itbl-ops.h" +#include "itbl-parse.h" + +#define DEBUG + +#ifdef DEBUG +#include <assert.h> +#define ASSERT(x) 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 + +/*======================================================================*/ +/* 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; /* mnemionic 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] = +{ + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 } +}; + +/* 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, "r"); + if (yyin == 0) + { + printf("Can't open processor instruction specification file \"%s\"\n", + insntbl); + return 1; + } + else + { + while (yyparse()); + } + fclose(yyin); + return 0; +} + +/* Add a register entry */ + +struct itbl_entry *itbl_add_reg(int yyprocessor, int yytype, char *regname, + int regnum) +{ +#if 0 /* ndef STAND_ALONE */ +#include "as.h" +#include "symbols.h" + /* Since register names don't have a prefix, we put them in the symbol table so + they can't be used as symbols. This also simplifies argument parsing as + we can let gas parse registers for us. The recorded register number is + regnum. */ + /* Use symbol_create here instead of symbol_new so we don't try to + output registers into the object file's symbol table. */ + symbol_table_insert (symbol_create (regname, reg_section, + regnum, &zero_address_frag)); +#endif + 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 +#include "as.h" +#include "symbols.h" +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; + + /* 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, + e->value, &zero_address_frag)); + } + } + 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, id, size, new_size, new_num_opcodes; + + ASSERT(itbl_num_opcodes > 0); + 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]))); + + new_size = sizeof(struct ITBL_OPCODE_STRUCT) * new_num_opcodes; + ASSERT(new_size > size); + + /* FIXME since ITBL_OPCODES culd be a static table, + we can't realloc or delete the old memory. */ + new_opcodes = (struct ITBL_OPCODE_STRUCT*)malloc(new_size); + if (!new_opcodes) + { + printf("Unable to allocate memory for new instructions\n"); + return; + } + if (size) /* copy prexisting opcodes table */ + memcpy(new_opcodes, ITBL_OPCODES, size); + + /* FIXME! some NUMOPCODES are calculated expressions. + These need to be changed before itbls can be supported. */ + + id = ITBL_NUM_MACROS; /* begin the next macro id after the last */ + 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 durring 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 durring 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 */ + +unsigned long itbl_get_reg_val(char *name) +{ + e_type t; + e_processor p; + int r=0; + for (p=e_p0; p<e_nprocs; p++) + for (t=e_regtype0; t<e_nregtypes; t++) + { + if (r = itbl_get_val(p, t, name), r) + return r; + } + 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 */ + +unsigned long itbl_get_val(e_processor processor, e_type type, char *name) +{ + struct itbl_entry *r; + /* type depends on instruction passed */ + r = find_entry_byname(processor,type,name); + if (r) + return r->value; + else + return 0; /* error; invalid operand */ +} + + +/* 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; + struct itbl_field *f; + char *n; + int processor; + + if (!name || !*name) + return 0; /* error! must have a 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 instrustion */ + 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, fall thru 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 instrustion */ + 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; + + if (f==e->fields) /* first operand is preceeded 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,"%s$%d",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 thru to IMMED */ + case e_immed: + sprintf(s,"%s0x%x",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 " ,\0" */ + +char *itbl_get_field(char **S) +{ + static char n[128]; + char *p, *ps, *s; + int len; + + s = *S; + if (!s || !*s) + return 0; + p = s+strlen(s); + if (ps=strchr(s,','),ps) p = ps; + if (ps=strchr(s,' '),ps) p = min(p,ps); + if (ps=strchr(s,'\0'),ps) p = min(p,ps); + if (p==0) + return 0; /* error! */ + len = p-s; + 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 = (struct itbl_entry*) malloc(sizeof(struct itbl_entry)); + if (e) + { + memset(e,0,sizeof(struct itbl_entry)); + e->name = (char *) malloc(sizeof(strlen(name))+1); + if (e->name) strcpy(e->name,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 = (struct itbl_field*) malloc(sizeof(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; +} + diff --git a/gas/itbl-ops.h b/gas/itbl-ops.h new file mode 100644 index 0000000..56e1b3a --- /dev/null +++ b/gas/itbl-ops.h @@ -0,0 +1,75 @@ + +/* External functions, constants and defines for itbl support */ + +#include "itbl-cpu.h" + +/* Defaults for definitions required by generic code */ +#ifndef ITBL_NUMBER_OF_PROCESSORS +#define ITBL_NUMBER_OF_PROCESSORS 1 +#endif + +#ifndef ITBL_MAX_BITPOS +#define ITBL_MAX_BITPOS 31 +#endif + +#ifndef ITBL_TYPE +#define ITBL_TYPE unsigned long +#endif + +#ifndef ITBL_IS_INSN +#define ITBL_IS_INSN(insn) 1 +#endif + +#ifndef ITBL_DECODE_PNUM +#define ITBL_DECODE_PNUM(insn) 0 +#endif + +#ifndef ITBL_ENCODE_PNUM +#define ITBL_ENCODE_PNUM(pnum) 0 +#endif + +typedef ITBL_TYPE t_insn; + +/* types of entries */ +typedef enum +{ + e_insn, + e_dreg, + e_regtype0 = e_dreg, + e_creg, + e_greg, + e_addr, + e_nregtypes = e_greg+1, + e_immed, + e_ntypes, + e_invtype /* invalid type */ +} e_type; + +typedef enum +{ + e_p0, + e_nprocs=NUMBER_OF_PROCESSORS, + e_invproc /* invalid processor */ +} e_processor; + +/* These routines are visible to the main part of the assembler */ + +int itbl_parse(char* insntbl); +void itbl_init(void); +char *itbl_get_field(char **s); +unsigned long itbl_assemble(char *name, char *operands); +int itbl_disassemble(char *str, unsigned long insn); +int itbl_parse(char *tbl); /* parses insn tbl */ +unsigned long itbl_get_reg_val(char *name); +unsigned long itbl_get_val(e_processor processor, e_type type, char *name); +char *itbl_get_name(e_processor processor, e_type type, unsigned long val); + +/* These routines are called by the table parser used to build the + * dynamic list of new processor instructions and registers. */ + +struct itbl_entry *itbl_add_reg(int yyproc, int yytype, char *regname, int regnum); +struct itbl_entry *itbl_add_insn(int yyproc, char *name, unsigned long value, + int sbit, int ebit, unsigned long flags); +struct itbl_field *itbl_add_operand(struct itbl_entry *e, int yytype, + int sbit, int ebit, unsigned long flags); + diff --git a/gas/itbl-parse.y b/gas/itbl-parse.y new file mode 100644 index 0000000..d90499d --- /dev/null +++ b/gas/itbl-parse.y @@ -0,0 +1,413 @@ +%{ +/* + +Yacc grammar for instruction table entries. + +======================================================================= +Original Instruction table specification document: + + MIPS Coprocessor Table Specification + ==================================== + +This document describes the format of the MIPS coprocessor table. The +table specifies a list of valid functions, data registers and control +registers that can be used in coprocessor instructions. This list, +together with the coprocessor instruction classes listed below, +specifies the complete list of coprocessor instructions that will +be recognized and assembled by the GNU assembler. In effect, +this makes the GNU assembler table-driven, where the table is +specified by the programmer. + +The table is an ordinary text file that the GNU assembler reads when +it starts. Using the information in the table, the assembler +generates an internal list of valid coprocessor registers and +functions. The assembler uses this internal list in addition to the +standard MIPS registers and instructions which are built-in to the +assembler during code generation. + +To specify the coprocessor table when invoking the GNU assembler, use +the command line option "--itbl file", where file is the +complete name of the table, including path and extension. + +Examples: + + gas -t cop.tbl test.s -o test.o + gas -t /usr/local/lib/cop.tbl test.s -o test.o + gas --itbl d:\gnu\data\cop.tbl test.s -o test.o + +Only one table may be supplied during a single invocation of +the assembler. + + +Instruction classes +=================== + +Below is a list of the valid coprocessor instruction classes for +any given coprocessor "z". These instructions are already recognized +by the assembler, and are listed here only for reference. + +Class format instructions +------------------------------------------------- +Class1: op base rt offset + LWCz rt,offset(base) + SWCz rt,offset(base) +Class2: COPz sub rt rd 0 + MTCz rt,rd + MFCz rt,rd + CTCz rt,rd + CFCz rt,rd +Class3: COPz CO cofun + COPz cofun +Class4: COPz BC br offset + BCzT offset + BCzF offset +Class5: COPz sub rt rd 0 + DMFCz rt,rd + DMTCz rt,rd +Class6: op base rt offset + LDCz rt,offset(base) + SDCz rt,offset(base) +Class7: COPz BC br offset + BCzTL offset + BCzFL offset + +The coprocessor table defines coprocessor-specific registers that can +be used with all of the above classes of instructions, where +appropriate. It also defines additional coprocessor-specific +functions for Class3 (COPz cofun) instructions, Thus, the table allows +the programmer to use convenient mnemonics and operands for these +functions, instead of the COPz mmenmonic and cofun operand. + +The names of the MIPS general registers and their aliases are defined +by the assembler and will be recognized as valid register names by the +assembler when used (where allowed) in coprocessor instructions. +However, the names and values of all coprocessor data and control +register mnemonics must be specified in the coprocessor table. + + +Table Grammar +============= + +Here is the grammar for the coprocessor table: + + table -> entry* + + entry -> [z entrydef] [comment] '\n' + + entrydef -> type name val + entrydef -> 'insn' name val funcdef ; type of entry (instruction) + + z -> 'p'['0'..'3'] ; processor number + type -> ['dreg' | 'creg' | 'greg' ] ; type of entry (register) + ; 'dreg', 'creg' or 'greg' specifies a data, control, or general + ; register mnemonic, respectively + name -> [ltr|dec]* ; mnemonic of register/function + val -> [dec|hex] ; register/function number (integer constant) + + funcdef -> frange flags fields + ; bitfield range for opcode + ; list of fields' formats + fields -> field* + field -> [','] ftype frange flags + flags -> ['*' flagexpr] + flagexpr -> '[' flagexpr ']' + flagexpr -> val '|' flagexpr + ftype -> [ type | 'immed' | 'addr' ] + ; 'immed' specifies an immediate value; see grammar for "val" above + ; 'addr' specifies a C identifier; name of symbol to be resolved at + ; link time + frange -> ':' val '-' val ; starting to ending bit positions, where + ; where 0 is least significant bit + frange -> (null) ; default range of 31-0 will be assumed + + comment -> [';'|'#'] [char]* + char -> any printable character + ltr -> ['a'..'z'|'A'..'Z'] + dec -> ['0'..'9']* ; value in decimal + hex -> '0x'['0'..'9' | 'a'..'f' | 'A'..'F']* ; value in hexidecimal + + +Examples +======== + +Example 1: + +The table: + + p1 dreg d1 1 ; data register "d1" for COP1 has value 1 + p1 creg c3 3 ; ctrl register "c3" for COP1 has value 3 + p3 func fill 0x1f:24-20 ; function "fill" for COP3 has value 31 and + ; no fields + +will allow the assembler to accept the following coprocessor instructions: + + LWC1 d1,0x100($2) + fill + +Here, the general purpose register "$2", and instruction "LWC1", are standard +mnemonics built-in to the MIPS assembler. + + +Example 2: + +The table: + + p3 dreg d3 3 ; data register "d3" for COP3 has value 3 + p3 creg c2 22 ; control register "c2" for COP3 has value 22 + p3 func fee 0x1f:24-20 dreg:17-13 creg:12-8 immed:7-0 + ; function "fee" for COP3 has value 31, and 3 fields + ; consisting of a data register, a control register, + ; and an immediate value. + +will allow the assembler to accept the following coprocessor instruction: + + fee d3,c2,0x1 + +and will emit the object code: + + 31-26 25 24-20 19-18 17-13 12-8 7-0 + COPz CO fun dreg creg immed + 010011 1 11111 00 00011 10110 00000001 + + 0x4ff07601 + + +Example 3: + +The table: + + p3 dreg d3 3 ; data register "d3" for COP3 has value 3 + p3 creg c2 22 ; control register "c2" for COP3 has value 22 + p3 func fuu 0x01f00001 dreg:17-13 creg:12-8 + +will allow the assembler to accept the following coprocessor +instruction: + + fuu d3,c2 + +and will emit the object code: + + 31-26 25 24-20 19-18 17-13 12-8 7-0 + COPz CO fun dreg creg + 010011 1 11111 00 00011 10110 00000001 + + 0x4ff07601 + +In this way, the programmer can force arbitrary bits of an instruction +to have predefined values. + +======================================================================= +Additional notes: + +Encoding of ranges: +To handle more than one bit position range within an instruction, +use 0s to mask out the ranges which don't apply. +May decide to modify the syntax to allow commas separate multiple +ranges within an instruction (range','range). + +Changes in grammar: The number of parms argument to the function entry +was deleted from the original format such that we now count the fields. + +---- +FIXME! should really change lexical analyzer +to recognize 'dreg' etc. in context sensative way. +Currently function names or mnemonics may be incorrectly parsed as keywords + +FIXME! hex is ambiguous with any digit + +*/ + +#include <stdio.h> +#include "itbl-ops.h" + +/* #define DEBUG */ + +#ifdef DEBUG +#ifndef DBG_LVL +#define DBG_LVL 1 +#endif +#else +#define DBG_LVL 0 +#endif + +#if DBG_LVL >= 1 +#define DBG(x) printf x +#else +#define DBG(x) +#endif + +#if DBG_LVL >= 2 +#define DBGL2(x) printf x +#else +#define DBGL2(x) +#endif + +static int sbit, ebit; +static struct itbl_entry *insn=0; +extern int insntbl_line; +int yyparse(void); +int yylex(void); + +%} + +%union { + char *str; + int num; + int processor; + unsigned long val; + } + +%token DREG CREG GREG IMMED ADDR INSN NUM ID NL PNUM +%type <val> value flags flagexpr +%type <num> number NUM ftype regtype pnum PNUM +%type <str> ID name + +%start insntbl + +%% + +insntbl: entrys + ; + +entrys: entry entrys + | + ; + +entry: pnum regtype name value NL + { + DBG(("line %d: entry pnum=%d type=%d name=%s value=x%x\n", + insntbl_line, $1, $2, $3, $4)); + itbl_add_reg($1, $2, $3, $4); + } + | pnum INSN name value range flags + { + DBG(("line %d: entry pnum=%d type=INSN name=%s value=x%x", + insntbl_line, $1, $3, $4)); + DBG((" sbit=%d ebit=%d flags=0x%x\n", sbit, ebit, $6)); + insn=itbl_add_insn($1, $3, $4, sbit, ebit, $6); + } + fieldspecs NL + | NL + | error NL + ; + +fieldspecs: ',' fieldspec fieldspecs + | fieldspec fieldspecs + | + ; + +ftype: regtype + { + DBGL2(("ftype\n")); + $$ = $1; + } + | ADDR + { + DBGL2(("addr\n")); + $$ = ADDR; + } + | IMMED + { + DBGL2(("immed\n")); + $$ = IMMED; + } + ; + +fieldspec: ftype range flags + { + DBG(("line %d: field type=%d sbit=%d ebit=%d, flags=0x%x\n", + insntbl_line, $1, sbit, ebit, $3)); + itbl_add_operand(insn, $1, sbit, ebit, $3); + } + ; + +flagexpr: NUM '|' flagexpr + { + $$ = $1 | $3; + } + | '[' flagexpr ']' + { + $$ = $2; + } + | NUM + { + $$ = $1; + } + ; + +flags: '*' flagexpr + { + DBGL2(("flags=%d\n", $2)); + $$ = $2; + } + | + { + $$ = 0; + } + ; + +range: ':' NUM '-' NUM + { + DBGL2(("range %d %d\n", $2, $4)); + sbit = $2; + ebit = $4; + } + | + { + sbit = 31; + ebit = 0; + } + ; + +pnum: PNUM + { + DBGL2(("pnum=%d\n",$1)); + $$ = $1; + } + ; + +regtype: DREG + { + DBGL2(("dreg\n")); + $$ = DREG; + } + | CREG + { + DBGL2(("creg\n")); + $$ = CREG; + } + | GREG + { + DBGL2(("greg\n")); + $$ = GREG; + } + ; + +name: ID + { + DBGL2(("name=%s\n",$1)); + $$ = $1; + } + ; + +number: NUM + { + DBGL2(("num=%d\n",$1)); + $$ = $1; + } + ; + +value: NUM + { + DBGL2(("val=x%x\n",$1)); + $$ = $1; + } + ; +%% + +void yyerror(char *msg) +{ + printf("line %d: %s\n", insntbl_line, msg); +} + + |