Browse Source
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.gdb-4_18-branch
8 changed files with 1456 additions and 0 deletions
@ -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 |
|||
|
|||
@ -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; } |
|||
@ -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; |
|||
} |
|||
|
|||
@ -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); |
|||
|
|||
@ -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); |
|||
} |
|||
|
|||
|
|||
Loading…
Reference in new issue