/* $Id$ */
/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 */
/* @(#)mach4.c	1.11 */
/*
 *  Motorola 68000/68010 syntax rules
 */

operation
	:	{ curr_instr = curr_token; }
		_operation
	;

_operation
	:	bcdx DREG ',' DREG
			{	emit2($1 | $2 | $4<<9);}
	|	bcdx '-' '(' AREG ')' ',' '-' '(' AREG ')'
			{	emit2($1 | $4 | $9<<9 | 010);}
	|	ADD sizedef ea_ea
			{	add($1, $2);}
	|	AND sizenon ea_ea
			{	and($1, $2);}
	|	SHIFT sizedef ea_ea
			{	shift_op($1, $2);}
	|	BR expr
			{	branch($1, $2);}
	|	DBR DREG ',' expr
			{	$4.val -= (DOTVAL+2);
				Xfit(fitw($4.val));
				emit2($1 | $2);
#ifdef RELOCATION
				newrelo($4.typ, RELPC|RELO2|RELBR|RELWR);
#endif
				emit2(loww($4.val));
			}
	|	BITOP ea_ea
			{	bitop($1);}
	|	OP_EA_D sizedef ea ',' DREG
			{	if ($2 != SIZE_W) {
					serror("illegal size");
				}
				emit2($1 | mrg_2 | $5<<9);
				ea_2(SIZE_W, DTA);
			}
	|	LEA ea ',' AREG
			{	emit2(040700 | mrg_2 | $4<<9);
				ea_2(SIZE_L, CTR);
			}
	|	op_ea ea
			{	emit2(($1&0177700) | mrg_2);
				ea_2($1&0300, $1&017);
			}
	|	OP_NOOP
			{	emit2($1);}
	|	CMP sizedef ea_ea
			{	cmp($2);}
	|	MOVE sizenon ea_ea
			{	move($2);}
	|	MOVEP sizedef ea_ea
			{	movep($2);}
	|	MOVEM sizedef regs ',' notimmreg
			{	movem(0, $2, $3);}
	|	MOVEM sizedef notimmreg ',' regs
			{	movem(1, $2, $5);}
	| 	MOVES sizedef ea_ea
			{	test68010();
				if (mrg_1 <= 017) {
					emit2(007000 | $2 | mrg_2);
					emit2(mrg_1 << 12 | 04000);
					ea_2($2,ALT|MEM);
				} else if (mrg_2 <= 017) {
					emit2(007000 | $2 | mrg_1);
					emit2(mrg_2 << 12);
					ea_1($2,ALT|MEM);
				} else
					badoperand();
			}
	|	MOVEC creg ',' reg
			{	test68010();
				emit2(047172); emit2($2 | $4<<12);
			}
	|	MOVEC reg ',' creg
			{	test68010();
				emit2(047173); emit2($4 | $2<<12);
			}
	|	EXG reg ',' reg
			{	if (($2 & 010) == 0)
					emit2(
						(0140500|$4|$2<<9)
						+
						(($4&010)<<3)
					);
				else
					emit2(
						(0140610|$2|($4&07)<<9)
						-
						(($4&010)<<3)
					);
			}
	|	OP_EXT sizedef DREG
			{	checksize($2, 2|4); emit2(044000 | $2+0100 | $3);}
	|	SWAP DREG
			{	emit2(044100 | $2);}
	|	STOP imm
			{	emit2($1); ea_2(SIZE_W, 0);}
	|	LINK AREG ',' imm
			{	emit2(047120 | $2); ea_2(SIZE_W, 0);}
	|	UNLK AREG
			{	emit2(047130 | $2);}
	|	TRAP '#' absexp
			{	Xfit(fit4($3)); emit2(047100|low4($3));}
	|	RTD imm
			{	test68010();
				emit2(047164);
				ea_2(SIZE_W, 0);
			}
	|	MODEL
			{	model = $1;}
	|	fp_op
	;
bcdx	:	ABCD
	|	ADDX sizedef
			{	$$ = $1 | $2;}
	;
creg	:	CREG
	|	SPEC	{	if ($1 != 075) badoperand(); $$ = 04000;}
	;
op_ea	:	OP_EA
	|	SZ_EA sizedef
			{	$$ = $1 | $2;}
	;
regs	:	rrange
	|	regs '/' rrange
			{	$$ = $1 | $3;}
	;
rrange	:	reg
			{	$$ = 1<<$1;}
	|	reg '-' reg
			{	if ($1 > $3)
					badoperand();
				for ($$ = 0; $1 <= $3; $1++)
					$$ |= (1<<$1);
			}
	;
ea	:	DREG
			{	mrg_2 = $1;}
	|	AREG
			{	mrg_2 = 010 | $1;}
	|	SPEC
			{	mrg_2 = $1;}
	|	notimmreg
	|	imm
	;
notimmreg
	:	'(' AREG ')'
			{	mrg_2 = 020 | $2;}
	|	'(' AREG ')' '+'
			{	mrg_2 = 030 | $2;}
	|	'-' '(' AREG ')'
			{	mrg_2 = 040 | $3;}
	|	expr sizenon
			{	exp_2 = $1; ea707172($2);
				RELOMOVE(rel_2, relonami);
			}
	|	expr '(' reg sizenon ')'
			{	exp_2 = $1; ea5x73($3, $4);
				RELOMOVE(rel_2, relonami);
			}
	|	expr '(' AREG ',' reg sizedef ')'
			{	exp_2 = $1; ea6x($3, $5, $6);
				RELOMOVE(rel_2, relonami);
			}
	|	expr '(' PC ')'
			{	exp_2 = $1; ea72();
				RELOMOVE(rel_2, relonami);
			}
	|	expr '(' PC ',' reg sizedef ')'
			{	exp_2 = $1; ea73($5, $6);
				RELOMOVE(rel_2, relonami);
			}
	;
imm	:	'#' expr
			{	mrg_2 = 074; exp_2 = $2;
				RELOMOVE(rel_2, relonami);
			}
	;
reg	:	DREG
	|	AREG
			{	$$ = $1 | 010;}
	;
sizedef	:	/* empty */
			{	$$ = SIZE_DEF;}
	|	SIZE
	;
sizenon	:	/* empty */
			{	$$ = SIZE_NON;}
	|	SIZE
	;
ea_ea	:	ea ','
			{	mrg_1 = mrg_2; exp_1 = exp_2;
				RELOMOVE(rel_1, rel_2);
			}
		ea
	;
fp_op	:	CP
			{	co_id = $1; }
		fp_op1
	|		{	co_id = DEF_FP; }
		fp_op1
	;
fp_op1	:	FMOVE fsize ea ',' FPCR
			{	check_fsize($2, FSIZE_L);
				if ((mrg_2&070) == 010 && $5 != 001)
					badoperand();
				emit2((0170000|co_id|mrg_2));
				emit2((0100000|($5<<10)));
				ea_2(SIZE_L, 0);
			}
	|	FMOVE fsize FPCR ',' ea
			{	check_fsize($2, FSIZE_L);
				if ((mrg_2&070) == 010 && $3 == 001)
					badoperand();
				emit2((0170000|co_id|mrg_2));
				emit2((0120000|($3<<10)));
				ea_2(SIZE_L, ALT);
			}
	|	FMOVE fsize FPREG ',' FPREG
			{	emit2(0170000|co_id);
				emit2(($3<<10)|($5<<7));
			}
	|	FMOVE fsize ea ',' FPREG
			{	ch_sz_dreg($2, mrg_2&070);
				emit2((0170000|co_id|mrg_2));
				emit2((0040000|($2<<10)|($5<<7)));
				ea_2(SIZE_L, DTA);
			}
	|	FMOVE fsize FPREG ',' ea
			{	ch_sz_dreg($2, mrg_2&070);
				if ($2 == FSIZE_P)
					serror("packed decimal needs k-factor");
				emit2((0170000|co_id|mrg_2));
				emit2((0060000|($2<<10)|($3<<7)));
				ea_2(SIZE_L, DTA|ALT);
			}
	|	FMOVE fsize FPREG ',' ea '{' '#' absexp '}'
			{	check_fsize($2, FSIZE_P);
				fit(sfit7($8));
				emit2((0170000|co_id|mrg_2));
				emit2((0066000|($3<<7)|low7($8)));
				ea_2(SIZE_L, MEM|DTA|ALT);
			}
	|	FMOVE fsize FPREG ',' ea '{' DREG '}'
			{	check_fsize($2, FSIZE_P);
				emit2((0170000|co_id|mrg_2));
				emit2((0076000|($3<<7)|($7<<4)));
				ea_2(SIZE_L, MEM|DTA|ALT);
			}
	|	FMOVECR fsize '#' absexp ',' FPREG
			{	fit(fit7($4));
				check_fsize($2, FSIZE_X);
				emit2(0170000|co_id);
				emit2(056000|($6<<7)|low7($4));
			} 
	|	FMOVEM FSIZE fregs ',' notimmreg
			{	check_fsize($2, FSIZE_X);
				if ((mrg_2&070) == 030)
					serror("bad addressing category");
				emit2((0170000|co_id|mrg_2));
				emit2(0160000 |
					(((mrg_2&070)==040 || ($3&04000)) ?
						$3 :
						(010000|reverse($3,8))));
				ea_2(SIZE_L, MEM|ALT);
			}
	|	FMOVEM FSIZE notimmreg ',' fregs
			{	check_fsize($2, FSIZE_X);
				if ((mrg_2&070) == 040)
					serror("bad addressing category");
				emit2((0170000|co_id|mrg_2));
				emit2((0150000|(($5&04000)?$5:reverse($5,8))));
				ea_2(SIZE_L, MEM);
			}
	|	FMOVEM SIZE fcregs ',' ea
			{	checksize($2, 4);
				if ((mrg_2&070) == 1 && $3!= 02000)
					serror("bad addressing category");
				if ((mrg_2 & 070) == 0 &&
				    $3 != 02000 && $3 != 04000 && $3 != 010000)
					serror("bad addressing category");
				emit2((0170000|co_id|mrg_2));
				emit2((0120000|$3));
				ea_2(SIZE_L, ALT);
			}
	|	FMOVEM SIZE ea ',' fcregs
			{	checksize($2, 4);
				if ((mrg_2&070) == 1 && $5!= 02000)
					serror("bad addressing category");
				if ((mrg_2 & 070) == 0 &&
				    $5 != 02000 && $5 != 04000 && $5 != 010000)
					serror("bad addressing category");
				emit2((0170000|co_id|mrg_2));
				emit2((0100000|$5));
				ea_2(SIZE_L, 0);
			}
	|	FDYADIC fsize ea ',' FPREG
			{	emit2((0170000|co_id|mrg_2));
				emit2((0040000|($2<<10)|($5<<7)|$1));
				ch_sz_dreg($2, mrg_2&070);
				ea_2(SIZE_L, DTA);
			}
	|	FDYADIC fsize FPREG ',' FPREG
			{	check_fsize($2, FSIZE_X);
				emit2(0170000|co_id);
				emit2(($3<<10)|($5<<7)|$1);
			}
	|	FMONADIC fsize ea ',' FPREG
			{	emit2((0170000|co_id|mrg_2));
				emit2((0040000|($2<<10)|($5<<7)|$1));
				ch_sz_dreg($2, mrg_2&070);
				ea_2(SIZE_L, DTA);
			}
	|	FMONADIC fsize FPREG ',' FPREG
			{	check_fsize($2, FSIZE_X);
				emit2(0170000|co_id);
				emit2(($3<<10)|($5<<7)|$1);
			}
	|	FMONADIC fsize FPREG
			{	check_fsize($2, FSIZE_X);
				emit2(0170000|co_id);
				emit2(($3<<10)|($3<<7)|$1);
			}
	|	FSINCOS fsize ea ',' FPREG ':' FPREG
			{	emit2(0170000|co_id|mrg_2);
				emit2(0040000|($2<<10)|($7<<7)|$1|$5);
				ea_2(SIZE_L, DTA);
			}
	|	FSINCOS fsize FPREG ',' FPREG ':' FPREG
			{	check_fsize($2, FSIZE_X);
				emit2(0170000|co_id);
				emit2(($3<<10)|($7<<7)|$1|$5);
			}
	|	FBCC expr
			{	fbranch($1, $2);}
	|	FDBCC DREG ',' expr
			{	emit2(0170110|co_id|$2);
				emit2($1);
				$4.val -= DOTVAL;
				fit(fitw($4.val));
#ifdef RELOCATION
				newrelo($4.typ, RELPC|RELO2|RELBR|RELWR);
#endif
				emit2(loww($4.val));
			}
	|	FNOP
			{	emit2(0170200|co_id);
				emit2(0);
			}
	|	FSCC ea
			{	emit2(0170100|co_id|mrg_2);
				emit2($1);
				ea_2(SIZE_B, DTA|ALT);
			}
	|	FTST fsize ea
			{	emit2((0170000|co_id|mrg_2));
				emit2((0040072|($2<<10)));
				ch_sz_dreg($2, mrg_2&070);
				ea_2(SIZE_L, DTA);
			}
	|	FTST fsize FPREG
			{	check_fsize($2, FSIZE_X);
				emit2(0170000|co_id);
				emit2(($3<<10)|072);
			}
	|	FSAVRES ea
			{	if ((mrg_2&070) == ($1&070))
					badoperand();
				emit2((0170000|co_id|($1&0700)|mrg_2));
				ea_2(0, $1&07);
			}
	|	FTRAPCC
			{	emit2(0170174|co_id);
				emit2($1);
			}
	|	FTRAPCC SIZE imm
			{	checksize($2, 2|4);
				emit2((0170170|co_id|($2==SIZE_L?03:02)));
				emit2($1);
				ea_2($2,0);
			}
	;
fregs	:	DREG
			{	$$ = 04000 | $1 << 4; }
	|	frlist
	;
frlist	:	frrange
	|	frlist '/' frrange
			{	$$ = $1 | $3;}
	;
frrange	:	FPREG
			{	$$ = 1 << $1; }
	|	FPREG '-' FPREG
			{	if ($1 > $3)
					badoperand();
				for ($$ = 0; $1 <= $3; $1++)
					$$ |= (1 << $1);
			}
	;
fcregs	:	FPCR
			{	$$ = $1 << 10; }
	|	fcregs '/' FPCR
			{	$$ = $1 | ($3 << 10); }
	;
fsize	:	/*	empty */
			{	$$ = FSIZE_X; }
	|	SIZE
			{	if ($1 == SIZE_L)
					$$ = FSIZE_L;
				else if ($1 == SIZE_W)
					$$ = FSIZE_W;
				else	$$ = FSIZE_B;
			}
	|	FSIZE
	;