diff options
Diffstat (limited to 'gas/config/rx-parse.y')
-rw-r--r-- | gas/config/rx-parse.y | 1555 |
1 files changed, 1555 insertions, 0 deletions
diff --git a/gas/config/rx-parse.y b/gas/config/rx-parse.y new file mode 100644 index 0000000..0bcafc8 --- /dev/null +++ b/gas/config/rx-parse.y @@ -0,0 +1,1555 @@ +/* rx-parse.y Renesas RX parser + Copyright 2008, 2009 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GAS; see the file COPYING. If not, write to the Free + Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ +%{ + +#include "as.h" +#include "safe-ctype.h" +#include "rx-defs.h" + +static int rx_lex (void); + +#define COND_EQ 0 +#define COND_NE 1 + +#define MEMEX 0x06 + +#define BSIZE 0 +#define WSIZE 1 +#define LSIZE 2 + +/* .sb .sw .l .uw */ +static int sizemap[] = { BSIZE, WSIZE, LSIZE, WSIZE }; + +/* Ok, here are the rules for using these macros... + + B*() is used to specify the base opcode bytes. Fields to be filled + in later, leave zero. Call this first. + + F() and FE() are used to fill in fields within the base opcode bytes. You MUST + call B*() before any F() or FE(). + + [UN]*O*(), PC*() appends operands to the end of the opcode. You + must call P() and B*() before any of these, so that the fixups + have the right byte location. + O = signed, UO = unsigned, NO = negated, PC = pcrel + + IMM() adds an immediate and fills in the field for it. + NIMM() same, but negates the immediate. + NBIMM() same, but negates the immediate, for sbb. + DSP() adds a displacement, and fills in the field for it. + + Note that order is significant for the O, IMM, and DSP macros, as + they append their data to the operand buffer in the order that you + call them. + + Use "disp" for displacements whenever possible; this handles the + "0" case properly. */ + +#define B1(b1) rx_base1 (b1) +#define B2(b1, b2) rx_base2 (b1, b2) +#define B3(b1, b2, b3) rx_base3 (b1, b2, b3) +#define B4(b1, b2, b3, b4) rx_base4 (b1, b2, b3, b4) + +/* POS is bits from the MSB of the first byte to the LSB of the last byte. */ +#define F(val,pos,sz) rx_field (val, pos, sz) +#define FE(exp,pos,sz) rx_field (exp_val (exp), pos, sz); + +#define O1(v) rx_op (v, 1, RXREL_SIGNED) +#define O2(v) rx_op (v, 2, RXREL_SIGNED) +#define O3(v) rx_op (v, 3, RXREL_SIGNED) +#define O4(v) rx_op (v, 4, RXREL_SIGNED) + +#define UO1(v) rx_op (v, 1, RXREL_UNSIGNED) +#define UO2(v) rx_op (v, 2, RXREL_UNSIGNED) +#define UO3(v) rx_op (v, 3, RXREL_UNSIGNED) +#define UO4(v) rx_op (v, 4, RXREL_UNSIGNED) + +#define NO1(v) rx_op (v, 1, RXREL_NEGATIVE) +#define NO2(v) rx_op (v, 2, RXREL_NEGATIVE) +#define NO3(v) rx_op (v, 3, RXREL_NEGATIVE) +#define NO4(v) rx_op (v, 4, RXREL_NEGATIVE) + +#define PC1(v) rx_op (v, 1, RXREL_PCREL) +#define PC2(v) rx_op (v, 2, RXREL_PCREL) +#define PC3(v) rx_op (v, 3, RXREL_PCREL) + +#define IMM(v,pos) F (immediate (v, RXREL_SIGNED, pos), pos, 2); \ + if (v.X_op != O_constant && v.X_op != O_big) rx_linkrelax_imm (pos) +#define NIMM(v,pos) F (immediate (v, RXREL_NEGATIVE, pos), pos, 2) +#define NBIMM(v,pos) F (immediate (v, RXREL_NEGATIVE_BORROW, pos), pos, 2) +#define DSP(v,pos,msz) if (!v.X_md) rx_relax (RX_RELAX_DISP, pos); \ + else rx_linkrelax_dsp (pos); \ + F (displacement (v, msz), pos, 2) + +#define id24(a,b2,b3) B3 (0xfb+a, b2, b3) + +static int rx_intop (expressionS, int); +static int rx_uintop (expressionS, int); +static int rx_disp3op (expressionS); +static int rx_disp5op (expressionS *, int); +static int rx_disp5op0 (expressionS *, int); +static int exp_val (expressionS exp); +static expressionS zero_expr (void); +static int immediate (expressionS, int, int); +static int displacement (expressionS, int); +static void rtsd_immediate (expressionS); + +static int need_flag = 0; +static int rx_in_brackets = 0; +static int rx_last_token = 0; +static char * rx_init_start; +static char * rx_last_exp_start = 0; +static int sub_op; +static int sub_op2; + +#define YYDEBUG 1 +#define YYERROR_VERBOSE 1 + +%} + +%name-prefix="rx_" + +%union { + int regno; + expressionS exp; +} + +%type <regno> REG FLAG CREG BCND BMCND SCCND +%type <regno> flag bwl bw memex +%type <exp> EXPR disp + +%token REG FLAG CREG + +%token EXPR UNKNOWN_OPCODE IS_OPCODE + +%token DOT_S DOT_B DOT_W DOT_L DOT_A DOT_UB DOT_UW + +%token ABS ADC ADD AND_ +%token BCLR BCND BMCND BNOT BRA BRK BSET BSR BTST +%token CLRPSW CMP +%token DBT DIV DIVU +%token EDIV EDIVU EMUL EMULU +%token FADD FCMP FDIV FMUL FREIT FSUB FTOI +%token INT ITOF +%token JMP JSR +%token MACHI MACLO MAX MIN MOV MOVU MUL MULHI MULLO MULU MVFACHI MVFACMI MVFACLO +%token MVFC MVTACHI MVTACLO MVTC MVTIPL +%token NEG NOP NOT +%token OR +%token POP POPC POPM PUSH PUSHA PUSHC PUSHM +%token RACW REIT REVL REVW RMPA ROLC RORC ROTL ROTR ROUND RTE RTFI RTS RTSD +%token SAT SATR SBB SCCND SCMPU SETPSW SHAR SHLL SHLR SMOVB SMOVF +%token SMOVU SSTR STNZ STOP STZ SUB SUNTIL SWHILE +%token TST +%token WAIT +%token XCHG XOR + +%% +/* ====================================================================== */ + +statement : + + UNKNOWN_OPCODE + { as_bad (_("Unknown opcode: %s"), rx_init_start); } + +/* ---------------------------------------------------------------------- */ + + | BRK + { B1 (0x00); } + + | DBT + { B1 (0x01); } + + | RTS + { B1 (0x02); } + + | NOP + { B1 (0x03); } + +/* ---------------------------------------------------------------------- */ + + | BRA EXPR + { if (rx_disp3op ($2)) + { B1 (0x08); rx_disp3 ($2, 5); } + else if (rx_intop ($2, 8)) + { B1 (0x2e); PC1 ($2); } + else if (rx_intop ($2, 16)) + { B1 (0x38); PC2 ($2); } + else if (rx_intop ($2, 24)) + { B1 (0x04); PC3 ($2); } + else + { rx_relax (RX_RELAX_BRANCH, 0); + rx_linkrelax_branch (); + /* We'll convert this to a longer one later if needed. */ + B1 (0x08); rx_disp3 ($2, 5); } } + + | BRA DOT_A EXPR + { B1 (0x04); PC3 ($3); } + + | BRA DOT_S EXPR + { B1 (0x08); rx_disp3 ($3, 5); } + +/* ---------------------------------------------------------------------- */ + + | BSR EXPR + { if (rx_intop ($2, 16)) + { B1 (0x39); PC2 ($2); } + else if (rx_intop ($2, 24)) + { B1 (0x05); PC3 ($2); } + else + { rx_relax (RX_RELAX_BRANCH, 0); + rx_linkrelax_branch (); + B1 (0x39); PC2 ($2); } } + | BSR DOT_A EXPR + { B1 (0x05), PC3 ($3); } + +/* ---------------------------------------------------------------------- */ + + | BCND DOT_S EXPR + { if ($1 == COND_EQ || $1 == COND_NE) + { B1 ($1 == COND_EQ ? 0x10 : 0x18); rx_disp3 ($3, 5); } + else + as_bad (_("Only BEQ and BNE may have .S")); } + +/* ---------------------------------------------------------------------- */ + + | BCND DOT_B EXPR + { B1 (0x20); F ($1, 4, 4); PC1 ($3); } + + | BRA DOT_B EXPR + { B1 (0x2e), PC1 ($3); } + +/* ---------------------------------------------------------------------- */ + + | BRA DOT_W EXPR + { B1 (0x38), PC2 ($3); } + | BSR DOT_W EXPR + { B1 (0x39), PC2 ($3); } + | BCND DOT_W EXPR + { if ($1 == COND_EQ || $1 == COND_NE) + { B1 ($1 == COND_EQ ? 0x3a : 0x3b); PC2 ($3); } + else + as_bad (_("Only BEQ and BNE may have .W")); } + | BCND EXPR + { if ($1 == COND_EQ || $1 == COND_NE) + { + rx_relax (RX_RELAX_BRANCH, 0); + rx_linkrelax_branch (); + B1 ($1 == COND_EQ ? 0x10 : 0x18); rx_disp3 ($2, 5); + } + else + { + rx_relax (RX_RELAX_BRANCH, 0); + /* This is because we might turn it into a + jump-over-jump long branch. */ + rx_linkrelax_branch (); + B1 (0x20); F ($1, 4, 4); PC1 ($2); + } } + +/* ---------------------------------------------------------------------- */ + + | MOV DOT_B '#' EXPR ',' disp '[' REG ']' + /* rx_disp5op changes the value if it succeeds, so keep it last. */ + { if ($8 <= 7 && rx_uintop ($4, 8) && rx_disp5op0 (&$6, BSIZE)) + { B2 (0x3c, 0); rx_field5s2 ($6); F ($8, 9, 3); O1 ($4); } + else + { B2 (0xf8, 0x04); F ($8, 8, 4); DSP ($6, 6, BSIZE); O1 ($4); + if ($4.X_op != O_constant && $4.X_op != O_big) rx_linkrelax_imm (12); } } + + | MOV DOT_W '#' EXPR ',' disp '[' REG ']' + { if ($8 <= 7 && rx_uintop ($4, 8) && rx_disp5op0 (&$6, WSIZE)) + { B2 (0x3d, 0); rx_field5s2 ($6); F ($8, 9, 3); O1 ($4); } + else + { B2 (0xf8, 0x01); F ($8, 8, 4); DSP ($6, 6, WSIZE); IMM ($4, 12); } } + + | MOV DOT_L '#' EXPR ',' disp '[' REG ']' + { if ($8 <= 7 && rx_uintop ($4, 8) && rx_disp5op0 (&$6, LSIZE)) + { B2 (0x3e, 0); rx_field5s2 ($6); F ($8, 9, 3); O1 ($4); } + else + { B2 (0xf8, 0x02); F ($8, 8, 4); DSP ($6, 6, LSIZE); IMM ($4, 12); } } + +/* ---------------------------------------------------------------------- */ + + | RTSD '#' EXPR ',' REG '-' REG + { B2 (0x3f, 0); F ($5, 8, 4); F ($7, 12, 4); rtsd_immediate ($3); + if ($5 == 0) + rx_error (_("RTSD cannot pop R0")); + if ($5 > $7) + rx_error (_("RTSD first reg must be <= second reg")); } + +/* ---------------------------------------------------------------------- */ + + | CMP REG ',' REG + { B2 (0x47, 0); F ($2, 8, 4); F ($4, 12, 4); } + +/* ---------------------------------------------------------------------- */ + + | CMP disp '[' REG ']' DOT_UB ',' REG + { B2 (0x44, 0); F ($4, 8, 4); F ($8, 12, 4); DSP ($2, 6, BSIZE); } + + | CMP disp '[' REG ']' memex ',' REG + { B3 (MEMEX, 0x04, 0); F ($6, 8, 2); F ($4, 16, 4); F ($8, 20, 4); DSP ($2, 14, sizemap[$6]); } + +/* ---------------------------------------------------------------------- */ + + | MOVU bw REG ',' REG + { B2 (0x5b, 0x00); F ($2, 5, 1); F ($3, 8, 4); F ($5, 12, 4); } + +/* ---------------------------------------------------------------------- */ + + | MOVU bw '[' REG ']' ',' REG + { B2 (0x58, 0x00); F ($2, 5, 1); F ($4, 8, 4); F ($7, 12, 4); } + + | MOVU bw EXPR '[' REG ']' ',' REG + { if ($5 <= 7 && $8 <= 7 && rx_disp5op (&$3, $2)) + { B2 (0xb0, 0); F ($2, 4, 1); F ($5, 9, 3); F ($8, 13, 3); rx_field5s ($3); } + else + { B2 (0x58, 0x00); F ($2, 5, 1); F ($5, 8, 4); F ($8, 12, 4); DSP ($3, 6, $2); } } + +/* ---------------------------------------------------------------------- */ + + | SUB '#' EXPR ',' REG + { if (rx_uintop ($3, 4)) + { B2 (0x60, 0); FE ($3, 8, 4); F ($5, 12, 4); } + else + /* This is really an add, but we negate the immediate. */ + { B2 (0x38, 0); F ($5, 8, 4); F ($5, 12, 4); NIMM ($3, 6); } } /* ? */ + + | CMP '#' EXPR ',' REG + { if (rx_uintop ($3, 4)) + { B2 (0x61, 0); FE ($3, 8, 4); F ($5, 12, 4); } + else if (rx_uintop ($3, 8)) + { B2 (0x75, 0x50); F ($5, 12, 4); UO1 ($3); } + else + { B2 (0x74, 0x00); F ($5, 12, 4); IMM ($3, 6); } } + + | ADD '#' EXPR ',' REG + { if (rx_uintop ($3, 4)) + { B2 (0x62, 0); FE ($3, 8, 4); F ($5, 12, 4); } + else + { B2 (0x70, 0); F ($5, 8, 4); F ($5, 12, 4); IMM ($3, 6); } } + + | MUL '#' EXPR ',' REG + { if (rx_uintop ($3, 4)) + { B2 (0x63, 0); FE ($3, 8, 4); F ($5, 12, 4); } + else + { B2 (0x74, 0x10); F ($5, 12, 4); IMM ($3, 6); } } + + | AND_ '#' EXPR ',' REG + { if (rx_uintop ($3, 4)) + { B2 (0x64, 0); FE ($3, 8, 4); F ($5, 12, 4); } + else + { B2 (0x74, 0x20); F ($5, 12, 4); IMM ($3, 6); } } + + | OR '#' EXPR ',' REG + { if (rx_uintop ($3, 4)) + { B2 (0x65, 0); FE ($3, 8, 4); F ($5, 12, 4); } + else + { B2 (0x74, 0x30); F ($5, 12, 4); IMM ($3, 6); } } + + | MOV DOT_L '#' EXPR ',' REG + { if (rx_uintop ($4, 4)) + { B2 (0x66, 0); FE ($4, 8, 4); F ($6, 12, 4); } + else if (rx_uintop ($4, 8)) + { B2 (0x75, 0x40); F ($6, 12, 4); UO1 ($4); } + else + { B2 (0xfb, 0x02); F ($6, 8, 4); IMM ($4, 12); } } + + | MOV '#' EXPR ',' REG + { if (rx_uintop ($3, 4)) + { B2 (0x66, 0); FE ($3, 8, 4); F ($5, 12, 4); } + else if (rx_uintop ($3, 8)) + { B2 (0x75, 0x40); F ($5, 12, 4); UO1 ($3); } + else + { B2 (0xfb, 0x02); F ($5, 8, 4); IMM ($3, 12); } } + +/* ---------------------------------------------------------------------- */ + + | RTSD '#' EXPR + { B1 (0x67); rtsd_immediate ($3); } + +/* ---------------------------------------------------------------------- */ + + | SHLR { sub_op = 0; } op_shift + | SHAR { sub_op = 1; } op_shift + | SHLL { sub_op = 2; } op_shift + +/* ---------------------------------------------------------------------- */ + + | PUSHM REG '-' REG + { + if ($2 == $4) + { B2 (0x7e, 0x80); F (LSIZE, 10, 2); F ($2, 12, 4); } + else + { B2 (0x6e, 0); F ($2, 8, 4); F ($4, 12, 4); } + if ($2 == 0) + rx_error (_("PUSHM cannot push R0")); + if ($2 > $4) + rx_error (_("PUSHM first reg must be <= second reg")); } + +/* ---------------------------------------------------------------------- */ + + | POPM REG '-' REG + { + if ($2 == $4) + { B2 (0x7e, 0xb0); F ($2, 12, 4); } + else + { B2 (0x6f, 0); F ($2, 8, 4); F ($4, 12, 4); } + if ($2 == 0) + rx_error (_("POPM cannot pop R0")); + if ($2 > $4) + rx_error (_("POPM first reg must be <= second reg")); } + +/* ---------------------------------------------------------------------- */ + + | ADD '#' EXPR ',' REG ',' REG + { B2 (0x70, 0x00); F ($5, 8, 4); F ($7, 12, 4); IMM ($3, 6); } + +/* ---------------------------------------------------------------------- */ + + | INT '#' EXPR + { B2(0x75, 0x60), UO1 ($3); } + +/* ---------------------------------------------------------------------- */ + + | BSET '#' EXPR ',' REG + { B2 (0x78, 0); FE ($3, 7, 5); F ($5, 12, 4); } + | BCLR '#' EXPR ',' REG + { B2 (0x7a, 0); FE ($3, 7, 5); F ($5, 12, 4); } + +/* ---------------------------------------------------------------------- */ + + | BTST '#' EXPR ',' REG + { B2 (0x7c, 0x00); FE ($3, 7, 5); F ($5, 12, 4); } + +/* ---------------------------------------------------------------------- */ + + | SAT REG + { B2 (0x7e, 0x30); F ($2, 12, 4); } + | RORC REG + { B2 (0x7e, 0x40); F ($2, 12, 4); } + | ROLC REG + { B2 (0x7e, 0x50); F ($2, 12, 4); } + +/* ---------------------------------------------------------------------- */ + + | PUSH bwl REG + { B2 (0x7e, 0x80); F ($2, 10, 2); F ($3, 12, 4); } + +/* ---------------------------------------------------------------------- */ + + | POP REG + { B2 (0x7e, 0xb0); F ($2, 12, 4); } + +/* ---------------------------------------------------------------------- */ + + | PUSHC CREG + { if ($2 < 16) + { B2 (0x7e, 0xc0); F ($2, 12, 4); } + else + as_bad (_("PUSHC can only push the first 16 control registers")); } + +/* ---------------------------------------------------------------------- */ + + | POPC CREG + { if ($2 < 16) + { B2 (0x7e, 0xe0); F ($2, 12, 4); } + else + as_bad (_("POPC can only pop the first 16 control registers")); } + +/* ---------------------------------------------------------------------- */ + + | SETPSW flag + { B2 (0x7f, 0xa0); F ($2, 12, 4); } + | CLRPSW flag + { B2 (0x7f, 0xb0); F ($2, 12, 4); } + +/* ---------------------------------------------------------------------- */ + + | JMP REG + { B2 (0x7f, 0x00); F ($2, 12, 4); } + | JSR REG + { B2 (0x7f, 0x10); F ($2, 12, 4); } + | BRA opt_l REG + { B2 (0x7f, 0x40); F ($3, 12, 4); } + | BSR opt_l REG + { B2 (0x7f, 0x50); F ($3, 12, 4); } + +/* ---------------------------------------------------------------------- */ + + | SCMPU + { B2 (0x7f, 0x83); } + | SMOVU + { B2 (0x7f, 0x87); } + | SMOVB + { B2 (0x7f, 0x8b); } + | SMOVF + { B2 (0x7f, 0x8f); } + +/* ---------------------------------------------------------------------- */ + + | SUNTIL bwl + { B2 (0x7f, 0x80); F ($2, 14, 2); } + | SWHILE bwl + { B2 (0x7f, 0x84); F ($2, 14, 2); } + | SSTR bwl + { B2 (0x7f, 0x88); F ($2, 14, 2); } + +/* ---------------------------------------------------------------------- */ + + | RMPA bwl + { B2 (0x7f, 0x8c); F ($2, 14, 2); } + +/* ---------------------------------------------------------------------- */ + + | RTFI + { B2 (0x7f, 0x94); } + | RTE + { B2 (0x7f, 0x95); } + | WAIT + { B2 (0x7f, 0x96); } + | SATR + { B2 (0x7f, 0x93); } + +/* ---------------------------------------------------------------------- */ + + | MVTIPL '#' EXPR + { B2 (0x7f, 0x98); FE ($3, 13, 3); } + +/* ---------------------------------------------------------------------- */ + + /* rx_disp5op changes the value if it succeeds, so keep it last. */ + | MOV bwl REG ',' EXPR '[' REG ']' + { if ($3 <= 7 && $7 <= 7 && rx_disp5op (&$5, $2)) + { B2 (0x80, 0); F ($2, 2, 2); F ($7, 9, 3); F ($3, 13, 3); rx_field5s ($5); } + else + { B2 (0xc3, 0x00); F ($2, 2, 2); F ($7, 8, 4); F ($3, 12, 4); DSP ($5, 4, $2); }} + +/* ---------------------------------------------------------------------- */ + + | MOV bwl EXPR '[' REG ']' ',' REG + { if ($5 <= 7 && $8 <= 7 && rx_disp5op (&$3, $2)) + { B2 (0x88, 0); F ($2, 2, 2); F ($5, 9, 3); F ($8, 13, 3); rx_field5s ($3); } + else + { B2 (0xcc, 0x00); F ($2, 2, 2); F ($5, 8, 4); F ($8, 12, 4); DSP ($3, 6, $2); } } + +/* ---------------------------------------------------------------------- */ + + /* MOV a,b - if a is a reg and b is mem, src and dest are + swapped. */ + + /* We don't use "disp" here because it causes a shift/reduce + conflict with the other displacement-less patterns. */ + + | MOV bwl REG ',' '[' REG ']' + { B2 (0xc3, 0x00); F ($2, 2, 2); F ($6, 8, 4); F ($3, 12, 4); } + +/* ---------------------------------------------------------------------- */ + + | MOV bwl '[' REG ']' ',' disp '[' REG ']' + { B2 (0xc0, 0); F ($2, 2, 2); F ($4, 8, 4); F ($9, 12, 4); DSP ($7, 4, $2); } + +/* ---------------------------------------------------------------------- */ + + | MOV bwl EXPR '[' REG ']' ',' disp '[' REG ']' + { B2 (0xc0, 0x00); F ($2, 2, 2); F ($5, 8, 4); F ($10, 12, 4); DSP ($3, 6, $2); DSP ($8, 4, $2); } + +/* ---------------------------------------------------------------------- */ + + | MOV bwl REG ',' REG + { B2 (0xcf, 0x00); F ($2, 2, 2); F ($3, 8, 4); F ($5, 12, 4); } + +/* ---------------------------------------------------------------------- */ + + | MOV bwl '[' REG ']' ',' REG + { B2 (0xcc, 0x00); F ($2, 2, 2); F ($4, 8, 4); F ($7, 12, 4); } + +/* ---------------------------------------------------------------------- */ + + | BSET '#' EXPR ',' disp '[' REG ']' DOT_B + { B2 (0xf0, 0x00); F ($7, 8, 4); FE ($3, 13, 3); DSP ($5, 6, BSIZE); } + | BCLR '#' EXPR ',' disp '[' REG ']' DOT_B + { B2 (0xf0, 0x08); F ($7, 8, 4); FE ($3, 13, 3); DSP ($5, 6, BSIZE); } + | BTST '#' EXPR ',' disp '[' REG ']' DOT_B + { B2 (0xf4, 0x00); F ($7, 8, 4); FE ($3, 13, 3); DSP ($5, 6, BSIZE); } + +/* ---------------------------------------------------------------------- */ + + | PUSH bwl disp '[' REG ']' + { B2 (0xf4, 0x08); F ($2, 14, 2); F ($5, 8, 4); DSP ($3, 6, $2); } + +/* ---------------------------------------------------------------------- */ + + | SBB { sub_op = 0; } op_dp20_rm + | NEG { sub_op = 1; sub_op2 = 1; } op_dp20_rms + | ADC { sub_op = 2; } op_dp20_rim + | ABS { sub_op = 3; sub_op2 = 2; } op_dp20_rms + | MAX { sub_op = 4; } op_dp20_rim + | MIN { sub_op = 5; } op_dp20_rim + | EMUL { sub_op = 6; } op_dp20_i + | EMULU { sub_op = 7; } op_dp20_i + | DIV { sub_op = 8; } op_dp20_rim + | DIVU { sub_op = 9; } op_dp20_rim + | TST { sub_op = 12; } op_dp20_rim + | XOR { sub_op = 13; } op_dp20_rim + | NOT { sub_op = 14; sub_op2 = 0; } op_dp20_rms + | STZ { sub_op = 14; } op_dp20_i + | STNZ { sub_op = 15; } op_dp20_i + +/* ---------------------------------------------------------------------- */ + + | EMUL { sub_op = 6; } op_xchg + | EMULU { sub_op = 7; } op_xchg + | XCHG { sub_op = 16; } op_xchg + | ITOF { sub_op = 17; } op_xchg + +/* ---------------------------------------------------------------------- */ + + | BSET REG ',' REG + { id24 (1, 0x63, 0x00); F ($4, 16, 4); F ($2, 20, 4); } + | BCLR REG ',' REG + { id24 (1, 0x67, 0x00); F ($4, 16, 4); F ($2, 20, 4); } + | BTST REG ',' REG + { id24 (1, 0x6b, 0x00); F ($4, 16, 4); F ($2, 20, 4); } + | BNOT REG ',' REG + { id24 (1, 0x6f, 0x00); F ($4, 16, 4); F ($2, 20, 4); } + + | BSET REG ',' disp '[' REG ']' DOT_B + { id24 (1, 0x60, 0x00); F ($6, 16, 4); F ($2, 20, 4); DSP ($4, 14, BSIZE); } + | BCLR REG ',' disp '[' REG ']' DOT_B + { id24 (1, 0x64, 0x00); F ($6, 16, 4); F ($2, 20, 4); DSP ($4, 14, BSIZE); } + | BTST REG ',' disp '[' REG ']' DOT_B + { id24 (1, 0x68, 0x00); F ($6, 16, 4); F ($2, 20, 4); DSP ($4, 14, BSIZE); } + | BNOT REG ',' disp '[' REG ']' DOT_B + { id24 (1, 0x6c, 0x00); F ($6, 16, 4); F ($2, 20, 4); DSP ($4, 14, BSIZE); } + +/* ---------------------------------------------------------------------- */ + + | FSUB { sub_op = 0; } float2_op + | FCMP { sub_op = 1; } float2_op + | FADD { sub_op = 2; } float2_op + | FMUL { sub_op = 3; } float2_op + | FDIV { sub_op = 4; } float2_op + | FTOI { sub_op = 5; } float2_op_ni + | ROUND { sub_op = 6; } float2_op_ni + +/* ---------------------------------------------------------------------- */ + + | SCCND DOT_L REG + { id24 (1, 0xdb, 0x00); F ($1, 20, 4); F ($3, 16, 4); } + | SCCND bwl disp '[' REG ']' + { id24 (1, 0xd0, 0x00); F ($1, 20, 4); F ($2, 12, 2); F ($5, 16, 4); DSP ($3, 14, $2); } + +/* ---------------------------------------------------------------------- */ + + | BMCND '#' EXPR ',' disp '[' REG ']' DOT_B + { id24 (1, 0xe0, 0x00); F ($1, 20, 4); FE ($3, 11, 3); + F ($7, 16, 4); DSP ($5, 14, BSIZE); } + +/* ---------------------------------------------------------------------- */ + + | BNOT '#' EXPR ',' disp '[' REG ']' DOT_B + { id24 (1, 0xe0, 0x0f); FE ($3, 11, 3); F ($7, 16, 4); + DSP ($5, 14, BSIZE); } + +/* ---------------------------------------------------------------------- */ + + | MULHI REG ',' REG + { id24 (2, 0x00, 0x00); F ($2, 16, 4); F ($4, 20, 4); } + | MULLO REG ',' REG + { id24 (2, 0x01, 0x00); F ($2, 16, 4); F ($4, 20, 4); } + | MACHI REG ',' REG + { id24 (2, 0x04, 0x00); F ($2, 16, 4); F ($4, 20, 4); } + | MACLO REG ',' REG + { id24 (2, 0x05, 0x00); F ($2, 16, 4); F ($4, 20, 4); } + +/* ---------------------------------------------------------------------- */ + + /* We don't have syntax for these yet. */ + | MVTACHI REG + { id24 (2, 0x17, 0x00); F ($2, 20, 4); } + | MVTACLO REG + { id24 (2, 0x17, 0x10); F ($2, 20, 4); } + | MVFACHI REG + { id24 (2, 0x1f, 0x00); F ($2, 20, 4); } + | MVFACMI REG + { id24 (2, 0x1f, 0x20); F ($2, 20, 4); } + | MVFACLO REG + { id24 (2, 0x1f, 0x10); F ($2, 20, 4); } + + | RACW '#' EXPR + { id24 (2, 0x18, 0x00); + if (rx_uintop ($3, 4) && $3.X_add_number == 1) + ; + else if (rx_uintop ($3, 4) && $3.X_add_number == 2) + F (1, 19, 1); + else + as_bad (_("RACW expects #1 or #2"));} + +/* ---------------------------------------------------------------------- */ + + | MOV bwl REG ',' '[' REG '+' ']' + { id24 (2, 0x20, 0); F ($2, 14, 2); F ($6, 16, 4); F ($3, 20, 4); } + | MOV bwl REG ',' '[' '-' REG ']' + { id24 (2, 0x24, 0); F ($2, 14, 2); F ($7, 16, 4); F ($3, 20, 4); } + +/* ---------------------------------------------------------------------- */ + + | MOV bwl '[' REG '+' ']' ',' REG + { id24 (2, 0x28, 0); F ($2, 14, 2); F ($4, 16, 4); F ($8, 20, 4); } + | MOV bwl '[' '-' REG ']' ',' REG + { id24 (2, 0x2c, 0); F ($2, 14, 2); F ($5, 16, 4); F ($8, 20, 4); } + +/* ---------------------------------------------------------------------- */ + + | MOVU bw '[' REG '+' ']' ',' REG + { id24 (2, 0x38, 0); F ($2, 15, 1); F ($4, 16, 4); F ($8, 20, 4); } + | MOVU bw '[' '-' REG ']' ',' REG + { id24 (2, 0x3c, 0); F ($2, 15, 1); F ($5, 16, 4); F ($8, 20, 4); } + +/* ---------------------------------------------------------------------- */ + + | ROTL { sub_op = 6; } op_shift_rot + | ROTR { sub_op = 4; } op_shift_rot + | REVW { sub_op = 5; } op_shift_rot + | REVL { sub_op = 7; } op_shift_rot + +/* ---------------------------------------------------------------------- */ + + | MVTC REG ',' CREG + { id24 (2, 0x68, 0x00); F ($4 % 16, 20, 4); F ($4 / 16, 15, 1); + F ($2, 16, 4); } + +/* ---------------------------------------------------------------------- */ + + | MVFC CREG ',' REG + { id24 (2, 0x6a, 0); F ($2, 15, 5); F ($4, 20, 4); } + +/* ---------------------------------------------------------------------- */ + + | ROTL '#' EXPR ',' REG + { id24 (2, 0x6e, 0); FE ($3, 15, 5); F ($5, 20, 4); } + | ROTR '#' EXPR ',' REG + { id24 (2, 0x6c, 0); FE ($3, 15, 5); F ($5, 20, 4); } + +/* ---------------------------------------------------------------------- */ + + | MVTC '#' EXPR ',' CREG + { id24 (2, 0x73, 0x00); F ($5, 19, 5); IMM ($3, 12); } + +/* ---------------------------------------------------------------------- */ + + | BMCND '#' EXPR ',' REG + { id24 (2, 0xe0, 0x00); F ($1, 16, 4); FE ($3, 11, 5); + F ($5, 20, 4); } + +/* ---------------------------------------------------------------------- */ + + | BNOT '#' EXPR ',' REG + { id24 (2, 0xe0, 0xf0); FE ($3, 11, 5); F ($5, 20, 4); } + +/* ---------------------------------------------------------------------- */ + + | MOV bwl REG ',' '[' REG ',' REG ']' + { id24 (3, 0x00, 0); F ($2, 10, 2); F ($6, 12, 4); F ($8, 16, 4); F ($3, 20, 4); } + + | MOV bwl '[' REG ',' REG ']' ',' REG + { id24 (3, 0x40, 0); F ($2, 10, 2); F ($4, 12, 4); F ($6, 16, 4); F ($9, 20, 4); } + + | MOVU bw '[' REG ',' REG ']' ',' REG + { id24 (3, 0xc0, 0); F ($2, 10, 2); F ($4, 12, 4); F ($6, 16, 4); F ($9, 20, 4); } + +/* ---------------------------------------------------------------------- */ + + | SUB { sub_op = 0; } op_subadd + | ADD { sub_op = 2; } op_subadd + | MUL { sub_op = 3; } op_subadd + | AND_ { sub_op = 4; } op_subadd + | OR { sub_op = 5; } op_subadd + +/* ---------------------------------------------------------------------- */ +/* There is no SBB #imm so we fake it with ADC. */ + + | SBB '#' EXPR ',' REG + { id24 (2, 0x70, 0x20); F ($5, 20, 4); NBIMM ($3, 12); } + +/* ---------------------------------------------------------------------- */ + + ; + +/* ====================================================================== */ + +op_subadd + : REG ',' REG + { B2 (0x43 + (sub_op<<2), 0); F ($1, 8, 4); F ($3, 12, 4); } + | disp '[' REG ']' DOT_UB ',' REG + { B2 (0x40 + (sub_op<<2), 0); F ($3, 8, 4); F ($7, 12, 4); DSP ($1, 6, BSIZE); } + | disp '[' REG ']' memex ',' REG + { B3 (MEMEX, sub_op<<2, 0); F ($5, 8, 2); F ($3, 16, 4); F ($7, 20, 4); DSP ($1, 14, sizemap[$5]); } + | REG ',' REG ',' REG + { id24 (4, sub_op<<4, 0), F ($5, 12, 4), F ($1, 16, 4), F ($3, 20, 4); } + ; + +/* sbb, neg, adc, abs, max, min, div, divu, tst, not, xor, stz, stnz, emul, emulu */ + +op_dp20_rm + : REG ',' REG + { id24 (1, 0x03 + (sub_op<<2), 0x00); F ($1, 16, 4); F ($3, 20, 4); } + | disp '[' REG ']' DOT_UB ',' REG + { id24 (1, 0x00 + (sub_op<<2), 0x00); F ($3, 16, 4); F ($7, 20, 4); DSP ($1, 14, BSIZE); } + | disp '[' REG ']' memex ',' REG + { B4 (MEMEX, 0x20 + ($5 << 6), 0x00 + sub_op, 0x00); + F ($3, 24, 4); F ($7, 28, 4); DSP ($1, 14, sizemap[$5]); } + ; + +op_dp20_i + : '#' EXPR ',' REG + { id24 (2, 0x70, sub_op<<4); F ($4, 20, 4); IMM ($2, 12); } + ; + +op_dp20_rim + : op_dp20_rm + | op_dp20_i + ; + +op_dp20_rms + : op_dp20_rm + | REG + { B2 (0x7e, sub_op2 << 4); F ($1, 12, 4); } + ; + +/* xchg, itof, emul, emulu */ +op_xchg + : REG ',' REG + { id24 (1, 0x03 + (sub_op<<2), 0); F ($1, 16, 4); F ($3, 20, 4); } + | disp '[' REG ']' DOT_UB ',' REG + { id24 (1, 0x00 + (sub_op<<2), 0); F ($3, 16, 4); F ($7, 20, 4); DSP ($1, 14, BSIZE); } + | disp '[' REG ']' memex ',' REG + { B4 (MEMEX, 0x20, 0x00 + sub_op, 0); F ($5, 8, 2); F ($3, 24, 4); F ($7, 28, 4); + DSP ($1, 14, sizemap[$5]); } + ; + +/* 000:SHLR, 001:SHAR, 010:SHLL, 011:-, 100:ROTR, 101:REVW, 110:ROTL, 111:REVL */ +op_shift_rot + : REG ',' REG + { id24 (2, 0x60 + sub_op, 0); F ($1, 16, 4); F ($3, 20, 4); } + ; +op_shift + : '#' EXPR ',' REG + { B2 (0x68 + (sub_op<<1), 0); FE ($2, 7, 5); F ($4, 12, 4); } + | '#' EXPR ',' REG ',' REG + { id24 (2, 0x80 + (sub_op << 5), 0); FE ($2, 11, 5); F ($4, 16, 4); F ($6, 20, 4); } + | op_shift_rot + ; + + + +float2_op + : '#' EXPR ',' REG + { id24 (2, 0x72, sub_op << 4); F ($4, 20, 4); O4 ($2); } + | float2_op_ni + ; +float2_op_ni + : REG ',' REG + { id24 (1, 0x83 + (sub_op << 2), 0); F ($1, 16, 4); F ($3, 20, 4); } + | disp '[' REG ']' opt_l ',' REG + { id24 (1, 0x80 + (sub_op << 2), 0); F ($3, 16, 4); F ($7, 20, 4); DSP ($1, 14, LSIZE); } + ; + +/* ====================================================================== */ + +disp : { $$ = zero_expr (); } + | EXPR { $$ = $1; } + ; + +flag : { need_flag = 1; } FLAG { need_flag = 0; $$ = $2; } + ; + +/* DOT_UB is not listed here, it's handled with a separate pattern. */ +/* Use sizemap[$n] to get LSIZE etc. */ +memex : DOT_B { $$ = 0; } + | DOT_W { $$ = 1; } + | { $$ = 2; } + | DOT_L { $$ = 2; } + | DOT_UW { $$ = 3; } + ; + +bwl : { $$ = LSIZE; } + | DOT_B { $$ = BSIZE; } + | DOT_W { $$ = WSIZE; } + | DOT_L { $$ = LSIZE; } + ; + +bw : { $$ = 1; } + | DOT_B { $$ = 0; } + | DOT_W { $$ = 1; } + ; + +opt_l : {} + | DOT_L {} + ; + +%% +/* ====================================================================== */ + +static struct +{ + const char * string; + int token; + int val; +} +token_table[] = +{ + { "r0", REG, 0 }, + { "r1", REG, 1 }, + { "r2", REG, 2 }, + { "r3", REG, 3 }, + { "r4", REG, 4 }, + { "r5", REG, 5 }, + { "r6", REG, 6 }, + { "r7", REG, 7 }, + { "r8", REG, 8 }, + { "r9", REG, 9 }, + { "r10", REG, 10 }, + { "r11", REG, 11 }, + { "r12", REG, 12 }, + { "r13", REG, 13 }, + { "r14", REG, 14 }, + { "r15", REG, 15 }, + + { "psw", CREG, 0 }, + { "pc", CREG, 1 }, + { "usp", CREG, 2 }, + { "fpsw", CREG, 3 }, + { "cpen", CREG, 4 }, + /* reserved */ + /* reserved */ + { "wr", CREG, 7 }, + + { "bpsw", CREG, 8 }, + { "bpc", CREG, 9 }, + { "isp", CREG, 10 }, + { "fintv", CREG, 11 }, + { "intb", CREG, 12 }, + + { "pbp", CREG, 16 }, + { "pben", CREG, 17 }, + + { "bbpsw", CREG, 24 }, + { "bbpc", CREG, 25 }, + + { ".s", DOT_S, 0 }, + { ".b", DOT_B, 0 }, + { ".w", DOT_W, 0 }, + { ".l", DOT_L, 0 }, + { ".a", DOT_A , 0}, + { ".ub", DOT_UB, 0 }, + { ".uw", DOT_UW , 0}, + + { "c", FLAG, 0 }, + { "z", FLAG, 1 }, + { "s", FLAG, 2 }, + { "o", FLAG, 3 }, + { "i", FLAG, 8 }, + { "u", FLAG, 9 }, + +#define OPC(x) { #x, x, IS_OPCODE } + OPC(ABS), + OPC(ADC), + OPC(ADD), + { "and", AND_, IS_OPCODE }, + OPC(BCLR), + OPC(BCND), + OPC(BMCND), + OPC(BNOT), + OPC(BRA), + OPC(BRK), + OPC(BSET), + OPC(BSR), + OPC(BTST), + OPC(CLRPSW), + OPC(CMP), + OPC(DBT), + OPC(DIV), + OPC(DIVU), + OPC(EDIV), + OPC(EDIVU), + OPC(EMUL), + OPC(EMULU), + OPC(FADD), + OPC(FCMP), + OPC(FDIV), + OPC(FMUL), + OPC(FREIT), + OPC(FSUB), + OPC(FTOI), + OPC(INT), + OPC(ITOF), + OPC(JMP), + OPC(JSR), + OPC(MVFACHI), + OPC(MVFACMI), + OPC(MVFACLO), + OPC(MVFC), + OPC(MVTACHI), + OPC(MVTACLO), + OPC(MVTC), + OPC(MVTIPL), + OPC(MACHI), + OPC(MACLO), + OPC(MAX), + OPC(MIN), + OPC(MOV), + OPC(MOVU), + OPC(MUL), + OPC(MULHI), + OPC(MULLO), + OPC(MULU), + OPC(NEG), + OPC(NOP), + OPC(NOT), + OPC(OR), + OPC(POP), + OPC(POPC), + OPC(POPM), + OPC(PUSH), + OPC(PUSHA), + OPC(PUSHC), + OPC(PUSHM), + OPC(RACW), + OPC(REIT), + OPC(REVL), + OPC(REVW), + OPC(RMPA), + OPC(ROLC), + OPC(RORC), + OPC(ROTL), + OPC(ROTR), + OPC(ROUND), + OPC(RTE), + OPC(RTFI), + OPC(RTS), + OPC(RTSD), + OPC(SAT), + OPC(SATR), + OPC(SBB), + OPC(SCCND), + OPC(SCMPU), + OPC(SETPSW), + OPC(SHAR), + OPC(SHLL), + OPC(SHLR), + OPC(SMOVB), + OPC(SMOVF), + OPC(SMOVU), + OPC(SSTR), + OPC(STNZ), + OPC(STOP), + OPC(STZ), + OPC(SUB), + OPC(SUNTIL), + OPC(SWHILE), + OPC(TST), + OPC(WAIT), + OPC(XCHG), + OPC(XOR), +}; + +#define NUM_TOKENS (sizeof (token_table) / sizeof (token_table[0])) + +static struct +{ + char * string; + int token; +} +condition_opcode_table[] = +{ + { "b", BCND }, + { "bm", BMCND }, + { "sc", SCCND }, +}; + +#define NUM_CONDITION_OPCODES (sizeof (condition_opcode_table) / sizeof (condition_opcode_table[0])) + +static struct +{ + char * string; + int val; +} +condition_table[] = +{ + { "z", 0 }, + { "eq", 0 }, + { "geu", 2 }, + { "c", 2 }, + { "gtu", 4 }, + { "pz", 6 }, + { "ge", 8 }, + { "gt", 10 }, + { "o", 12}, + /* always = 14 */ + { "nz", 1 }, + { "ne", 1 }, + { "ltu", 3 }, + { "nc", 3 }, + { "leu", 5 }, + { "n", 7 }, + { "lt", 9 }, + { "le", 11 }, + { "no", 13 } + /* never = 15 */ +}; + +#define NUM_CONDITIONS (sizeof (condition_table) / sizeof (condition_table[0])) + +void +rx_lex_init (char * beginning, char * ending) +{ + rx_init_start = beginning; + rx_lex_start = beginning; + rx_lex_end = ending; + rx_in_brackets = 0; + rx_last_token = 0; + + setbuf (stdout, 0); +} + +static int +check_condition (char * base) +{ + char * cp; + unsigned int i; + + if ((unsigned) (rx_lex_end - rx_lex_start) < strlen (base) + 1) + return 0; + if (memcmp (rx_lex_start, base, strlen (base))) + return 0; + cp = rx_lex_start + strlen (base); + for (i = 0; i < NUM_CONDITIONS; i ++) + { + if (strcasecmp (cp, condition_table[i].string) == 0) + { + rx_lval.regno = condition_table[i].val; + return 1; + } + } + return 0; +} + +static int +rx_lex (void) +{ + unsigned int ci; + char * save_input_pointer; + + while (ISSPACE (*rx_lex_start) + && rx_lex_start != rx_lex_end) + rx_lex_start ++; + + rx_last_exp_start = rx_lex_start; + + if (rx_lex_start == rx_lex_end) + return 0; + + if (ISALPHA (*rx_lex_start) + || (*rx_lex_start == '.' && ISALPHA (rx_lex_start[1]))) + { + unsigned int i; + char * e; + char save; + + for (e = rx_lex_start + 1; + e < rx_lex_end && ISALNUM (*e); + e ++) + ; + save = *e; + *e = 0; + + if (rx_last_token == 0) + for (ci = 0; ci < NUM_CONDITION_OPCODES; ci ++) + if (check_condition (condition_opcode_table[ci].string)) + { + *e = save; + rx_lex_start = e; + rx_last_token = condition_opcode_table[ci].token; + return condition_opcode_table[ci].token; + } + + for (i = 0; i < NUM_TOKENS; i++) + if (strcasecmp (rx_lex_start, token_table[i].string) == 0 + && !(token_table[i].val == IS_OPCODE && rx_last_token != 0) + && !(token_table[i].token == FLAG && !need_flag)) + { + rx_lval.regno = token_table[i].val; + *e = save; + rx_lex_start = e; + rx_last_token = token_table[i].token; + return token_table[i].token; + } + *e = save; + } + + if (rx_last_token == 0) + { + rx_last_token = UNKNOWN_OPCODE; + return UNKNOWN_OPCODE; + } + + if (rx_last_token == UNKNOWN_OPCODE) + return 0; + + if (*rx_lex_start == '[') + rx_in_brackets = 1; + if (*rx_lex_start == ']') + rx_in_brackets = 0; + + if (rx_in_brackets + || rx_last_token == REG + || strchr ("[],#", *rx_lex_start)) + { + rx_last_token = *rx_lex_start; + return *rx_lex_start ++; + } + + save_input_pointer = input_line_pointer; + input_line_pointer = rx_lex_start; + rx_lval.exp.X_md = 0; + expression (&rx_lval.exp); + + /* We parse but ignore any :<size> modifier on expressions. */ + if (*input_line_pointer == ':') + { + char *cp; + + for (cp = input_line_pointer + 1; *cp && cp < rx_lex_end; cp++) + if (!ISDIGIT (*cp)) + break; + if (cp > input_line_pointer+1) + input_line_pointer = cp; + } + + rx_lex_start = input_line_pointer; + input_line_pointer = save_input_pointer; + rx_last_token = EXPR; + return EXPR; +} + +int +rx_error (char * str) +{ + int len; + + len = rx_last_exp_start - rx_init_start; + + as_bad ("%s", rx_init_start); + as_bad ("%*s^ %s", len, "", str); + return 0; +} + +static int +rx_intop (expressionS exp, int nbits) +{ + long v; + + if (exp.X_op == O_big && nbits == 32) + return 1; + if (exp.X_op != O_constant) + return 0; + v = exp.X_add_number; + + switch (nbits) + { + case 4: + return -0x8 <= v && v <= 0x7; + case 5: + return -0x10 <= v && v <= 0x17; + case 8: + return -0x80 <= v && v <= 0x7f; + case 16: + return -0x8000 <= v && v <= 0x7fff; + case 24: + return -0x800000 <= v && v <= 0x7fffff; + case 32: + return 1; + default: + printf ("rx_intop passed %d\n", nbits); + abort (); + } + return 1; +} + +static int +rx_uintop (expressionS exp, int nbits) +{ + unsigned long v; + + if (exp.X_op != O_constant) + return 0; + v = exp.X_add_number; + + switch (nbits) + { + case 4: + return v <= 0xf; + case 8: + return v <= 0xff; + case 16: + return v <= 0xffff; + case 24: + return v <= 0xffffff; + default: + printf ("rx_uintop passed %d\n", nbits); + abort (); + } + return 1; +} + +static int +rx_disp3op (expressionS exp) +{ + unsigned long v; + + if (exp.X_op != O_constant) + return 0; + v = exp.X_add_number; + if (v < 3 || v > 10) + return 0; + return 1; +} + +static int +rx_disp5op (expressionS * exp, int msize) +{ + long v; + + if (exp->X_op != O_constant) + return 0; + v = exp->X_add_number; + + switch (msize) + { + case BSIZE: + if (0 < v && v <= 31) + return 1; + break; + case WSIZE: + if (v & 1) + return 0; + if (0 < v && v <= 63) + { + exp->X_add_number >>= 1; + return 1; + } + break; + case LSIZE: + if (v & 3) + return 0; + if (0 < v && v <= 127) + { + exp->X_add_number >>= 2; + return 1; + } + break; + } + return 0; +} + +/* Just like the above, but allows a zero displacement. */ + +static int +rx_disp5op0 (expressionS * exp, int msize) +{ + if (exp->X_op != O_constant) + return 0; + if (exp->X_add_number == 0) + return 1; + return rx_disp5op (exp, msize); +} + +static int +exp_val (expressionS exp) +{ + if (exp.X_op != O_constant) + { + rx_error (_("constant expected")); + return 0; + } + return exp.X_add_number; +} + +static expressionS +zero_expr (void) +{ + /* Static, so program load sets it to all zeros, which is what we want. */ + static expressionS zero; + zero.X_op = O_constant; + return zero; +} + +static int +immediate (expressionS exp, int type, int pos) +{ + /* We will emit constants ourself here, so negate them. */ + if (type == RXREL_NEGATIVE && exp.X_op == O_constant) + exp.X_add_number = - exp.X_add_number; + if (type == RXREL_NEGATIVE_BORROW) + { + if (exp.X_op == O_constant) + exp.X_add_number = - exp.X_add_number - 1; + else + rx_error (_("sbb cannot use symbolic immediates")); + } + + if (rx_intop (exp, 8)) + { + rx_op (exp, 1, type); + return 1; + } + else if (rx_intop (exp, 16)) + { + rx_op (exp, 2, type); + return 2; + } + else if (rx_intop (exp, 24)) + { + rx_op (exp, 3, type); + return 3; + } + else if (rx_intop (exp, 32)) + { + rx_op (exp, 4, type); + return 0; + } + else if (type == RXREL_SIGNED) + { + /* This is a symbolic immediate, we will relax it later. */ + rx_relax (RX_RELAX_IMM, pos); + rx_op (exp, linkrelax ? 4 : 1, type); + return 1; + } + else + { + /* Let the linker deal with it. */ + rx_op (exp, 4, type); + return 0; + } +} + +static int +displacement (expressionS exp, int msize) +{ + int val; + int vshift = 0; + + if (exp.X_op == O_symbol + && exp.X_md) + { + switch (exp.X_md) + { + case BFD_RELOC_GPREL16: + switch (msize) + { + case BSIZE: + exp.X_md = BFD_RELOC_RX_GPRELB; + break; + case WSIZE: + exp.X_md = BFD_RELOC_RX_GPRELW; + break; + case LSIZE: + exp.X_md = BFD_RELOC_RX_GPRELL; + break; + } + O2 (exp); + return 2; + } + } + + if (exp.X_op != O_constant) + { + rx_error (_("displacements must be constants")); + return -1; + } + val = exp.X_add_number; + + if (val == 0) + return 0; + + switch (msize) + { + case BSIZE: + break; + case WSIZE: + if (val & 1) + rx_error (_("word displacement not word-aligned")); + vshift = 1; + break; + case LSIZE: + if (val & 3) + rx_error (_("long displacement not long-aligned")); + vshift = 2; + break; + default: + as_bad (_("displacement with unknown size (internal bug?)\n")); + break; + } + + val >>= vshift; + exp.X_add_number = val; + + if (0 <= val && val <= 255 ) + { + O1 (exp); + return 1; + } + + if (0 <= val && val <= 65535) + { + O2 (exp); + return 2; + } + if (val < 0) + rx_error (_("negative displacements not allowed")); + else + rx_error (_("displacement too large")); + return -1; +} + +static void +rtsd_immediate (expressionS exp) +{ + int val; + + if (exp.X_op != O_constant) + { + rx_error (_("rtsd size must be constant")); + return; + } + val = exp.X_add_number; + if (val & 3) + rx_error (_("rtsd size must be multiple of 4")); + + if (val < 0 || val > 1020) + rx_error (_("rtsd size must be 0..1020")); + + val >>= 2; + exp.X_add_number = val; + O1 (exp); +} |