Move source into src directory
[a56.git] / src / a56.y
diff --git a/src/a56.y b/src/a56.y
new file mode 100644 (file)
index 0000000..9c3399b
--- /dev/null
+++ b/src/a56.y
@@ -0,0 +1,2114 @@
+%{
+/*******************************************************
+ *
+ *  a56 - a DSP56001 assembler
+ *
+ *  Written by Quinn C. Jensen
+ *  July 1990
+ *
+ *******************************************************/
+
+/*
+ * Copyright (C) 1990-1994 Quinn C. Jensen
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation.  The author makes no representations
+ * about the suitability of this software for any purpose.  It is
+ * provided "as is" without express or implied warranty.
+ *
+ */
+
+/*
+ *  a56.y - The YACC grammar for the assembler.
+ *
+ *  Note:  This module requires a "BIG" version of YACC.  I had to
+ *  recompile YACC in the largest mode available.
+ *
+ *  Other notes:
+ *
+ *  MOVEC, MOVEM and MOVEP must be used explicitly--MOVE can't yet figure
+ *  out which form to use.
+ *
+ */
+
+#include "a56.h"
+#include <math.h>
+
+unsigned int w0, w1;                   /* workspace for the actual generated code */
+BOOL uses_w1;                                  /* says whether w1 is alive */
+unsigned int pc;                               /* current program counter */
+int seg;                                               /* current segment P: X: Y: or L: */
+int expr_seg;                                  /* segment of current expression */
+
+int just_rep = 0;                              /* keeps track of REP instruction */
+int hot_rreg = -1;                             /* rreg loaded by prev inst. or -1 */
+int hot_nreg = -1;                             /* nreg loaded by prev inst. or -1 */
+int hot_mreg = -1;                             /* mreg loaded by prev inst. or -1 */
+int prev_hot_rreg = -1;                        /* rreg loaded by prev inst. or -1 */
+int prev_hot_nreg = -1;                        /* nreg loaded by prev inst. or -1 */
+int prev_hot_mreg = -1;                        /* mreg loaded by prev inst. or -1 */
+
+int substatement = 0;                  /* in a substatement */
+BOOL long_symbolic_expr = FALSE; /* a parser flag */
+char *new_include = NULL;              /* file to be included */
+
+/* listing stuff */
+
+char segs[] = "uPXYL*";
+extern BOOL list_on_next;              /* listing to turn on or off */
+BOOL list_on;                                  /* listing on at the moment */
+extern char *cur_line;                 /* points to line being lex'd */
+char list_buf[1024 + 80];              /* listing buffer */
+char list_buf2[1024 + 80];             /* listing buffer for two-line code */
+BOOL uses_buf2 = FALSE;                        /* list_buf2 is alive */
+BOOL list_print_line = FALSE;  /* whether or not to print line in listing */
+char *spaces(), *luntab();
+
+struct n binary_op();
+struct n unary_op();
+struct n sym_ref();
+
+#define R_R6                           0x0001
+#define R_R5                           0x0002
+#define R_R4                           0x0004
+#define R_DATA_ALU_ACCUM       0x0008
+#define R_CTL_REG                      0x0010
+#define R_FUNKY_CTL_REG                0x0020
+#define R_SDX                          0x0040
+#define R_SDY                          0x0080
+#define R_LSD                          0x0100
+#define R_AB                           0x0200
+#define R_XREG                         0x0400
+#define R_YREG                         0x0800
+/* registers to which short immediate move is an unsigned int */
+#define R_UINT                         0x1000
+/* registers to which short immediate move is an signed frac */
+#define R_SFRAC                                0x2000
+%}
+
+%union {
+       int ival;                       /* integer value */
+       struct n n;                     /* just like in struct sym */
+       double dval;            /* floating point value */
+       char *sval;                     /* string */
+       int cval;                       /* character */
+       char cond;                      /* condition */
+       struct regs {
+               int r6, r5, r4, data_alu_accum, ctl_reg, funky_ctl_reg;
+               int sdx, sdy, lsd, ab, xreg, yreg;
+               int flags;
+       } regs;
+       struct ea {
+               int mode;
+               int ext;
+               int pp;
+       } ea;
+}
+
+%token <n> CHEX CDEC FRAC 
+%token <ival> AREG BREG MREG NREG RREG XREG YREG
+%token <ival> OP OPA OPP
+%token <cond> OP_JCC OP_JSCC OP_TCC
+%token <sval> SYM
+%token <sval> STRING
+%token <cval> CHAR
+%token XMEM
+%token YMEM
+%token LMEM
+%token PMEM
+%token AAAA
+%token A10
+%token BBBB
+%token B10
+%token AABB
+%token BBAA
+%token XXXX
+%token YYYY
+%token SR
+%token MR
+%token CCR
+%token OMR
+%token SP
+%token SSH
+%token SSL
+%token LA
+%token LC
+%token EOL
+%token EOS
+%token LEXBAD
+
+%token OP_ABS
+%token OP_ADC
+%token OP_ADD
+%token OP_ADDL
+%token OP_ADDR
+%token OP_ASL
+%token OP_ASR
+%token OP_CLR
+%token OP_CMP
+%token OP_CMPM
+%token OP_DIV
+%token OP_MAC
+%token OP_MACR
+%token OP_MPY
+%token OP_MPYR
+%token OP_NEG
+%token OP_NORM
+%token OP_RND
+%token OP_SBC
+%token OP_SUB
+%token OP_SUBL
+%token OP_SUBR
+%token OP_TFR
+%token OP_TST
+%token OP_AND
+%token OP_ANDI
+%token OP_EOR
+%token OP_LSL
+%token OP_LSR
+%token OP_NOT
+%token OP_OR
+%token OP_ORI
+%token OP_ROL
+%token OP_ROR
+%token OP_BCLR
+%token OP_BSET
+%token OP_BCHG
+%token OP_BTST
+%token OP_DO
+%token OP_ENDDO
+%token OP_LUA
+%token OP_MOVE
+%token OP_MOVEC
+%token OP_MOVEM
+%token OP_MOVEP
+%token OP_ILLEGAL
+%token OP_INCLUDE
+%token OP_JMP
+%token OP_JCLR
+%token OP_JSET
+%token OP_JSR
+%token OP_JSCLR
+%token OP_JSSET
+%token OP_NOP
+%token OP_REP
+%token OP_RESET
+%token OP_RTI
+%token OP_RTS
+%token OP_STOP
+%token OP_SWI
+%token OP_WAIT
+%token OP_EQU
+%token OP_ORG
+%token OP_DC
+%token OP_DS
+%token OP_DSM
+%token OP_END
+%token OP_PAGE
+%token OP_PSECT
+%token OP_ALIGN
+%token OP_INT
+%token SHL
+%token SHR
+%token OP_PI
+%token OP_SIN
+%token OP_COS
+%token OP_TAN
+%token OP_ATAN
+%token OP_ASIN
+%token OP_ACOS
+%token OP_EXP
+%token OP_LN
+%token OP_LOG
+%token OP_POW
+
+%type <n> num num_or_sym 
+%type <n> num_or_sym_expr
+%type <n> expr
+%type <n> ix
+%type <n> ix_long
+
+%type <ival> abs_addr abs_short_addr io_short_addr 
+%type <ival> a_b x_or_y ea b5_10111_max
+%type <ival> p6_ean_a6 ea_no_ext p6_ea_a6 ea_a6 ea_a12
+%type <ival> ea_short
+%type <ival> prog_ctl_reg
+%type <ival> op8_1 op8_2 op8_3 op8_4 op8_5 op8_6 op8_7 op8_8
+%type <ival> mpy_arg mpy_srcs plus_minus
+%type <ival> sd3
+%type <ival> funky_ctl_reg tcc_sd space
+%type <sval> opt_label
+
+%type <regs> regs
+%type <ea> movep_ea_pp
+
+%left '|'
+%left '^'
+%left SHL SHR
+%left '&'
+%left '+' '-'
+%left '*' '/' '%'
+%right '~'
+
+%start input
+
+%%
+
+/*%%%********************* top syntax ***********************/
+
+input  :       /* empty */
+       |       input statement
+       ;
+
+statement
+       :       good_stuff EOL
+                       {
+                       if(pass == 2 && list_on && list_print_line) {
+                               printf(ldebug ? "\n(%s|%s)\n" : "%s%s\n",
+                                       list_buf, substatement == 0 ? luntab(cur_line) : "");
+                               if(uses_buf2)
+                                       printf(ldebug ? "\n(%s|)\n" : "%s\n",
+                                               list_buf2);
+                               list_buf[0] = list_buf2[0] = '\0';
+                       }
+                       curline++;
+                       uses_buf2 = FALSE;
+                       list_print_line = TRUE;
+                       list_on = list_on_next;
+                       substatement = 0;
+                       if(NOT check_psect(seg, pc) && pass == 2)
+                               yyerror("%04X: psect violation", pc);
+                       }
+       |       good_stuff EOS
+                       {
+                       if(pass == 2 && list_on && list_print_line) {
+                               printf(ldebug ? "\n(%s" : "%s", list_buf);
+                               if(substatement == 0)
+                                       printf(ldebug ? "|%s)\n" : "%s\n", luntab(cur_line));
+                               else
+                                       printf(ldebug ? ")\n" : "\n");
+                               if(uses_buf2)
+                                       printf(ldebug ? "\n(%s|)\n" : "%s\n",
+                                               list_buf2);
+                               list_buf[0] = list_buf2[0] = '\0';
+                       }
+                       substatement++;
+                       uses_buf2 = FALSE;
+                       list_print_line = TRUE;
+                       list_on = list_on_next;
+                       if(NOT check_psect(seg, pc) && pass == 2)
+                               yyerror("%04X: psect violation", pc);
+                       }
+       |       error EOL
+                       {curline++; substatement = 0;}
+       ;
+
+good_stuff
+       :       /* empty */
+                       {sprintf(list_buf, "%s", spaces(0));}
+       |       cpp_droppings
+                       {list_print_line = FALSE;}
+       |       assembler_ops
+                       {long_symbolic_expr = FALSE;}
+       |       label_field operation_field
+                       {char *printcode();
+                       if(pass == 2) {
+                               gencode(seg, pc, w0);
+                               sprintf(list_buf, "%c:%04X %s ", segs[seg], pc, printcode(w0));
+                               pc++;
+                               if(uses_w1) {
+                                       gencode(seg, pc, w1);
+                                       sprintf(list_buf2, "%c:%04X %s", segs[seg], pc,
+                                               printcode(w1 & 0xFFFFFF));
+                                       uses_buf2++;
+                                       pc++;
+                               }
+                       } else {
+                               pc++;
+                               if(uses_w1)
+                                       pc++;
+                       }
+                       w0 = w1 = 0; uses_w1 = FALSE; 
+                       long_symbolic_expr = FALSE;}
+       |       SYM 
+                       {sym_def($1, INT, seg, pc);
+                       free($1);
+                       if(pass == 2 && list_on) {
+                               sprintf(list_buf, "%c:%04X%s", segs[seg], pc, spaces(14-8));
+                       long_symbolic_expr = FALSE;
+                       }}
+       ;
+
+cpp_droppings
+       :       '#' num STRING
+                       {if(strlen($3) > 0)
+                               curfile = $3;
+                       else
+                               curfile = "<stdin>";
+                       curline = $2.val.i - 1;}
+       ;
+
+assembler_ops
+       :       SYM OP_EQU expr
+                       {sym_def($1, $3.type, ANY, $3.val.i, $3.val.f);
+                       free($1);
+                       if(pass == 2 && list_on) {
+                               if($3.type == INT)
+                                       sprintf(list_buf, "%06X%s",
+                                               $3.val.i & 0xFFFFFF,
+                                               spaces(14-8));
+                               else
+                                       sprintf(list_buf, "%10g%s", $3.val.f,
+                                               spaces(14-4));
+                       }}
+       |       OP_ALIGN expr
+                       {int ival = n2int($2);
+                       if($2.type == UNDEF) {
+                               yyerror("illegal forward reference");
+                       } else if (ival <= 1) {
+                               yyerror("%d: illegal alignment", ival);
+                       } else {
+                               if(pc % ival != 0)
+                                       pc += ival - pc % ival;
+                       }
+                       if(pass == 2 && list_on)
+                               sprintf(list_buf, "%c:%04X%s", segs[seg], pc, 
+                                       spaces(14-8));
+                       }
+       |       OP_PSECT SYM
+                       {struct psect *pp = find_psect($2);
+                       if(NOT pp) {
+                               if(pass == 2)
+                                       yyerror("%s: undefined psect", $2);
+                       } else {
+                               seg = pp->seg;
+                               pc = pp->pc;
+                               set_psect(pp);
+                               if(pass == 2 && list_on)
+                                       sprintf(list_buf, "%c:%04X%s", segs[seg], pc,
+                                               spaces(14-8));
+                       }
+                       free($2);}
+       |       OP_PSECT SYM space expr ':' expr
+                       {new_psect($2, $3, n2int($4), n2int($6));
+                       if(pass == 2 && list_on)
+                               sprintf(list_buf, "%c:%04X %04X%s", 
+                                       segs[$3], n2int($4), n2int($6), spaces(14-8+4+1));
+                       }
+       |       OP_ORG space expr
+                       {pc = n2int($3);
+                       seg = $2;
+                       if(pass == 2 && list_on)
+                               sprintf(list_buf, "%c:%04X%s", segs[seg], pc, 
+                                       spaces(14-8));
+                       }
+       |       OP_ORG space expr ',' space expr
+                       {pc = n2int($3);
+                       seg = $2;
+                       if(pass == 2 && list_on)
+                               sprintf(list_buf, "%c:%04X%s", segs[seg], pc, 
+                                       spaces(14-8));
+                       }
+       |       label_field OP_DC dc_list
+       |       label_field OP_DS expr
+                       {pc += n2int($3);
+                       if(pass == 2 && list_on)
+                               sprintf(list_buf, "%c:%04X%s", segs[seg], pc, 
+                                       spaces(14-8));
+                       }
+       |       opt_label OP_DSM expr
+                       {int size = n2int($3);
+                       if(size)
+                       {    int align = 1;
+                            while(align < size)
+                                align <<= 1;
+                            pc += (align - (pc % align));
+                       }
+                       if($1)
+                       {   sym_def($1, INT, seg, pc);
+                           free($1);
+                       }
+                       pc += size;
+                       }
+       |       OP_PAGE num ',' num ',' num ',' num
+                       {if(pass == 2 && list_on) {
+                               sprintf(list_buf, "%s", spaces(0));
+                       }}
+       |       OP_INCLUDE STRING
+                       {if(pass == 2 && list_on) {
+                               printf(ldebug ? "\n(%s|%s)\n" : "%s%s\n",
+                                       spaces(0), luntab(cur_line));
+                               list_print_line = FALSE;
+                       }
+                       include($2); /* free($2); */
+                       }
+       |       OP_END
+                       {if(pass == 2 && list_on) {
+                               sprintf(list_buf, "%s", spaces(0));
+                       }}
+       ;
+
+dc_list
+       :       dc_list ',' dc_stuff
+       |       dc_stuff
+       ;
+
+dc_stuff
+       :       STRING
+                       {int len = strlen($1), i; char *cp; w0 = 0;
+                       if(len % 3 == 2)
+                               len++;  /* force empty word */
+                       for(i = 0, cp = $1; i < len; i++, cp++) {
+                               w0 |= (*cp & 0xFF) << (2 - (i % 3)) * 8;
+                               if(i % 3 == 2 || i == len - 1) {
+                                       if(pass == 2) {
+                                               if(list_on) sprintf(list_buf, "%c:%04X %06X%s",
+                                                       segs[seg], pc, w0, 
+                                                       spaces(14-6+5));
+                                               gencode(seg, pc, w0);
+                                       }
+                                       pc++; w0 = 0;
+                               }
+                       }
+                       free($1);}
+       |       expr
+                       {int frac = n2frac($1);
+                       if(pass == 2) {
+                               if(list_on) {
+                                       sprintf(list_buf, "%c:%04X %06X%s", segs[seg], pc, 
+                                               frac & 0xFFFFFF, spaces(14-6+5));
+                               }
+                               gencode(seg, pc, frac);
+                       }
+                       pc++;}
+
+space
+       :       PMEM
+                       {$$ = PROG;}
+       |       XMEM
+                       {$$ = XDATA;}
+       |       YMEM
+                       {$$ = YDATA;}
+       |       LMEM
+                       {$$ = LDATA;}
+       ;       
+
+label_field
+       :       SYM
+                       {sym_def($1, INT, seg, pc);
+                       free($1);}
+       |       /* empty */
+       ;
+
+opt_label
+       :       SYM             {$$ = $1;}
+       |       /* empty */     {$$ = NULL;}
+       ;
+
+operation_field
+       :       operation
+                       {prev_hot_rreg = hot_rreg;
+                       prev_hot_nreg = hot_nreg;
+                       prev_hot_mreg = hot_mreg;
+                       hot_rreg = hot_nreg = hot_mreg = -1;
+                       if(just_rep) 
+                               just_rep--;}
+       ;
+
+operation
+       :       no_parallel
+       |       parallel_ok
+                       {w0 |= 0x200000;}
+       |       parallel_ok parallel_move
+       ;
+
+/*%%%************* instructions that allow parallel moves ****************/
+
+parallel_ok
+       :
+               OP_MPY mpy_arg
+                       {w0 |= 0x80 | $2 << 2;}
+       |       OP_MPYR mpy_arg
+                       {w0 |= 0x81 | $2 << 2;}
+       |       OP_MAC mpy_arg
+                       {w0 |= 0x82 | $2 << 2;}
+       |       OP_MACR mpy_arg
+                       {w0 |= 0x83 | $2 << 2;}
+
+       |       OP_SUB op8_1
+                       {w0 |= 0x04 | $2 << 3;}
+       |       OP_ADD op8_1
+                       {w0 |= 0x00 | $2 << 3;}
+       |       OP_MOVE
+                       {w0 |= 0x00;}
+
+       |       OP_TFR op8_2
+                       {w0 |= 0x01 | $2 << 3;}
+       |       OP_CMP op8_2
+                       {w0 |= 0x05 | $2 << 3;}
+       |       OP_CMPM op8_2
+                       {w0 |= 0x07 | $2 << 3;}
+
+       |       OP_RND op8_3
+                       {w0 |= 0x11 | $2 << 3;}
+       |       OP_ADDL op8_3
+                       {w0 |= 0x12 | $2 << 3;}
+       |       OP_CLR op8_3
+                       {w0 |= 0x13 | $2 << 3;}
+       |       OP_SUBL op8_3
+                       {w0 |= 0x16 | $2 << 3;}
+       |       OP_NOT op8_3
+                       {w0 |= 0x17 | $2 << 3;}
+
+       |       OP_ADDR op8_4
+                       {w0 |= 0x02 | $2 << 3;}
+       |       OP_TST op8_4
+                       {w0 |= 0x03 | $2 << 3;}
+       |       OP_SUBR op8_4
+                       {w0 |= 0x06 | $2 << 3;}
+
+       |       OP_AND op8_5
+                       {w0 |= 0x46 | $2 << 3;}
+       |       OP_OR op8_5
+                       {w0 |= 0x42 | $2 << 3;}
+       |       OP_EOR op8_5
+                       {w0 |= 0x43 | $2 << 3;}
+
+       |       OP_ASR op8_6
+                       {w0 |= 0x22 | $2 << 3;}
+       |       OP_LSR op8_6
+                       {w0 |= 0x23 | $2 << 3;}
+       |       OP_ABS op8_6
+                       {w0 |= 0x26 | $2 << 3;}
+       |       OP_ROR op8_6
+                       {w0 |= 0x27 | $2 << 3;}
+
+       |       OP_ASL op8_7
+                       {w0 |= 0x32 | $2 << 3;}
+       |       OP_LSL op8_7
+                       {w0 |= 0x33 | $2 << 3;}
+       |       OP_NEG op8_7
+                       {w0 |= 0x36 | $2 << 3;}
+       |       OP_ROL op8_7
+                       {w0 |= 0x37 | $2 << 3;}
+
+       |       OP_ADC op8_8
+                       {w0 |= 0x21 | $2 << 3;}
+       |       OP_SBC op8_8
+                       {w0 |= 0x25 | $2 << 3;}
+       ;
+
+mpy_arg        :       plus_minus mpy_srcs ',' a_b
+                       {$$ = $1 | $4 << 1 | $2 << 2;}
+       ;
+
+plus_minus
+       :       '+'    
+                       {$$ = 0;}
+       |       '-'    
+                       {$$ = 1;}
+       |       
+                       {$$ = 0;}
+       ;
+
+mpy_srcs
+       :       XREG ',' XREG
+                       {switch ($1 << 4 | $3) {
+                               case 0x00: $$ = 0x0; break;
+                               case 0x01: 
+                               case 0x10: $$ = 0x2; break;
+                               case 0x11: 
+                                       yyerror("illegal source operands"); 
+                                       break;
+                       }}                              
+       |       YREG ',' YREG
+                       {switch ($1 << 4 | $3) {
+                               case 0x00: $$ = 0x1; break;
+                               case 0x01: 
+                               case 0x10: $$ = 0x3; break;
+                               case 0x11: 
+                                       yyerror("illegal source operands"); 
+                                       break;
+                       }}                              
+       |       XREG ',' YREG
+                       {switch ($1 << 4 | $3) {
+                               case 0x00: $$ = 0x5; break;
+                               case 0x01: $$ = 0x4; break;
+                               case 0x10: $$ = 0x6; break;
+                               case 0x11: $$ = 0x7; break;
+                       }}                              
+       |       YREG ',' XREG
+                       {switch ($1 << 4 | $3) {
+                               case 0x00: $$ = 0x5; break;
+                               case 0x01: $$ = 0x6; break;
+                               case 0x10: $$ = 0x4; break;
+                               case 0x11: $$ = 0x7; break;
+                       }}                              
+       ;
+
+op8_1  :       BBBB ',' AAAA
+                       {$$ = 0x2;}
+       |       AAAA ',' BBBB
+                       {$$ = 0x3;}
+       |       XXXX ',' a_b
+                       {$$ = 0x4 | $3;}
+       |       YYYY ',' a_b
+                       {$$ = 0x6 | $3;}
+       |       XREG ',' a_b
+                       {$$ = 0x8 | $1 << 2 | $3;}
+       |       YREG ',' a_b
+                       {$$ = 0xA | $1 << 2 | $3;}
+       ;
+
+op8_2  :       BBBB ',' AAAA
+                       {$$ = 0x0;}
+       |       AAAA ',' BBBB
+                       {$$ = 0x1;}
+       |       XREG ',' a_b
+                       {$$ = 0x8 | $1 << 2 | $3;}
+       |       YREG ',' a_b
+                       {$$ = 0xA | $1 << 2 | $3;}
+       ;
+
+op8_3  :       AAAA
+                       {$$ = 0x0;}
+       |       BBBB
+                       {$$ = 0x1;}
+       |       BBBB ',' AAAA
+                       {$$ = 0x0;}
+       |       AAAA ',' BBBB
+                       {$$ = 0x1;}
+       ;
+
+op8_4  :       op8_3
+                       {$$ = $1;}
+       ;
+
+op8_5  :       XREG ',' a_b
+                       {$$ = 0x0 | $1 << 2 | $3;}
+       |       YREG ',' a_b
+                       {$$ = 0x2 | $1 << 2 | $3;}
+       ;
+
+op8_6  :       a_b
+                       {$$ = $1;}
+       ;
+
+op8_7  :       a_b
+                       {$$ = $1;}
+       ;
+
+op8_8  :       XXXX ',' a_b
+                       {$$ = 0x0 | $3;}
+       |       YYYY ',' a_b
+                       {$$ = 0x2 | $3;}
+       ;
+
+a_b    :       AAAA
+                       {$$ = 0;}
+       |       BBBB
+                       {$$ = 1;}
+       ;
+
+no_parallel
+       :       control_inst
+                       {if(just_rep == 1)
+                               yyerror("instruction not allowed after REP");}
+       |       bit_inst
+       |       move_inst
+       |       arith_inst
+       ;
+
+/*%%%************** non-parallel arithmetic and logical ********************/
+
+arith_inst
+       :       OP_NORM RREG ',' a_b
+                       {w0 |= 0x01D815 | $2 << 8 | $4 << 3;}
+       |       OP_DIV sd3
+                       {w0 |= 0x018040 | $2 << 3;}
+       |       or_op ix ',' funky_ctl_reg
+                       {w0 |= 0x0000F8 | (n2int($2) & 0xFF) << 8 | $4;}
+       |       and_op ix ',' funky_ctl_reg
+                       {w0 |= 0x0000B8 | (n2int($2) & 0xFF) << 8 | $4;}
+       ;
+
+or_op  :       OP_OR
+       |       OP_ORI
+       ;
+
+and_op :       OP_AND
+       |       OP_ANDI
+       ;
+
+/*%%%******************************* control instructions **********************/
+
+control_inst
+       :       OP_JSCC ea_a12
+                       {if($2) {
+                               w0 |= 0x0BC0A0 | $1 << 0;
+                       } else {
+                               w0 |= 0x0F0000 | $1 << 12;
+                       }}
+       |       OP_JCC ea_a12
+                       {if($2) {
+                               w0 |= 0x0AC0A0 | $1 << 0;
+                       } else {
+                               w0 |= 0x0E0000 | $1 << 12;
+                       }}
+       |       OP_JSR ea_a12
+                       {if($2) {
+                               w0 |= 0x0BC080;
+                       } else {
+                               w0 |= 0x0D0000;
+                       }}
+       |       OP_JMP ea_a12
+                       {if($2) {
+                               w0 |= 0x0AC080;
+                       } else {
+                               w0 |= 0x0C0000;
+                       }}
+
+       |       OP_JSSET control_args
+                       {w0 |= 0x0B0020;}
+       |       OP_JSCLR control_args
+                       {w0 |= 0x0B0000;}
+       |       OP_JSET control_args
+                       {w0 |= 0x0A0020;}
+       |       OP_JCLR control_args
+                       {w0 |= 0x0A0000;}
+
+       |       OP_REP rep_args
+                       {just_rep = 2;}
+       |       OP_DO do_args
+                       {uses_w1++;}
+       |       OP_ENDDO
+                       {w0 |= 0x00008C;}
+       |       OP_STOP
+                       {w0 |= 0x000087;}
+       |       OP_WAIT
+                       {w0 |= 0x000086;}
+       |       OP_RESET
+                       {w0 |= 0x000084;}
+       |       OP_RTS
+                       {w0 |= 0x00000C;}
+       |       OP_SWI
+                       {w0 |= 0x000006;}
+       |       OP_ILLEGAL
+                       {w0 |= 0x000005;}
+       |       OP_RTI
+                       {w0 |= 0x000004;}
+       |       OP_NOP
+                       {w0 |= 0x000000;
+                       just_rep = 0;}
+       ;
+
+do_args
+       :       ix ',' abs_addr
+                       {int ival = n2int($1);
+                       w0 |= 0x060080 | (ival & 0xFF) << 8 | (ival & 0xF00)>> 8;
+                       if(ival > 0xFFF && pass == 2) {
+                               yywarning("warning: immediate operand truncated");
+                       }
+                       w1 |= $3-1;}
+       |       regs ',' abs_addr
+                       {w0 |= 0x06C000 | $1.r6 << 8;
+                       hot_rreg = hot_nreg = hot_mreg = -1;
+                       w1 |= $3-1;}
+       |       x_or_y ea_no_ext ',' abs_addr
+                       {w0 |= 0x064000 | $2 << 8 | $1 << 6;
+                       w1 |= $4-1;}
+       |       x_or_y abs_short_addr ',' abs_addr      /* allow forced */
+                       {w0 |= 0x060000 | ($2 & 0x3F) << 8 | $1 << 6;
+                       /*
+                        * $$$ oops, can't check expr_seg because both abs_short_addr and
+                        * abs_addr touch it
+                        */
+                       if($2 > 0x003F && pass == 2)
+                               yywarning("warning: address operand truncated");
+                       w1 |= $4-1;}
+       |       x_or_y abs_addr ',' abs_addr
+                       {w0 |= 0x060000 | ($2 & 0x3F) << 8 | $1 << 6;
+                       /*
+                        * $$$ oops, can't check expr_seg because both abs_short_addr and
+                        * abs_addr touch it
+                        */
+                       if($2 > 0x003F && pass == 2)
+                               yywarning("warning: address operand truncated");
+                       w1 |= $4-1;}
+       ;                       
+
+rep_args
+       :       ix
+                       {int ival = n2int($1);
+                       w0 |= 0x0600A0 | (ival & 0xFF) << 8 | (ival & 0xF00)>> 8;
+                       if(ival > 0xFFF && pass == 2) {
+                               yywarning("warning: immediate operand truncated");
+                       }}
+       |       regs
+                       {w0 |= 0x06C020 | $1.r6 << 8;
+                       hot_rreg = hot_nreg = hot_mreg = -1;}
+       |       x_or_y ea_no_ext
+                       {w0 |= 0x064020 | $1 << 6 | $2 << 8;}
+       |       x_or_y abs_addr
+                       {w0 |= 0x060020 | $1 << 6 | ($2 & 0x3F) << 8;
+                       if(expr_seg != ANY && ($1 == 0 && expr_seg != XDATA ||
+                               $1 == 1 && expr_seg != YDATA))
+                               yywarning("warning: space mismatch");
+                       if($2 > 0x003F && pass == 2)
+                               yywarning("warning: address operand truncated");
+                       }
+       |       x_or_y abs_short_addr   /* forced */
+                       {w0 |= 0x060020 | $1 << 6 | ($2 & 0x3F) << 8;
+                       if(expr_seg != ANY && ($1 == 0 && expr_seg != XDATA ||
+                               $1 == 1 && expr_seg != YDATA))
+                               yywarning("warning: space mismatch");
+                       if($2 > 0x003F && pass == 2)
+                               yywarning("warning: address operand truncated");
+                       }
+       ;
+
+control_args
+       :       b5_10111_max ',' x_or_y p6_ean_a6 ',' abs_addr
+                       {w0 |= $1 << 0 | $3 << 6;
+                       uses_w1++;
+                       w1 = $6;}
+       |       b5_10111_max ',' regs ',' abs_addr
+                       {w0 |= 0x00C000 | $1 << 0 | $3.r6 << 8;
+                       hot_rreg = hot_nreg = hot_mreg = -1;
+                       uses_w1++;
+                       w1 = $5;}
+       ;
+
+p6_ean_a6
+       :       abs_addr        /* in pass 2 can always discern size. */
+                               /* Sometimes in pass one, too.  But since */
+                               /* address extension is always used for the */
+                               /* branch target, pass 1 can assume the */
+                               /* symbol value will fit; warning in pass 2 */
+                               /* if it doesn't */
+                       {if($1 != -1) { /* symbol defined */
+                               w0 |= ($1 & 0x3F) << 8;
+                               if($1 >= 0xFFC0) {
+                                       w0 |= 0x008080;
+                               } else {
+                                       w0 |= 0x000080;
+                                       if($1 > 0x003F && pass == 2)
+                                               yywarning("warning: address operand truncated");
+                               }
+                       }}
+       |       abs_short_addr
+                       {if($1 != -1) {
+                               if($1 > 0x3F && pass == 2)
+                                       yywarning("warning: address operand truncated");
+                               w0 |= 0x000080 | ($1 & 0x3F) << 8;
+                       }}
+       |       io_short_addr
+                       {if($1 != -1) {
+                               if($1 < 0xFFC0 && pass == 2)
+                                       yywarning("warning: address operand truncated");
+                               w0 |= 0x008080 | ($1 & 0x3F) << 8;
+                       }}
+       |       ea_no_ext
+                       {w0 |= 0x004080 | $1 << 8;}
+       ;
+
+/*%%%**************************** bit instructions ***************************/
+
+bit_inst
+       :       OP_BTST bit_args
+                       {w0 |= 0x0B0020;}
+       |       OP_BCHG bit_args
+                       {w0 |= 0x0B0000;}
+       |       OP_BSET bit_args
+                       {w0 |= 0x0A0020;}
+       |       OP_BCLR bit_args
+                       {w0 |= 0x0A0000;}
+       ;                       
+
+bit_args
+       :       b5_10111_max ',' x_or_y p6_ea_a6
+                       {w0 |= $1 << 0 | $3 << 6;
+                       }
+       |       b5_10111_max ',' regs
+                       {w0 |= 0x00C040 | $1 << 0 | $3.r6 << 8;}
+       ;               
+
+p6_ea_a6
+       :       io_short_addr   /* must be forced to tell from abs_addr */
+                       {if($1 != -1) {
+                               w0 |= ($1 & 0x3F) << 8 | 0x008000;
+                               if($1 < 0xFFC0 && pass == 2)
+                                       yywarning("warning: address operand truncated");
+                       }}
+       |       abs_short_addr  /* must be forced to tell from abs_addr */
+                       {if($1 != -1) {
+                               w0 |= ($1 & 0x3F) << 8 | 0x000000;
+                               if($1 > 0x003F && pass == 2)
+                                       yywarning("warning: address operand truncated");
+                       }}
+       |       ea      /* can use abs_addr */
+                       {w0 |= 0x004000;}
+       ;
+
+/*%%%************************** move instructions **********************/
+
+move_inst
+       :       OP_MOVEP movep_args
+       |       OP_MOVEM movem_args
+       |       OP_MOVEC movec_args
+       |       OP_LUA ea_short ',' regs
+                       {w0 |= 0x044010 | $2 << 8 | $4.r4;}
+       |       OP_TCC tcc_args
+                       {w0 |= $1 << 12;}
+       ;               
+
+tcc_args
+       :       tcc_sd
+                       {w0 |= 0x020000 | $1 << 3;}
+       |       tcc_sd RREG ',' RREG
+                       {w0 |= 0x030000 | $1 << 3 | $2 << 8 | $4;}
+       ;
+
+tcc_sd
+       :       regs /* a_b */ ',' regs /* a_b */
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       if($1.flags & R_AB && $3.flags & R_AB) {
+                               if($1.ab == $3.ab) 
+                                       yyerror("source and dest must be different");
+                               $$ = $3.ab;
+                       } else if($1.flags & R_XREG && $3.flags & R_AB) {
+                               $$ = 0x8 | $1.xreg << 2 | $3.ab;
+                       } else if($1.flags & R_YREG && $3.flags & R_AB) {
+                               $$ = 0xA | $1.yreg << 2 | $3.ab;
+                       } else 
+                               yyerror("illegal TCC operands");
+                       }
+       ;
+
+sd3    :       regs /* XREG */ ',' regs /* a_b */
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       if($1.flags & R_XREG && $3.flags & R_AB) {
+                               $$ = $1.xreg << 2 | $3.ab;
+                       } else if($1.flags & R_YREG && $3.flags & R_AB) {
+                               $$ = $1.yreg << 2 | 2 | $3.ab;
+                       }}
+       ;
+
+movec_args
+       :       x_or_y ea ',' regs /* ctl_reg */
+                       {if(NOT ($4.flags & R_CTL_REG))
+                               yyerror("bad MOVEC target register");
+                       if(expr_seg != ANY && ($1 == 0 && expr_seg != XDATA ||
+                               $1 == 1 && expr_seg != YDATA))
+                               yywarning("warning: space mismatch");
+                       if($1 == 0) {
+                               w0 |= 0x05C020 | $4.ctl_reg;
+                       } else {
+                               w0 |= 0x05C060 | $4.ctl_reg;
+                       }}
+       |       regs /* ctl_reg */ ',' x_or_y ea
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       if(NOT ($1.flags & R_CTL_REG))
+                               yyerror("bad MOVEC source register");
+                       if($3 == 0) {
+                               w0 |= 0x054020 | $1.ctl_reg;
+                       } else {
+                               w0 |= 0x054060 | $1.ctl_reg;
+                       }}
+       |       ix ',' regs /* ctl_reg */
+                       {int ival = n2int($1);
+                       if(NOT ($3.flags & R_CTL_REG))
+                               yyerror("bad MOVEC target register");
+                       if(ival < 256 && NOT long_symbolic_expr) {
+                               w0 |= 0x0500A0 | (ival & 0xFF) << 8 | $3.ctl_reg; 
+                       } else {
+                               w0 |= 0x05C020 | 0x003400 | $3.ctl_reg;
+                               uses_w1++; w1 = ival & 0xFFFF;
+                       }}
+       |       x_or_y abs_short_addr ',' regs /* ctl_reg */
+                       {if($1 == 0) {
+                               w0 |= 0x058020 | ($2 & 0x3F) << 8 | $4.ctl_reg;
+                       } else {
+                               w0 |= 0x058060 | ($2 & 0x3F) << 8 | $4.ctl_reg;
+                       }
+                       if(NOT ($4.flags & R_CTL_REG))
+                               yyerror("bad MOVEC target register");
+                       if($2 > 0x003F && pass == 2)
+                               yywarning("warning: address operand truncated");
+                       }
+       |       regs /* ctl_reg */ ',' x_or_y abs_short_addr
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       if($3 == 0) {
+                               w0 |= 0x050020 | ($4 & 0x3F) << 8 | $1.ctl_reg;
+                       } else {
+                               w0 |= 0x050060 | ($4 & 0x3F) << 8 | $1.ctl_reg;
+                       }
+                       if(NOT ($1.flags & R_CTL_REG))
+                               yyerror("bad MOVEC source register");
+                       if($4 > 0x003F && pass == 2)
+                               yywarning("warning: address operand truncated");
+                       }
+       |       regs /* ctl_reg */ ',' regs
+                       {if($1.flags & R_CTL_REG) {
+                               w0 |= 0x0440A0 | $3.r6 << 8 | $1.ctl_reg;
+                       } else if($3.flags & R_CTL_REG) {
+                               w0 |= 0x04C0A0 | $1.r6 << 8 | $3.ctl_reg;
+                       } else if($1.flags & $3.flags & R_CTL_REG) {
+                               /* bogus? $$$ */
+                               w0 |= 0x04C0A0 | ($1.ctl_reg | 0x20) << 8 | 
+                               $3.ctl_reg;
+                       } else {
+                               yyerror("MOVEC must reference a control reg");
+                       }}
+       ;
+
+movep_args
+       :       x_or_y movep_ea_pp ',' x_or_y movep_ea_pp
+                       {w0 |= 0x084080;
+                       switch($2.pp << 1 | $5.pp) {
+                               case 0: case 3:
+                                       yyerror("illegal MOVEP; can't move EA to EA or IO to IO");
+                                       break;
+                               case 1: /* ea, pp */
+                                       w0 |= $4 << 16 | 1 << 15 | $1 << 6 |
+                                               ($5.ext & 0x3F);
+                                       if($2.mode == 0x003000) {
+                                               w0 |= 0x003000;
+                                               uses_w1++;
+                                               w1 = $2.ext;
+                                       } else {
+                                               w0 |= $2.mode;
+                                       }
+                                       break;
+                               case 2: /* pp, ea */
+                                       w0 |= $1 << 16 | 0 << 15 | $4 << 6 |
+                                               ($2.ext & 0x3F);
+                                       if($5.mode == 0x003000) {
+                                               w0 |= 0x003000;
+                                               uses_w1++;
+                                               w1 = $5.ext;
+                                       } else {
+                                               w0 |= $5.mode;
+                                       }
+                                       break;
+                       }}
+       |       ix ',' x_or_y num_or_sym
+                       {w0 |= 0x084080;
+                       w0 |= $3 << 16 | 1 << 15 | 0x34 << 8 | 
+                               (n2int($4) & 0x3F);
+                       uses_w1++;
+                       w1 = n2int($1);}
+       |       PMEM ea ',' x_or_y num_or_sym
+                       {w0 |= 0x084040;
+                       w0 |= $4 << 16 | 1 << 15 | (n2int($5) & 0x3F);}
+       |       x_or_y movep_ea_pp ',' PMEM ea
+                       {w0 |= 0x084040;
+                       if($2.mode != 0x003000 && $2.mode != 0)
+                               yyerror("illegal MOVEP");
+                       w0 |= $1 << 16 | 0 << 15 | ($2.ext & 0x3F);}
+       |       regs ',' x_or_y num_or_sym
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       w0 |= 0x084000;
+                       w0 |= $3 << 16 | 1 << 15 | $1.r6 << 8 | 
+                               (n2int($4) & 0x3F);}
+       |       x_or_y movep_ea_pp ',' regs
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       w0 |= 0x084000;
+                       if(!$2.pp)
+                               yyerror("illegal MOVEP");
+                       w0 |= $1 << 16 | 0 << 15 | $4.r6 << 8 | ($2.ext & 0x3F);}
+       ;
+
+movep_ea_pp
+       :       abs_addr
+                       {if($1 != UNDEF && $1 >= 0xFFC0) {
+                               /* defined symbol or constant, in i/o range */
+                               $$.pp = 1;
+                               $$.mode = 0;
+                       } else {
+                               /* either out of i/o range or symbol not */
+                               /* yet defined:  assume ea extension */
+                               $$.pp = 0;
+                               $$.mode = 0x003000;
+                       }
+                       $$.ext = $1;}
+       |       io_short_addr   /* forced i/o short */
+                       {$$.pp = 1;
+                       $$.mode = 0;
+                       if($1 < 0xFFC0 && pass == 2)
+                               yywarning("warning: address operand truncated");
+                       $$.ext = $1;}
+       |       ea_no_ext
+                       {$$.pp = 0;
+                       $$.mode = $1 << 8;
+                       $$.ext = $1;}
+       ;
+
+movem_args
+       :       regs ',' PMEM ea_a6
+                       {w0 |= 0x070000 | 0 << 15 | $1.r6;}
+       |       PMEM ea_a6 ',' regs
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       w0 |= 0x070000 | 1 << 15 | $4.r6;}
+       ;
+
+/*%%%**************** memory reference fields ************************/
+
+b5_10111_max
+       :       ix
+                       {int ival = n2int($1);
+                       $$ = ival; if(ival > 0x17) 
+                               yyerror("%d: illegal bit number", ival);}
+       ;
+
+x_or_y
+       :       XMEM
+                       {$$ = 0;}
+       |       YMEM
+                       {$$ = 1;}
+       ;
+
+/*%%%**************** effective address fields ************************/
+
+ea_a6
+       :       ea
+                       {w0 |= 0x004080;}
+       |       abs_short_addr
+                       {w0 |= ($1 & 0x3F) << 8;
+                       if($1 > 0x003F && pass == 2)
+                               yywarning("warning: address operand truncated");
+                       }
+       ;
+
+ea_a12
+       :       ea
+                       {$$ = 1;}
+       |       abs_short_addr
+                       {w0 |= $1 & 0xFFF; $$ = 0;
+                       if($1 > 0x0FFF && pass == 2)
+                               yywarning("warning: address operand truncated");
+                       }
+       ;
+
+ea     :       abs_addr
+                       {w0 |= 0x003000;
+                       uses_w1++;
+                       w1 |= $1;
+                       $$ = 0x003000;}
+       |       ea_no_ext
+                       {w0 |= $1 << 8;
+                       $$ = $1 << 8;
+                       expr_seg = ANY;}
+       ;
+
+ea_no_ext
+       :       ea_short
+                       {$$ = $1;}
+       |       '(' RREG ')'
+                       {if($2 == prev_hot_rreg) yywarning("warning: r%d just loaded", $2);
+                       $$ = 4 << 3 | $2;}
+       |       '(' RREG '+' NREG ')'
+                       {if($2 == prev_hot_rreg) yywarning("warning: r%d just loaded", $2);
+                       if($4 == prev_hot_nreg) yywarning("warning: n%d just loaded", $4);
+                       if($2 == prev_hot_mreg) yywarning("warning: m%d just loaded", $2);
+                       $$ = 5 << 3 | $2;
+                       if($2 != $4) yyerror("Rn and Nn must be same number");}
+       |       '-' '(' RREG ')'
+                       {if($3 == prev_hot_rreg) yywarning("warning: r%d just loaded", $3);
+                       if($3 == prev_hot_mreg) yywarning("warning: m%d just loaded", $3);
+                       $$ = 7 << 3 | $3;}
+       ;
+
+ea_short
+       :       '(' RREG ')' '-' NREG
+                       {if($2 == prev_hot_rreg) yywarning("warning: r%d just loaded", $2);
+                       if($5 == prev_hot_nreg) yywarning("warning: n%d just loaded", $5);
+                       if($2 == prev_hot_mreg) yywarning("warning: m%d just loaded", $2);
+                       $$ = 0 << 3 | $2;
+                       expr_seg = ANY;
+                       if($2 != $5) yyerror("Rn and Nn must be same number");}
+       |       '(' RREG ')' '+' NREG
+                       {if($2 == prev_hot_rreg) yywarning("warning: r%d just loaded", $2);
+                       if($5 == prev_hot_nreg) yywarning("warning: n%d just loaded", $5);
+                       if($2 == prev_hot_mreg) yywarning("warning: m%d just loaded", $2);
+                       $$ = 1 << 3 | $2;
+                       expr_seg = ANY;
+                       if($2 != $5) yyerror("Rn and Nn must be same number");}
+       |       '(' RREG ')' '-'
+                       {if($2 == prev_hot_rreg) yywarning("warning: r%d just loaded", $2);
+                       if($2 == prev_hot_mreg) yywarning("warning: m%d just loaded", $2);
+                       expr_seg = ANY;
+                       $$ = 2 << 3 | $2;}
+       |       '(' RREG ')' '+'
+                       {if($2 == prev_hot_rreg) yywarning("warning: r%d just loaded", $2);
+                       if($2 == prev_hot_mreg) yywarning("warning: m%d just loaded", $2);
+                       expr_seg = ANY;
+                       $$ = 3 << 3 | $2;}
+       ;
+
+/*%%%******************* register fields ******************************/
+
+regs
+       :       XREG
+                       {$$.r6 = $$.r5 = 0x04 | $1;
+                       $$.sdx = $1;
+                       $$.xreg = $1;
+                       $$.flags = R_R6|R_R5|R_XREG|R_SDX|R_SFRAC;}
+       |       YREG
+                       {$$.r6 = $$.r5 = 0x06 | $1;
+                       $$.sdy = $1;
+                       $$.yreg = $1;
+                       $$.flags = R_R6|R_R5|R_SDY|R_YREG|R_SFRAC;}
+       |       AREG
+                       {switch($1) {
+                               case 0: 
+                                       $$.r6 = $$.r5 = 0x08 | 0; 
+                                       break;
+                               case 1: 
+                                       $$.r6 = $$.r5 = 0x08 | 4; 
+                                       break;
+                               case 2: 
+                                       $$.r6 = $$.r5 = 0x08 | 2; 
+                                       break;
+                       }
+                       $$.flags = R_R6|R_R5|R_UINT;}
+       |       BREG
+                       {switch($1) {
+                               case 0: 
+                                       $$.r6 = $$.r5 = 0x08 | 1; break;
+                               case 1: 
+                                       $$.r6 = $$.r5 = 0x08 | 5; break;
+                               case 2: 
+                                       $$.r6 = $$.r5 = 0x08 | 3; break;
+                       }
+                       $$.flags = R_R6|R_R5|R_UINT;}
+       |       AAAA
+                       {$$.r6 = $$.r5 = 0x0E;
+                       $$.sdx = $$.sdy = 0x2;
+                       $$.ab = 0;
+                       $$.lsd = 4;
+                       $$.flags = R_R6|R_R5|R_SDX|R_SDY|R_AB|R_LSD|R_SFRAC;}
+       |       BBBB
+                       {$$.r6 = $$.r5 = 0x0F;
+                       $$.sdx = $$.sdy = 0x3;
+                       $$.ab = 1;
+                       $$.lsd = 5;
+                       $$.flags = R_R6|R_R5|R_SDX|R_SDY|R_AB|R_LSD|R_SFRAC;}
+       |       RREG
+                       {$$.r6 = $$.r5 = 0x10 | $1;
+                       $$.r4 = 0x00 | $1;
+                       $$.flags = R_R6|R_R5|R_R4|R_UINT;
+                       hot_rreg = $1;}
+       |       NREG
+                       {$$.r6 = $$.r5 = 0x18 | $1;
+                       $$.r4 = 0x08 | $1;
+                       $$.flags = R_R6|R_R5|R_R4|R_UINT;
+                       hot_nreg = $1;}
+       |       MREG
+                       {$$.r6 = 0x20 | $1;
+                       $$.r5 = -1;
+                       $$.ctl_reg = $1;
+                       $$.flags = R_R6|R_R5|R_CTL_REG|R_UINT;
+                       hot_mreg = $1;}
+       |       prog_ctl_reg
+                       {$$.r6 = 0x38 | $1;
+                       $$.r5 = -1;
+                       $$.ctl_reg = 0x18 | $1;
+                       $$.flags = R_R6|R_R5|R_CTL_REG|R_UINT;}
+       |       A10
+                       {$$.lsd  = 0;
+                       $$.flags = R_LSD;}
+       |       B10
+                       {$$.lsd = 1;
+                       $$.flags = R_LSD;}
+       |       XXXX
+                       {$$.lsd = 2;
+                       $$.flags = R_LSD;}
+       |       YYYY
+                       {$$.lsd = 3;
+                       $$.flags = R_LSD;}
+       |       AABB
+                       {$$.lsd = 6;
+                       $$.flags = R_LSD;}
+       |       BBAA
+                       {$$.lsd = 7;
+                       $$.flags = R_LSD;}
+       ;
+
+prog_ctl_reg
+       :       SR
+                       {$$ = 1;}
+       |       OMR
+                       {$$ = 2;}
+       |       SP
+                       {$$ = 3;}
+       |       SSH
+                       {$$ = 4;}
+       |       SSL
+                       {$$ = 5;}
+       |       LA
+                       {$$ = 6;}
+       |       LC
+                       {$$ = 7;}
+       ;
+
+funky_ctl_reg
+       :       MR
+                       {$$ = 0;}
+       |       CCR
+                       {$$ = 1;}
+       |       OMR
+                       {$$ = 2;}
+       ;
+
+/*%%%************************* parallel moves *************/
+
+parallel_move
+       :       i_move
+       |       u_move
+       |       x_or_y_move
+       |       xr_move
+       |       ry_move
+       |       r_move
+       |       xy_move
+       |       l_move
+       ;
+
+i_move  :      ix ',' regs
+                       {int ival = n2int($1);
+                       int frac = n2frac($1);
+                       int value;
+                       BOOL shortform = FALSE;
+                       if($3.flags & R_CTL_REG) {
+                               yyerror("please use MOVEC for control register moves");
+                               break;
+                       }
+                       if(($3.flags & R_SFRAC) && $1.type == FLT) {
+                               if((frac & 0xFFFF) == 0 && 
+                                       NOT long_symbolic_expr) {
+                                       value = frac >> 16;
+                                       shortform++;
+                               } else {
+                                       value = frac;
+                               }
+                       } else {
+                               if(ival <= 0xFF && ival >= -0xFF && NOT long_symbolic_expr) {
+                                       value = ival;
+                                       shortform++;
+                               } else {
+                                       value = ival;
+                               }
+                       }
+
+                       if(shortform) {
+                               w0 |= 0x200000 | (value & 0xFF) << 8 |
+                                       $3.r5 << 16;
+                       } else {
+                               w0 |= 0x400000 | 0x00F400 |
+                                       ($3.r5 >> 3 & 3) << 20 | 
+                                       ($3.r5 & 7) << 16;
+                               uses_w1++; w1 = value;
+                       }}
+       ;
+
+r_move :       regs ',' regs
+                       {
+                               if($3.flags & R_CTL_REG) {
+                                       yyerror("please use MOVEC for control register moves");
+                                       break;
+                               }
+                               if($1.flags & R_R5 & $3.flags) 
+                                       w0 |= 0x200000 | $3.r5 << 8 | $1.r5 << 13;
+                               else
+                                       yyerror("illegal R move");
+                       }       
+       ;
+
+u_move :       ea_short
+                       {w0 |= 0x204000 | $1 << 8;}
+       ;
+
+x_or_y_move
+       :       x_or_y ea ',' regs
+                       {w0 |= 0x40C000 | $1 << 19;
+                       if(expr_seg != ANY && ($1 == 0 && expr_seg != XDATA ||
+                               $1 == 1 && expr_seg != YDATA))
+                               yywarning("warning: space mismatch");
+                       if($4.flags & R_CTL_REG) {
+                               yyerror("please use MOVEC for control register moves");
+                               break;
+                       }
+                       w0 |= ($4.r5 >> 3 & 3) << 20 | ($4.r5 & 7) << 16;}
+       |       x_or_y abs_short_addr ',' regs
+                       {w0 |= 0x408000 | $1 << 19 | ($2 & 0x3F) << 8;
+                       if(expr_seg != ANY && ($1 == 0 && expr_seg != XDATA ||
+                               $1 == 1 && expr_seg != YDATA))
+                               yywarning("warning: space mismatch");
+                       if($4.flags & R_CTL_REG) {
+                               yyerror("please use MOVEC for control register moves");
+                               break;
+                       }
+                       if($2 > 0x003F && pass == 2)
+                               yywarning("warning: address operand truncated");
+                       w0 |= ($4.r5>> 3 & 3) << 20 | ($4.r5 & 7) << 16;}
+       |       regs ',' x_or_y ea
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       w0 |= 0x404000 | $3 << 19;
+                       if(expr_seg != ANY && ($3 == 0 && expr_seg != XDATA ||
+                               $3 == 1 && expr_seg != YDATA))
+                               yywarning("warning: space mismatch");
+                       if($1.flags & R_CTL_REG) {
+                               yyerror("please use MOVEC for control register moves");
+                               break;
+                       }
+                       w0 |= ($1.r5 >> 3 & 3) << 20 | ($1.r5 & 7) << 16;}
+       |       regs ',' x_or_y abs_short_addr
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       w0 |= 0x400000 | $3 << 19 | ($4 & 0x3F) << 8;
+                       if($1.flags & R_CTL_REG) {
+                               yyerror("please use MOVEC for control register moves");
+                               break;
+                       }
+                       if(expr_seg != ANY && ($3 == 0 && expr_seg != XDATA ||
+                               $3 == 1 && expr_seg != YDATA))
+                               yywarning("warning: space mismatch");
+                       if($4 > 0x003F && pass == 2)
+                               yywarning("warning: address operand truncated");
+                       w0 |= ($1.r5 >> 3 & 3) << 20 | ($1.r5 & 7) << 16;}
+       |       ix_long ',' regs
+                       {w0 |= 0x400000 | 0x00F400 | ($3.r5 >> 3 & 3) << 20 |
+                           ($3.r5 & 7) << 16;
+                       if($3.flags & R_CTL_REG) {
+                               yyerror("please use MOVEC for control register moves");
+                               break;
+                       }
+                       uses_w1++; w1 = n2frac($1);
+                       }
+       ;
+
+xr_move
+       :       x_or_y /* XMEM */ ea ',' regs   regs /* a_b */ ',' YREG
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       if(expr_seg != ANY && ($1 == 0 && expr_seg != XDATA ||
+                               $1 == 1 && expr_seg != YDATA))
+                               yywarning("warning: space mismatch");
+                       if($1 == 0 && $5.flags & R_AB) {
+                               w0 |= 0x108000 | $4.sdx << 18 | $5.ab << 17 |
+                                       $7 << 16;
+                       } else {
+                               yyerror("illegal X:R move");
+                       }}
+       |       ix ',' regs             regs /* a_b */ ',' YREG
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       if($4.flags & R_AB) {
+                               w0 |= 0x10B400 | $3.sdx << 18 | $4.ab << 17 |
+                                       $6 << 16;
+                               uses_w1++;
+                               w1 |= n2frac($1) & 0xFFFFFF;
+                       } else {
+                               yyerror("illegal X:R move");
+                       }}
+       |       regs ',' x_or_y /* XMEM */ ea   regs /* a_b */ ',' regs/*YREG*/
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       if(expr_seg != ANY && ($3 == 0 && expr_seg != XDATA ||
+                               $3 == 1 && expr_seg != YDATA))
+                               yywarning("warning: space mismatch");
+                       if($1.flags & R_SDX && $3 == 0 && $5.flags & R_AB &&
+                               $7.flags & R_YREG) {
+                               w0 |= 0x100000 | $1.sdx << 18 | $5.ab << 17 |
+                                       $7.yreg << 16;
+                       } else if($1.flags & R_AB && $3 == 0 && 
+                               $5.flags & R_XREG && $7.flags & R_AB) {
+                               if($5.xreg != 0) yyerror("must use X0");
+                               if($1.ab == 0 && $7.ab == 0)
+                                       w0 |= 0x080000;
+                               else if($1.ab == 1 && $7.ab == 1)
+                                       w0 |= 0x090000;
+                               else
+                                       yyerror("illegal X:R move");
+                       } else {
+                               yyerror("illegal X:R move");
+                       }}
+       ;
+
+ry_move        :       regs /* a_b */ ',' regs /* XREG */      YMEM ea ',' regs
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       if($3.flags & R_XREG && $7.flags & (R_YREG|R_AB)) {
+                               w0 |= 0x10C000 | $1.ab << 19 | $3.xreg << 18 |
+                                       $7.sdy << 16;
+                       } else {
+                               yyerror("illegal R:Y move");
+                       }}
+       |       regs /* a_b */ ',' regs /* XREG */      ix ',' regs
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       if($3.flags & R_XREG && $6.flags & (R_YREG|R_AB)) {
+                               w0 |= 0x10F400 | $1.ab << 19 | $3.xreg << 18 |
+                                       $6.sdy << 16;
+                               uses_w1++;
+                               w1 |= n2frac($4) & 0xFFFFFF;
+                       } else {
+                               yyerror("illegal R:Y move");
+                       }}
+       |       regs /* a_b */ ',' regs /* XREG */      regs ',' YMEM ea
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       if($1.flags & R_AB && $3.flags & R_XREG) {
+                               w0 |= 0x104000 | $1.ab << 19 | $3.xreg << 18 |
+                               $4.sdy << 16;
+                       } else if ($1.flags & R_YREG && $3.flags & R_AB &&
+                               $4.flags & R_AB) {
+                               if($1.yreg != 0) yyerror("must use Y0");
+                               if($3.ab == 0 && $4.ab == 0)
+                                       w0 |= 0x088000;
+                               else if($3.ab == 1 && $4.ab == 1)
+                                       w0 |= 0x098000;
+                               else
+                                       yyerror("illegal R:Y move");
+                       } else {
+                               yyerror("illegal R:Y move");
+                       }}
+       ;
+
+l_move
+       :       LMEM ea ',' regs /* lsd */
+                       {if($4.flags & R_CTL_REG) {
+                               yyerror("please use MOVEC for control register moves");
+                               break;
+                       }
+                       w0 |= 0x40C000 | ($4.lsd & 3) << 16 | ($4.lsd >> 2) << 19;}
+       |       regs /* lsd */ ',' LMEM ea
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       if($1.flags & R_CTL_REG) {
+                               yyerror("please use MOVEC for control register moves");
+                               break;
+                       }
+                       w0 |= 0x404000 | ($1.lsd & 3) << 16 | ($1.lsd >> 2) << 19;}
+       |       LMEM abs_short_addr ',' regs /* lsd */
+                       {w0 |= 0x408000 | ($4.lsd & 3) << 16 | ($4.lsd >> 2) << 19;
+                       if($4.flags & R_CTL_REG) {
+                               yyerror("please use MOVEC for control register moves");
+                               break;
+                       }
+                       if($2 > 0x003F && pass == 2)
+                               yywarning("warning: address operand truncated");
+                       w0 |= ($2 & 0x3F) << 8;}
+       |       regs /* lsd */ ',' LMEM abs_short_addr
+                       {hot_rreg = hot_nreg = hot_mreg = -1;
+                       w0 |= 0x400000 | ($1.lsd & 3) << 16 | ($1.lsd >> 2) << 19;
+                       if($1.flags & R_CTL_REG) {
+                               yyerror("please use MOVEC for control register moves");
+                               break;
+                       }
+                       if($4 > 0x003F && pass == 2)
+                               yywarning("warning: address operand truncated");
+                       w0 |= ($4 & 0x3F) << 8;}
+       ;
+
+xy_move
+       :       x_or_y /*XMEM*/ ea /*ea_strange*/ ',' regs      YMEM ea /*ea_strange*/ ',' regs
+                       {int eax = $2, eay = $6,
+                            regx = ($4.flags & R_AB) ? $4.ab | 2 : $4.xreg,
+                            regy = ($8.flags & R_AB) ? $8.ab | 2 : $8.yreg;
+                       hot_rreg = hot_nreg = hot_mreg = -1;
+                       if((eax & 0x400) == (eay & 0x400))
+                               yyerror("registers must be in opposite halves");
+                       if(!($4.flags & (R_AB | R_XREG)))
+                               yyerror("invalid X move register");
+                       if(!($8.flags & (R_AB | R_YREG)))
+                               yyerror("invalid Y move register");
+                       if($4.flags & R_AB &&
+                          $8.flags & R_AB &&
+                          $4.ab == $8.ab)
+                               yyerror("duplicate destination register");
+                       w0 = w0 & 0xFF | 0xC08000;      /* both write */
+                       w0 |= eax & 0x1f00 | (eay & 0x300) << 5 | (eay & 0x1800) << 9 | regx << 18 | regy << 16;}
+       |       x_or_y /*XMEM*/ ea /*ea_strange*/ ',' regs      regs ',' YMEM ea /*ea_strange*/
+                       {int eax = $2, eay = $8,
+                            regx = ($4.flags & R_AB) ? $4.ab | 2 : $4.xreg,
+                            regy = ($5.flags & R_AB) ? $5.ab | 2 : $5.yreg;
+                       hot_rreg = hot_nreg = hot_mreg = -1;
+                       if((eax & 0x400) == (eay & 0x400))
+                               yyerror("registers must be in opposite halves");
+                       if(!($4.flags & (R_AB | R_XREG)))
+                               yyerror("invalid X move register");
+                       if(!($5.flags & (R_AB | R_YREG)))
+                               yyerror("invalid Y move register");
+                       w0 = w0 & 0xFF | 0x808000;      /* X:write, Y:read */
+                       w0 |= eax & 0x1f00 | (eay & 0x300) << 5 | (eay & 0x1800) << 9 | regx << 18 | regy << 16;}
+       |       regs ',' x_or_y /*XMEM*/ ea /*ea_strange*/      YMEM ea /*ea_strange*/ ',' regs
+                       {int eax = $4, eay = $6,
+                            regx = ($1.flags & R_AB) ? $1.ab | 2 : $1.xreg,
+                            regy = ($8.flags & R_AB) ? $8.ab | 2 : $8.yreg;
+                       hot_rreg = hot_nreg = hot_mreg = -1;
+                       if((eax & 0x400) == (eay & 0x400))
+                               yyerror("registers must be in opposite halves");
+                       if(!($1.flags & (R_AB | R_XREG)))
+                               yyerror("invalid X move register");
+                       if(!($8.flags & (R_AB | R_YREG)))
+                               yyerror("invalid Y move register");
+                       w0 = w0 & 0xFF | 0xC00000;      /* X:read, Y:write */
+                       w0 |= eax & 0x1f00 | (eay & 0x300) << 5 | (eay & 0x1800) << 9 | regx << 18 | regy << 16;}
+       |       regs ',' x_or_y /*XMEM*/ ea /*ea_strange*/      regs ',' YMEM ea /*ea_strange*/
+                       {int eax = $4, eay = $8,
+                            regx = ($1.flags & R_AB) ? $1.ab | 2 : $1.xreg,
+                            regy = ($5.flags & R_AB) ? $5.ab | 2 : $5.yreg;
+                       hot_rreg = hot_nreg = hot_mreg = -1;
+                       if((eax & 0x400) == (eay & 0x400))
+                               yyerror("registers must be in opposite halves");
+                       if(!($1.flags & (R_AB | R_XREG)))
+                               yyerror("invalid X move register");
+                       if(!($5.flags & (R_AB | R_YREG)))
+                               yyerror("invalid Y move register");
+                       w0 = w0 & 0xFF | 0x800000;      /* both read */
+                       w0 |= eax & 0x1f00 | (eay & 0x300) << 5 | (eay & 0x1800) << 9 | regx << 18 | regy << 16;}
+       ;
+
+/*%%%******* absolute address and immediate data fields ************/
+
+num    :       CHEX
+                       {$$ = $1;}
+       |       CDEC
+                       {$$ = $1;}
+       |       FRAC
+                       {$$ = $1;}
+       ;
+
+ix     :       '#' expr
+                       {$$ = $2; expr_seg = ANY;}
+       |       '#' '<' expr
+                       {$$.val.i = n2int($3) & 0xFF;
+                       $$.type = INT;
+                       expr_seg = ANY;
+                       long_symbolic_expr = FALSE;}
+       ;
+
+ix_long        :       '#' '>' expr
+                       {$$ = $3; expr_seg = ANY;}
+       ;
+
+abs_addr
+       :       expr
+                       {$$ = n2int($1);
+                       expr_seg = $1.seg;}
+       ;
+
+abs_short_addr
+       :       '<' expr
+                       {$$ = n2int($2);
+                       expr_seg = $2.seg;}
+       ;
+
+io_short_addr
+       :       SHL expr
+                       {$$ = n2int($2);
+                       expr_seg = $2.seg;} 
+       ;
+
+num_or_sym
+       :       num
+                       {$$ = $1;}
+       |       SYM
+                       {$$ = sym_ref($1); free($1);}
+       |       io_short_addr
+                       {$$.type = INT; $$.val.i = $1; $$.seg = expr_seg;}
+       ;
+
+num_or_sym_expr
+       :       num
+                       {$$ = $1; expr_seg = $1.seg;}
+       |       SYM
+                       {$$ = sym_ref($1);
+                       free($1);
+                       long_symbolic_expr++;
+                       expr_seg = $$.seg;
+                       }
+       |       CHAR
+                       {$$.type = INT; $$.val.i = $1 & 0xFFFFFF; expr_seg = $$.seg = ANY;}
+       |       '*'
+                       {$$.type = INT; $$.val.i = pc; expr_seg = $$.seg = ANY;}
+       |       OP_PI
+                       {$$.type = FLT; $$.val.f = acos(-1.0); expr_seg = $$.seg = ANY;}
+       ;
+
+expr
+       :       OP_INT '(' expr ')'
+                       {$$.type = INT; 
+                       if($3.type == INT)
+                               $$.val.i = $3.val.i;
+                       else
+                               $$.val.i = $3.val.f;
+                       $$.seg = $3.seg;
+                       }
+       |       expr '|' expr
+                       {$$ = binary_op($1, '|', $3);}
+       |       expr '^' expr
+                       {$$ = binary_op($1, '^', $3);}
+       |       expr '&' expr
+                       {$$ = binary_op($1, '&', $3);}
+       |       expr SHR expr
+                       {$$ = binary_op($1, SHR, $3);}
+       |       expr SHL expr
+                       {$$ = binary_op($1, SHL, $3);}
+       |       expr '-' expr
+                       {$$ = binary_op($1, '-', $3);}
+       |       expr '+' expr
+                       {$$ = binary_op($1, '+', $3);}
+       |       expr '%' expr
+                       {$$ = binary_op($1, '%', $3);}
+       |       expr '/' expr
+                       {$$ = binary_op($1, '/', $3);}
+       |       expr '*' expr
+                       {$$ = binary_op($1, '*', $3);}
+       |       '-' expr %prec '~'
+                       {$$ = unary_op('-', $2);}
+       |       '~' expr
+                       {$$ = unary_op('~', $2);}
+       |       OP_SIN '(' expr ')'
+                       {$$ = unary_op('s', $3);}
+       |       OP_COS '(' expr ')'
+                       {$$ = unary_op('c', $3);}
+       |       OP_TAN '(' expr ')'
+                       {$$ = unary_op('t', $3);}
+       |       OP_ASIN '(' expr ')'
+                       {$$ = unary_op('S', $3);}
+       |       OP_ACOS '(' expr ')'
+                       {$$ = unary_op('C', $3);}
+       |       OP_ATAN '(' expr ')'
+                       {$$ = unary_op('T', $3);}
+       |       OP_EXP '(' expr ')'
+                       {$$ = unary_op('e', $3);}
+       |       OP_LN '(' expr ')'
+                       {$$ = unary_op('l', $3);}
+       |       OP_LOG '(' expr ')'
+                       {$$ = unary_op('L', $3);}
+       |       OP_ABS '(' expr ')'
+                       {$$ = unary_op('a', $3);}
+       |       OP_POW '(' expr ',' expr ')'
+                       {$$ = binary_op($3, 'p', $5);}
+       |       '(' expr ')'
+                       {$$ = $2;}
+       |       num_or_sym_expr
+                       {$$ = $1;}
+       ;
+
+/*%%%****************** end ******************************/
+
+%%
+
+#include <stdio.h>
+#include <setjmp.h>
+#include <sys/signal.h>
+
+int yydebug;
+
+struct n binary_op(a1, op, a2)
+struct n a1, a2;
+int op;
+{
+       struct n result;
+       int iarg1, iarg2;
+       double farg1, farg2;
+
+       if(a1.type == UNDEF || a2.type == UNDEF) {
+               result.type = UNDEF;
+               return result;
+       }
+
+       iarg1 = a1.type == INT ? a1.val.i : a1.val.f;
+       iarg2 = a2.type == INT ? a2.val.i : a2.val.f;
+
+       farg1 = a1.type == INT ? a1.val.i : a1.val.f;
+       farg2 = a2.type == INT ? a2.val.i : a2.val.f;
+
+       /* figure out target segment */
+
+       if(a1.seg == a2.seg)
+               result.seg = a1.seg;
+       else if(a1.seg == ANY)
+               result.seg = a2.seg;
+       else if(a2.seg == ANY)
+               result.seg = a1.seg;
+       else
+               result.seg = NONE;
+
+       /* promote to float automatically */
+
+       if(a1.type != a2.type) {
+               if(a1.type == INT) {
+                       a1.val.f = a1.val.i;            /* truncate */
+                       a1.type = FLT;
+               } else {
+                       a2.val.f = a2.val.i;            /* truncate */
+               }
+       }
+       result.type = a1.type;
+
+       /* do the op */
+
+       switch(op) {
+       case '+':
+               if(result.type == INT) result.val.i = a1.val.i + a2.val.i;
+               else result.val.f = a1.val.f + a2.val.f;
+               break;
+       case '-':
+               if(result.type == INT) result.val.i = a1.val.i - a2.val.i;
+               else result.val.f = a1.val.f - a2.val.f;
+               break;
+       case '*':
+               if(result.type == INT) result.val.i = a1.val.i * a2.val.i;
+               else result.val.f = a1.val.f * a2.val.f;
+               break;
+       case '/':
+               if(result.type == INT) result.val.i = a1.val.i / a2.val.i;
+               else result.val.f = a1.val.f / a2.val.f;
+               break;
+       case '%':
+               result.val.i = iarg1 % iarg2;
+               result.type = INT;
+               break;
+       case SHL:
+               result.val.i = iarg1 << iarg2;
+               result.type = INT;
+               break;
+       case SHR:
+               result.val.i = iarg1 >> iarg2;
+               result.type = INT;
+               break;
+       case '|':
+               result.val.i = iarg1 | iarg2;
+               result.type = INT;
+               break;
+       case '&':
+               result.val.i = iarg1 & iarg2;
+               result.type = INT;
+               break;
+       case '^':
+               result.val.i = iarg1 ^ iarg2;
+               result.type = INT;
+               break;
+       case 'p':
+               result.val.f = pow(farg1, farg2);
+               result.type = FLT;
+       }
+
+       return result;
+}
+
+jmp_buf unary_env;
+
+void
+sigfpu()
+{
+       longjmp(unary_env, 1);
+}
+
+char *unary_name(op)
+{
+       switch(op) {
+       case 's':       return "sin";
+       case 'c':       return "cos";
+       case 't':       return "tan";
+       case 'S':       return "asin";
+       case 'C':       return "acos";
+       case 'T':       return "atan";
+       case 'e':       return "exp";
+       case 'l':       return "ln";
+       case 'L':       return "log";
+       case 'a':       return "abs";
+       }
+}
+
+struct n unary_op(op, a1)
+int op;
+struct n a1;
+{
+       struct n result;
+       void (*orig)();
+       double farg;
+
+       if(a1.type == UNDEF) {
+               result.type = UNDEF;
+               return result;
+       }
+
+       result.seg = a1.seg;
+
+       /* do the op */
+
+       orig = signal(SIGFPE, sigfpu);
+       if(setjmp(unary_env)) {
+               yyerror("floating point exception in function %s", unary_name(op));
+               result.val.i = result.val.f = 0;
+       } else {
+               farg = a1.type == INT ? (double)a1.val.i : a1.val.f;
+               switch(op) {
+               case '-':
+                       result.type = a1.type;
+                       if(a1.type == INT) result.val.i = -a1.val.i;
+                       else result.val.f = -a1.val.f;
+                       break;
+               case '~':
+                       result.type = a1.type;
+                       if(a1.type == INT) result.val.i = ~a1.val.i;
+                       else result.val.f = ~(int)a1.val.f;
+                       break;
+               case 'a':
+                       result.type = a1.type;
+                       if(a1.type == INT) result.val.i = a1.val.i < 0 ? -a1.val.i : a1.val.i;
+                       else result.val.f = result.val.f = a1.val.f < 0 ? -a1.val.f : a1.val.f;
+                       break;
+               case 's':
+                       result.type = FLT;
+                       result.val.f = sin(farg);
+                       break;
+               case 'c':
+                       result.type = FLT;
+                       result.val.f = cos(farg);
+                       break;
+               case 't':
+                       result.type = FLT;
+                       result.val.f = tan(farg);
+                       break;
+               case 'S':
+                       result.type = FLT;
+                       result.val.f = asin(farg);
+                       break;
+               case 'C':
+                       result.type = FLT;
+                       result.val.f = acos(farg);
+                       break;
+               case 'T':
+                       result.type = FLT;
+                       result.val.f = atan(farg);
+                       break;
+               case 'e':
+                       result.type = FLT;
+                       result.val.f = exp(farg);
+                       break;
+               case 'l':
+                       result.type = FLT;
+                       result.val.f = log(farg);
+                       break;
+               case 'L':
+                       result.type = FLT;
+                       result.val.f = log10(farg);
+                       break;
+               }
+       }
+       signal(SIGFPE, orig);
+
+       return result;
+}
+
+n2int(n)
+struct n n;
+{
+       if(n.type == UNDEF)
+               return UNDEF;
+       else if(n.type == INT)
+               return n.val.i;
+       else
+               return n.val.f;
+}
+
+n2frac(n)
+struct n n;
+{
+       double adval = n.val.f >= 0.0 ? n.val.f : -n.val.f;
+
+       if(n.type == UNDEF)
+               return UNDEF;
+       else if(n.type == INT)
+               return n.val.i;
+       else if(n.val.f == -1.0)
+               return 0x800000;
+
+       adval -= (double)(int)adval;
+       adval *= (double)0x800000;
+       adval += 0.5;
+
+       if(n.val.f >= 0.0)
+               return adval;
+       else
+               return -adval;
+}
+
+extern struct {int n; char *name;} tok_tab[];
+extern int n_tok;
+
+char *tok_print(tok)
+int tok;
+{
+       int i;
+       static char buf[32];
+
+       if(tok == '1') {
+               i = 1;
+       }
+
+       if(tok < 256) {
+               sprintf(buf, tok < ' ' ? "\\z%02X" : "%c", tok & 0xFF);
+               return buf;
+       } else {
+               for(i = 0; i < n_tok; i++) {
+                       if(tok == tok_tab[i].n)
+                               return tok_tab[i].name;
+               }
+       }
+       return "*bogus*";
+}
+
+yyerror(s, a0, a1, a2, a3)
+char *s, *a0, *a1, *a2, *a3;
+{
+       extern int error;
+       char buf[1024];
+
+       error++;
+       sprintf(buf, s, a0, a1, a2, a3);
+
+       if(pass == 2) {
+               fprintf(stderr, "%s: line %d: %s (tok=%s)\n", curfile, curline,
+                       buf, tok_print(yychar));
+               fprintf(stderr, "%s\n", cur_line);
+               printf("%s: line %d: %s (tok=%s)\n", curfile, curline,
+                       buf, tok_print(yychar));
+       }
+}
+
+yywarning(s, a0, a1, a2, a3)
+char *s, *a0, *a1, *a2, *a3;
+{
+       extern int warning;
+       char buf[1024];
+
+       warning++;
+       sprintf(buf, s, a0, a1, a2, a3);
+
+       if(pass == 2) {
+               fprintf(stderr, "%s: line %d: %s (tok=%s)\n", curfile, curline,
+                       buf, tok_print(yychar));
+               fprintf(stderr, "%s\n", cur_line);
+               printf("%s: line %d: %s (tok=%s)\n", curfile, curline,
+                       buf, tok_print(yychar));
+       }
+}
+
+char *luntab(s)
+char *s;
+{
+       static char buf[1024];
+       int p;
+
+       strcpy(buf, s);
+
+       untab(buf);
+       p = strlen(buf);
+
+       if(buf[p - 1] == '\n')
+               buf[p - 1] = '\0';
+
+       return buf;
+}