/*	$Id$	*/
/*-
 * Copyright (c) 2007 Gregory McGarry <g.mcgarry@ieee.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * A template has five logical sections:
 *
 *	1) subtree (operator); goal to achieve (cookie)
 *	2) left node descendent of operator (node class; type)
 *	3) right node descendent of operator (node class; type)
 *	4) resource requirements (number of scratch registers);
 *	   subtree rewriting rule
 *	5) emitted instructions
 */

#include "pass2.h"

#define TUWORD	TUNSIGNED|TULONG
#define TSWORD	TINT|TLONG
#define TWORD	TUWORD|TSWORD

struct optab table[] = {
/* First entry must be an empty entry */
{ -1, FOREFF, SANY, TANY, SANY, TANY, 0, 0, "", },

/* PCONVs are not necessary */
{ PCONV,	INAREG,
	SAREG,	TWORD|TPOINT,
	SAREG,	TWORD|TPOINT,
		0,	RLEFT,
		COM "pointer conversion\n", },


/*
 * Conversions of integral types
 *
 * For each deunsigned type, they look something like this:
 *
 * signed -> bigger signed	- nothing to do
 * signed -> bigger unsigned	- clear the top bits (of source type)
 *
 * signed -> smaller signed	- sign-extend the bits (to dest type)
 * signed -> smaller unsigned	- clear the top bits (of dest type)
 * unsigned -> smaller signed	- sign-extend top bits (to dest type)
 * unsigned -> smaller unsigned	- clear the top bits (of dest type)
 *
 * unsigned -> bigger		- nothing to do
 */

{ SCONV,	INAREG,
	SAREG,	TCHAR,
	SAREG,	TSWORD|TSHORT,
		0,	RLEFT,
		COM "convert char to short/int\n", },

{ SCONV,	INAREG,
	SAREG,	TCHAR,
	SAREG,	TUWORD|TUSHORT|TUCHAR,
		NAREG|NASL,	RESC1,
		"	and A1,AL,#255" COM "convert char to uchar/ushort/uint\n", },

{ SCONV,	INAREG | FEATURE_EXTEND,
	SAREG,	TUCHAR,
	SAREG,	TCHAR,
		NAREG|NASL,	RESC1,
		"	sxtb A1,AL" COM "convert uchar to char\n", },

{ SCONV,	INAREG,
	SAREG,	TUCHAR,
	SAREG,	TCHAR,
		NAREG|NASL,	RESC1,
		"	mov A1,AL,asl #24" COM "convert uchar to char\n"
		"	mov A1,A1,asr #24\n", },

{ SCONV,	INAREG,
	SAREG,	TUCHAR,
	SAREG,	TWORD|TSHORT|TUSHORT,
		0,	RLEFT,
		COM "convert uchar to (u)short/(u)int\n", },

{ SCONV,	INAREG,
	SAREG,	TSHORT,
	SAREG,	TSWORD,
		0,	RLEFT,
		COM "convert short to int\n", },

{ SCONV,	INAREG | FEATURE_EXTEND,
	SAREG,	TSHORT,
	SAREG,	TUWORD|TUSHORT,
		NAREG|NASL,	RESC1,
		"	uxth A1,AL" COM "convert short to uint\n", },

{ SCONV,	INAREG,
	SAREG,	TSHORT,
	SAREG,	TUWORD|TUSHORT,
		NAREG|NASL,	RESC1,
		"	mov A1,AL,asl #16" COM "convert short to uint\n"
		"	mov A1,AL,lsr #16\n", },

{ SCONV,	INAREG | FEATURE_EXTEND,
	SAREG,	TUSHORT,
	SAREG,	TSHORT,
		NAREG|NASL,	RESC1,
		"	sxth A1,AL" COM "convert ushort to short\n", },

{ SCONV,	INAREG,
	SAREG,	TUSHORT,
	SAREG,	TSHORT,
		NAREG|NASL,	RESC1,
		"	mov A1,AL,asl #16" COM "convert ushort to short\n"
		"	mov A1,A1,asr #16\n", },

{ SCONV,	INAREG | FEATURE_EXTEND,
	SAREG,	TSHORT|TUSHORT,
	SAREG,	TCHAR,
		NAREG|NASL,	RESC1,
		"	sxtb A1,AL" COM "convert (u)short to char\n", },

{ SCONV,	INAREG,
	SAREG,	TSHORT|TUSHORT,
	SAREG,	TCHAR,
		NAREG|NASL,	RESC1,
		"	mov A1,AL,asl #24" COM "convert (u)short to char\n"
		"	mov A1,A1,asr #24\n", },

{ SCONV,	INAREG,
	SAREG,	TSHORT|TUSHORT,
	SAREG,	TCHAR,
		NAREG|NASL,	RESC1,
		"	sxtb A1,AL" COM "convert (u)short to char\n", },

{ SCONV,	INAREG,
	SAREG,	TSHORT|TUSHORT,
	SAREG,	TUCHAR,
		NAREG|NASL,	RESC1,
		"	and A1,AL,#255" COM "convert (u)short to uchar\n", },

{ SCONV,	INAREG,
	SAREG,	TUSHORT,
	SAREG,	TWORD,
		0,	RLEFT,
		COM "convert ushort to (u)int\n", },

{ SCONV,	INAREG | FEATURE_EXTEND,
	SAREG,	TWORD,
	SAREG,	TCHAR,
		NAREG|NASL,	RESC1,
		"	sxtb A1,AL" COM "convert (u)int to char\n", },

{ SCONV,	INAREG,
	SAREG,	TWORD,
	SAREG,	TCHAR,
		NAREG|NASL,	RESC1,
		"	mov A1,AL,asl #24" COM "convert (u)int to char\n"
		"	mov A1,A1,asr #24\n", },

{ SCONV,	INAREG | FEATURE_EXTEND,
	SAREG,	TWORD,
	SAREG,	TSHORT,
		NAREG|NASL,	RESC1,
		"	sxth A1,AL" COM "convert (u)int to short\n", },

{ SCONV,	INAREG,
	SAREG,	TWORD,
	SAREG,	TSHORT,
		NAREG|NASL,	RESC1,
		"	mov A1,AL,asl #16" COM "convert (u)int to short\n"
		"	mov A1,A1,asr #16\n", },

{ SCONV,	INAREG,
	SAREG,	TWORD,
	SAREG,	TUCHAR,
		NAREG|NASL,	RESC1,
		"	and A1,AL,#255" COM "convert uchar to char\n", },

{ SCONV,	INAREG | FEATURE_EXTEND,
	SAREG,	TWORD,
	SAREG,	TUSHORT,
		NAREG|NASL,	RESC1,
		"	uxth A1,AL" COM "convert int to ushort\n", },

{ SCONV,	INAREG,
	SAREG,	TWORD,
	SAREG,	TUSHORT,
		NAREG|NASL,	RESC1,
		"	mov A1,AL,asl #16" COM "convert int to ushort\n"
		"	mov A1,AL,lsr #16\n", },

{ SCONV,	INAREG,
	SAREG,	TPOINT|TWORD,
	SAREG,	TWORD|TPOINT,
		0,	RLEFT,
		COM "convert between pointers and words\n", },

{ SCONV,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SBREG,	TLONGLONG|TULONGLONG,
		0,	RLEFT,
		COM "convert (u)longlong to (u)longlong\n", },

/* convert (u)char/(u)short/(u)int to longlong */
{ SCONV,	INBREG,
	SAREG,	TCHAR|TUCHAR|TSHORT|TUSHORT|TWORD,
	SBREG,	TLONGLONG|TULONGLONG,
		NBREG|NBSL,		RESC1,
		"	mov A1,AL" COM "convert (u)char/(u)short/(u)int to (u)longlong\n"
		"	mov U1,AL,asr #31\n", },

{ SCONV,	INAREG | FEATURE_EXTEND,
	SBREG,	TLONGLONG|TULONGLONG,
	SAREG,	TCHAR,
		NAREG,		RESC1,
		"	sxtb A1,AL" COM "convert (u)longlong to char\n", },

{ SCONV,	INAREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SAREG,	TCHAR,
		NAREG,		RESC1,
		"	mov A1,AL,asl #24" COM "convert (u)longlong to char\n"
		"	mov A1,A1,asr #24\n", },

{ SCONV,	INAREG | FEATURE_EXTEND,
	SBREG,	TLONGLONG|TULONGLONG,
	SAREG,	TSHORT,
		NAREG,		RESC1,
		"	sxth A1,AL" COM "convert (u)longlong to short\n", },

{ SCONV,	INAREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SAREG,	TSHORT,
		NAREG,		RESC1,
		"	mov A1,AL,asl #16" COM "convert (u)longlong to short\n"
		"	mov A1,A1,asr #16\n", },

{ SCONV,	INAREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SAREG,	TWORD,
		NAREG,		RESC1,
		"	mov A1,AL" COM "convert (u)longlong to (u)int\n", },

{ SCONV,	INAREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SAREG,	TUCHAR,
		NAREG,		RESC1,
		"	and A1,AL,#255" COM "convert (u)longlong to uchar\n", },

{ SCONV,	INAREG | FEATURE_EXTEND,
	SBREG,	TLONGLONG|TULONGLONG,
	SAREG,	TUSHORT,
		NAREG,		RESC1,
		"	uxth A1,AL" COM "convert (u)longlong to ushort\n", },

{ SCONV,	INAREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SAREG,	TUSHORT,
		NAREG,		RESC1,
		"	mov A1,AL,asl #16" COM "convert (u)longlong to ushort\n"
		"	mov A1,A1,lsr #16\n", },

/* conversions on load from memory */

/* char */
{ SCONV,	INAREG,
	SOREG,	TCHAR,
	SAREG,	TWORD,
		NASL|NAREG,	RESC1,
		"	ldrsb A1,AL" COM "convert char to int/long\n", },

/* uchar */
{ SCONV,	INAREG,
	SOREG,	TUCHAR,
	SAREG,	TWORD,
		NASL|NAREG,	RESC1,
		"	ldrb A1,AL" COM "convert uchar to int/long\n", },
 
/* short */
{ SCONV,	INAREG | FEATURE_HALFWORDS,
	SOREG,	TSHORT,
	SAREG,	TWORD,
		NASL|NAREG,	RESC1,
		"	ldrsh A1,AL" COM "convert short to int/long\n", },

/* ushort */
{ SCONV,	INAREG | FEATURE_HALFWORDS,
	SOREG,	TSHORT,
	SAREG,	TWORD,
		NASL|NAREG,	RESC1,
		"	ldrh A1,AL" COM "convert ushort to int/long\n", },

/* short */
{ SCONV,	INAREG,
	SOREG,	TSHORT|TUSHORT,
	SAREG,	TWORD,
		2*NAREG|NASL,	RESC1,
		"ZH", },

{ SCONV,	INAREG | FEATURE_FPA,
	SCREG,	TFLOAT,
	SAREG,	TWORD,
		NAREG,		RESC1,
		"	fix AL,AR" COM "convert float to int\n", },

{ SCONV,	INAREG | FEATURE_VFP,
	SCREG,	TFLOAT,
	SAREG,	TSWORD,
		NAREG,		RESC1,
		"	ftosis AL,AR" COM "convert float to int\n", },

{ SCONV,	INAREG | FEATURE_VFP,
	SCREG,	TFLOAT,
	SAREG,	TSWORD,
		NAREG,		RESC1,
		"	ftouis AL,AR" COM "convert float to int\n", },

{ SCONV,	INAREG,
	SAREG,	TFLOAT,
	SAREG,	TWORD,
		NSPECIAL|NAREG,		RESC1,
		"ZF", },

{ SCONV,	INBREG | FEATURE_FPA,
	SCREG,	TFLOAT,
	SBREG,	TULONGLONG|TLONGLONG,
		NBREG,		RESC1,
		COM "unimplemented\n", },

{ SCONV,	INBREG | FEATURE_VFP,
	SCREG,	TFLOAT,
	SBREG,	TULONGLONG|TLONGLONG,
		NBREG,		RESC1,
		COM "unimplemented\n", },

{ SCONV,	INBREG,
	SAREG,	TFLOAT,
	SBREG,	TULONGLONG|TLONGLONG,
		NSPECIAL|NBREG,		RESC1,
		"ZF", },

{ SCONV,	INAREG | FEATURE_FPA,
	SCREG,	TDOUBLE|TLDOUBLE,
	SAREG,	TWORD,
		NAREG,		RESC1,
		"	fix AL,AR" COM "convert double/ldouble to int\n", },

{ SCONV,	INAREG | FEATURE_VFP,
	SCREG,	TDOUBLE|TLDOUBLE,
	SAREG,	TSWORD,
		NAREG,		RESC1,
		"	ftosid AL,AR" COM "convert double/ldouble to int\n", },

{ SCONV,	INAREG | FEATURE_VFP,
	SCREG,	TDOUBLE|TLDOUBLE,
	SAREG,	TUWORD,
		NAREG,		RESC1,
		"	ftouid AL,AR" COM "convert double/ldouble to int\n", },

{ SCONV,	INAREG,
	SBREG,	TDOUBLE|TLDOUBLE,
	SAREG,	TWORD,
		NSPECIAL|NAREG,		RESC1,
		"ZF", },

{ SCONV,	INBREG | FEATURE_FPA,
	SCREG,	TDOUBLE|TLDOUBLE,
	SBREG,	TLONGLONG|TULONGLONG,
		NBREG,		RESC1,
		COM "unimplemented\n", },

{ SCONV,	INBREG | FEATURE_VFP,
	SCREG,	TDOUBLE|TLDOUBLE,
	SBREG,	TULONGLONG|TLONGLONG,
		NBREG,		RESC1,
		COM "unimplemented\n", },

{ SCONV,	INBREG,
	SBREG,	TDOUBLE|TLDOUBLE,
	SBREG,	TULONGLONG|TLONGLONG,
		NSPECIAL|NBREG,		RESC1,
		"ZF", },

{ SCONV,	INCREG | FEATURE_FPA,
	SAREG,	TWORD,
	SCREG,	TFLOAT,
		NCREG,		RESC1,
		"	flts AL,AR" COM "convert int to float\n" },

{ SCONV,	INCREG | FEATURE_VFP,
	SAREG,	TSWORD,
	SCREG,	TFLOAT,
		NCREG,		RESC1,
		"	fsitos AL,AR" COM "convert int to float\n" },

{ SCONV,	INCREG | FEATURE_VFP,
	SAREG,	TUWORD,
	SCREG,	TFLOAT,
		NCREG,		RESC1,
		"	fuitos AL,AR" COM "convert int to float\n" },

{ SCONV,	INAREG,
	SAREG,	TWORD,
	SAREG,	TFLOAT,
		NSPECIAL|NAREG,		RESC1,
		"ZF", },

{ SCONV,	INCREG | FEATURE_FPA,
	SBREG,	TULONGLONG|TLONGLONG,
	SCREG,	TFLOAT,
		NCREG,		RESC1,
		COM "unimplemented\n", },

{ SCONV,	INCREG | FEATURE_VFP,
	SBREG,	TULONGLONG|TLONGLONG,
	SCREG,	TFLOAT,
		NCREG,		RESC1,
		COM "unimplemented\n", },

{ SCONV,	INAREG,
	SBREG,	TULONGLONG|TLONGLONG,
	SAREG,	TFLOAT,
		NAREG,		RESC1,
		COM "unimplemented\n", },

{ SCONV,	INCREG | FEATURE_FPA,
	SAREG,	TWORD,
	SCREG,	TDOUBLE,
		NCREG,		RESC1,
		"	fltd AL,AR" COM "convert int to double\n" },

{ SCONV,	INCREG | FEATURE_VFP,
	SAREG,	TSWORD,
	SCREG,	TDOUBLE,
		NCREG,		RESC1,
		"	fsitod AL,AR" COM "convert int to double\n" },

{ SCONV,	INCREG | FEATURE_VFP,
	SAREG,	TUWORD,
	SCREG,	TDOUBLE,
		NCREG,		RESC1,
		"	fuitod AL,AR" COM "convert int to double\n" },

{ SCONV,	INBREG,
	SAREG,	TWORD,
	SBREG,	TDOUBLE,
		NSPECIAL|NBREG,		RESC1,
		"ZF", },

{ SCONV,	INCREG | FEATURE_FPA,
	SBREG,	TLONGLONG|TULONGLONG,
	SCREG,	TDOUBLE,
		NCREG,		RESC1,
		COM "unimplemented\n", },

{ SCONV,	INCREG | FEATURE_VFP,
	SBREG,	TLONGLONG|TULONGLONG,
	SCREG,	TDOUBLE,
		NCREG,		RESC1,
		COM "unimplemented\n", },

{ SCONV,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SBREG,	TDOUBLE,
		NSPECIAL|NBREG,		RESC1,
		"ZF", },

{ SCONV,	INCREG | FEATURE_FPA,
	SAREG,	TWORD,
	SCREG,	TLDOUBLE,
		NCREG,		RESC1,
		"	flte AL,AR" COM "convert int to ldouble\n" },

{ SCONV,	INCREG | FEATURE_VFP,
	SAREG,	TSWORD,
	SCREG,	TLDOUBLE,
		NCREG,		RESC1,
		"	fsitod AL,AR" COM "convert int to ldouble\n" },

{ SCONV,	INCREG | FEATURE_VFP,
	SAREG,	TUWORD,
	SCREG,	TLDOUBLE,
		NCREG,		RESC1,
		"	fuitod AL,AR" COM "convert uint to ldouble\n" },

{ SCONV,	INBREG,
	SAREG,	TWORD,
	SBREG,	TLDOUBLE,
		NSPECIAL|NBREG,		RESC1,
		"ZF", },

{ SCONV,	INCREG | FEATURE_FPA,
	SBREG,	TLONGLONG|TULONGLONG,
	SCREG,	TLDOUBLE,
		NCREG,		RESC1,
		COM "unimplemented\n", },

{ SCONV,	INCREG | FEATURE_VFP,
	SBREG,	TLONGLONG|TULONGLONG,
	SCREG,	TLDOUBLE,
		NCREG,		RESC1,
		COM "unimplemented\n", },

{ SCONV,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SBREG,	TLDOUBLE,
		NSPECIAL|NBREG,		RESC1,
		"ZF", },

{ SCONV,	INCREG | FEATURE_FPA,
	SCREG,	TDOUBLE|TLDOUBLE,
	SCREG,	TFLOAT,
		NCREG,		RESC1,
		COM "unimplemented\n", },

{ SCONV,	INCREG | FEATURE_VFP,
	SCREG,	TDOUBLE|TLDOUBLE,
	SCREG,	TFLOAT,
		NCREG,		RESC1,
		"	fcvtds AL,AR" COM "convert float to double\n" },

{ SCONV,	INAREG,
	SBREG,	TDOUBLE|TLDOUBLE,
	SAREG,	TFLOAT,
		NSPECIAL|NAREG,		RESC1,
		"ZF", },

{ SCONV,	INCREG | FEATURE_FPA,
	SCREG,	TFLOAT,
	SCREG,	TDOUBLE|TLDOUBLE,
		NCREG,		RESC1,
		COM "unimplemented\n", },

{ SCONV,	INCREG | FEATURE_VFP,
	SCREG,	TFLOAT,
	SCREG,	TDOUBLE|TLDOUBLE,
		NCREG,		RESC1,
		"	fcvtsd AL,AR" COM "convert float to double\n" },

{ SCONV,	INBREG,
	SAREG,	TFLOAT,
	SBREG,	TDOUBLE|TLDOUBLE,
		NSPECIAL|NBREG,		RESC1,
		"ZF", },

{ SCONV,	INCREG | FEATURE_FPA,
	SCREG,	TDOUBLE|TLDOUBLE,
	SCREG,	TDOUBLE|TLDOUBLE,
		0,		RLEFT,
		COM "convert (l)double to (l)double", },

{ SCONV,	INCREG | FEATURE_VFP,
	SCREG,	TDOUBLE|TLDOUBLE,
	SCREG,	TDOUBLE|TLDOUBLE,
		0,		RLEFT,
		COM "convert (l)double to (l)double", },

{ SCONV,	INBREG,
	SBREG,	TDOUBLE|TLDOUBLE,
	SBREG,	TDOUBLE|TLDOUBLE,
		0,		RLEFT,
		COM "convert (l)double to (l)double", },

/*
 * Subroutine calls.
 */

{ CALL,		FOREFF,
	SCON|SNAME,	TANY,
	SANY,		TANY,
		0,	0,
		"	bl CL" COM "call (args, no result) to scon/sname (CL)\n"
		"ZC", },

{ UCALL,	FOREFF,
	SCON|SNAME,	TANY,
	SANY,		TANY,
		0,	0,
		"	bl CL" COM "call (no args, no result) to scon/sname (CL)\n", },

{ CALL,		INAREG,
	SCON|SNAME,	TANY,
	SAREG,		TWORD|TPOINT|TSHORT|TUSHORT|TCHAR|TUCHAR,
		NAREG|NASL,	RESC1,	/* should be 0 */
		"	bl CL" COM "call (args, result in r0) to scon/sname (CL)\n"
		"ZC", },

{ CALL,		INBREG,
	SCON|SNAME,	TANY,
	SBREG,		TLONGLONG|TULONGLONG,
		NBREG|NBSL,	RESC1,	/* should be 0 */
		"	bl CL" COM "call (args, result in r0:r1) to scon/sname (CL)\n"
		"ZC", },

{ CALL,		INCREG | FEATURE_FPA,
	SCON|SNAME,	TANY,
	SCREG,		TFLOAT,
		NCREG|NCSL,	RESC1,	/* should be 0 */
		"	bl CL" COM "call (args, result r0) to scon/sname (CL)\n"
		"ZC", },

{ CALL,		INCREG | FEATURE_FPA,
	SCON|SNAME,	TANY,
	SCREG,		TDOUBLE|TLDOUBLE,
		NCREG|NCSL,	RESC1,	/* should be 0 */
		"	bl CL" COM "call (args, result in r0:r1) to scon/sname (CL)\n"
		"ZC", },

{ CALL,		INAREG,
	SCON|SNAME,	TANY,
	SAREG,		TFLOAT,
		NAREG|NASL,	RESC1,	/* should be 0 */
		"	bl CL" COM "call (args, result r0) to scon/sname (CL)\n"
		"ZC", },

{ CALL,		INBREG,
	SCON|SNAME,	TANY,
	SBREG,		TDOUBLE|TLDOUBLE,
		NBREG|NBSL,	RESC1,	/* should be 0 */
		"	bl CL" COM "call (args, result in r0:r1) to scon/sname (CL)\n"
		"ZC", },

{ UCALL,	INAREG,
	SCON|SNAME,	TANY,
	SAREG,		TWORD|TPOINT|TSHORT|TUSHORT|TCHAR|TUCHAR,
		NAREG|NASL,	RESC1,	/* should be 0 */
		"	bl CL" COM "call (no args, result in r0) to scon/sname (CL)\n", },

{ UCALL,	INBREG,
	SCON|SNAME,	TANY,
	SBREG,		TLONGLONG|TULONGLONG,
		NBREG|NBSL,	RESC1,	/* should be 0 */
		"	bl CL" COM "call (no args, result in r0:r1) to scon/sname (CL)\n", },

{ UCALL,	INCREG | FEATURE_FPA,
	SCON|SNAME,	TANY,
	SCREG,		TFLOAT,
		NCREG|NCSL,	RESC1,	/* should be 0 */
		"	bl CL" COM "call (no args, result in r0) to scon/sname (CL)\n", },

{ UCALL,	INCREG | FEATURE_FPA,
	SCON|SNAME,	TANY,
	SCREG,		TDOUBLE|TLDOUBLE,
		NCREG|NCSL,	RESC1,	/* should be 0 */
		"	bl CL" COM "call (no args, result in r0:r1) to scon/sname (CL)\n", },

{ CALL,		FOREFF,
	SAREG,	TANY,
	SANY,	TANY,
		0,	0,
		"	mov lr,pc\n"
		"	mov pc,AL\n"
		"ZC", },

{ UCALL,	FOREFF,
	SAREG,	TANY,
	SANY,	TANY,
		0,	0,
		"	mov lr,pc\n"
		"	mov pc,AL\n", },

{ CALL,		INAREG,
	SAREG,	TANY,
	SANY,	TANY,
		NAREG,	RESC1,
		"	mov lr,pc\n"
		"	mov pc,AL\n"
		"ZC", },

{ UCALL,	INAREG,
	SAREG,	TANY,
	SANY,	TANY,
		NAREG,	RESC1,
		"	mov lr,pc\n"
		"	mov pc,AL\n", },

{ CALL,		INBREG,
	SAREG,	TANY,
	SANY,	TANY,
		NBREG,	RESC1,
		"	mov lr,pc\n"
		"	mov pc,AL\n"
		"ZC", },

{ UCALL,	INBREG,
	SAREG,	TANY,
	SANY,	TANY,
		NBREG,	RESC1,
		"	mov lr,pc\n"
		"	mov pc,AL\n", },

/* struct return */
{ USTCALL,	FOREFF,
	SCON,	TANY,
	SANY,	TANY,
		0,	0,
		"	bl CL\n", },

{ USTCALL,	INAREG,
	SCON,	TANY,
	SANY,	TANY,
		NAREG|NASL,	RESC1,	/* should be 0 */
		"	bl CL\n", },

{ USTCALL,	INAREG,
	SNAME|SAREG,	TANY,
	SANY,		TANY,
		NAREG|NASL,	RESC1,	/* should be 0 */
		"	mov lr,pc\n"
		"	mov pc,AL\n", },

{ STCALL,	FOREFF,
	SCON,	TANY,
	SANY,	TANY,
		0,	0,
		"	bl CL\n"
		"ZC", },

{ STCALL,	INAREG,
	SCON,	TANY,
	SANY,	TANY,
		NAREG|NASL,	RESC1,	/* should be 0 */
		"	bl CL\n"
		"ZC", },

{ STCALL,	INAREG,
	SNAME|SAREG,	TANY,
	SANY,	TANY,
		NAREG|NASL,	RESC1,	/* should be 0 */
		"	mov lr,pc\n"
		"	mov pc,AL\n"
		"ZC", },

/*
 * The next rules handle all binop-style operators.
 */

{ PLUS,		INAREG,
	SAREG,	TWORD|TPOINT,
	SCCON,	TANY,
		NAREG,	RESC1,
		"	add A1,AL,AR" COM "addition of constant\n", },

{ PLUS,		INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SSCON,	TANY,
		NBREG|NBSL,	RESC1,
		"	adds A1,AL,AR" COM "64-bit addition of constant\n"
		"	adc U1,UL,UR\n", },

{ PLUS,		INAREG,
	SAREG,	TWORD|TPOINT,
	SAREG,	TWORD|TPOINT,
		NAREG|NASL,	RESC1,
		"	add A1,AL,AR" COM "addition\n", },

{ PLUS,		INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SBREG,	TLONGLONG|TULONGLONG,
		NBREG|NBSL,	RESC1,
		"	adds A1,AL,AR" COM "64-bit addition\n"
		"	adc U1,UL,UR\n", },

{ PLUS,		INCREG | FEATURE_FPA,
	SCREG,	TFLOAT,
	SCREG,	TFLOAT,
		NCREG,	RESC1,
		"	adfs A1,AL,AR" COM "float add\n", },

{ PLUS,		INCREG | FEATURE_VFP,
	SCREG,	TFLOAT,
	SCREG,	TFLOAT,
		NCREG,	RESC1,
		"	fadds A1,AL,AR" COM "float add\n", },

{ PLUS,		INAREG,
	SAREG,	TFLOAT,
	SAREG,	TFLOAT,
		NSPECIAL|NAREG,	RESC1,
		"ZF", },

{ PLUS,		INCREG | FEATURE_FPA,
	SCREG,	TDOUBLE,
	SCREG,	TDOUBLE,
		NCREG,	RESC1,
		"	adfd A1,AL,AR" COM "double add\n", },

{ PLUS,		INCREG | FEATURE_VFP,
	SCREG,	TDOUBLE,
	SCREG,	TDOUBLE,
		NCREG,	RESC1,
		"	faddd A1,AL,AR" COM "double add\n", },

{ PLUS,		INBREG,
	SBREG,	TDOUBLE,
	SBREG,	TDOUBLE,
		NSPECIAL|NBREG,	RESC1,
		"ZF", },

{ PLUS,		INCREG | FEATURE_FPA,
	SCREG,	TLDOUBLE,
	SCREG,	TLDOUBLE,
		NCREG,	RESC1,
		"	adfe A1,AL,AR" COM "ldouble add\n", },

{ PLUS,		INCREG | FEATURE_VFP,
	SCREG,	TLDOUBLE,
	SCREG,	TLDOUBLE,
		NCREG,	RESC1,
		"	faddd A1,AL,AR" COM "ldouble add\n", },

{ PLUS,		INBREG,
	SBREG,	TLDOUBLE,
	SBREG,	TLDOUBLE,
		NSPECIAL|NBREG,	RESC1,
		"ZF", },

{ MINUS,	INAREG,
	SAREG,	TWORD|TPOINT,
	SCCON,	TANY,
		NAREG|NASL,	RESC1,
		"	sub A1,AL,AR" COM "subtraction of constant\n", },

{ MINUS,	INAREG,
	SAREG,	TWORD|TPOINT,
	SAREG,	TWORD|TPOINT,
		NAREG|NASL,	RESC1,
		"	sub A1,AL,AR" COM "subtraction\n", },

{ MINUS,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SCCON,	TANY,
		NBREG|NBSL,	RESC1,
		"	subs A1,AL,AR" COM "64-bit subtraction of constant\n"
		"	rsc  U1,UL,AR\n", },

{ MINUS,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SBREG,	TLONGLONG|TULONGLONG,
		NBREG|NBSL,	RESC1,
		"	subs A1,AL,AR" COM "64-bit subtraction\n"
		"	sbc  U1,UL,AR\n", },

{ MINUS,	INCREG | FEATURE_FPA,
	SCREG,	TFLOAT,
	SCREG,	TFLOAT,
		NCREG,	RESC1,
		"	sufs A1,AL,AR" COM "float subtraction\n", },

{ MINUS,	INCREG | FEATURE_VFP,
	SCREG,	TFLOAT,
	SCREG,	TFLOAT,
		NCREG,	RESC1,
		"	fsubs A1,AL,AR" COM "float subtraction\n", },

{ MINUS,	INAREG,
	SAREG,	TFLOAT,
	SAREG,	TFLOAT,
		NSPECIAL|NAREG,	RESC1,
		"ZF", },

{ MINUS,	INCREG | FEATURE_FPA,
	SCREG,	TDOUBLE,
	SCREG,	TDOUBLE,
		NCREG,	RESC1,
		"	sufd A1,AL,AR" COM "double subtraction\n", },

{ MINUS,	INCREG | FEATURE_VFP,
	SCREG,	TDOUBLE,
	SCREG,	TDOUBLE,
		NCREG,	RESC1,
		"	fsubd A1,AL,AR" COM "double subtraction\n", },

{ MINUS,	INBREG,
	SBREG,	TDOUBLE,
	SBREG,	TDOUBLE,
		NSPECIAL|NBREG,	RESC1,
		"ZF", },

{ MINUS,	INCREG | FEATURE_FPA,
	SCREG,	TLDOUBLE,
	SCREG,	TLDOUBLE,
		NCREG,	RESC1,
		"	sufe A1,AL,AR" COM "ldouble subtraction\n", },

{ MINUS,	INCREG | FEATURE_VFP,
	SCREG,	TLDOUBLE,
	SCREG,	TLDOUBLE,
		NCREG,	RESC1,
		"	fsubd A1,AL,AR" COM "double subtraction\n", },

{ MINUS,	INBREG,
	SBREG,	TLDOUBLE,
	SBREG,	TLDOUBLE,
		NSPECIAL|NBREG,	RESC1,
		"ZF", },

/*
 * The next rules handle all shift operators.
 */

{ LS,	INAREG,
	SAREG,	TWORD|TSHORT|TUSHORT|TCHAR|TUCHAR,
	SAREG,	TANY,
		NAREG|NASL,	RESC1,
		"	mov A1,AL,asl AR" COM "left shift\n", },

{ LS,	INAREG,
	SAREG,	TWORD|TSHORT|TUSHORT|TCHAR|TUCHAR,
	SCCON,	TANY,
		NAREG|NASL,	RESC1,
		"	mov A1,AL,asl AR" COM "left shift by constant\n", },

{ LS,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SCON,	TANY,
		NBREG,	RESC1,
		"ZO" },

{ LS,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SAREG,	TANY,
		NSPECIAL|NBREG,	RESC1,
		"ZE" },

{ RS,	INAREG,
	SAREG,	TSWORD|TSHORT|TCHAR,
	SAREG,	TANY,
		NAREG|NASL,	RESC1,
		"	mov A1,AL,asr AR" COM "right shift\n", },

{ RS,	INAREG,
	SAREG,	TUWORD|TUSHORT|TUCHAR,
	SAREG,	TANY,
		NAREG|NASL,	RESC1,
		"	mov A1,AL,lsr AR" COM "right shift\n", },

{ RS,	INAREG,
	SAREG,	TSWORD|TSHORT|TCHAR,
	SCCON,	TANY,
		NAREG|NASL,	RESC1,
		"	mov A1,AL,asr AR" COM "right shift by constant\n", },

{ RS,	INAREG,
	SAREG,	TUWORD|TUSHORT|TUCHAR,
	SCCON,	TANY,
		NAREG|NASL,	RESC1,
		"	mov A1,AL,lsr AR" COM "right shift by constant\n", },

{ RS,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SCON,	TANY,
		NBREG,	RESC1,
		"ZO" },

{ RS,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SAREG,	TANY,
		NSPECIAL|NBREG,	RESC1,
		"ZE" },


/*
 * The next rules takes care of assignments. "=".
 */

{ ASSIGN,	FOREFF|INAREG,
	SOREG|SNAME,	TWORD|TPOINT,
	SAREG,		TWORD|TPOINT,
		0,	RDEST,
		"	str AR,AL" COM "assign word\n", },

{ ASSIGN,	FOREFF|INBREG,
	SOREG|SNAME,	TLONGLONG|TULONGLONG,
	SBREG,		TLONGLONG|TULONGLONG,
		0,	RDEST,
		"	str AR,AL" COM "assign 64-bit value\n"
		"	str UR,UL\n", },

/* XXX don't know if this works */
{ ASSIGN,	FOREFF|INBREG,
	SAREG,		TPTRTO|TLONGLONG|TULONGLONG,
	SBREG,		TLONGLONG|TULONGLONG,
		0,	RDEST,
		"	stmdb AL,{AR-UR}" COM "assign 64-bit value\n", },

{ ASSIGN,	FOREFF|INAREG,
	SOREG|SNAME,	TCHAR|TUCHAR,
	SAREG,		TCHAR|TUCHAR,
		0,	RDEST,
		"	strb AR,AL" COM "assign (u)char\n", },

{ ASSIGN,	FOREFF|INAREG | FEATURE_HALFWORDS,
	SOREG|SNAME,	TSHORT|TUSHORT,
	SAREG,		TSHORT|TUSHORT,
		0,	RDEST,
		"	strh AR,AL" COM "assign (u)short\n", },

{ ASSIGN,	FOREFF|INAREG,
	SOREG|SNAME,	TSHORT|TUSHORT,
	SAREG,		TSHORT|TUSHORT,
		NAREG|NASL,	RDEST,
		"ZH", },

{ ASSIGN, 	FOREFF|INCREG | FEATURE_FPA,
	SOREG|SNAME,	TFLOAT,
	SCREG,		TFLOAT,
		0,	RDEST,
		"	stfs AR,AL" COM "assign float\n", },

{ ASSIGN, 	FOREFF|INCREG | FEATURE_VFP,
	SOREG|SNAME,	TFLOAT,
	SCREG,		TFLOAT,
		0,	RDEST,
		COM "unimplemented\n", },

{ ASSIGN, 	FOREFF|INAREG,
	SOREG|SNAME,	TFLOAT,
	SAREG,		TFLOAT,
		0,	RDEST,
		"	str AR,AL" COM "assign float (soft-float)\n", },

{ ASSIGN, 	FOREFF|INCREG | FEATURE_FPA,
	SOREG|SNAME,	TDOUBLE,
	SCREG,		TDOUBLE,
		0,	RDEST,
		"	stfd AR,AL" COM "assign double\n", },

{ ASSIGN, 	FOREFF|INCREG | FEATURE_VFP,
	SOREG|SNAME,	TDOUBLE,
	SCREG,		TDOUBLE,
		0,	RDEST,
		COM "unimplemented\n", },

{ ASSIGN, 	FOREFF|INBREG,
	SOREG|SNAME,	TDOUBLE,
	SBREG,		TDOUBLE,
		0,	RDEST,
		"	str AR,AL" COM "assign double (soft-float)\n"
		"	str UR,UL\n", },

{ ASSIGN, 	FOREFF|INCREG | FEATURE_FPA,
	SOREG|SNAME,	TLDOUBLE,
	SCREG,		TLDOUBLE,
		0,	RDEST,
		"	stfe AR,AL" COM "assign ldouble\n", },

{ ASSIGN, 	FOREFF|INCREG | FEATURE_VFP,
	SOREG|SNAME,	TLDOUBLE,
	SCREG,		TLDOUBLE,
		0,	RDEST,
		COM "not implemented", },

{ ASSIGN, 	FOREFF|INBREG,
	SOREG|SNAME,	TLDOUBLE,
	SBREG,		TLDOUBLE,
		0,	RDEST,
		"	str AR,AL" COM "assign ldouble (soft-float)\n"
		"	str UR,UL\n", },

/* assign register to register */
{ ASSIGN,	FOREFF|INAREG,
	SAREG,		TWORD|TPOINT|TSHORT|TUSHORT|TCHAR|TUCHAR,
	SAREG,		TWORD|TPOINT|TSHORT|TUSHORT|TCHAR|TUCHAR,
		0,	RDEST,
		"	mov AL,AR" COM "assign AR to AL\n", },

{ ASSIGN,      FOREFF|INBREG,
        SBREG,	TLONGLONG|TULONGLONG,
        SBREG,	TLONGLONG|TULONGLONG,
                0,	RDEST,
		"	mov AL,AR" COM "assign UR:AR to UL:AL\n"
                "	mov UL,UR\n", },

{ ASSIGN,	FOREFF|INCREG | FEATURE_FPA,
	SCREG,	TFLOAT,
	SCREG,	TFLOAT,
		0,	RDEST,
		"	mvf AL,AR" COM "assign float reg to float reg\n", },

{ ASSIGN,	FOREFF|INCREG | FEATURE_VFP,
	SCREG,	TFLOAT,
	SCREG,	TFLOAT,
		0,	RDEST,
		"	fcpys AL,AR" COM "assign float reg to float reg\n", },

{ ASSIGN,	FOREFF|INAREG,
	SAREG,	TFLOAT,
	SAREG,	TFLOAT,
		0,	RDEST,
		"	mov AL,AR" COM "assign float reg to float reg\n", },

{ ASSIGN,	FOREFF|INCREG | FEATURE_FPA,
	SCREG,	TDOUBLE|TLDOUBLE,
	SCREG,	TDOUBLE|TLDOUBLE,
		0,	RDEST,
		"	mvf AL,AR" COM "assign float reg to float reg\n", },

{ ASSIGN,	FOREFF|INCREG | FEATURE_VFP,
	SCREG,	TDOUBLE|TLDOUBLE,
	SCREG,	TDOUBLE|TLDOUBLE,
		0,	RDEST,
		"	fcpyd AL,AR" COM "assign float reg to float reg\n", },

{ ASSIGN,	FOREFF|INBREG,
	SBREG,	TDOUBLE|TLDOUBLE,
	SBREG,	TDOUBLE|TLDOUBLE,
		0,	RDEST,
		"	mov AL,AR" COM "assign (l)double reg to (l)double reg\n"
		"	mov UL,UR\n", },

{ ASSIGN,	FOREFF|INAREG,
	SFLD,		TANY,
	SOREG|SNAME,	TANY,
		3*NAREG,	RDEST,
		"	ldr A1,AR" COM "bit-field assignment\n"
		"	ldr A2,AL\n"
		"	ldr A3,=M\n"
		"	mov A1,A1,asl H\n"
		"	and A1,A1,A3\n"
		"	bic A2,A2,A3\n"
		"	orr A3,A2,A1\n"
		"	str A3,AL\n"
		"F	ldr AD,AR\n"
		"FZB", },

{ ASSIGN,	FOREFF|INAREG,
	SFLD,	TANY,
	SAREG,	TANY,
		3*NAREG,	RDEST,
		"	ldr A2,AL" COM "bit-field assignment\n"
		"	ldr A3,=M\n"
		"	mov A1,AR,asl H\n"
		"	and A1,A1,A3\n"
		"	bic A2,A2,A3\n"
		"	orr A3,A2,A1\n"
		"	str A3,AL\n"
		"F	mov AD,AR\n"
		"FZB", },

{ STASG,	INAREG|FOREFF,
	SOREG|SNAME,	TANY,
	SAREG,		TPTRTO|TANY,
		NSPECIAL,	RDEST,
		"ZQ", },

/*
 * DIV/MOD/MUL 
 */

{ DIV,	INAREG,
	SAREG,	TWORD,
	SAREG,	TWORD,
		NSPECIAL|NAREG|NASL,	RESC1,
		"ZE", },

{ DIV,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SBREG,	TLONGLONG|TULONGLONG,
		NSPECIAL|NBREG|NBSL,	RESC1,
		"ZE", },

{ DIV,	INCREG | FEATURE_FPA,
	SCREG,		TFLOAT,
	SCREG,		TFLOAT,
		NCREG,	RESC1,
		"	dvfs A1,AL,AL" COM "fast (float) divide\n", },

{ DIV,	INCREG | FEATURE_VFP,
	SCREG,		TFLOAT,
	SCREG,		TFLOAT,
		NCREG,	RESC1,
		"	fdivs A1,AL,AL" COM "fast (float) divide\n", },

{ DIV,	INAREG,
	SAREG,		TFLOAT,
	SAREG,		TFLOAT,
		NSPECIAL|NAREG,	RESC1,
		"ZF", },

{ DIV,	INCREG | FEATURE_FPA,
	SCREG,		TDOUBLE,
	SCREG,		TDOUBLE,
		NCREG,	RESC1,
		"	dvfd A1,AL,AL" COM "double divide\n", },

{ DIV,	INCREG | FEATURE_VFP,
	SCREG,		TDOUBLE,
	SCREG,		TDOUBLE,
		NCREG,	RESC1,
		"	fdivd A1,AL,AL" COM "double divide\n", },

{ DIV,	INBREG,
	SBREG,		TDOUBLE,
	SBREG,		TDOUBLE,
		NSPECIAL|NBREG,	RESC1,
		"ZF", },

{ DIV,	INCREG | FEATURE_FPA,
	SCREG,		TLDOUBLE,
	SCREG,		TLDOUBLE,
		NCREG,	RESC1,
		"	dvfe A1,AL,AR" COM "long double load\n", },

{ DIV,	INCREG | FEATURE_VFP,
	SCREG,		TLDOUBLE,
	SCREG,		TLDOUBLE,
		NCREG,	RESC1,
		"	fdivd A1,AL,AL" COM "double divide\n", },

{ DIV,	INBREG,
	SBREG,		TLDOUBLE,
	SBREG,		TLDOUBLE,
		NSPECIAL|NBREG,	RESC1,
		"ZF", },

{ MOD,	INAREG,
	SAREG,	TWORD,
	SAREG,	TWORD,
		NSPECIAL|NAREG,	RESC1,
		"ZE", },

{ MOD,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SBREG,	TLONGLONG|TULONGLONG,
		NSPECIAL|NBREG,	RESC1,
		"ZE", },

{ MUL,	INAREG | FEATURE_MUL,
	SAREG,		TWORD|TPOINT|TSHORT|TUSHORT|TCHAR|TUCHAR,
	SAREG,		TWORD|TPOINT|TSHORT|TUSHORT|TCHAR|TUCHAR,
		NAREG,	RESC1,
		"	mul A1,AL,AR\n", },

{ MUL,	INAREG,
	SAREG,		TWORD|TPOINT|TSHORT|TUSHORT|TCHAR|TUCHAR,
	SAREG,		TWORD|TPOINT|TSHORT|TUSHORT|TCHAR|TUCHAR,
		NSPECIAL|NAREG,	RESC1,
		"ZE", },

{ MUL,	INBREG | FEATURE_MULL,
	SAREG,		TUWORD|TPOINT|TUSHORT|TUCHAR,
	SAREG,		TUWORD|TPOINT|TUSHORT|TUCHAR,
		NBREG,	RESC1,
		"	smull U1,A1,AL,AR\n", },

{ MUL,	INBREG | FEATURE_MUL,
	SAREG,		TUWORD|TPOINT|TUSHORT|TUCHAR,
	SAREG,		TUWORD|TPOINT|TUSHORT|TUCHAR,
		NBREG,	RESC1,
		"	mul A1,AL,AR\n"
		"	mov U1,A1,asr #31\n", },

{ MUL,	INBREG,
	SAREG,		TUWORD|TPOINT|TUSHORT|TUCHAR,
	SAREG,		TUWORD|TPOINT|TUSHORT|TUCHAR,
		NSPECIAL|NBREG,	RESC1,
		"ZE", },

{ MUL,	INBREG | FEATURE_MULL,
	SAREG,		TSWORD|TSHORT|TCHAR,
	SAREG,		TSWORD|TSHORT|TCHAR,
		NBREG,	RESC1,
		"	umull U1,A1,AL,AR\n", },

{ MUL,	INBREG | FEATURE_MUL,
	SAREG,		TSWORD|TSHORT|TCHAR,
	SAREG,		TSWORD|TSHORT|TCHAR,
		NBREG,	RESC1,
		"	mul A1,AL,AR\n"
		"	mov U1,#0\n", },

{ MUL,	INBREG,
	SAREG,		TSWORD|TSHORT|TCHAR,
	SAREG,		TSWORD|TSHORT|TCHAR,
		NSPECIAL|NBREG,	RESC1,
		"ZE", },

{ MUL,	INBREG | FEATURE_MULL,
	SBREG,		TLONGLONG|TULONGLONG,
	SBREG,		TLONGLONG|TULONGLONG,
		NBREG,	RESC1,
		"	umull U1,A1,AL,AR\n", },

{ MUL,	INBREG | FEATURE_MUL,
	SBREG,		TLONGLONG|TULONGLONG,
	SBREG,		TLONGLONG|TULONGLONG,
		NBREG,	RESC1,
		"	mul A1,AL,AR\n"
		"	mov U1,A1,asr #31\n", },

{ MUL,	INBREG,
	SBREG,		TLONGLONG|TULONGLONG,
	SBREG,		TLONGLONG|TULONGLONG,
		NSPECIAL|NBREG,	RESC1,
		"ZE", },

{ MUL,	INCREG | FEATURE_FPA,
	SCREG,		TFLOAT,
	SCREG,		TFLOAT,
		NCREG,	RESC1,
		"	fmls A1,AL,AL" COM "fast (float) multiply\n", },

{ MUL,	INCREG | FEATURE_VFP,
	SCREG,		TFLOAT,
	SCREG,		TFLOAT,
		NCREG,	RESC1,
		"	fmuls A1,AL,AL" COM "float multiply\n", },

{ MUL,	INAREG,
	SAREG,		TFLOAT,
	SAREG,		TFLOAT,
		NSPECIAL|NAREG,	RESC1,
		"ZF", },

{ MUL,	INCREG | FEATURE_FPA,
	SCREG,		TDOUBLE|TLDOUBLE,
	SCREG,		TDOUBLE|TLDOUBLE,
		NCREG,	RESC1,
		"	mufd A1,AL,AL" COM "fast (l)double multiply\n", },

{ MUL,	INCREG | FEATURE_VFP,
	SCREG,		TDOUBLE|TLDOUBLE,
	SCREG,		TDOUBLE|TLDOUBLE,
		NCREG,	RESC1,
		"	muld A1,AL,AL" COM "(l)double multiply\n", },

{ MUL,	INBREG,
	SBREG,		TDOUBLE|TLDOUBLE,
	SBREG,		TDOUBLE|TLDOUBLE,
		NSPECIAL|NBREG,	RESC1,
		"ZF", },

/*
 * Indirection operators.
 */

{ UMUL,	INAREG,
	SANY,		TANY,
	SOREG|SNAME,	TWORD|TPOINT,
		NAREG,	RESC1,
		"	ldr A1,AL" COM "word load\n", },

{ UMUL,	INAREG,
	SANY,		TANY,
	SOREG|SNAME,	TCHAR,
		NAREG,	RESC1,
		"	ldrsb A1,AL" COM "char load\n", },

{ UMUL,	INAREG,
	SANY,		TANY,
	SOREG|SNAME,	TUCHAR,
		NAREG,	RESC1,
		"	ldrb A1,AL" COM "uchar load\n", },

{ UMUL,	INAREG | FEATURE_HALFWORDS,
	SANY,		TANY,
	SOREG|SNAME,	TUSHORT,
		NAREG,	RESC1,
		"	ldrh A1,AL" COM "short load\n", },

{ UMUL,	INAREG | FEATURE_HALFWORDS,
	SANY,		TANY,
	SOREG|SNAME,	TSHORT,
		NAREG,	RESC1,
		"	ldrsh A1,AL" COM "short load\n", },

{ UMUL,	INAREG,
	SANY,		TANY,
	SOREG|SNAME,	TSHORT|TUSHORT,
		2*NAREG|NASL,	RESC1,
		"ZH", },

{ UMUL, INBREG,
	SANY,		TANY,
	SOREG|SNAME,	TLONGLONG|TULONGLONG,
		NBREG,	RESC1,
		"	ldr A1,AL" COM "64-bit load\n"
		"	ldr U1,UL\n", },

{ UMUL, INCREG | FEATURE_FPA,
	SANY,		TANY,
	SOREG|SNAME,	TFLOAT,
		NCREG,	RESC1,
		"	ldfs A1,AL" COM "float load\n", },

{ UMUL, INCREG | FEATURE_VFP,
	SANY,		TANY,
	SOREG|SNAME,	TFLOAT,
		NCREG,	RESC1,
		COM "not implemented\n", },

{ UMUL, INAREG,
	SANY,		TANY,
	SOREG|SNAME,	TFLOAT,
		NAREG,	RESC1,
		"	ldr A1,AL" COM "float load\n", },

{ UMUL, INCREG | FEATURE_FPA,
	SANY,		TANY,
	SOREG|SNAME,	TDOUBLE,
		NCREG,	RESC1,
		"	ldfd A1,AL" COM "double load\n", },

{ UMUL, INCREG | FEATURE_VFP,
	SANY,		TANY,
	SOREG|SNAME,	TDOUBLE,
		NCREG,	RESC1,
		COM "not implemented\n", },

{ UMUL, INBREG,
	SANY,		TANY,
	SOREG|SNAME,	TDOUBLE,
		NBREG,	RESC1,
		"	ldr A1,AL" COM "double load\n"
		"	ldr U1,UL\n", },

{ UMUL, INCREG | FEATURE_FPA,
	SANY,		TANY,
	SOREG|SNAME,	TLDOUBLE,
		NCREG,	RESC1,
		"	ldfe A1,AL" COM "long double load\n", },

{ UMUL, INCREG | FEATURE_VFP,
	SANY,		TANY,
	SOREG|SNAME,	TLDOUBLE,
		NCREG,	RESC1,
		COM "not implemented\n", },

{ UMUL, INBREG,
	SANY,		TANY,
	SOREG|SNAME,	TLDOUBLE,
		NBREG,	RESC1,
		"	ldr A1,AL" COM "long double load (soft-float)\n"
		"	ldr U1,UL\n", },

/*
 * Logical/branching operators
 */

/* compare with register */
{ OPLOG,	FORCC,
	SAREG,	TSWORD|TSHORT|TCHAR,
	SAREG,	TSWORD|TSHORT|TCHAR,
		0, 	RESCC,
		"	cmp AL,AR" COM "AR-AL (sets flags)\n", },

/* compare with register */
{ OPLOG,	FORCC,
	SAREG,	TUWORD|TPOINT|TUSHORT|TUCHAR,
	SAREG,	TUWORD|TPOINT|TUSHORT|TUCHAR,
		0, 	RESCC,
		"	cmp AL,AR" COM "AR-AL (sets flags)\n", },

/* compare with register */
{ OPLOG,	FORCC,
	SBREG,	TLONGLONG|TULONGLONG,
	SBREG,	TLONGLONG|TULONGLONG,
		0, 	RESCC,
		"ZD", },

{ OPLOG,	FORCC | FEATURE_FPA,
	SCREG,	TFLOAT,
	SCREG,	TFLOAT,
		NSPECIAL,	RESCC,
		"	cmfs AL,AR" COM "float compare\n", },

{ OPLOG,	FORCC | FEATURE_VFP,
	SCREG,	TFLOAT,
	SCREG,	TFLOAT,
		0,	RESCC,
		"	fcmps AL,AR" COM "float compare\n", },

{ OPLOG,	FORCC,
	SAREG,	TFLOAT,
	SAREG,	TFLOAT,
		NSPECIAL,	RESCC,
		"ZF", },

{ OPLOG,	FORCC | FEATURE_FPA,
	SCREG,	TDOUBLE,
	SCREG,	TDOUBLE,
		NSPECIAL,	RESCC,
		"	cmfd AL,AR" COM "double compare\n", },

{ OPLOG,	FORCC | FEATURE_VFP,
	SCREG,	TDOUBLE,
	SCREG,	TDOUBLE,
		0,	RESCC,
		"	fcmpd AL,AR" COM "double compare\n", },

{ OPLOG,	FORCC,
	SBREG,	TDOUBLE,
	SBREG,	TDOUBLE,
		NSPECIAL,	RESCC,
		"ZF", },

{ OPLOG,	FORCC | FEATURE_FPA,
	SCREG,	TLDOUBLE,
	SCREG,	TLDOUBLE,
		NSPECIAL,	RESCC,
		"	cmfe AL,AR" COM "ldouble compare\n", },

{ OPLOG,	FORCC | FEATURE_VFP,
	SCREG,	TLDOUBLE,
	SCREG,	TLDOUBLE,
		0,	RESCC,
		"	fcmpd AL,AR" COM "double compare\n", },

{ OPLOG,	FORCC,
	SBREG,	TLDOUBLE,
	SBREG,	TLDOUBLE,
		NSPECIAL,	RESCC,
		"ZF", },

/* AND/OR/ER */
{ AND,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SBREG,	TLONGLONG|TULONGLONG,
		NBREG|NBSL,	RESC1|RESCC,
		"	and A1,AL,AR" COM "64-bit and\n"
		"	and U1,UL,UR\n", },

{ OR,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SBREG,	TLONGLONG|TULONGLONG,
		NBREG|NBSL,	RESC1,
		"	orr A1,AL,AR" COM "64-bit or\n"
		"	orr U1,UL,UR\n" },

{ ER,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SBREG,	TLONGLONG|TULONGLONG,
		NBREG|NBSL,	RESC1,
		"	eor A1,AL,AR" COM "64-bit xor\n"
		"	eor U1,UL,UR\n" },

{ OPSIMP,	INAREG,
	SAREG,	TWORD|TSHORT|TUSHORT|TCHAR|TUCHAR,
	SAREG,	TWORD|TSHORT|TUSHORT|TCHAR|TUCHAR,
		NAREG|NASL,	RESC1|RESCC,
		"	O A1,AL,AR\n", },

{ OPSIMP,	INAREG|FORCC,
	SAREG,	TWORD|TSHORT|TUSHORT|TCHAR|TUCHAR,
	SAREG,	TWORD|TSHORT|TUSHORT|TCHAR|TUCHAR,
		NAREG|NASL,	RESC1,
		"	Os A1,AL,AR\n", },


/*
 * Jumps.
 */
{ GOTO, 	FOREFF,
	SCON,	TANY,
	SANY,	TANY,
		0,	RNOP,
		"	b LL\n", },

#if 0
{ GOTO, 	FOREFF,
	SAREG,	TANY,
	SANY,	TANY,
		0,	RNOP,
		"	mov pc,AL\n", },
#endif

/*
 * Convert LTYPE to reg.
 */

{ OPLTYPE,	INAREG,
	SANY,		TANY,
	SOREG|SNAME,	TWORD|TPOINT,
		NAREG,	RESC1,
		"	ldr A1,AL" COM "load word from memory\n", },

{ OPLTYPE,      INBREG,
        SANY,   	TANY,
        SOREG|SNAME,	TLONGLONG|TULONGLONG,
                NBREG,  RESC1,
                "	ldr A1,AL" COM "load long long from memory\n"
		"	ldr U1,UL\n", },

{ OPLTYPE,	INAREG,
	SANY,		TANY,
	SOREG|SNAME,	TCHAR,
		NAREG,	RESC1,
		"	ldrsb A1,AL" COM "load char from memory\n" },

{ OPLTYPE,	INAREG,
	SANY,		TANY,
	SOREG|SNAME,	TUCHAR,
		NAREG,	RESC1,
		"	ldrb A1,AL" COM "load uchar from memory\n", },

{ OPLTYPE,	INAREG | FEATURE_HALFWORDS,
	SANY,		TANY,
	SOREG|SNAME,	TSHORT,
		NAREG,	RESC1,
		"	ldrsh A1,AL" COM "load short from memory\n", },

{ OPLTYPE,	INAREG | FEATURE_HALFWORDS,
	SANY,		TANY,
	SOREG|SNAME,	TUSHORT,
		NAREG,	RESC1,
		"	ldrh A1,AL" COM "load ushort from memory\n", },

{ OPLTYPE,	INAREG,
	SANY,		TANY,
	SOREG|SNAME,	TSHORT|TUSHORT,
		2*NAREG,	RESC1,
		"ZH", },

#if 0
{ OPLTYPE,	INAREG,
	SANY,		TANY,
	SCON,		TPOINT,
		NAREG,	RESC1,
		"	ldr A1,AL" COM "load integer constant\n", },
#endif

{ OPLTYPE,	INAREG,
	SANY,		TANY,
	SCON,		TANY,
		NAREG,	RESC1,
		"ZI", },

{ OPLTYPE,	INBREG,
	SANY,	TANY,
	SCON,	TANY,
		NBREG,	RESC1,
		"ZJ", },

{ OPLTYPE,	INAREG,
	SANY,	TANY,
	SAREG,	TANY,
		NAREG,	RESC1,
		"	mov A1,AL" COM "load AL into A1\n" },

{ OPLTYPE,      INBREG,
        SANY,   TANY,
        SBREG,	TLONGLONG|TULONGLONG,
                NBREG,  RESC1,
		"	mov A1,AL" COM "load UL:AL into U1:A1\n"
                "       mov U1,UL\n", },

{ OPLTYPE,	INCREG | FEATURE_FPA,
	SANY,		TANY,
	SOREG|SNAME,	TFLOAT,
		NCREG,	RESC1,
		"	ldfs A1,AL" COM "load float\n", },

{ OPLTYPE,	INCREG | FEATURE_VFP,
	SANY,		TANY,
	SOREG|SNAME,	TFLOAT,
		NCREG,	RESC1,
		COM "not implemented\n", },

{ OPLTYPE,	INAREG,
	SANY,		TANY,
	SOREG|SNAME,	TFLOAT,
		NAREG,	RESC1,
		"	ldr A1,AL" COM "load float (soft-float)\n", },

{ OPLTYPE,	INCREG | FEATURE_FPA,
	SANY,		TANY,
	SOREG|SNAME,	TDOUBLE,
		NCREG,	RESC1,
		"	ldfd A1,AL" COM "load double\n", },

{ OPLTYPE,	INCREG | FEATURE_VFP,
	SANY,		TANY,
	SOREG|SNAME,	TDOUBLE,
		NCREG,	RESC1,
		COM "not implemented\n" },

{ OPLTYPE,	INBREG,
	SANY,		TANY,
	SOREG|SNAME,	TDOUBLE,
		NBREG,	RESC1,
		"	ldr A1,AL" COM "load double (soft-float)\n"
		"	ldr U1,UL\n", },

{ OPLTYPE,	INCREG | FEATURE_FPA,
	SANY,		TANY,
	SOREG|SNAME,	TLDOUBLE,
		NCREG,	RESC1,
		"	ldfe A1,AL" COM "load ldouble\n", },

{ OPLTYPE,	INCREG | FEATURE_VFP,
	SANY,		TANY,
	SOREG|SNAME,	TLDOUBLE,
		NCREG,	RESC1,
		COM "not implemented\n", },

{ OPLTYPE,	INBREG,
	SANY,		TANY,
	SOREG|SNAME,	TLDOUBLE,
		NBREG,	RESC1,
		"	ldr A1,AL" COM "load ldouble (soft-float)\n"
		"	ldr U1,UL\n", },

/*
 * Negate a word.
 */

{ UMINUS,	INAREG,
	SAREG,	TWORD|TPOINT|TSHORT|TUSHORT|TCHAR|TUCHAR,
	SAREG,	TWORD|TPOINT|TSHORT|TUSHORT|TCHAR|TUCHAR,
		NAREG|NASL,	RESC1,
		"	rsb A1,AL,#0" COM "negation\n", },

{ UMINUS,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SBREG,	TLONGLONG|TULONGLONG,
		NBREG|NBSL,	RESC1,
		"	rsbs A1,AL,#0" COM "64-bit negation\n"
		"	rsc U1,UL,#0\n", },

{ UMINUS,	INCREG | FEATURE_FPA,
	SCREG,	TFLOAT,
	SCREG,	TFLOAT,
		NCREG,	RESC1,
		"	mvfs A1,AL" COM "float negation\n", },

{ UMINUS,	INCREG | FEATURE_VFP,
	SCREG,	TFLOAT,
	SCREG,	TFLOAT,
		NCREG,	RESC1,
		"	negs A1,AL" COM "float negation\n", },

{ UMINUS,	INAREG,
	SAREG,	TFLOAT,
	SAREG,	TFLOAT,
		NSPECIAL|NAREG,	RESC1,
		"ZF", },

{ UMINUS,	INCREG | FEATURE_FPA,
	SCREG,	TDOUBLE,
	SCREG,	TDOUBLE,
		NCREG,	RESC1,
		"	mvfd A1,AL" COM "double negation\n", },

{ UMINUS,	INCREG | FEATURE_VFP,
	SCREG,	TDOUBLE,
	SCREG,	TDOUBLE,
		NCREG,	RESC1,
		"	negd A1,AL" COM "double negation\n", },

{ UMINUS,	INBREG,
	SBREG,	TDOUBLE,
	SBREG,	TDOUBLE,
		NSPECIAL|NBREG,	RESC1,
		"ZF", },

{ UMINUS,	INCREG | FEATURE_FPA,
	SCREG,	TLDOUBLE,
	SCREG,	TLDOUBLE,
		NCREG,	RESC1,
		"	mvfe A1,AL" COM "ldouble negation\n", },

{ UMINUS,	INCREG | FEATURE_VFP,
	SCREG,	TLDOUBLE,
	SCREG,	TLDOUBLE,
		NCREG,	RESC1,
		"	negd A1,AL" COM "ldouble negation\n", },

{ UMINUS,	INBREG,
	SBREG,	TLDOUBLE,
	SBREG,	TLDOUBLE,
		NSPECIAL|NBREG,	RESC1,
		"ZF", },

{ COMPL,	INAREG,
	SAREG,	TWORD|TSHORT|TUSHORT|TCHAR|TUCHAR,
	SANY,	TANY,
		NAREG|NASL,	RESC1,
		"	mvn A1,AL" COM "complement\n", },

{ COMPL,	INBREG,
	SBREG,	TLONGLONG|TULONGLONG,
	SANY,	TANY,
		NBREG|NBSL,	RESC1,
		"	mvn A1,AL" COM "64-bit complement\n"
		"	mvn U1,UL\n", },

/*
 * Arguments to functions.
 */

{ FUNARG,       FOREFF,
        SAREG,  TWORD|TPOINT|TSHORT|TUSHORT|TCHAR|TUCHAR,
        SANY,   TWORD|TPOINT|TSHORT|TUSHORT|TCHAR|TUCHAR,
                0,      0,
		"	stmfd sp!,{AL}" COM "save function arg to stack\n", },

{ FUNARG,       FOREFF,
        SBREG,  TLONGLONG|TULONGLONG,
        SANY,	TLONGLONG|TULONGLONG,
                0,      0,
		"	stmfd sp!,{AL,UL}" COM "save function arg to stack (endianness problem here?)\n", },

{ FUNARG,	FOREFF,
	SCREG,	TFLOAT,
	SANY,	TFLOAT,
		0,	0,
		"	stmfd sp!,{AL}" COM "save function arg to stack\n", },

{ FUNARG,       FOREFF,
        SCREG,  TDOUBLE|TLDOUBLE,
        SANY,  TDOUBLE|TLDOUBLE,
                0,      0,
		"	stmfd sp!,{AL,UL}" COM "save function arg to stack (endianness problem here?)\n", },

# define DF(x) FORREW,SANY,TANY,SANY,TANY,REWRITE,x,""

{ UMUL, DF( UMUL ), },

{ ASSIGN, DF(ASSIGN), },

{ STASG, DF(STASG), },

{ FLD, DF(FLD), },

{ OPLEAF, DF(NAME), },

/* { INIT, DF(INIT), }, */

{ OPUNARY, DF(UMINUS), },

{ OPANY, DF(BITYPE), },

{ FREE,	FREE,	FREE,	FREE,	FREE,	FREE,	FREE,	FREE,	"help; I'm in trouble\n" },
};

int tablesize = sizeof(table)/sizeof(table[0]);
