OpenCores
URL https://opencores.org/ocsvn/open8_urisc/open8_urisc/trunk

Subversion Repositories open8_urisc

[/] [open8_urisc/] [trunk/] [gnu/] [binutils/] [gas/] [config/] [rx-parse.y] - Rev 196

Go to most recent revision | Compare with Previous | Blame | View Log

/* 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 (0x70, 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
          { B3 (0x75, 0x70, 0x00); FE ($3, 20, 4); }

/* ---------------------------------------------------------------------- */

        /* 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 },
  /* reserved */
  /* 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_pid_register != -1 && memcmp (rx_lex_start, "%pidreg", 7) == 0)
      || (rx_gp_register != -1 && memcmp (rx_lex_start, "%gpreg", 6) == 0)
      || (*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 (strcmp (rx_lex_start, "%pidreg") == 0)
        {
          {
            rx_lval.regno = rx_pid_register;
            *e = save;
            rx_lex_start = e;
            rx_last_token = REG;
            return REG;
          }
        }

      if (strcmp (rx_lex_start, "%gpreg") == 0)
        {
          {
            rx_lval.regno = rx_gp_register;
            *e = save;
            rx_lex_start = e;
            rx_last_token = REG;
            return REG;
          }
        }

      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 (const 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_subtract)
    {
      exp.X_md = BFD_RELOC_RX_DIFF;
      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);
}

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.