diff options
author | Richard Henderson <rth@redhat.com> | 1999-05-03 07:29:11 +0000 |
---|---|---|
committer | Richard Henderson <rth@redhat.com> | 1999-05-03 07:29:11 +0000 |
commit | 252b5132c753830d5fd56823373aed85f2a0db63 (patch) | |
tree | 1af963bfd8d3e55167b81def4207f175eaff3a56 /gas/config/m68k-parse.y | |
download | gdb-252b5132c753830d5fd56823373aed85f2a0db63.zip gdb-252b5132c753830d5fd56823373aed85f2a0db63.tar.gz gdb-252b5132c753830d5fd56823373aed85f2a0db63.tar.bz2 |
19990502 sourceware importbinu_ss_19990502
Diffstat (limited to 'gas/config/m68k-parse.y')
-rw-r--r-- | gas/config/m68k-parse.y | 1061 |
1 files changed, 1061 insertions, 0 deletions
diff --git a/gas/config/m68k-parse.y b/gas/config/m68k-parse.y new file mode 100644 index 0000000..70a4e4f --- /dev/null +++ b/gas/config/m68k-parse.y @@ -0,0 +1,1061 @@ +/* m68k.y -- bison grammar for m68k operand parsing + Copyright (C) 1995, 96, 1997, 1998 Free Software Foundation, Inc. + Written by Ken Raeburn and Ian Lance Taylor, Cygnus Support + + 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 2, 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, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* This file holds a bison grammar to parse m68k operands. The m68k + has a complicated operand syntax, and gas supports two main + variations of it. Using a grammar is probably overkill, but at + least it makes clear exactly what we do support. */ + +%{ + +#include "as.h" +#include "tc-m68k.h" +#include "m68k-parse.h" + +/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, + etc), as well as gratuitiously global symbol names If other parser + generators (bison, byacc, etc) produce additional global names that + conflict at link time, then those parser generators need to be + fixed instead of adding those names to this list. */ + +#define yymaxdepth m68k_maxdepth +#define yyparse m68k_parse +#define yylex m68k_lex +#define yyerror m68k_error +#define yylval m68k_lval +#define yychar m68k_char +#define yydebug m68k_debug +#define yypact m68k_pact +#define yyr1 m68k_r1 +#define yyr2 m68k_r2 +#define yydef m68k_def +#define yychk m68k_chk +#define yypgo m68k_pgo +#define yyact m68k_act +#define yyexca m68k_exca +#define yyerrflag m68k_errflag +#define yynerrs m68k_nerrs +#define yyps m68k_ps +#define yypv m68k_pv +#define yys m68k_s +#define yy_yys m68k_yys +#define yystate m68k_state +#define yytmp m68k_tmp +#define yyv m68k_v +#define yy_yyv m68k_yyv +#define yyval m68k_val +#define yylloc m68k_lloc +#define yyreds m68k_reds /* With YYDEBUG defined */ +#define yytoks m68k_toks /* With YYDEBUG defined */ +#define yylhs m68k_yylhs +#define yylen m68k_yylen +#define yydefred m68k_yydefred +#define yydgoto m68k_yydgoto +#define yysindex m68k_yysindex +#define yyrindex m68k_yyrindex +#define yygindex m68k_yygindex +#define yytable m68k_yytable +#define yycheck m68k_yycheck + +#ifndef YYDEBUG +#define YYDEBUG 1 +#endif + +/* Internal functions. */ + +static enum m68k_register m68k_reg_parse PARAMS ((char **)); +static int yylex PARAMS ((void)); +static void yyerror PARAMS ((const char *)); + +/* The parser sets fields pointed to by this global variable. */ +static struct m68k_op *op; + +%} + +%union +{ + struct m68k_indexreg indexreg; + enum m68k_register reg; + struct m68k_exp exp; + unsigned long mask; + int onereg; +} + +%token <reg> DR AR FPR FPCR LPC ZAR ZDR LZPC CREG +%token <indexreg> INDEXREG +%token <exp> EXPR + +%type <indexreg> zireg zdireg +%type <reg> zadr zdr apc zapc zpc optzapc optczapc +%type <exp> optcexpr optexprc +%type <mask> reglist ireglist reglistpair +%type <onereg> reglistreg + +%% + +/* An operand. */ + +operand: + generic_operand + | motorola_operand + | mit_operand + ; + +/* A generic operand. */ + +generic_operand: + DR + { + op->mode = DREG; + op->reg = $1; + } + | AR + { + op->mode = AREG; + op->reg = $1; + } + | FPR + { + op->mode = FPREG; + op->reg = $1; + } + | FPCR + { + op->mode = CONTROL; + op->reg = $1; + } + | CREG + { + op->mode = CONTROL; + op->reg = $1; + } + | EXPR + { + op->mode = ABSL; + op->disp = $1; + } + | '#' EXPR + { + op->mode = IMMED; + op->disp = $2; + } + | '&' EXPR + { + op->mode = IMMED; + op->disp = $2; + } + | reglist + { + op->mode = REGLST; + op->mask = $1; + } + ; + +/* An operand in Motorola syntax. This includes MRI syntax as well, + which may or may not be different in that it permits commutativity + of index and base registers, and permits an offset expression to + appear inside or outside of the parentheses. */ + +motorola_operand: + '(' AR ')' + { + op->mode = AINDR; + op->reg = $2; + } + | '(' AR ')' '+' + { + op->mode = AINC; + op->reg = $2; + } + | '-' '(' AR ')' + { + op->mode = ADEC; + op->reg = $3; + } + | '(' EXPR ',' zapc ')' + { + op->reg = $4; + op->disp = $2; + if (($4 >= ZADDR0 && $4 <= ZADDR7) + || $4 == ZPC) + op->mode = BASE; + else + op->mode = DISP; + } + | '(' zapc ',' EXPR ')' + { + op->reg = $2; + op->disp = $4; + if (($2 >= ZADDR0 && $2 <= ZADDR7) + || $2 == ZPC) + op->mode = BASE; + else + op->mode = DISP; + } + | EXPR '(' zapc ')' + { + op->reg = $3; + op->disp = $1; + if (($3 >= ZADDR0 && $3 <= ZADDR7) + || $3 == ZPC) + op->mode = BASE; + else + op->mode = DISP; + } + | '(' LPC ')' + { + op->mode = DISP; + op->reg = $2; + } + | '(' ZAR ')' + { + op->mode = BASE; + op->reg = $2; + } + | '(' LZPC ')' + { + op->mode = BASE; + op->reg = $2; + } + | '(' EXPR ',' zapc ',' zireg ')' + { + op->mode = BASE; + op->reg = $4; + op->disp = $2; + op->index = $6; + } + | '(' EXPR ',' zapc ',' zpc ')' + { + if ($4 == PC || $4 == ZPC) + yyerror (_("syntax error")); + op->mode = BASE; + op->reg = $6; + op->disp = $2; + op->index.reg = $4; + op->index.size = SIZE_UNSPEC; + op->index.scale = 1; + } + | '(' EXPR ',' zdireg optczapc ')' + { + op->mode = BASE; + op->reg = $5; + op->disp = $2; + op->index = $4; + } + | '(' zdireg ',' EXPR ')' + { + op->mode = BASE; + op->disp = $4; + op->index = $2; + } + | EXPR '(' zapc ',' zireg ')' + { + op->mode = BASE; + op->reg = $3; + op->disp = $1; + op->index = $5; + } + | '(' zapc ',' zireg ')' + { + op->mode = BASE; + op->reg = $2; + op->index = $4; + } + | EXPR '(' zapc ',' zpc ')' + { + if ($3 == PC || $3 == ZPC) + yyerror (_("syntax error")); + op->mode = BASE; + op->reg = $5; + op->disp = $1; + op->index.reg = $3; + op->index.size = SIZE_UNSPEC; + op->index.scale = 1; + } + | '(' zapc ',' zpc ')' + { + if ($2 == PC || $2 == ZPC) + yyerror (_("syntax error")); + op->mode = BASE; + op->reg = $4; + op->index.reg = $2; + op->index.size = SIZE_UNSPEC; + op->index.scale = 1; + } + | EXPR '(' zdireg optczapc ')' + { + op->mode = BASE; + op->reg = $4; + op->disp = $1; + op->index = $3; + } + | '(' zdireg optczapc ')' + { + op->mode = BASE; + op->reg = $3; + op->index = $2; + } + | '(' '[' EXPR optczapc ']' ',' zireg optcexpr ')' + { + op->mode = POST; + op->reg = $4; + op->disp = $3; + op->index = $7; + op->odisp = $8; + } + | '(' '[' EXPR optczapc ']' optcexpr ')' + { + op->mode = POST; + op->reg = $4; + op->disp = $3; + op->odisp = $6; + } + | '(' '[' zapc ']' ',' zireg optcexpr ')' + { + op->mode = POST; + op->reg = $3; + op->index = $6; + op->odisp = $7; + } + | '(' '[' zapc ']' optcexpr ')' + { + op->mode = POST; + op->reg = $3; + op->odisp = $5; + } + | '(' '[' EXPR ',' zapc ',' zireg ']' optcexpr ')' + { + op->mode = PRE; + op->reg = $5; + op->disp = $3; + op->index = $7; + op->odisp = $9; + } + | '(' '[' zapc ',' zireg ']' optcexpr ')' + { + op->mode = PRE; + op->reg = $3; + op->index = $5; + op->odisp = $7; + } + | '(' '[' EXPR ',' zapc ',' zpc ']' optcexpr ')' + { + if ($5 == PC || $5 == ZPC) + yyerror (_("syntax error")); + op->mode = PRE; + op->reg = $7; + op->disp = $3; + op->index.reg = $5; + op->index.size = SIZE_UNSPEC; + op->index.scale = 1; + op->odisp = $9; + } + | '(' '[' zapc ',' zpc ']' optcexpr ')' + { + if ($3 == PC || $3 == ZPC) + yyerror (_("syntax error")); + op->mode = PRE; + op->reg = $5; + op->index.reg = $3; + op->index.size = SIZE_UNSPEC; + op->index.scale = 1; + op->odisp = $7; + } + | '(' '[' optexprc zdireg optczapc ']' optcexpr ')' + { + op->mode = PRE; + op->reg = $5; + op->disp = $3; + op->index = $4; + op->odisp = $7; + } + ; + +/* An operand in MIT syntax. */ + +mit_operand: + optzapc '@' + { + /* We use optzapc to avoid a shift/reduce conflict. */ + if ($1 < ADDR0 || $1 > ADDR7) + yyerror (_("syntax error")); + op->mode = AINDR; + op->reg = $1; + } + | optzapc '@' '+' + { + /* We use optzapc to avoid a shift/reduce conflict. */ + if ($1 < ADDR0 || $1 > ADDR7) + yyerror (_("syntax error")); + op->mode = AINC; + op->reg = $1; + } + | optzapc '@' '-' + { + /* We use optzapc to avoid a shift/reduce conflict. */ + if ($1 < ADDR0 || $1 > ADDR7) + yyerror (_("syntax error")); + op->mode = ADEC; + op->reg = $1; + } + | optzapc '@' '(' EXPR ')' + { + op->reg = $1; + op->disp = $4; + if (($1 >= ZADDR0 && $1 <= ZADDR7) + || $1 == ZPC) + op->mode = BASE; + else + op->mode = DISP; + } + | optzapc '@' '(' optexprc zireg ')' + { + op->mode = BASE; + op->reg = $1; + op->disp = $4; + op->index = $5; + } + | optzapc '@' '(' EXPR ')' '@' '(' optexprc zireg ')' + { + op->mode = POST; + op->reg = $1; + op->disp = $4; + op->index = $9; + op->odisp = $8; + } + | optzapc '@' '(' EXPR ')' '@' '(' EXPR ')' + { + op->mode = POST; + op->reg = $1; + op->disp = $4; + op->odisp = $8; + } + | optzapc '@' '(' optexprc zireg ')' '@' '(' EXPR ')' + { + op->mode = PRE; + op->reg = $1; + op->disp = $4; + op->index = $5; + op->odisp = $9; + } + ; + +/* An index register, possibly suppressed, which need not have a size + or scale. */ + +zireg: + INDEXREG + | zadr + { + $$.reg = $1; + $$.size = SIZE_UNSPEC; + $$.scale = 1; + } + ; + +/* A register which may be an index register, but which may not be an + address register. This nonterminal is used to avoid ambiguity when + trying to parse something like (0,d5,a6) as compared to (0,a6,d5). */ + +zdireg: + INDEXREG + | zdr + { + $$.reg = $1; + $$.size = SIZE_UNSPEC; + $$.scale = 1; + } + ; + +/* An address or data register, or a suppressed address or data + register. */ + +zadr: + zdr + | AR + | ZAR + ; + +/* A data register which may be suppressed. */ + +zdr: + DR + | ZDR + ; + +/* Either an address register or the PC. */ + +apc: + AR + | LPC + ; + +/* Either an address register, or the PC, or a suppressed address + register, or a suppressed PC. */ + +zapc: + apc + | LZPC + | ZAR + ; + +/* An optional zapc. */ + +optzapc: + /* empty */ + { + $$ = ZADDR0; + } + | zapc + ; + +/* The PC, optionally suppressed. */ + +zpc: + LPC + | LZPC + ; + +/* ',' zapc when it may be omitted. */ + +optczapc: + /* empty */ + { + $$ = ZADDR0; + } + | ',' zapc + { + $$ = $2; + } + ; + +/* ',' EXPR when it may be omitted. */ + +optcexpr: + /* empty */ + { + $$.exp.X_op = O_absent; + $$.size = SIZE_UNSPEC; + } + | ',' EXPR + { + $$ = $2; + } + ; + +/* EXPR ',' when it may be omitted. */ + +optexprc: + /* empty */ + { + $$.exp.X_op = O_absent; + $$.size = SIZE_UNSPEC; + } + | EXPR ',' + { + $$ = $1; + } + ; + +/* A register list for the movem instruction. */ + +reglist: + reglistpair + | reglistpair '/' ireglist + { + $$ = $1 | $3; + } + | reglistreg '/' ireglist + { + $$ = (1 << $1) | $3; + } + ; + +/* We use ireglist when we know we are looking at a reglist, and we + can safely reduce a simple register to reglistreg. If we permitted + reglist to reduce to reglistreg, it would be ambiguous whether a + plain register were a DREG/AREG/FPREG or a REGLST. */ + +ireglist: + reglistreg + { + $$ = 1 << $1; + } + | reglistpair + | reglistpair '/' ireglist + { + $$ = $1 | $3; + } + | reglistreg '/' ireglist + { + $$ = (1 << $1) | $3; + } + ; + +reglistpair: + reglistreg '-' reglistreg + { + if ($1 <= $3) + $$ = (1 << ($3 + 1)) - 1 - ((1 << $1) - 1); + else + $$ = (1 << ($1 + 1)) - 1 - ((1 << $3) - 1); + } + ; + +reglistreg: + DR + { + $$ = $1 - DATA0; + } + | AR + { + $$ = $1 - ADDR0 + 8; + } + | FPR + { + $$ = $1 - FP0 + 16; + } + | FPCR + { + if ($1 == FPI) + $$ = 24; + else if ($1 == FPS) + $$ = 25; + else + $$ = 26; + } + ; + +%% + +/* The string to parse is stored here, and modified by yylex. */ + +static char *str; + +/* The original string pointer. */ + +static char *strorig; + +/* If *CCP could be a register, return the register number and advance + *CCP. Otherwise don't change *CCP, and return 0. */ + +static enum m68k_register +m68k_reg_parse (ccp) + register char **ccp; +{ + char *start = *ccp; + char c; + char *p; + symbolS *symbolp; + + if (flag_reg_prefix_optional) + { + if (*start == REGISTER_PREFIX) + start++; + p = start; + } + else + { + if (*start != REGISTER_PREFIX) + return 0; + p = start + 1; + } + + if (! is_name_beginner (*p)) + return 0; + + p++; + while (is_part_of_name (*p) && *p != '.' && *p != ':' && *p != '*') + p++; + + c = *p; + *p = 0; + symbolp = symbol_find (start); + *p = c; + + if (symbolp != NULL && S_GET_SEGMENT (symbolp) == reg_section) + { + *ccp = p; + return S_GET_VALUE (symbolp); + } + + /* In MRI mode, something like foo.bar can be equated to a register + name. */ + while (flag_mri && c == '.') + { + ++p; + while (is_part_of_name (*p) && *p != '.' && *p != ':' && *p != '*') + p++; + c = *p; + *p = '\0'; + symbolp = symbol_find (start); + *p = c; + if (symbolp != NULL && S_GET_SEGMENT (symbolp) == reg_section) + { + *ccp = p; + return S_GET_VALUE (symbolp); + } + } + + return 0; +} + +/* The lexer. */ + +static int +yylex () +{ + enum m68k_register reg; + char *s; + int parens; + int c = 0; + int tail = 0; + char *hold; + + if (*str == ' ') + ++str; + + if (*str == '\0') + return 0; + + /* Various special characters are just returned directly. */ + switch (*str) + { + case '@': + /* In MRI mode, this can be the start of an octal number. */ + if (flag_mri) + { + if (isdigit (str[1]) + || ((str[1] == '+' || str[1] == '-') + && isdigit (str[2]))) + break; + } + /* Fall through. */ + case '#': + case '&': + case ',': + case ')': + case '/': + case '[': + case ']': + return *str++; + case '+': + /* It so happens that a '+' can only appear at the end of an + operand. If it appears anywhere else, it must be a unary + plus on an expression. */ + if (str[1] == '\0') + return *str++; + break; + case '-': + /* A '-' can only appear in -(ar), rn-rn, or ar@-. If it + appears anywhere else, it must be a unary minus on an + expression. */ + if (str[1] == '\0') + return *str++; + s = str + 1; + if (*s == '(') + ++s; + if (m68k_reg_parse (&s) != 0) + return *str++; + break; + case '(': + /* A '(' can only appear in `(reg)', `(expr,...', `([', `@(', or + `)('. If it appears anywhere else, it must be starting an + expression. */ + if (str[1] == '[' + || (str > strorig + && (str[-1] == '@' + || str[-1] == ')'))) + return *str++; + s = str + 1; + if (m68k_reg_parse (&s) != 0) + return *str++; + /* Check for the case of '(expr,...' by scanning ahead. If we + find a comma outside of balanced parentheses, we return '('. + If we find an unbalanced right parenthesis, then presumably + the '(' really starts an expression. */ + parens = 0; + for (s = str + 1; *s != '\0'; s++) + { + if (*s == '(') + ++parens; + else if (*s == ')') + { + if (parens == 0) + break; + --parens; + } + else if (*s == ',' && parens == 0) + { + /* A comma can not normally appear in an expression, so + this is a case of '(expr,...'. */ + return *str++; + } + } + } + + /* See if it's a register. */ + + reg = m68k_reg_parse (&str); + if (reg != 0) + { + int ret; + + yylval.reg = reg; + + if (reg >= DATA0 && reg <= DATA7) + ret = DR; + else if (reg >= ADDR0 && reg <= ADDR7) + ret = AR; + else if (reg >= FP0 && reg <= FP7) + return FPR; + else if (reg == FPI + || reg == FPS + || reg == FPC) + return FPCR; + else if (reg == PC) + return LPC; + else if (reg >= ZDATA0 && reg <= ZDATA7) + ret = ZDR; + else if (reg >= ZADDR0 && reg <= ZADDR7) + ret = ZAR; + else if (reg == ZPC) + return LZPC; + else + return CREG; + + /* If we get here, we have a data or address register. We + must check for a size or scale; if we find one, we must + return INDEXREG. */ + + s = str; + + if (*s != '.' && *s != ':' && *s != '*') + return ret; + + yylval.indexreg.reg = reg; + + if (*s != '.' && *s != ':') + yylval.indexreg.size = SIZE_UNSPEC; + else + { + ++s; + switch (*s) + { + case 'w': + case 'W': + yylval.indexreg.size = SIZE_WORD; + ++s; + break; + case 'l': + case 'L': + yylval.indexreg.size = SIZE_LONG; + ++s; + break; + default: + yyerror (_("illegal size specification")); + yylval.indexreg.size = SIZE_UNSPEC; + break; + } + } + + yylval.indexreg.scale = 1; + + if (*s == '*' || *s == ':') + { + expressionS scale; + + ++s; + + hold = input_line_pointer; + input_line_pointer = s; + expression (&scale); + s = input_line_pointer; + input_line_pointer = hold; + + if (scale.X_op != O_constant) + yyerror (_("scale specification must resolve to a number")); + else + { + switch (scale.X_add_number) + { + case 1: + case 2: + case 4: + case 8: + yylval.indexreg.scale = scale.X_add_number; + break; + default: + yyerror (_("invalid scale value")); + break; + } + } + } + + str = s; + + return INDEXREG; + } + + /* It must be an expression. Before we call expression, we need to + look ahead to see if there is a size specification. We must do + that first, because otherwise foo.l will be treated as the symbol + foo.l, rather than as the symbol foo with a long size + specification. The grammar requires that all expressions end at + the end of the operand, or with ',', '(', ']', ')'. */ + + parens = 0; + for (s = str; *s != '\0'; s++) + { + if (*s == '(') + { + if (parens == 0 + && s > str + && (s[-1] == ')' || isalnum ((unsigned char) s[-1]))) + break; + ++parens; + } + else if (*s == ')') + { + if (parens == 0) + break; + --parens; + } + else if (parens == 0 + && (*s == ',' || *s == ']')) + break; + } + + yylval.exp.size = SIZE_UNSPEC; + if (s <= str + 2 + || (s[-2] != '.' && s[-2] != ':')) + tail = 0; + else + { + switch (s[-1]) + { + case 's': + case 'S': + case 'b': + case 'B': + yylval.exp.size = SIZE_BYTE; + break; + case 'w': + case 'W': + yylval.exp.size = SIZE_WORD; + break; + case 'l': + case 'L': + yylval.exp.size = SIZE_LONG; + break; + default: + break; + } + if (yylval.exp.size != SIZE_UNSPEC) + tail = 2; + } + +#ifdef OBJ_ELF + { + /* Look for @PLTPC, etc. */ + char *cp; + + yylval.exp.pic_reloc = pic_none; + cp = s - tail; + if (cp - 6 > str && cp[-6] == '@') + { + if (strncmp (cp - 6, "@PLTPC", 6) == 0) + { + yylval.exp.pic_reloc = pic_plt_pcrel; + tail += 6; + } + else if (strncmp (cp - 6, "@GOTPC", 6) == 0) + { + yylval.exp.pic_reloc = pic_got_pcrel; + tail += 6; + } + } + else if (cp - 4 > str && cp[-4] == '@') + { + if (strncmp (cp - 4, "@PLT", 4) == 0) + { + yylval.exp.pic_reloc = pic_plt_off; + tail += 4; + } + else if (strncmp (cp - 4, "@GOT", 4) == 0) + { + yylval.exp.pic_reloc = pic_got_off; + tail += 4; + } + } + } +#endif + + if (tail != 0) + { + c = s[-tail]; + s[-tail] = 0; + } + + hold = input_line_pointer; + input_line_pointer = str; + expression (&yylval.exp.exp); + str = input_line_pointer; + input_line_pointer = hold; + + if (tail != 0) + { + s[-tail] = c; + str = s; + } + + return EXPR; +} + +/* Parse an m68k operand. This is the only function which is called + from outside this file. */ + +int +m68k_ip_op (s, oparg) + char *s; + struct m68k_op *oparg; +{ + memset (oparg, 0, sizeof *oparg); + oparg->error = NULL; + oparg->index.reg = ZDATA0; + oparg->index.scale = 1; + oparg->disp.exp.X_op = O_absent; + oparg->odisp.exp.X_op = O_absent; + + str = strorig = s; + op = oparg; + + return yyparse (); +} + +/* The error handler. */ + +static void +yyerror (s) + const char *s; +{ + op->error = s; +} |