%{
/* m68k_parse.c: line assembler for generic m68k_cpu
  
   Copyright (c) 2009-2010, Holger Veit

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   HOLGER VEIT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

   Except as contained in this notice, the name of Holger Veit et al shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Holger Veit et al.

   04-Oct-09    HV      Initial version
   20-Sep-14    PS      Adapted for AltairZ80
   
   use "bison m68kasm.y -o m68kasm.c" to create m68kasm.c
*/

#include "sim_defs.h"
#include <ctype.h>
#include <string.h>

struct _ea {
	int ea;
	int cnt;
	t_value arg[10];
};
struct _rea {
	int reg;
	struct _ea ea;
};
struct _mask {
	int x;
	int d;
};
struct _brop {
	int opc;
	int len;
};

static int oplen;
const static int movemx[] = {   0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
                                0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080 };
const static int movemd[] = {   0x0080, 0x0040, 0x0020, 0x0010, 0x0008, 0x0004, 0x0002, 0x0001,
                                0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100 };
static int yyrc;
static int yyerrc;
static int yylex(void);
static int _genop(t_value arg);
static int _genea(struct _ea arg);
static int _genbr(t_value arg,t_value,int);
static void yyerror(char* s);

#define YYDEBUG 1
%}

%union {
	int rc;
	int reg;
	int wl;
	int opc;
	struct _ea ea;
	t_value num;
	struct _rea rea;
	struct _mask mask;
	struct _brop brop;
}

%token       A0 A1 A2 A3 A4 A5 A6 A7 D0 D1 D2 D3 D4 D5 D6 D7
%token       CCR SR USP PC
%token <num> NUMBER
%token       ABCD ADD ADDA ADDI ADDQ ADDX AND ANDI OR ORI SBCD SUB SUBA SUBI SUBQ SUBX
%token       ASL ASR LSL LSR ROL ROR ROXL ROXR
%token		 BCC BCS BEQ BGE BGT BHI BLE BLS BLT BMI BNE BPL BVC BVS BSR BRA
%token		 BCLR BSET BCHG BTST CHK CMP CMPA CMPI CMPM EOR EORI EXG EXT
%token		 DIVU DIVS MULU MULS
%token		 DBCC DBCS DBEQ DBF DBGE DBGT DBHI DBLE DBLS DBLT DBMI DBNE DBPL DBT DBVC DBVS
%token		 SCC SCS SEQ SF SGE SGT SHI SLE SLS SLT SMI SNE SPL ST SVC SVS
%token		 ILLEGAL NOP RESET RTE RTR RTS TRAPV
%token		 JMP JSR LEA LINK MOVE MOVEA MOVEM MOVEP MOVEQ
%token		 CLR NEG NEGX NBCD NOT PEA STOP TAS SWAP TRAP TST UNLK 

%token		 PREDEC POSTINC BSIZE WSIZE LSIZE SSIZE

%start      stmt
%type <opc> bcdop bcdarg dualop immop qop immop2 shftarg monop btop
%type <opc> mdop dbop jop arop
%type <opc> direct
%type <brop> brop
%type <rea> dualarg shftop
%type <reg> dreg areg
%type <ea> ea0 ea1 ea2 ea3 ea4 ea5 ea6 ea70 ea72 ea73 ea74 eama eaa eaall eada eadas easr ead eac eacad eacai
%type <wl> szwl szbwl szmv szm szs
%type <mask> regs reglist

%%
stmt:
	bcdop bcdarg { _genop($1 | $2); yyrc = -1; }
|	dualop dualarg { _genop($1 | $2.reg | $2.ea.ea); yyrc = _genea($2.ea) -1; } 
|   immop '#' NUMBER ',' eada { _genop($1 | $5.ea); if (oplen==0) { _genop($3 & 0xff); yyrc = _genea($5) - 3; }
		else if (oplen==1) { _genop($3); yyrc = _genea($5) - 3; } else { _genop($3>>16); _genop($3 & 0xffff); yyrc = _genea($5)-5; } } 
|   qop '#' NUMBER ',' eaa { _genop($1 | (($3&7)<<9) | $5.ea); yyrc = _genea($5) - 1; } 
|   immop2 '#' NUMBER ',' eadas { _genop($1 | $5.ea); if (oplen==0) { _genop($3 & 0xff); yyrc = _genea($5) - 3; }
		else if (oplen==1) { _genop($3); yyrc = _genea($5) - 3; } else { _genop($3>>16); _genop($3 & 0xffff); yyrc = _genea($5)-5; } } 
|   shftop { _genop($1.reg); if (($1.reg&0xc0)==0xc0) yyrc = _genea($1.ea) - 1; else { yyrc = -1; } }
|	brop NUMBER { yyrc = _genbr($1.opc,$2,$1.len); }
|	btop dreg ',' eada { _genop($1 | ($2<<9) | 0x100 | $4.ea); yyrc = _genea($4) - 1; }
|	btop '#' NUMBER ',' eada { _genop($1 | 0x0800 | $5.ea); _genop($3); yyrc = _genea($5) - 3; }
|	CHK ead ',' dreg { _genop(0x4180 | ($4<<9) | $2.ea); yyrc = _genea($2) - 1; }
|	monop eada { _genop($1 | $2.ea); yyrc = _genea($2) - 1; }
|	CMP szbwl eaall ',' dreg { _genop(0xb000 | ($2<<6) | ($5<<9) | $3.ea); yyrc = _genea($3) - 1; }
|	mdop ead ',' dreg { _genop($1 | ($4<<9) | $2.ea); yyrc = _genea($2) - 1; }
|	CMPA szwl eaall ',' areg { _genop(0xb0c0 | ($2<<8) | ($5<<9) | $3.ea); yyrc = _genea($3) - 1; }
|	CMPM szbwl ea3 ',' ea3 { _genop(0xb108 | ($5.ea<<9) | ($2<<6) | $3.ea); yyrc = -1; }
|	dbop dreg ',' NUMBER { yyrc = _genbr($1 | $2, $4, 1); }
|	EOR szbwl dreg ',' eada { _genop(0xb000 | ($2 << 6) | 0x100 | $5.ea); yyrc = _genea($5) - 1; }
|	EXG dreg ',' dreg { _genop(0xc140 | ($2<<9) | $4); yyrc = -1; }
|	EXG areg ',' areg { _genop(0xc148 | ($2<<9) | $4); yyrc = -1; }
|	EXG areg ',' dreg { _genop(0xc188 | ($4<<9) | $2); yyrc = -1; }
|	EXG dreg ',' areg { _genop(0xc188 | ($2<<9) | $4); yyrc = -1; }
|	EXT szwl dreg { _genop(0x4840 | ($2<<6) | $3); yyrc = -1; }
|	direct { _genop($1); yyrc = -1; }
|	jop eac { _genop($1 | $2.ea); yyrc = _genea($2) -1; }
|	LEA eac ',' areg { _genop(0x41c0 | $2.ea); yyrc = _genea($2) - 1; }
|	LINK areg ',' '#' NUMBER { _genop(0x4e50 | $2); _genop($5); yyrc = -3; }
|	MOVE szmv eaall ',' eadas { if ($5.ea==074) { _genop(0x44c0 | ($5.cnt==1?0x0200:0x0000) | $3.ea); yyrc = _genea($3) - 1; }
							    else { int tmp = (($5.ea&070)>>3)|(($5.ea&7)<<3); _genop(0x0000 | ($2<<12) | (tmp<<6) | $3.ea);
    	                           	   yyrc = _genea($3) - 1; yyrc += _genea($5); } }
|	MOVE SR ',' eada { _genop(0x40c0 | $4.ea); yyrc = _genea($4) - 1; }
|	MOVE USP ',' areg { _genop(0x4e68 | $4);  yyrc = -1; }
|	MOVE areg ',' USP { _genop(0x4e60 | $2);  yyrc = -1; }
|	MOVEA szm eada ',' areg { _genop(0x0040 | ($2<<12) | ($5<<9) | $3.ea); yyrc = _genea($3) - 1; }
|	MOVEM szwl reglist ',' eacad { _genop(0x4880 | ($2<<6) | $5.ea); _genop(($5.ea&070)==040 ? $3.d : $3.x); yyrc = _genea($5) - 3; }
|	MOVEM szwl eacai ',' reglist  { _genop(0x4c80 | ($2<<6) | $3.ea); _genop($5.x); yyrc = _genea($3) - 3; }
|	MOVEP szwl dreg ',' ea5 { _genop(0x0108 | ($3<<9) | ($2<<6) | ($5.ea & 7)); yyrc = _genea($5) - 1; }
|	MOVEP szwl ea5 ',' dreg { _genop(0x0188 | ($5<<9) | ($2<<6) | ($3.ea & 7)); yyrc = _genea($3) - 1; }
|   MOVEQ '#' NUMBER ',' dreg { _genop(0x7000 | ($5<<9) | ($3&0xff)); yyrc = -1; }
|	STOP '#' NUMBER { _genop(0x4e72); yyrc = _genop($3&0xffff) - 1; }
|	arop szwl eaall ',' areg { _genop($1 | ($5<<9) | ($2<<8) | $3.ea); yyrc = _genea($3) - 1; }
|	SWAP dreg { _genop(0x4840 | $2); yyrc = -1; }
|	TRAP '#' NUMBER { _genop(0x4e40 | ($3 & 0x0f)); yyrc = -1; }
|	UNLK areg { _genop(0x4e58 | $2); yyrc = -1; }
;

arop:
	ADDA { $$ = 0xd0c0; }
|	SUBA { $$ = 0x90c0; };

bcdop:
	ABCD { $$ = 0xc100; }
|	ADDX szbwl { $$ = 0xd100 | ($2<<6); }
|	SBCD { $$ = 0x8100; }
|	SUBX szbwl { $$ = 0x9100 | ($2<<6); }
;

dualop:
	ADD szbwl { $$ = 0xd000 | ($2<<6); }
|	AND szbwl { $$ = 0xc000 | ($2<<6); }
|	OR szbwl  { $$ = 0x8000 | ($2<<6); }
|	SUB szbwl { $$ = 0x9000 | ($2<<6); }
;

immop:
	ADDI szbwl { $$ = 0x0600 | ($2<<6); }
|	CMPI szbwl { $$ = 0x0c00 | ($2<<6); }
|	SUBI szbwl { $$ = 0x0400 | ($2<<6); }
;

immop2:
	ANDI szbwl { $$ = 0x0200 | ($2<<6); }
|	EORI szbwl { $$ = 0x0a00 | ($2<<6); }
|	ORI szbwl  { $$ = 0x0000 | ($2<<6); }
;

qop:
	ADDQ szbwl { $$ = 0x5000 | ($2<<6); }
|	SUBQ szbwl { $$ = 0x5100 | ($2<<6); }
;

shftop:
	ASL eama { $$.reg = 0xe1c0 | $2.ea; $$.ea = $2; }
|	ASL szbwl shftarg { $$.reg = 0xe100 | ($2<<6) | $3; }
|	ASR eama { $$.reg = 0xe0c0 | $2.ea; $$.ea = $2; }
|	ASR szbwl shftarg { $$.reg = 0xe000 | ($2<<6) | $3; }
|	LSL eama { $$.reg = 0xe3c0 | $2.ea; $$.ea = $2; }
|	LSL szbwl shftarg { $$.reg = 0xe108 | ($2<<6) | $3; }
|	LSR eama { $$.reg = 0xe2c0 | $2.ea; $$.ea = $2; }
|	LSR szbwl shftarg { $$.reg = 0xe008 | ($2<<6) | $3; }
|	ROL eama { $$.reg = 0xe7c0 | $2.ea; $$.ea = $2; }
|	ROL szbwl shftarg { $$.reg = 0xe118 | ($2<<6) | $3; }
|	ROR eama { $$.reg = 0xe6c0 | $2.ea; $$.ea = $2; }
|	ROR szbwl shftarg { $$.reg = 0xe018 | ($2<<6) | $3; }
|	ROXL eama { $$.reg = 0xe5c0 | $2.ea; $$.ea = $2; }
|	ROXL szbwl shftarg { $$.reg = 0xe100 | ($2<<6) | $3; }
|	ROXR eama { $$.reg = 0xe4c0 | $2.ea; $$.ea = $2; }
|	ROXR szbwl shftarg { $$.reg = 0xe000 | ($2<<6) | $3; }
;

brop:
	BCC { $$.opc = 0x6400; $$.len = 1; }
|	BCS { $$.opc = 0x6500; $$.len = 1; }
|	BEQ { $$.opc = 0x6700; $$.len = 1; }
|	BGE { $$.opc = 0x6c00; $$.len = 1; }
|	BGT { $$.opc = 0x6e00; $$.len = 1; }
|	BHI { $$.opc = 0x6200; $$.len = 1; }
|	BLE { $$.opc = 0x6f00; $$.len = 1; }
|	BLS { $$.opc = 0x6300; $$.len = 1; }
|	BLT { $$.opc = 0x6d00; $$.len = 1; }
|	BMI { $$.opc = 0x6b00; $$.len = 1; }
|	BNE { $$.opc = 0x6600; $$.len = 1; }
|	BPL { $$.opc = 0x6a00; $$.len = 1; }
|	BVC { $$.opc = 0x6800; $$.len = 1; }
|	BVS { $$.opc = 0x6900; $$.len = 1; }
|	BSR { $$.opc = 0x6100; $$.len = 1; }
|	BRA { $$.opc = 0x6000; $$.len = 1; }
|	BCC szs { $$.opc = 0x6400; $$.len = 0; }
|	BCS szs { $$.opc = 0x6500; $$.len = 0; }
|	BEQ szs { $$.opc = 0x6700; $$.len = 0; }
|	BGE szs { $$.opc = 0x6c00; $$.len = 0; }
|	BGT szs { $$.opc = 0x6e00; $$.len = 0; }
|	BHI szs { $$.opc = 0x6200; $$.len = 0; }
|	BLE szs { $$.opc = 0x6f00; $$.len = 0; }
|	BLS szs { $$.opc = 0x6300; $$.len = 0; }
|	BLT szs { $$.opc = 0x6d00; $$.len = 0; }
|	BMI szs { $$.opc = 0x6b00; $$.len = 0; }
|	BNE szs { $$.opc = 0x6600; $$.len = 0; }
|	BPL szs { $$.opc = 0x6a00; $$.len = 0; }
|	BVC szs { $$.opc = 0x6800; $$.len = 0; }
|	BVS szs { $$.opc = 0x6900; $$.len = 0; }
|	BSR szs { $$.opc = 0x6100; $$.len = 0; }
|	BRA szs { $$.opc = 0x6000; $$.len = 0; }
;

btop:
	BCHG { $$ = 0x0040; }
|	BCLR { $$ = 0x0080; }
|	BSET { $$ = 0x00c0; }
|	BTST { $$ = 0x0000; }
;

monop:
	CLR szbwl { $$ = 0x4200 | ($2<<6); }
|	NBCD { $$ = 0x4800; }
|	NEG szbwl { $$ = 0x4400 | ($2<<6); }
|	NEGX szbwl { $$ = 0x4000 | ($2<<6); }
|	NOT szbwl { $$ = 0x4600 | ($2<<6); }
|	SCC { $$ = 0x54c0; }
|	SCS { $$ = 0x55c0; }
|	SEQ { $$ = 0x57c0; }
|	SF { $$ = 0x51c0; }
|	SGE { $$ = 0x5cc0; }
|	SGT { $$ = 0x5ec0; }
|	SHI { $$ = 0x52c0; }
|	SLE { $$ = 0x5fc0; }
|	SLS { $$ = 0x53c0; }
|	SLT { $$ = 0x5dc0; }
|	SMI { $$ = 0x5bc0; }
|	SNE { $$ = 0x56c0; }
|	SPL { $$ = 0x5ac0; }
|	ST { $$ = 0x50c0; }
|	SVC { $$ = 0x58c0; }
|	SVS { $$ = 0x59c0; }
|	TAS { $$ = 0x4ac0; }
|	TST szbwl { $$ = 0x4a00 | ($2<<6); }
;

mdop:
	DIVS { $$ = 0x81c0; }
|	DIVU { $$ = 0x80c0; }
|	MULS { $$ = 0xc1c0; }
|	MULU { $$ = 0xc0c0; }
;

dbop:
	DBCC { $$ = 0x54c8; }
|	DBCS { $$ = 0x55c8; }
|	DBEQ { $$ = 0x57c8; }
|	DBGE { $$ = 0x5cc8; }
|	DBGT { $$ = 0x5ec8; }
|	DBHI { $$ = 0x52c8; }
|	DBLE { $$ = 0x5fc8; }
|	DBLS { $$ = 0x53c8; }
|	DBLT { $$ = 0x5dc8; }
|	DBMI { $$ = 0x5bc8; }
|	DBNE { $$ = 0x56c8; }
|	DBPL { $$ = 0x5ac8; }
|	DBVC { $$ = 0x58c8; }
|	DBVS { $$ = 0x59c8; }
|	DBF  { $$ = 0x51c8; }
|	DBT  { $$ = 0x50c8; }
;

direct:
	ILLEGAL { $$ = 0x4afc; }
|	NOP { $$ = 0x4e71; }
|	RESET { $$ = 0x4e70; }
|	RTE { $$ = 0x4e73; }
|	RTR { $$ = 0x4e77; }
|	RTS { $$ = 0x4e75; }
|	TRAPV { $$ = 0x4e76; }
;

jop:
	JMP { $$ = 0x4ec0; }
|	JSR { $$ = 0x4e80; }
|	PEA { $$ = 0x4840; };

shftarg:
	dreg ',' dreg { $$ = ($1<<9) | 0x20 | $3; }
|	'#' NUMBER ',' dreg { $$ = (($2 & 7)<<9) | $4; };

bcdarg:
	ea0 ',' ea0 { $$ = (($1.ea & 7) << 9) |          ($3.ea & 7); }
|	ea4 ',' ea4 { $$ = (($1.ea & 7) << 9) | 0x0008 | ($3.ea & 7); };

dualarg:
	dreg ',' eaa  { if (($3.ea & 070)==0) { /* dx,dy must be swapped */
						$$.reg = ($3.ea & 7)<<9; $3.ea = $1 & 7; $$.ea = $3; }
					else { $$.reg = ($1<<9) | 0x100; $$.ea = $3; } }
|	eama ',' dreg { $$.reg = ($3<<9); $$.ea = $1; };

areg:
	A0 { $$=0; }
|	A1 { $$=1; }
|	A2 { $$=2; }
|	A3 { $$=3; }
|	A4 { $$=4; }
|	A5 { $$=5; }
|	A6 { $$=6; }
|	A7 { $$=7; };

dreg:
	D0 { $$=0; }
|	D1 { $$=1; }
|	D2 { $$=2; }
|	D3 { $$=3; }
|	D4 { $$=4; }
|	D5 { $$=5; }
|	D6 { $$=6; }
|	D7 { $$=7; };

szs:
	SSIZE { $$ = 1; oplen = 0; }

szwl:
	WSIZE { $$ = 0; oplen = 1; }
|	LSIZE { $$ = 1; oplen = 2; };

szbwl:
	BSIZE { $$ = 0; oplen = 0; }
|	WSIZE { $$ = 1; oplen = 1; }
|	LSIZE { $$ = 2; oplen = 2; };

szmv:
	BSIZE { $$ = 1; oplen = 0; }
|	WSIZE { $$ = 3; oplen = 1; }
|	LSIZE { $$ = 2; oplen = 2; };

szm:
	WSIZE { $$ = 3; oplen = 1; }
|	LSIZE { $$ = 2; oplen = 2; };

reglist: 
	regs { $$ = $1; }
|	regs '/' reglist { $$.x = $1.x | $3.x; $$.d = $1.d | $3.d; };

regs:
	areg { $$.x = movemx[$1]; $$.d = movemd[$1]; }
|	dreg { $$.x = movemx[$1+8]; $$.d = movemd[$1+8]; }
|	areg '-' areg { int i,l=$1,h=$3; if (l>h) { l=$3; h=$1; } $$.x = $$.d = 0; 
					for (i=l; i<=h; i++) { $$.d |= movemx[i]; $$.d |= movemd[i]; } }
|	dreg '-' dreg { int i,l=$1,h=$3; if (l>h) { l=$3; h=$1; } $$.x = $$.d = 0; 
					for (i=l; i<=h; i++) { $$.x |= movemx[i+8]; $$.d |= movemd[i+8]; } }
;

eama:         ea1 | ea2 | ea3 | ea4 | ea5 | ea6 | ea70 | ea72 | ea73 | ea74;
eaa:    ea0 | ea1 | ea2 | ea3 | ea4 | ea5 | ea6 | ea70;
ead:    ea0 |       ea2 | ea3 | ea4 | ea5 | ea6 | ea70 | ea72 | ea73 | ea74;
eaall:	ea0 | eama;
eada:   ea0 |       ea2 | ea3 | ea4 | ea5 | ea6 | ea70;
eadas:  eada | easr;
eac:                ea2 |             ea5 | ea6 | ea70 | ea72 | ea73;
eacai:              ea2 | ea3 |       ea5 | ea6 | ea70;
eacad:              ea2 |       ea4 | ea5 | ea6 | ea70;

ea0:
	dreg { $$.ea = $1; $$.cnt = 0; };
ea1:
	areg { $$.ea = 010 | $1; $$.cnt = 0; };
ea2:
	'(' areg ')' { $$.ea = 020 | $2; $$.cnt = 0; };
ea3:
	'(' areg POSTINC { $$.ea = 030 | $2; $$.cnt = 0; };
ea4:
	PREDEC areg ')' { $$.ea = 040 | $2; $$.cnt = 0; };
ea5:
	'(' NUMBER ',' areg ')' { $$.ea = 050 | $4; $$.cnt = 1; $$.arg[0] = $2; };
ea6:
	'(' NUMBER ',' areg ',' dreg szwl ')' 
		{ $$.ea = 060 | $4; $$.cnt = 1; $$.arg[0] = 0x8000 | ($6<<12) | ($7<<11) | ($2 & 0xff); }
|  	'(' NUMBER ',' areg ',' areg szwl ')' 
		{ $$.ea = 060 | $4; $$.cnt = 1; $$.arg[0] =          ($6<<12) | ($7<<11) | ($2 & 0xff); };
ea70:
	'(' NUMBER ')' szwl { if ($4==0) { $$.ea = 070; $$.cnt = 1; $$.arg[0] = $2; } 
				          else {       $$.ea = 071; $$.cnt = 2; $$.arg[0] = $2 >> 16; $$.arg[1] = $2 & 0xffff; } }
|   '(' NUMBER ')' { int tmp = ($2>>15) & 0x1ffff; if (tmp==0 || tmp==0x1ffff) { $$.ea = 070; $$.cnt = 1; $$.arg[0] = $2; }
			 else { $$.ea = 070; $$.cnt = 2;  $$.arg[0] = $2 >> 16; $$.arg[1] = $2 & 0xffff; } };
ea72:
	'(' NUMBER ',' PC ')' { $$.ea = 072; $$.cnt = 1; $$.arg[0] = $2; }
|	NUMBER { $$.ea = 072; $$.cnt = 1; $$.arg[0] = $1; };
ea73:
	'(' NUMBER ',' PC ',' dreg szwl ')' 
		{ $$.ea = 073; $$.cnt = 1; $$.arg[0] = 0x8000 | ($6<<12) | ($7<<11) | ($2 & 0xff); }
|  	'(' NUMBER ',' PC ',' areg szwl ')' 
		{ $$.ea = 073; $$.cnt = 1; $$.arg[0] = ($6<<12) | ($7<<11) | ($2 & 0xff); };
ea74:
	'#' NUMBER { $$.ea = 074; if (oplen==0) { $$.cnt = 1; $$.arg[0] = $2 & 0xff; }
				 else if (oplen==1) { $$.cnt = 1; $$.arg[0] = $2 & 0xffff; }
				 else { $$.cnt = 2; $$.arg[0] = $2 >> 16; $$.arg[1] = $2 & 0xffff; } };
easr:
	CCR { $$.ea = 074; $$.cnt = 0; }
|	SR { $$.ea = 074; $$.cnt = 1; };
%%

static void yyerror(char* s)
{
	/* do not emit anything, but set error flag */
	yyerrc = 1;
}

struct _optable {
	char* mnem;
	int token;
};

static struct _optable ops[] = {
	{ "abcd",	ABCD },		{ "add",	ADD },		{ "adda",	ADDA },		{ "addi",	ADDI },
	{ "addq",	ADDQ },		{ "addx",	ADDX },		{ "and",	AND },		{ "andi",	ANDI },
	{ "asl",	ASL }, 		{ "asr",	ASR },		{ "bcc",	BCC },		{ "bcs",	BCS },
	{ "beq",	BEQ },		{ "bge",	BGE },		{ "bgt",	BGT },		{ "bhi",	BHI },
	{ "ble",	BLE },		{ "bls",	BLS },		{ "blt",	BLT },		{ "bmi",	BMI },
	{ "bne",	BNE },		{ "bpl",	BPL },		{ "bvc",	BVC },		{ "bvs",	BVS },
	{ "bchg",	BCHG },		{ "bclr",	BCLR },		{ "bra",	BRA },		{ "bset",	BSET },
	{ "bsr",	BSR },		{ "btst",	BTST },		{ "chk",	CHK },		{ "clr",	CLR },
	{ "cmp",	CMP },		{ "cmpa",	CMPA },		{ "cmpi",	CMPI },		{ "cmpm",	CMPM },
	{ "dbcc",	DBCC },		{ "dbcs",	DBCS },		{ "dbeq",	DBEQ },		{ "dbf",	DBF },
	{ "dbge",	DBGE },		{ "dbgt",	DBGT },		{ "dbhi",	DBHI },		{ "dble",	DBLE },
	{ "dbls",	DBLS },		{ "dblt",	DBLT },		{ "dbmi",	DBMI },		{ "dbne",	DBNE },
	{ "dbpl",	DBPL },		{ "dbt",	DBT },		{ "dbvc",	DBVC },		{ "dbvs",	DBVS },
	{ "divs",	DIVS },		{ "divu",	DIVU },		{ "eor",	EOR },		{ "eori",	EORI },
	{ "exg",	EXG },		{ "ext",	EXT },		{ "illegal",ILLEGAL },	{ "jmp",	JMP },
	{ "jsr",	JSR },		{ "lea",	LEA },		{ "link",	LINK },		{ "lsl",	LSL },
	{ "lsr",	LSR }, 		{ "move",	MOVE },		{ "movea",	MOVEA },	{ "movem",	MOVEM },
	{ "movep",	MOVEP },	{ "moveq",	MOVEQ },	{ "muls",	MULS },		{ "mulu",	MULU },
	{ "nbcd",	NBCD },		{ "neg",	NEG },		{ "negx",	NEGX },		{ "nop",	NOP },
	{ "not",	NOT },		{ "or",		OR },		{ "ori",	ORI },		{ "pea",	PEA },
	{ "reset",	RESET },	{ "rol",	ROL },		{ "ror",	ROR },		{ "roxl",	ROXL },
	{ "roxr",	ROXR },		{ "rte",	RTE },		{ "rtr",	RTR },
	{ "rts",	RTS },		{ "scc",	SCC },		{ "scs",	SCS },		{ "seq",	SEQ },
	{ "sf",		SF },		{ "sge",	SGE },		{ "sgt",	SGT },		{ "shi",	SHI },
	{ "sle",	SLE },		{ "sls",	SLS },		{ "slt",	SLT },		{ "smi",	SMI },
	{ "sne",	SNE },		{ "spl",	SPL },		{ "st",		ST },		{ "svc",	SVC },
	{ "svs",	SVS },		{ "stop",	STOP },		{ "sub",	SUB },		{ "suba",	SUBA },
	{ "subi",	SUBI },		{ "subq",	SUBQ },		{ "subx",	SUBX },		{ "swap",	SWAP },
	{ "tas",	TAS },		{ "trap",	TRAP },		{ "trapv",	TRAPV },	{ "tst",	TST },
	{ "unlk",	UNLK },		{ "a0",		A0 },		{ "a1",		A1 },		{ "a2",		A2 },
	{ "a3",		A3 },		{ "a4",		A4 },		{ "a5",		A5 },		{ "a6",		A6 },
	{ "a7",		A7 },		{ "d0",		D0 },		{ "d1",		D1 },		{ "d2",		D2 },
	{ "d3",		D3 },		{ "d4",		D4 },		{ "d5",		D5 },		{ "d6",		D6 },
	{ "d7",		D7 },		{ "ccr",	CCR },		{ "sr",		SR },		{ "usp",	USP },
	{ "pc",		PC },		
	{ 0, 		0 }
};

typedef struct _ophash {
	struct _ophash* next;
	struct _optable* op;
} OPHASH;
#define OPHASHSIZE 97

static OPHASH **ophash = 0;

static int getophash(const char* s)
{
	int h = 0;
	while (*s++) h += (int)*s;
	return h % OPHASHSIZE;
}

static int oplookup(const char* s)
{
	int idx = getophash(s);
	OPHASH* oph = ophash[idx];
	if (oph) {
		if (oph->next) {
			while (oph) {
				if (!strcmp(s,oph->op->mnem)) return oph->op->token;
				oph = oph->next;
			}
			return 0;
		}
		return oph->op->token;
	}
	return 0;
}

static void init_ophash() 
{
	struct _optable* op = ops;
	OPHASH* oph;
	ophash = (OPHASH**)calloc(sizeof(OPHASH*),OPHASHSIZE);
	while (op->mnem) {
		int idx = getophash(op->mnem);
		oph = (OPHASH*)malloc(sizeof(OPHASH));
		oph->next = ophash[idx];
		oph->op = op;
		ophash[idx] = oph;
		op++;
	}
}

static char* yystream;

static int yylex()
{
	char ident[30];
	char *p = ident;
	char c = yystream[0];
	
	while (c != 0 && (c=='\t' || c==' ')) {
		c = *++yystream;
	}
	if (c==0) return EOF;
	
	if (isalpha(c)) {
		while (isalnum(c) && (p-ident)<28) {
			*p++ = tolower(c); c = *++yystream;
		}
		*p = 0;
		if (p>ident) { return oplookup(ident); }
		return EOF;
	} else if (isdigit(c)) {
		*p++ = c; 
		if (yystream[1]=='x' || yystream[1]=='X') { *p++ = 'x'; yystream++; }
		c = *++yystream;
		while ((isdigit(c) || isxdigit(c)) && (p-ident)<28) {
			*p++ = c; c = *++yystream;
		}
		*p = 0;
		yylval.num = strtol(ident,0,0);
		return NUMBER;
    } else if (c=='$') {
    	if (isdigit(yystream[1]) || isxdigit(yystream[1])) {
			c = *++yystream;
			while ((isdigit(c) || isxdigit(c)) && (p-ident)<28) {
				*p++ = c; c = *++yystream;
			}
			*p = 0;
			yylval.num = strtol(ident,0,16);
			return NUMBER;
		} else return '$';
	} else if (c == '-' && yystream[1] == '(') {
		yystream += 2; return PREDEC;
	} else if (c == ')' && yystream[1] == '+') {
		yystream += 2; return POSTINC;
	} else if (c == '.') {
		switch (yystream[1]) {
		case 'b': yystream += 2; return BSIZE;
		case 'w': yystream += 2; return WSIZE;
		case 'l': yystream += 2; return LSIZE;
		case 's': yystream += 2; return SSIZE;
		default: yystream++; return '.';
		}
	} else {
		++yystream; return c;
	}
}

static t_value *yyvalptr;
static t_addr yyaddr;

t_stat parse_sym_m68k(char* c, t_addr a, UNIT* u, t_value* val, int32 sw)
{
	char ch;
	
	if (!ophash) init_ophash();

	yyvalptr = val;
	yyaddr = a;

	yystream = c;
	yyerrc = 0;
	
	ch = *yystream;
	while (ch != 0 && (ch=='\t' || ch==' ')) {
		ch = *++yystream;
	}
	if (ch == 0) return 0;

	if (sw & SWMASK('Y')) yydebug = 1 - yydebug;
	if ((sw & SWMASK('A')) || ch=='\'') {
		if ((ch = yystream[1])) {
			val[0] = (uint32)ch;
			return SCPE_OK;
		} else return SCPE_ARG;
	}
	if ((sw & SWMASK('C')) || ch=='"') {
		if ((ch = yystream[1])) {
			val[0] = ((uint32)ch << 8) | (uint32)yystream[1];
			return SCPE_OK;
		} else return SCPE_ARG;
	}
	
	yyparse();
//    sim_printf("rc=%d\n",yyrc);
	if (yyerrc) return SCPE_ARG;
	return yyrc;
}

static int _genop(t_value arg)
{
//    sim_printf("_genop(%x)@%x\n",arg,(int)yyvalptr);
    *yyvalptr = (arg >> 8) & 0xff;
    yyvalptr++;
    *yyvalptr = arg & 0xff;
    yyvalptr++;
    return -1;
}

static int _genea(struct _ea arg)
{
	int i;
	for (i=0; i<arg.cnt; i++) _genop(arg.arg[i]);
	return -(arg.cnt*2)-1;
}

static int _genbr(t_value arg,t_addr tgt,int len) 
{
	t_addr a = tgt - yyaddr -2;
	if (len==1) {
		_genop(arg);
		_genop(a & 0xffff);
		a &= 0xffff8000;
		if (a != 0x00000000 && a != 0xffff8000) return SCPE_ARG;
		return -3;
	} else {
		_genop(arg | (a&0xff));
		a &= 0xffffff80;
		if (a != 0x00000000 && a != 0xffffff80) return SCPE_ARG;
		return -1;
	}
}
