%{
#include "a.h"
%}
%union
{
       Sym     *sym;
       long    lval;
       double  dval;
       char    sval[8];
       Gen     gen;
}
%left   '|'
%left   '^'
%left   '&'
%left   '<' '>'
%left   '+' '-'
%left   '*' '/' '%'
%token  <lval>  LMOVW LMOVD LMOVB LSWAP LADDW LCMP
%token  <lval>  LBRA LFMOV LFCONV LFADD LCPOP LTRAP LJMPL LXORW
%token  <lval>  LNOP LEND LRETT LUNIMP LTEXT LDATA LRETRN
%token  <lval>  LCONST LSP LSB LFP LPC LCREG LFLUSH
%token  <lval>  LREG LFREG LR LC LF
%token  <lval>  LFSR LFPQ LPSR LSCHED
%token  <dval>  LFCONST
%token  <sval>  LSCONST
%token  <sym>   LNAME LLAB LVAR
%type   <lval>  con expr pointer offset sreg
%type   <gen>   addr rreg name psr creg freg
%type   <gen>   imm ximm fimm rel fsr fpq
%%
prog:
|       prog line

line:
       LLAB ':'
       {
               if($1->value != pc)
                       yyerror("redeclaration of %s", $1->name);
               $1->value = pc;
       }
       line
|       LNAME ':'
       {
               $1->type = LLAB;
               $1->value = pc;
       }
       line
|       LNAME '=' expr ';'
       {
               $1->type = LVAR;
               $1->value = $3;
       }
|       LVAR '=' expr ';'
       {
               if($1->value != $3)
                       yyerror("redeclaration of %s", $1->name);
               $1->value = $3;
       }
|       LSCHED ';'
       {
               nosched = $1;
       }
|       ';'
|       inst ';'
|       error ';'

inst:
/*
* B.1 load integer instructions
*/
       LMOVW rreg ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVW addr ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVD addr ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVB rreg ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVB addr ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
/*
* B.2 load floating instructions
*      includes CSR
*/
|       LMOVD addr ',' freg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LFMOV addr ',' freg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LFMOV fimm ',' freg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LFMOV freg ',' freg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LFMOV freg ',' addr
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVW addr ',' fsr
       {
               outcode($1, &$2, NREG, &$4);
       }
/*
* B.3 load coprocessor instructions
*      excludes CSR
*/
|       LMOVW addr ',' creg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVD addr ',' creg
       {
               outcode($1, &$2, NREG, &$4);
       }
/*
* B.4 store integer instructions
*/
|       LMOVW rreg ',' addr
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVW imm ',' addr
       {
               if($2.offset != 0)
                       yyerror("constant must be zero");
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVD rreg ',' addr
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVB rreg ',' addr
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVB imm ',' addr
       {
               if($2.offset != 0)
                       yyerror("constant must be zero");
               outcode($1, &$2, NREG, &$4);
       }
/*
* B.5 store floating instructions
*      includes CSR and CQ
*/
|       LMOVW freg ',' addr
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVD freg ',' addr
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVW fsr ',' addr
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVD fpq ',' addr
       {
               outcode($1, &$2, NREG, &$4);
       }
/*
* B.6 store coprocessor instructions
*      excludes CSR and CQ
*/
|       LMOVW creg ',' addr
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVD creg ',' addr
       {
               outcode($1, &$2, NREG, &$4);
       }
/*
* B.7 atomic load unsigned byte (TAS)
* B.8 swap
*/
|       LSWAP addr ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
/*
* B.9  add instructions
* B.10 tagged add instructions
* B.11 subtract instructions
* B.12 tagged subtract instructions
* B.13 multiply step instruction
* B.14 logical instructions
* B.15 shift instructions
* B.17 save/restore
*/
|       LADDW rreg ',' sreg ',' rreg
       {
               outcode($1, &$2, $4, &$6);
       }
|       LADDW imm ',' sreg ',' rreg
       {
               outcode($1, &$2, $4, &$6);
       }
|       LADDW rreg ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LADDW imm ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LXORW rreg ',' sreg ',' rreg
       {
               outcode($1, &$2, $4, &$6);
       }
|       LXORW imm ',' sreg ',' rreg
       {
               outcode($1, &$2, $4, &$6);
       }
|       LXORW rreg ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LXORW imm ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
/*
* B.16 set hi
*      other pseudo moves
*/
|       LMOVW imm ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVD imm ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVW ximm ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVD ximm ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
/*
* B.18 branch on integer condition
* B.19 floating point branch on condition
* B.20 coprocessor branch on condition
*/
|       LBRA comma rel
       {
               outcode($1, &nullgen, NREG, &$3);
       }
/*
* B.21 call instruction
* B.22 jump and link instruction
*/
|       LJMPL comma rel
       {
               outcode($1, &nullgen, NREG, &$3);
       }
|       LJMPL comma addr
       {
               outcode($1, &nullgen, NREG, &$3);
       }
|       LJMPL comma sreg ',' rel
       {
               outcode($1, &nullgen, $3, &$5);
       }
|       LJMPL comma sreg ',' addr
       {
               outcode($1, &nullgen, $3, &$5);
       }
/*
* B.23 return from trap
*/
|       LRETT rreg ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
/*
* B.28 instruction cache flush
*/
|       LFLUSH rel comma
       {
               outcode($1, &$2, NREG, &nullgen);
       }
|       LFLUSH addr comma
       {
               outcode($1, &$2, NREG, &nullgen);
       }
/*
* B.24 trap on condition
*/
|       LTRAP rreg ',' sreg
       {
               outcode($1, &$2, $4, &nullgen);
       }
|       LTRAP imm ',' sreg
       {
               outcode($1, &$2, $4, &nullgen);
       }
|       LTRAP rreg comma
       {
               outcode($1, &$2, NREG, &nullgen);
       }
|       LTRAP comma
       {
               outcode($1, &nullgen, NREG, &nullgen);
       }
/*
* B.25 read state register instructions
*/
|       LMOVW psr ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
/*
* B.26 write state register instructions BOTCH XOR
*/
|       LMOVW rreg ',' psr
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LMOVW imm ',' psr
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LXORW rreg ',' sreg ',' psr
       {
               outcode($1, &$2, $4, &$6);
       }
|       LXORW imm ',' sreg ',' psr
       {
               outcode($1, &$2, $4, &$6);
       }
/*
* B.27 unimplemented trap
*/
|       LUNIMP  comma
       {
               outcode($1, &nullgen, NREG, &nullgen);
       }
|       LUNIMP imm comma
       {
               outcode($1, &$2, NREG, &nullgen);
       }
/*
* B.29 floating point operate
*/
|       LFCONV freg ',' freg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LFADD freg ',' freg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LFADD freg ',' freg ',' freg
       {
               outcode($1, &$2, $4.reg, &$6);
       }
/*
* B.30 coprocessor operate
*/
|       LCPOP creg ',' creg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LCPOP creg ',' creg ',' creg
       {
               outcode($1, &$2, $4.reg, &$6);
       }
/*
* CMP
*/
|       LCMP rreg ',' rreg
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LCMP rreg ',' imm
       {
               outcode($1, &$2, NREG, &$4);
       }
/*
* NOP
*/
|       LNOP comma
       {
               outcode($1, &nullgen, NREG, &nullgen);
       }
|       LNOP rreg comma
       {
               outcode($1, &$2, NREG, &nullgen);
       }
|       LNOP freg comma
       {
               outcode($1, &$2, NREG, &nullgen);
       }
|       LNOP ',' rreg
       {
               outcode($1, &nullgen, NREG, &$3);
       }
|       LNOP ',' freg
       {
               outcode($1, &nullgen, NREG, &$3);
       }
/*
* END
*/
|       LEND comma
       {
               outcode($1, &nullgen, NREG, &nullgen);
       }
/*
* TEXT/GLOBL
*/
|       LTEXT name ',' imm
       {
               outcode($1, &$2, NREG, &$4);
       }
|       LTEXT name ',' con ',' imm
       {
               outcode($1, &$2, $4, &$6);
       }
/*
* DATA
*/
|       LDATA name '/' con ',' imm
       {
               outcode($1, &$2, $4, &$6);
       }
|       LDATA name '/' con ',' ximm
       {
               outcode($1, &$2, $4, &$6);
       }
|       LDATA name '/' con ',' fimm
       {
               outcode($1, &$2, $4, &$6);
       }
/*
* RETURN
*/
|       LRETRN  comma
       {
               outcode($1, &nullgen, NREG, &nullgen);
       }

rel:
       con '(' LPC ')'
       {
               $$ = nullgen;
               $$.type = D_BRANCH;
               $$.offset = $1 + pc;
       }
|       LNAME offset
       {
               $$ = nullgen;
               if(pass == 2)
                       yyerror("undefined label: %s", $1->name);
               $$.type = D_BRANCH;
               $$.sym = $1;
               $$.offset = $2;
       }
|       LLAB offset
       {
               $$ = nullgen;
               $$.type = D_BRANCH;
               $$.sym = $1;
               $$.offset = $1->value + $2;
       }

rreg:
       sreg
       {
               $$ = nullgen;
               $$.type = D_REG;
               $$.reg = $1;
       }

fsr:
       LFSR
       {
               $$ = nullgen;
               $$.type = D_PREG;
               $$.reg = $1;
       }

fpq:
       LFPQ
       {
               $$ = nullgen;
               $$.type = D_PREG;
               $$.reg = $1;
       }

psr:
       LPSR
       {
               $$ = nullgen;
               $$.type = D_PREG;
               $$.reg = $1;
       }

creg:
       LCREG
       {
               $$ = nullgen;
               $$.type = D_CREG;
               $$.reg = $1;
       }
|       LC '(' con ')'
       {
               $$ = nullgen;
               $$.type = D_CREG;
               $$.reg = $3;
       }

freg:
       LFREG
       {
               $$ = nullgen;
               $$.type = D_FREG;
               $$.reg = $1;
       }
|       LF '(' con ')'
       {
               $$ = nullgen;
               $$.type = D_FREG;
               $$.reg = $3;
       }

ximm:
       '$' addr
       {
               $$ = $2;
               $$.type = D_CONST;
       }
|       '$' LSCONST
       {
               $$ = nullgen;
               $$.type = D_SCONST;
               memcpy($$.sval, $2, sizeof($$.sval));
       }

fimm:
       '$' LFCONST
       {
               $$ = nullgen;
               $$.type = D_FCONST;
               $$.dval = $2;
       }
|       '$' '-' LFCONST
       {
               $$ = nullgen;
               $$.type = D_FCONST;
               $$.dval = -$3;
       }

imm:    '$' con
       {
               $$ = nullgen;
               $$.type = D_CONST;
               $$.offset = $2;
       }

sreg:
       LREG
|       LR '(' con ')'
       {
               if($$ < 0 || $$ >= NREG)
                       print("register value out of range\n");
               $$ = $3;
       }

addr:
       '(' sreg ')'
       {
               $$ = nullgen;
               $$.type = D_OREG;
               $$.reg = $2;
               $$.offset = 0;
       }
|       '(' sreg ',' con ')'
       {
               $$ = nullgen;
               $$.type = D_ASI;
               $$.reg = $2;
               $$.offset = $4;
       }
|       '(' sreg '+' sreg ')'
       {
               $$ = nullgen;
               $$.type = D_OREG;
               $$.reg = $2;
               $$.xreg = $4;
               $$.offset = 0;
       }
|       name
|       con '(' sreg ')'
       {
               $$ = nullgen;
               $$.type = D_OREG;
               $$.reg = $3;
               $$.offset = $1;
       }

name:
       con '(' pointer ')'
       {
               $$ = nullgen;
               $$.type = D_OREG;
               $$.name = $3;
               $$.sym = S;
               $$.offset = $1;
       }
|       LNAME offset '(' pointer ')'
       {
               $$ = nullgen;
               $$.type = D_OREG;
               $$.name = $4;
               $$.sym = $1;
               $$.offset = $2;
       }
|       LNAME '<' '>' offset '(' LSB ')'
       {
               $$ = nullgen;
               $$.type = D_OREG;
               $$.name = D_STATIC;
               $$.sym = $1;
               $$.offset = $4;
       }

comma:
|       ','

offset:
       {
               $$ = 0;
       }
|       '+' con
       {
               $$ = $2;
       }
|       '-' con
       {
               $$ = -$2;
       }

pointer:
       LSB
|       LSP
|       LFP

con:
       LCONST
|       LVAR
       {
               $$ = $1->value;
       }
|       '-' con
       {
               $$ = -$2;
       }
|       '+' con
       {
               $$ = $2;
       }
|       '~' con
       {
               $$ = ~$2;
       }
|       '(' expr ')'
       {
               $$ = $2;
       }

expr:
       con
|       expr '+' expr
       {
               $$ = $1 + $3;
       }
|       expr '-' expr
       {
               $$ = $1 - $3;
       }
|       expr '*' expr
       {
               $$ = $1 * $3;
       }
|       expr '/' expr
       {
               $$ = $1 / $3;
       }
|       expr '%' expr
       {
               $$ = $1 % $3;
       }
|       expr '<' '<' expr
       {
               $$ = $1 << $4;
       }
|       expr '>' '>' expr
       {
               $$ = $1 >> $4;
       }
|       expr '&' expr
       {
               $$ = $1 & $3;
       }
|       expr '^' expr
       {
               $$ = $1 ^ $3;
       }
|       expr '|' expr
       {
               $$ = $1 | $3;
       }