X-Git-Url: https://jxself.org/git/?p=a56.git;a=blobdiff_plain;f=src%2Fa56.y;fp=src%2Fa56.y;h=9c3399b3bb724c8ffacafbde3c877b7971adc1a1;hp=0000000000000000000000000000000000000000;hb=34923afb4a618598083726717bf2f20d310f4f6e;hpb=c6db6f4c5cb82f5e09472163bf11be0c165965ee diff --git a/src/a56.y b/src/a56.y new file mode 100644 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 + +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 CHEX CDEC FRAC +%token AREG BREG MREG NREG RREG XREG YREG +%token OP OPA OPP +%token OP_JCC OP_JSCC OP_TCC +%token SYM +%token STRING +%token 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 num num_or_sym +%type num_or_sym_expr +%type expr +%type ix +%type ix_long + +%type abs_addr abs_short_addr io_short_addr +%type a_b x_or_y ea b5_10111_max +%type p6_ean_a6 ea_no_ext p6_ea_a6 ea_a6 ea_a12 +%type ea_short +%type prog_ctl_reg +%type op8_1 op8_2 op8_3 op8_4 op8_5 op8_6 op8_7 op8_8 +%type mpy_arg mpy_srcs plus_minus +%type sd3 +%type funky_ctl_reg tcc_sd space +%type opt_label + +%type regs +%type 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 = ""; + 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 +#include +#include + +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; +}