/* jit-m68k.def
 * M68000 instruction definition.
 *
 * Copyright (c) 1997 T. J. Wilkinson & Associates, London, UK.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * Written by Richard Henderson <rth@tamu.edu>, 1997.
 */

#define	DEBUG

#include <assert.h>
#include <string.h>
#include "classMethod.h"
#include "access.h"

#define	REG_d0			0
#define	REG_d1			1
#define REG_a0			8
#define REG_fp			14
#define REG_sp			15
#define REG_fp0			16

#ifdef DEBUG
static const char * const rnames[] = {
	"%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
	"%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%fp", "%sp",
	"%fp0", "%fp1", "%fp2", "%fp3", "%fp4", "%fp5", "%fp6", "%fp7",
};
#define	regname(n)	rnames[n]
#endif

#define assert_dreg(x)	assert(x >= 0 && x <= 7)
#define assert_areg(x)	assert(x >= 8 && x <= 15)
#define assert_freg(x)	assert(x >= 16 && x <= 23)
#define assert_s8(x)	assert(x >= -0x80 && x < 0x80)
#define assert_s16(x)	assert(x >= -0x8000 && x < 0x8000)

#define LSLOT(s)	((s)+1)
#define HSLOT(s)	(s)

/* FIXME -- detecting 68040 properly lets us do fp to IEEE spec.  */
#define is_68040	0

/* --------------------------------------------------------------------- */
/* Instruction assembly */

/* Here we make implementing the instructions a bit easier, but calling
   them slightly more difficult, by sending specific modes to each insn
   variant.  These are encoded in the suffix of the function name like so:

	d	data register
	a	address register
	f	floating point register
	i	immediate
	I	address register indirect
	o	address register indirect with displacement
	p	address register indirect with predecrement
	x	address register indirect with index & 8-bit displacement
*/

#ifdef DEBUG
int jit_debug = 0;
#define	debug(x)	(jit_debug ? printf("%lx:\t", CODEPC), printf x : 0)
#else
#define	debug(x)	((void)0)
#endif


#define MODE_d		0
#define MODE_a		1
#define MODE_ind	2
#define MODE_postinc	3
#define MODE_predec	4
#define MODE_inddisp	5
#define MODE_index	6
#define MODE_src_imm	074
#define MODE_src_absw	070
#define MODE_src_absl	071

#define OPMODE_s	(is_68040 ? 0x40 : 0)
#define OPMODE_d	(is_68040 ? 0x44 : 0)

#define COPROCID	1


static inline void
op_addl_id(int imm, int dst)
{
	debug(("addl #%d, %s\n", imm, regname(dst)));
	assert_dreg(dst);
	WOUT = 0xD080 | (dst << 9) | MODE_src_imm;
	LOUT = imm;
}

static inline void
op_addl_dd(int src, int dst)
{
	debug(("addl %s, %s\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_dreg(dst);
	WOUT = 0xD080 | (dst << 9) | (MODE_d << 3) | (src & 7);
}

static inline void
op_addaw_ia(int imm, int dst)
{
	debug(("addaw #%d, %s\n", imm, regname(dst)));
	assert_s16(imm);
	assert_areg(dst);
	WOUT = 0xD0C0 | ((dst & 7) << 9) | MODE_src_imm;
	WOUT = imm;
}

static inline void
op_addal_ia(int imm, int dst)
{
	debug(("addal #%d, %s\n", imm, regname(dst)));
	assert_areg(dst);
	WOUT = 0xD1C0 | ((dst & 7) << 9) | MODE_src_imm;
	LOUT = imm;
}

static inline void
op_addal_da(int src, int dst)
{
	debug(("addal %s, %s\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_areg(dst);
	WOUT = 0xD1C0 | ((dst & 7) << 9) | (MODE_d << 3) | (src & 7);
}

static inline void
op_addql_ia(int imm, int dst)
{
	debug(("addql #%d, %s\n", imm, regname(dst)));
	assert(imm >= 1 && imm <= 8);
	assert_areg(dst);
	WOUT = 0x5080 | ((imm & 7) << 9) | (MODE_a << 3) | (dst & 7);
}

static inline void
op_addql_id(int imm, int dst)
{
	debug(("addql #%d, %s\n", imm, regname(dst)));
	assert(imm >= 1 && imm <= 8);
	assert_dreg(dst);
	WOUT = 0x5080 | ((imm & 7) << 9) | (MODE_d << 3) | (dst & 7);
}

static inline void
op_addxl_dd(int src, int dst)
{
	debug(("addxl %s, %s\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_dreg(dst);
	WOUT = 0xD180 | (dst << 9) | src;
}

static inline void
op_andl_id(int imm, int dst)
{
	debug(("andl #0x%x, %s\n", imm, regname(dst)));
	assert_dreg(dst);
	WOUT = 0xC080 | (dst << 9) | MODE_src_imm;
	LOUT = imm;
}

static inline void
op_andl_dd(int src, int dst)
{
	debug(("andl %s, %s\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_dreg(dst);
	WOUT = 0xC080 | (dst << 9) | (MODE_d << 3) | (src & 7);
}

static inline void
op_asr_id(int imm, int dst)
{
	debug(("asr #%d, %s\n", imm, regname(dst)));
	assert_dreg(dst);
	assert(imm >= 1 && imm <= 8);
	WOUT = 0xE080 | ((imm & 7) << 9) | dst;
}

static inline void
op_asr_dd(int src, int dst)
{
	debug(("asr %s, %s\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_dreg(dst);
	WOUT = 0xE0A0 | ((src & 7) << 9) | dst;
}

static inline void
op_beq_32(int disp)
{
	debug(("beq %+d\n", disp));
	WOUT = 0x67FF;
	LOUT = disp;
}

static inline void
op_bne_32(int disp)
{
	debug(("bne %+d\n", disp));
	WOUT = 0x66FF;
	LOUT = disp;
}

static inline void
op_blt_32(int disp)
{
	debug(("blt %+d\n", disp));
	WOUT = 0x6DFF;
	LOUT = disp;
}

static inline void
op_ble_32(int disp)
{
	debug(("ble %+d\n", disp));
	WOUT = 0x6FFF;
	LOUT = disp;
}

static inline void
op_bgt_32(int disp)
{
	debug(("bgt %+d\n", disp));
	WOUT = 0x6EFF;
	LOUT = disp;
}

static inline void
op_bge_32(int disp)
{
	debug(("bge %+d\n", disp));
	WOUT = 0x6CFF;
	LOUT = disp;
}

static inline void
op_blo_32(int disp)
{
	debug(("blo %+d\n", disp));
	WOUT = 0x65FF;
	LOUT = disp;
}

static inline void
op_bra_32(int disp)
{
	debug(("bra %+d\n", disp));
	WOUT = 0x60FF;
	LOUT = disp;
}

static inline void
op_bsr_32(int disp)
{
	debug(("bsr %+d\n", disp));
	WOUT = 0x61FF;
	LOUT = disp;
}

static inline void
op_clrl_d(int dst)
{
	debug(("clrl %s\n", regname(dst)));
	assert_dreg(dst);
	WOUT = 0x4280 | (MODE_d << 3) | (dst & 7);
}

static inline void
op_cmpl_dd(int src1, int src2)
{
	debug(("cmpl %s, %s\n", regname(src1), regname(src2)));
	assert_dreg(src1);
	assert_dreg(src2);
	WOUT = 0xB080 | (src2 << 9) | (MODE_d << 7) | (src1 & 7);
}

static inline void
op_cmpal_aa(int src1, int src2)
{
	debug(("cmpl %s, %s\n", regname(src1), regname(src2)));
	assert_areg(src1);
	assert_areg(src2);
	WOUT = 0xB1C0 | (src2 << 9) | (MODE_a << 3) | (src1 & 7);
}

static inline void
op_cmpil_ia(int imm, int src2)
{
	debug(("cmpil #%d, %s\n", imm, regname(src2)));
	assert_areg(src2);
	WOUT = 0x0C80 | (MODE_a << 3) | (src2 & 7);
	LOUT = imm;
}

static inline void
op_cmpil_id(int imm, int src2)
{
	debug(("cmpil #%d, %s\n", imm, regname(src2)));
	assert_dreg(src2);
	WOUT = 0x0C80 | (MODE_d << 3) | (src2 & 7);
	LOUT = imm;
}

static inline void
op_divsl_ddd(int src, int r, int q)
{
	debug(("divsl %s, %s:%s\n", regname(src), regname(r), regname(q)));
	assert_dreg(src);
	assert_dreg(r);
	assert_dreg(q);
	WOUT = 0x4C40 | (MODE_d << 3) | (src & 7);
	WOUT = 0x0800 | (q << 12) | r;
}

static inline void
op_eorl_dd(int src, int dst)
{
	debug(("eorl %s, %s\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_dreg(dst);
	WOUT = 0xB180 | ((src & 7) << 9) | (MODE_d << 3) | (dst & 7);
}

static inline void
op_extbl_d(int dst)
{
	debug(("extbl %s\n", regname(dst)));
	assert_dreg(dst);
	WOUT = 0x49C0 | dst;
}

static inline void
op_extwl_d(int dst)
{
	debug(("extwl %s\n", regname(dst)));
	assert_dreg(dst);
	WOUT = 0x48C0 | dst;
}

static inline void
op_exg_aa(int r1, int r2)
{
	debug(("exg %s, %s\n", regname(r1), regname(r2)));
	assert_areg(r1);
	assert_areg(r2);
	WOUT = 0xC148 | ((r1 & 7) << 9) | (r2 & 7);
}

static inline void
op_exg_da(int r1, int r2)
{
	debug(("exg %s, %s\n", regname(r1), regname(r2)));
	assert_dreg(r1);
	assert_areg(r2);
	WOUT = 0xC188 | (r1 << 9) | (r2 & 7);
}

static inline void
op_exg_dd(int r1, int r2)
{
	debug(("exg %s, %s\n", regname(r1), regname(r2)));
	assert_dreg(r1);
	assert_dreg(r2);
	WOUT = 0xC140 | (r1 << 9) | r2;
}

static inline void
op_jmp_I(int dst)
{
	debug(("jmp (%s)\n", regname(dst)));
	assert_areg(dst);
	WOUT = 0x4EC0 | (MODE_ind << 3) | (dst & 7);
}

static inline void
op_jsr_I(int dst)
{
	debug(("jsr (%s)\n", regname(dst)));
	assert_areg(dst);
	WOUT = 0x4E80 | (MODE_ind << 3) | (dst & 7);
}

static inline void
op_linkw_ai(int areg, int disp)
{
	debug(("linkw %s, #%d\n", regname(areg), disp));
	assert_s16(disp);
	assert_areg(areg);
	WOUT = 0x4E50 | (areg & 7);
	WOUT = disp;
}

static inline void
op_lsl_id(int imm, int dst)
{
	debug(("lsl #%d, %s\n", imm, regname(dst)));
	assert_dreg(dst);
	assert(imm >= 1 && imm <= 8);
	WOUT = 0xE188 | ((imm & 7) << 9) | dst;
}

static inline void
op_lsl_dd(int src, int dst)
{
	debug(("lsl %s, %s\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_dreg(dst);
	WOUT = 0xE1A8 | ((src & 7) << 9) | dst;
}

static inline void
op_lsr_id(int imm, int dst)
{
	debug(("lsr #%d, %s\n", imm, regname(dst)));
	assert_dreg(dst);
	assert(imm >= 1 && imm <= 8);
	WOUT = 0xE088 | ((imm & 7) << 9) | dst;
}

static inline void
op_lsr_dd(int src, int dst)
{
	debug(("lsr %s, %s\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_dreg(dst);
	WOUT = 0xE0A8 | ((src & 7) << 9) | dst;
}

static inline void
op_moveb_Id(int src, int dst)
{
	debug(("moveb (%s), %s\n", regname(src), regname(dst)));
	assert_areg(src);
	assert_dreg(dst);
	WOUT = (0x1000 | ((dst & 7) << 9) | (MODE_d << 6)
		| (MODE_ind << 3) | (src & 7));
}

static inline void
op_moveb_dI(int src, int dst)
{
	debug(("moveb %s, (%s)\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_areg(dst);
	WOUT = (0x1000 | ((dst & 7) << 9) | (MODE_ind << 6)
		| (MODE_d << 3) | (src & 7));
}

static inline void
op_movew_Id(int src, int dst)
{
	debug(("movew (%s), %s\n", regname(src), regname(dst)));
	assert_areg(src);
	assert_dreg(dst);
	WOUT = (0x3000 | ((dst & 7) << 9) | (MODE_d << 6)
		| (MODE_ind << 3) | (src & 7));
}

static inline void
op_movew_dI(int src, int dst)
{
	debug(("movew %s, (%s)\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_areg(dst);
	WOUT = (0x3000 | ((dst & 7) << 9) | (MODE_ind << 6)
		| (MODE_d << 3) | (src & 7));
}

static inline void
op_movel_ao(int src, int base, int disp)
{
	debug(("movel %s, %d(%s)\n", regname(src), disp, regname(base)));
	assert_areg(src);
	assert_areg(base);
	assert_s16(disp);
	WOUT = (0x2000 | ((base & 7) << 9) | (MODE_inddisp << 6)
		| (MODE_a << 3) | (src & 7));
	WOUT = disp;
}

static inline void
op_movel_ad(int src, int dst)
{
	debug(("movel %s, %s\n", regname(src), regname(dst)));
	assert_areg(src);
	assert_dreg(dst);
	WOUT = (0x2000 | ((dst & 7) << 9) | (MODE_d << 6)
		| (MODE_a << 3) | (src & 7));
}

static inline void
op_movel_dd(int src, int dst)
{
	debug(("movel %s, %s\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_dreg(dst);
	WOUT = (0x2000 | ((dst & 7) << 9) | (MODE_d << 6)
		| (MODE_d << 3) | (src & 7));
}

static inline void
op_movel_do(int src, int base, int disp)
{
	debug(("movel %s, %d(%s)\n", regname(src), disp, regname(base)));
	assert_dreg(src);
	assert_areg(base);
	assert_s16(disp);
	WOUT = (0x2000 | ((base & 7) << 9) | (MODE_inddisp << 6)
		| (MODE_d << 3) | (src & 7));
	WOUT = disp;
}

static inline void
op_movel_id(int imm, int dst)
{
	debug(("movel #%d, %s\n", imm, regname(dst)));
	assert_dreg(dst);
	WOUT = 0x2000 | ((dst & 7) << 9) | (MODE_d << 6) | MODE_src_imm;
	LOUT = imm;
}

static inline void
op_movel_Id(int src, int dst)
{
	debug(("movel (%s), %s\n", regname(src), regname(dst)));
	assert_areg(src);
	assert_dreg(dst);
	WOUT = (0x2000 | ((dst & 7) << 9) | (MODE_d << 6)
		| (MODE_ind << 3) | (src & 7));
}

static inline void
op_movel_aI(int src, int dst)
{
	debug(("movel %s, (%s)\n", regname(src), regname(dst)));
	assert_areg(src);
	assert_areg(dst);
	WOUT = (0x2000 | ((dst & 7) << 9) | (MODE_ind << 6)
		| (MODE_a << 3) | (src & 7));
}

static inline void
op_movel_dI(int src, int dst)
{
	debug(("movel %s, (%s)\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_areg(dst);
	WOUT = (0x2000 | ((dst & 7) << 9) | (MODE_ind << 6)
		| (MODE_d << 3) | (src & 7));
}

static inline void
op_movel_ip(int imm, int dst)
{
	debug(("movel #%d, -(%s)\n", imm, regname(dst)));
	assert_areg(dst);
	WOUT = 0x2000 | ((dst & 7) << 9) | (MODE_predec << 6) | MODE_src_imm;
	LOUT = imm;
}

static inline void
op_movel_ap(int src, int dst)
{
	debug(("movel %s, -(%s)\n", regname(src), regname(dst)));
	assert_areg(src);
	assert_areg(dst);
	WOUT = (0x2000 | ((dst & 7) << 9) | (MODE_predec << 6)
		| (MODE_a << 3) | (src & 7));
}

static inline void
op_movel_dp(int src, int dst)
{
	debug(("movel %s, -(%s)\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_areg(dst);
	WOUT = (0x2000 | ((dst & 7) << 9) | (MODE_predec << 6) 
		| (MODE_d << 3) | (src & 7));
}

static inline void
op_movel_op(int base, int disp, int dst)
{
	debug(("movel %d(%s), -(%s)\n", disp, regname(base), regname(dst)));
	assert_areg(base);
	assert_s16(disp);
	assert_areg(dst);
	WOUT = (0x2000 | ((dst & 7) << 9) | (MODE_predec << 6) 
		| (MODE_inddisp << 3) | (base & 7));
	WOUT = disp;
}

static inline void
op_movel_od(int base, int disp, int dst)
{
	debug(("movel %d(%s), %s\n", disp, regname(base), regname(dst)));
	assert_areg(base);
	assert_s16(disp);
	assert_dreg(dst);
	WOUT = (0x2000 | ((dst & 7) << 9) | (MODE_d << 6)
		| (MODE_inddisp << 3) | (base & 7));
	WOUT = disp;
}

static inline void
op_moveal_aa(int src, int dst)
{
	debug(("moveal %s, %s\n", regname(src), regname(dst)));
	assert_areg(src);
	assert_areg(dst);
	WOUT = 0x2040 | ((dst & 7) << 9) | (MODE_a << 3) | (src & 7);
}

static inline void
op_moveal_Ia(int src, int dst)
{
	debug(("moveal (%s), %s\n", regname(src), regname(dst)));
	assert_areg(src);
	assert_areg(dst);
	WOUT = 0x2040 | ((dst & 7) << 9) | (MODE_ind << 3) | (src & 7);
}

static inline void
op_moveal_oa(int base, int disp, int dst)
{
	debug(("moveal %d(%s), %s\n", disp, regname(base), regname(dst)));
	assert_areg(base);
	assert_s16(disp);
	assert_areg(dst);
	WOUT = 0x2040 | ((dst & 7) << 9) | (MODE_inddisp << 3) | (base & 7);
	WOUT = disp;
}

static inline void
op_moveal_ia(int imm, int dst)
{
	debug(("moveal #0x%x, %s\n", imm, regname(dst)));
	assert_areg(dst);
	WOUT = 0x2040 | ((dst & 7) << 9) | MODE_src_imm;
	LOUT = imm;
}

static inline void
op_moveml_ro(int mask, int areg, int disp)
{
	debug(("moveml 0x%04x, %d(%s)\n", mask, disp, regname(areg)));
	assert_s16(disp);
	assert_areg(areg);
	WOUT = 0x48C0 | (MODE_inddisp << 3) | (areg & 7);
	WOUT = mask;
}

static inline void
op_moveml_or(int areg, int disp, int mask)
{
	debug(("moveml %d(%s), 0x%04x\n", disp, regname(areg), mask));
	assert_s16(disp);
	assert_areg(areg);
	WOUT = 0x4CC0 | (MODE_inddisp << 3) | (areg & 7);
	WOUT = mask;
}

static inline void
op_moveq_id(int imm, int dst)
{
	debug(("moveq #%d, %s\n", imm, regname(dst)));
	assert_dreg(dst);
	assert_s8(imm);
	WOUT = 0x7000 | (dst << 9) | (imm & 0xFF);
}

static inline void
op_mulsl_id(int imm, int dst)
{
	debug(("mulsl #%d, %s\n", imm, regname(dst)));
	assert_dreg(dst);
	WOUT = 0x4C00 | MODE_src_imm;
	WOUT = 0x0800 | (dst << 12);
	LOUT = imm;
}

static inline void
op_mulsl_dd(int src, int dst)
{
	debug(("mulsl %s, %s\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_dreg(dst);
	WOUT = 0x4C00 | (MODE_d << 3) | src;
	WOUT = 0x0800 | (dst << 12);
}

static inline void
op_negl_d(int dst)
{
	debug(("negl %s\n", regname(dst)));
	assert_dreg(dst);
	WOUT = 0x4480 | (MODE_d << 3) | dst;
}

static inline void
op_negxl_d(int dst)
{
	debug(("negxl %s\n", regname(dst)));
	assert_dreg(dst);
	WOUT = 0x4080 | (MODE_d << 3) | dst;
}

static inline void
op_nop(void)
{
	debug(("nop\n"));
	WOUT = 0x4E71;
}

static inline void
op_orl_dd(int src, int dst)
{
	debug(("orl %s, %s\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_dreg(dst);
	WOUT = 0x8080 | (dst << 9) | (MODE_d << 3) | src;
}

static inline void
op_pea_l(int addr)
{
	debug(("pea 0x%08x\n", addr));
	WOUT = 0x4840 | MODE_src_absl;
	LOUT = addr;
}

static inline void
op_rts(void)
{
	debug(("rts\n"));
	WOUT = 0x4E75;
}

static inline void
op_subl_id(int imm, int dst)
{
	debug(("subl #%d, %s\n", imm, regname(dst)));
	assert_dreg(dst);
	WOUT = 0x9080 | (dst << 9) | MODE_src_imm;
	LOUT = imm;
}

static inline void
op_subl_dd(int src, int dst)
{
	debug(("subl %s, %s\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_dreg(dst);
	WOUT = 0x9080 | (dst << 9) | (MODE_d << 3) | src;
}

static inline void
op_subaw_ia(int disp, int dst)
{
	debug(("subaw #%d, %s\n", disp, regname(dst)));
	assert_s16(disp);
	assert_areg(dst);
	WOUT = 0x90C0 | ((dst & 7) << 9) | MODE_src_imm;
	WOUT = disp;
}

static inline void
op_subql_id(int imm, int dst)
{
	debug(("subql #%d, %s\n", imm, regname(dst)));
	assert(imm >= 1 && imm <= 8);
	assert_dreg(dst);
	WOUT = 0x5180 | ((imm & 7) << 9) | (MODE_d << 3) | dst;
}

static inline void
op_subxl_dd(int src, int dst)
{
	debug(("subxl %s, %s\n", regname(src), regname(dst)));
	assert_dreg(src);
	assert_dreg(dst);
	WOUT = 0x9180 | (dst << 9) | src;
}

static inline void
op_tst_a(int src)
{
	debug(("tst %s\n", regname(src)));
	assert_areg(src);
	WOUT = 0x4A80 | (MODE_a << 3) | (src & 7);
}

static inline void
op_tst_d(int src)
{
	debug(("tst %s\n", regname(src)));
	assert_dreg(src);
	WOUT = 0x4A80 | (MODE_d << 3) | (src & 7);
}

static inline void
op_unlk_a(int areg)
{
	debug(("unlk %s\n", regname(areg)));
	assert_areg(areg);
	WOUT = 0x4E58 | (areg & 7);
}

/* --------------------------------------------------------------------- */

static inline void
_op_fop_ff(int src, int dst, int opmode)
{
	assert_freg(src);
	assert_freg(dst);
	WOUT = 0xF000 | (COPROCID << 9);
	WOUT = ((src & 7) << 10) | ((dst & 7) << 7) | opmode;
}

static inline void
op_fsaddx_ff(int src, int dst)
{
	debug(("fsaddx %s, %s\n", regname(src), regname(dst)));
	_op_fop_ff(src, dst, 0x22 | OPMODE_s);
}

static inline void
op_fdaddx_ff(int src, int dst)
{
	debug(("fdaddx %s, %s\n", regname(src), regname(dst)));
	_op_fop_ff(src, dst, 0x22 | OPMODE_d);
}

static inline void
op_fsdivx_ff(int src, int dst)
{
	debug(("fsdivx %s, %s\n", regname(src), regname(dst)));
	_op_fop_ff(src, dst,  0x20 | OPMODE_s);
}

static inline void
op_fddivx_ff(int src, int dst)
{
	debug(("fddivx %s, %s\n", regname(src), regname(dst)));
	_op_fop_ff(src, dst,  0x20 | OPMODE_d);
}

static inline void
op_fmoves_fI(int src, int base)
{
	debug(("fmoves %s, (%s)\n", regname(src), regname(base)));
	assert_freg(src);
	assert_areg(base);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_ind << 3) | (base & 7);
	WOUT = 0x6400 | ((src & 7) << 7);
}

static inline void
op_fmoves_fp(int src, int base)
{
	debug(("fmoves %s, -(%s)\n", regname(src), regname(base)));
	assert_freg(src);
	assert_areg(base);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_predec << 3) | (base & 7);
	WOUT = 0x6400 | ((src & 7) << 7);
}

static inline void
op_fmoves_fo(int src, int base, int disp)
{
	debug(("fmoves %s, %d(%s)\n", regname(src), disp, regname(base)));
	assert_freg(src);
	assert_areg(base);
	assert_s16(disp);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_inddisp << 3) | (base & 7);
	WOUT = 0x6400 | ((src & 7) << 7);
	WOUT = disp;
}

static inline void
op_fmoves_fd(int src, int dst)
{
	debug(("fmoves %s, %s\n", regname(src), regname(dst)));
	assert_freg(src);
	assert_dreg(dst);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_d << 3) | (dst & 7);
	WOUT = 0x6400 | ((src & 7) << 7);
}

static inline void
op_fmoved_fI(int src, int base)
{
	debug(("fmoved %s, (%s)\n", regname(src), regname(base)));
	assert_freg(src);
	assert_areg(base);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_ind << 3) | (base & 7);
	WOUT = 0x7400 | ((src & 7) << 7);
}

static inline void
op_fmoved_fp(int src, int base)
{
	debug(("fmoved %s, -(%s)\n", regname(src), regname(base)));
	assert_freg(src);
	assert_areg(base);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_predec << 3) | (base & 7);
	WOUT = 0x7400 | ((src & 7) << 7);
}

static inline void
op_fmoved_fo(int src, int base, int disp)
{
	debug(("fmoved %s, %d(%s)\n", regname(src), disp, regname(base)));
	assert_freg(src);
	assert_areg(base);
	assert_s16(disp);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_inddisp << 3) | (base & 7);
	WOUT = 0x7400 | ((src & 7) << 7);
	WOUT = disp;
}

static inline void
op_fsmoves_df(int src, int dst)
{
	debug(("fsmoves %s, %s\n", regname(src), regname(dst)));
 	assert_dreg(src);
	assert_freg(dst);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_d << 3) | (src & 7);
	WOUT = 0x4400 | ((dst & 7) << 7) | OPMODE_s;
}

static inline void
op_fsmoves_If(int base, int dst)
{
	debug(("fsmoves (%s), %s\n", regname(base), regname(dst)));
 	assert_areg(base);
	assert_freg(dst);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_ind << 3) | (base & 7);
	WOUT = 0x4400 | ((dst & 7) << 7) | OPMODE_s;
}

static inline void
op_fdmoved_If(int base, int dst)
{
	debug(("fdmoved (%s), %s\n", regname(base), regname(dst)));
 	assert_areg(base);
	assert_freg(dst);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_ind << 3) | (base & 7);
	WOUT = 0x5400 | ((dst & 7) << 7) | OPMODE_d;
}

static inline void
op_fsmoves_of(int base, int disp, int dst)
{
	debug(("fsmoves %d(%s), %s\n", disp, regname(base), regname(dst)));
 	assert_areg(base);
	assert_s16(disp);
	assert_freg(dst);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_inddisp << 3) | (base & 7);
	WOUT = 0x4400 | ((dst & 7) << 7) | OPMODE_s;
	WOUT = disp;
}

static inline void
op_fdmoved_of(int base, int disp, int dst)
{
	debug(("fdmoved %d(%s), %s\n", disp, regname(base), regname(dst)));
 	assert_areg(base);
	assert_s16(disp);
	assert_freg(dst);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_inddisp << 3) | (base & 7);
	WOUT = 0x5400 | ((dst & 7) << 7) | OPMODE_d;
	WOUT = disp;
}

static inline void
op_fsmovel_df(int src, int dst)
{
	debug(("fsmovel %s, %s\n", regname(src), regname(dst)));
 	assert_dreg(src);
	assert_freg(dst);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_d << 3) | (src & 7);
	WOUT = 0x4000 | ((dst & 7) << 7) | OPMODE_s;
}

static inline void
op_fdmovel_df(int src, int dst)
{
	debug(("fdmovel %s, %s\n", regname(src), regname(dst)));
 	assert_dreg(src);
	assert_freg(dst);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_d << 3) | (src & 7);
	WOUT = 0x4000 | ((dst & 7) << 7) | OPMODE_d;
}

static inline void
op_fsmovex_ff(int src, int dst)
{
	debug(("fsmovex %s, %s\n", regname(src), regname(dst)));
	assert_freg(src);
	assert_freg(dst);
	WOUT = 0xF000 | (COPROCID << 9); 
	WOUT = ((src & 7) << 10) | ((dst & 7) << 7) | OPMODE_s;
}

static inline void
op_fdmovex_ff(int src, int dst)
{
	debug(("fdmovex %s, %s\n", regname(src), regname(dst)));
	assert_freg(src);
	assert_freg(dst);
	WOUT = 0xF000 | (COPROCID << 9); 
	WOUT = ((src & 7) << 10) | ((dst & 7) << 7) | OPMODE_d;
}

static inline void
op_fmovemx_ro(int mask, int base, int disp)
{
	debug(("fmovemx 0x%02x, %d(%s)\n", mask, disp, regname(base)));
	assert_s16(disp);
	assert_areg(base);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_inddisp << 3) | (base & 7);
	WOUT = 0xD000 | mask;
}

static inline void
op_fmovemx_or(int base, int disp, int mask)
{
	debug(("fmovemx %d(%s), 0x%02x\n", disp, regname(base), mask));
	assert_s16(disp);
	assert_areg(base);
	WOUT = 0xF000 | (COPROCID << 9) | (MODE_inddisp << 3) | (base & 7);
	WOUT = 0xE000 | mask;
}

static inline void
op_fsmulx_ff(int src, int dst)
{
	debug(("fsmulx %s, %s\n", regname(src), regname(dst)));
	_op_fop_ff(src, dst,  0x23 | OPMODE_s);
}

static inline void
op_fdmulx_ff(int src, int dst)
{
	debug(("fdmulx %s, %s\n", regname(src), regname(dst)));
	_op_fop_ff(src, dst,  0x23 | OPMODE_d);
}

static inline void
op_fsnegx_ff(int src, int dst)
{
	debug(("fsnegx %s, %s\n", regname(src), regname(dst)));
	_op_fop_ff(src, dst,  0x1A | OPMODE_s);
}

static inline void
op_fdnegx_ff(int src, int dst)
{
	debug(("fdnegx %s, %s\n", regname(src), regname(dst)));
	_op_fop_ff(src, dst,  0x1A | OPMODE_d);
}

static inline void
op_fremx_ff(int src, int dst)
{
	debug(("fremx %s, %s\n", regname(src), regname(dst)));
	_op_fop_ff(src, dst, 0x25);
}

static inline void
op_fssubx_ff(int src, int dst)
{
	debug(("fssubx %s, %s\n", regname(src), regname(dst)));
	_op_fop_ff(src, dst,  0x28 | OPMODE_s);
}

static inline void
op_fdsubx_ff(int src, int dst)
{
	debug(("fdsubx %s, %s\n", regname(src), regname(dst)));
	_op_fop_ff(src, dst,  0x28 | OPMODE_d);
}


/* --------------------------------------------------------------------- */

define_insn(unimplemented, unimplemented)
{
	abort();
}

define_insn(nop, nop)
{
	op_nop();
}

/* --------------------------------------------------------------------- */

define_insn(prologue, prologue_xxC)
{
	Method* meth = const_method(2);
	int framesize;

#ifdef DEBUG
	if (jit_debug) {
		printf("\n%s.%s %s\n", meth->class->name->data,
			meth->name->data, meth->signature->data);
	}
#endif

	/* Work out size of stack frame */
	framesize = (maxLocal - maxArgs + maxStack + maxTemp) * SLOTSIZE;
	op_linkw_ai(REG_fp, -framesize);
}

define_insn(epilogue, epilogue_xxx)
{
	op_unlk_a(REG_fp);
}

define_insn(eprologue, eprologue_xxx)
{
	int framesize = (maxLocal - maxArgs + maxStack + maxTemp) * SLOTSIZE;

	/* Be careful not to dealocate our current frame even for
	   an instant.  */
	op_moveal_aa(REG_fp, REG_a0);
	op_subaw_ia(framesize, REG_a0);
	op_moveal_aa(REG_a0, REG_sp);
}

/* --------------------------------------------------------------------- */

define_insn(spill_int, spilli_RCx)
{
	int r = sreg_int(0);
	int o = const_int(1);

	op_movel_do(r, REG_fp, o);
}

define_insn(spill_ref, spillr_RCx)
{
	int r = sreg_ref(0);
	int o = const_int(1);

	op_movel_ao(r, REG_fp, o);
}

define_insn(spill_float, spillf_RCx)
{
	int r = sreg_float(0);
	int o = const_int(1);

	op_fmoves_fo(r, REG_fp, o);
}

define_insn(spill_double, spilld_RCx)
{
	int r = sreg_double(0);
	int o = const_int(1);

	op_fmoved_fo(r, REG_fp, o);
}

define_insn(reload_int, reloadi_RCx)
{
	int r = lreg_int(0);
	int o = const_int(1);

	op_movel_od(REG_fp, o, r);
}

define_insn(reload_ref, reloadr_RCx)
{
	int r = lreg_ref(0);
	int o = const_int(1);

	op_moveal_oa(REG_fp, o, r);
}

define_insn(reload_float, reloadf_RCx)
{
	int r = lreg_float(0);
	int o = const_int(1);

	op_fsmoves_of(REG_fp, o, r);
}

define_insn(reload_double, reloadd_RCx)
{
	int r = lreg_double(0);
	int o = const_int(1);

	op_fdmoved_of(REG_fp, o, r);
}

/* --------------------------------------------------------------------- */

define_insn(move_int_const, movei_RxC)
{
	int i = const_int(2);
	int w = wreg_int(0);

	if (i >= -0x80 && i < 0x80)
		op_moveq_id(i, w);
	else
		op_movel_id(i, w);
}

define_insn(move_ref_const, mover_RxC)
{
	int i = const_int(2);
	int w = wreg_ref(0);

	op_moveal_ia(i, w);
}

define_insn(move_label_const, mover_RxL)
{
	label* l = const_label(2);
	int w = wreg_ref(0);

	l->type |= Labsolute | Llong;
	l->at = CODEPC+2;

	op_moveal_ia(0, w);
}

define_insn(move_any, movea_RxR)
{
	int r1, r2;

	/* If the source is already in a register, allocate the destination
	   to the same register type.  Otherwise arbitrarily load as int. */

	if (slotInRegister(2, Rref)) {
		r2 = rreg_ref(2);
		r1 = wreg_ref(0);
		if (r1 != r2) {
			op_moveal_aa(r2, r1);
		}
	}
	else if (slotInRegister(2, Rfloat)) {
		r2 = rreg_float(2);
		r1 = wreg_float(0);
		if (r1 != r2) {
			op_fsmovex_ff(r2, r1);
		}
	}
	else if (slotInRegister(2, Rint)) {
		r2 = rreg_int(2);
		r1 = wreg_int(0);
		if (r1 != r2) {
			op_movel_dd(r2, r1);
		}
	}
	else {
		r2 = rslot_int(2);
		r1 = wreg_int(0);
		op_movel_od(REG_fp, r2, r1);
	}
}

define_insn(move_int, movei_RxR)
{
	int r = rreg_int(2);
	int w = wreg_int(0);

	if (r != w) {
		op_movel_dd(r, w);
	}
}

define_insn(move_ref, mover_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_ref(0);

	if (r != w) {
		op_moveal_aa(r, w);
	}
}

define_insn(move_float, movef_RxR)
{
	int r = rreg_float(2);
	int w = wreg_float(0);

	if (r != w) {
		op_fsmovex_ff(r, w);
	}
}

define_insn(move_double, moved_RxR)
{
	int r = rreg_double(2);
	int w = wreg_double(0);

	if (r != w) {
		op_fdmovex_ff(r, w);
	}
}

/* --------------------------------------------------------------------- */

define_insn(add_int_const, addi_RRC)
{
	int o = const_int(2);
	int rw = rwreg_int(0);

	assert(rreg_int(1) == rw);

	if (o >= 1 && o <= 8) {
		op_addql_id(o, rw);
	}
	else {
		op_addl_id(o, rw);
	}
}

define_insn(add_int, addi_RRR)
{
	int r = rreg_int(2);
	int w = rwreg_int(0);

	assert(rreg_int(1) == w);

	op_addl_dd(r, w);
}

define_insn(adc_int, adci_RRR)
{
	int r = rreg_int(2);
	int w = rwreg_int(0);

	assert(rreg_int(1) == w);

	op_addxl_dd(r, w);
}

define_insn(add_ref_const, addr_RRC)
{
	int o = const_int(2);
	int rw = rwreg_ref(0);

	assert(rreg_ref(1) == rw);

	if (o >= 1 && o <= 8) {
		op_addql_ia(o, rw);
	}
	else if (o >= -0x8000 && o < 0x8000) {
		op_addaw_ia(o, rw);
	}
	else {
		op_addal_ia(o, rw);
	}
}

define_insn(add_ref, addr_RRR)
{
	int r = rreg_int(2);
	int w = rwreg_ref(0);

	assert(rreg_ref(1) == w);

	op_addal_da(r, w);
}

define_insn(add_float, addf_RRR)
{
	int r = rreg_float(2);
	int w = rwreg_float(0);

	assert(rreg_float(1) == w);

	op_fsaddx_ff(r, w);
}

define_insn(add_double, addd_RRR)
{
	int r = rreg_double(2);
	int w = rwreg_double(0);

	assert(rreg_double(1) == w);

	op_fdaddx_ff(r, w);
}

define_insn(sub_int_const, subi_RRC)
{
	int o = const_int(2);
	int rw = rwreg_int(0);

	assert(rreg_int(1) == rw);

	if (o >= 1 && o <= 8) {
		op_subql_id(o, rw);
	}
	else {
		op_subl_id(o, rw);
	}
}

define_insn(sub_int, subi_RRR)
{
	int r = rreg_int(2);
	int w = rwreg_int(0);

	assert(rreg_int(1) == w);

	op_subl_dd(r, w);
}

define_insn(sbc_int, sbci_RRR)
{
	int r = rreg_int(2);
	int w = rwreg_int(0);

	assert(rreg_int(1) == w);

	op_subxl_dd(r, w);
}

define_insn(sub_float, subf_RRR)
{
	int r = rreg_float(2);
	int w = rwreg_float(0);

	assert(rreg_float(1) == w);

	op_fssubx_ff(r, w);
}

define_insn(sub_double, subd_RRR)
{
	int r = rreg_double(2);
	int w = rwreg_double(0);

	assert(rreg_double(1) == w);

	op_fdsubx_ff(r, w);
}

define_insn(neg_int, negi_RxR)
{
	int rw = rwreg_int(0);

	assert(rreg_int(2) == rw);

	op_negl_d(rw);
}

define_insn(ngc_int, ngci_RxR)
{
	int rw = rwreg_int(0);

	assert(rreg_int(2) == rw);

	op_negxl_d(rw);
}

define_insn(neg_float, negf_RxR)
{
	int r = rreg_float(2);
	int w = wreg_float(0);

	op_fsnegx_ff(r, w);
}

define_insn(neg_double, negd_RxR)
{
	int r = rreg_double(2);
	int w = wreg_double(0);

	op_fdnegx_ff(r, w);
}

define_insn(mul_int_const, muli_RRC)
{
	int o = const_int(2);
	int rw = rwreg_int(0);

	assert(rreg_int(1) == rw);

	op_mulsl_id(o, rw);
}

define_insn(mul_int, muli_RRR)
{
	int r = rreg_int(2);
	int w = rwreg_int(0);

	assert(rreg_int(1) == w);

	op_mulsl_dd(r, w);
}

define_insn(mul_float, mulf_RRR)
{
	int r = rreg_float(2);
	int w = rwreg_float(0);

	assert(rreg_float(1) == w);

	op_fsmulx_ff(r, w);
}

define_insn(mul_double, muld_RRR)
{
	int r = rreg_double(2);
	int w = rwreg_double(0);

	assert(rreg_double(1) == w);

	op_fdmulx_ff(r, w);
}

define_insn(div_int, divi_RRR)
{
	int r = rreg_int(2);
	int w = rwreg_int(0);

	assert(rreg_int(1) == w);

	op_divsl_ddd(r, w, w);
}

define_insn(div_float, divf_RRR)
{
	int r = rreg_float(2);
	int w = rwreg_float(0);

	assert(rreg_double(1) == w);

	op_fsdivx_ff(r, w);
}

define_insn(div_double, divd_RRR)
{
	int r = rreg_double(2);
	int w = rwreg_double(0);

	assert(rreg_double(1) == w);

	op_fddivx_ff(r, w);
}

define_insn(rem_int, remi_RRR)
{
	int d = rreg_int(2);
	int n = rreg_int(1);

	clobberRegister(d);
	clobberRegister(n);

	op_divsl_ddd(d, d, n);

	forceRegister(seq_slot(s, 0), d, Rint);
}

define_insn(rem_float, remf_RRR)
{
	int r = rreg_float(2);
	int w = rwreg_float(0);

	assert(rreg_float(1) == w);

	op_fremx_ff(r, w);
}

define_insn(rem_double, remd_RRR)
{
	int r = rreg_double(2);
	int w = rwreg_double(0);

	assert(rreg_double(1) == w);

	op_fremx_ff(r, w);
}

/* --------------------------------------------------------------------- */

define_insn(and_int_const, andi_RRC)
{
	int o = const_int(2);
	int rw = rwreg_int(0);

	assert(rreg_int(1) == rw);

	op_andl_id(o, rw);
}

define_insn(and_int, andi_RRR)
{
	int r = rreg_int(2);
	int w = rwreg_int(0);

	assert(rreg_int(1) == w);

	op_andl_dd(r, w);
}

define_insn(or_int, ori_RRR)
{
	int r = rreg_int(2);
	int w = rwreg_int(0);

	assert(rreg_int(1) == w);

	op_orl_dd(r, w);
}

define_insn(xor_int, xori_RRR)
{
	int r = rreg_int(2);
	int w = rwreg_int(0);

	assert(rreg_int(1) == w);

	op_eorl_dd(r, w);
}

define_insn(ashr_int_const, ashri_RRC)
{
	int o = const_int(2);
	int rw = rwreg_int(0);

	assert(rreg_int(1) == rw);

	op_asr_id(o, rw);
}

define_insn(ashr_int, ashri_RRR)
{
	int r = rreg_int(2);
	int w = rwreg_int(0);

	assert(rreg_int(1) == w);

	op_asr_dd(r, w);
}

define_insn(lshr_int_const, lshri_RRC)
{
	int o = const_int(2);
	int rw = rwreg_int(0);

	assert(rreg_int(1) == rw);

	op_lsr_id(o, rw);
}

define_insn(lshr_int, lshri_RRR)
{
	int r = rreg_int(2);
	int w = rwreg_int(0);

	assert(rreg_int(1) == w);

	op_lsr_dd(r, w);
}

define_insn(lshl_int_const, lshli_RRC)
{
	int o = const_int(2);
	int rw = rwreg_int(0);

	assert(rreg_int(1) == rw);

	op_lsl_id(o, rw);
}

define_insn(lshl_int, lshli_RRR)
{
	int r = rreg_int(2);
	int w = rwreg_int(0);

	assert(rreg_int(1) == w);

	op_lsl_dd(r, w);
}

define_insn(cmp_int_const, cmpi_xRC)
{
	int v = const_int(2);
	int r = rreg_int(1);

	if (v != 0) {
		op_cmpil_id(v, r);
	}
	else {
		op_tst_d(r);
	}
}

define_insn(cmp_int, cmpi_xRR)
{
	int r1 = rreg_int(1);
	int r2 = rreg_int(2);

	op_cmpl_dd(r2, r1);
}

define_insn(cmp_ref_const, cmpr_xRC)
{
	int v = const_int(2);
	int r = rreg_ref(1);

	if (v != 0) {
		op_cmpil_ia(v, r);
	}
	else {
		op_tst_a(r);
	}
}

define_insn(cmp_ref, cmpr_xRR)
{
	int r1 = rreg_ref(1);
	int r2 = rreg_ref(2);

	op_cmpal_aa(r2, r1);
}

define_insn(swap_any, swap_RxR)
{
	int r1, r2, type = 0;

	if (slotInRegister(2, Rref)) {
		r2 = rwreg_ref(2);
		type |= 2;
	}
	else {
		r2 = rwreg_int(2);
	}

	if (slotInRegister(0, Rref)) {
		r1 = rwreg_ref(0);
		type |= 1;
	}
	else {
		r1 = rwreg_int(0);
	}

	switch (type) {
	case 0:
		op_exg_dd(r1, r2);
		break;
	case 3:
		op_exg_aa(r1, r2);
		break;
	case 2:
		op_exg_da(r1, r2);
		break;
	case 1:
		op_exg_da(r2, r1);
		break;
	default:
		abort();
	}
}

/* --------------------------------------------------------------------- */

define_insn(load_int, loadi_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_int(0);

	op_movel_Id(r, w);
}

define_insn(load_offset_int, loadi_RRC)
{
	int o = const_int(2);
	int r = rreg_ref(1);
	int w = wreg_int(0);

	op_movel_od(r, o, w);
}

define_insn(load_ref, loadr_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_ref(0);

	op_moveal_Ia(r, w);
}

define_insn(load_offset_ref, loadr_RRC)
{
	int o = const_int(2);
	int r = rreg_ref(1);
	int w = wreg_ref(0);

	assert(o >= -0x8000 && o < 0x8000);
	op_moveal_oa(r, o, w);
}

define_insn(load_float, loadf_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_float(0);

	op_fsmoves_If(r, w);
}

define_insn(load_double, loadd_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_double(0);

	op_fdmoved_If(r, w);
}

define_insn(load_byte, loadb_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_int(0);

	op_moveb_Id(r, w);
	op_extbl_d(w);
}

define_insn(load_char, loadc_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_int(0);

	op_clrl_d(w);
	op_movew_Id(r, w);
}

define_insn(load_short, loads_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_int(0);

	op_movew_Id(r, w);
	op_extwl_d(w);
}

define_insn(store_int, storei_xRR)
{
	int r = rreg_int(2);
	int w = rreg_ref(1);

	op_movel_dI(r, w);
}

define_insn(store_offset_int, storei_xRRC)
{
	int o = const_int(2);
	int w = rreg_ref(1);
	int r = rreg_int(0);

	assert(o >= -0x8000 && o < 0x8000);
	op_movel_do(r, w, o);
}

define_insn(store_ref, storer_xRR)
{
	int r = rreg_ref(2);
	int w = rreg_ref(1);

	op_movel_aI(r, w);
}

define_insn(store_offset_ref, storer_xRRC)
{
	int o = const_int(2);
	int w = rreg_ref(1);
	int r = rreg_ref(0);

	assert(o >= -0x8000 && o < 0x8000);
	op_movel_ao(r, w, o);
}

define_insn(store_float, storef_xRR)
{
	int r = rreg_float(2);
	int w = rreg_ref(1);

	op_fmoves_fI(r, w);
}

define_insn(store_double, stored_xRR)
{
	int r = rreg_double(2);
	int w = rreg_ref(1);

	op_fmoved_fI(r, w);
}

define_insn(store_byte, storeb_xRR)
{
	int r = rreg_int(2);
	int w = rreg_ref(1);

	op_moveb_dI(r, w);
}

define_insn(store_short, stores_xRR)
{
	int r = rreg_int(2);
	int w = rreg_ref(1);

	op_movew_dI(r, w);
}

/* --------------------------------------------------------------------- */

define_insn(cvt_int_byte, cvtib_RxR)
{
	int rw = rwreg_int(0);

	assert(rreg_int(2) == rw);

	op_extbl_d(rw);
}

define_insn(cvt_int_short, cvtis_RxR)
{
	int rw = rwreg_int(0);

	assert(rreg_int(2) == rw);

	op_extwl_d(rw);
}

define_insn(cvt_int_float, cvtif_RxR)
{
	int r = rreg_int(2);
	int w = wreg_float(0);

	op_fsmovel_df(r, w);
}

define_insn(cvt_int_double, cvtid_RxR)
{
	int r = rreg_int(2);
	int w = wreg_double(0);

	op_fdmovel_df(r, w);
}

define_insn(cvt_float_double, cvtfd_RxR)
{
	int r = rreg_float(2);
	int w = wreg_double(0);

	if (r != w) {
		op_fdmovex_ff(r, w);
	}
}

define_insn(cvt_double_float, cvtdf_RxR)
{
	int r = rreg_double(2);
	int w = wreg_float(0);

	if (is_68040) {
		op_fsmovex_ff(r, w);
	}
	else {
		/* FIXME -- implement something to return a scratch
		   register.  Until then, arbitrarily kill d0.  */
		clobberRegister(REG_d0);
		op_fmoves_fd(r, REG_d0);
		op_fsmoves_df(REG_d0, w);
	}
}

/* --------------------------------------------------------------------- */

define_insn(build_key, set_word_xxC)
{
	jint val = const_int(2);

	debug((".long %08x\n", val));
	LOUT = val;
}

define_insn(build_code_ref, set_wordpc_xxC)
{
	label* l = const_label(2);

	l->type |= Labsolute | Llong;
	l->at = CODEPC;
	l->from = 0;

	debug((".long ?\n"));
	LOUT = 0;
}

/* --------------------------------------------------------------------- */

define_insn(set_label, set_label_xxC)
{
	label* l = const_label(2);

	l->to = CODEPC;
}

define_insn(branch, branch_xCC)
{
	label* l = const_label(1);
	int bt = const_int(2);

	l->type |= Llong | Lrelative | Lrangecheck;
	l->at = CODEPC+2;
	l->from = CODEPC+2;

	switch (bt) {
	case ba:
		op_bra_32(0);
		break;
	case beq:
		op_beq_32(0);
		break;
	case bne:
		op_bne_32(0);
		break;
	case blt:
		op_blt_32(0);
		break;
	case ble:
		op_ble_32(0);
		break;
	case bgt:
		op_bgt_32(0);
		break;
	case bge:
		op_bge_32(0);
		break;
	case bult:
		op_blo_32(0);
		break;
	default:
		abort();
	}
}

define_insn(branch_indirect, branch_indirect_xRC)
{
	int r = rreg_ref(1);
	assert(const_int(2) == ba);

	op_jmp_I(r);
}

define_insn(call_ref, call_xCC)
{
        label* l = const_label(1);
        assert(const_int(2) == ba);

	l->type |= Lrelative | Llong;
	l->at = CODEPC+2;
	l->from = CODEPC+2;

	op_bsr_32(0);
}

define_insn(call, call_xRC)
{
	int r = rreg_ref(1);
	assert(const_int(2) == ba);

	op_jsr_I(r);
}

define_insn(ret, ret_xxx)
{
	op_rts();
}

static void
m68k_pusharg_single(sequence *s)
{
	int r;

	if (slotInRegister(1, Rint)) {
		r = rreg_int(1);
		op_movel_dp(r, REG_sp);
	}
	else if (slotInRegister(1, Rref)) {
		r = rreg_ref(1);
		op_movel_ap(r, REG_sp);
	}
	else {
		r = rslot_int(1);
		op_movel_op(REG_fp, r, REG_sp);
	}
}

define_insn(pusharg_int_const, pushi_xCC)
{
	int o = const_int(1);

	op_movel_ip(o, REG_sp);
}


define_insn(pusharg_int, pushi_xRC)
{
	m68k_pusharg_single(s);
}

define_insn(pusharg_ref_const, pushr_xCC)
{
	int o = const_int(1);

	op_movel_ip(o, REG_sp);
}

define_insn(push_ref, pushr_xRC)
{
	m68k_pusharg_single(s);
}

define_insn(push_float, pushf_xRC)
{
	int r;

	if (slotInRegister(1, Rfloat)) {
		r = rreg_float(1);
		op_fmoves_fp(r, REG_sp);
	}
	else {
		r = rslot_float(1);
		op_movel_op(REG_fp, r, REG_sp);
	}
}

define_insn(push_double, pushd_xRC)
{
	int r = rreg_double(1);

	op_fmoved_fp(r, REG_sp);
}

define_insn(popargs, popargs_xxC)
{
	int o = const_int(2) * 4;

	if (o < 0) {
		abort();
	}
	else if (o > 0) {
		if (o <= 0x8) {
			op_addql_ia(o, REG_sp);
		}
		else if (o < 0x8000) {
			op_addaw_ia(o, REG_sp);
		}
		else {
			op_addal_ia(o, REG_sp);
		}
	}
}

define_insn(return_int, returni_Rxx)
{
	forceRegister(seq_dst(s), REG_d0, Rint);
}

define_insn(return_ref, returnr_Rxx)
{
	if (M68K_RETURN_REF == REG_a0) {
		forceRegister(seq_dst(s), REG_a0, Rref);
	}
	else {
		forceRegister(seq_dst(s), REG_d0, Rint);
	}
}

define_insn(return_long, returnl_Rxx)
{
	forceRegister(LSLOT(seq_dst(s)), REG_d1, Rint);
	forceRegister(HSLOT(seq_dst(s)), REG_d0, Rint);
}

define_insn(return_float, returnf_Rxx)
{
	forceRegister(seq_dst(s), REG_fp0, Rfloat);
}

define_insn(return_double, returnd_Rxx)
{
	forceRegister(seq_dst(s), REG_fp0, Rdouble);
}

define_insn(returnarg_int, returnargi_xxR)
{
	int r;

	if (slotInRegister(2, Rint)) {
		r = rreg_int(2);
		if (r != REG_d0) {
			op_movel_dd(r, REG_d0);
		}
	}
	else {
		r = rslot_int(2);
		assert(r >= -0x8000 && r < 0x8000);
		op_movel_od(REG_fp, r, REG_d0);
	}
}

define_insn(returnarg_ref, returnargr_xxR)
{
	int r;

	if (slotInRegister(2, Rref)) {
		r = rreg_ref(2);
		if (M68K_RETURN_REF == REG_a0 && r != REG_a0) {
			op_moveal_aa(r, REG_a0);
			r = REG_a0;
		}
	}
	else {
		r = rslot_ref(2);
		assert(r >= -0x8000 && r < 0x8000);
		if (M68K_RETURN_REF == REG_a0) {
			op_moveal_oa(REG_fp, r, REG_a0);
			r = REG_a0;
		}
		else {
			op_movel_od(REG_fp, r, REG_d0);
			r = REG_d0;
		}
	}

	if (r != REG_d0) {
		op_movel_ad(r, REG_d0);
	}
}

define_insn(returnarg_long, returnargl_xxR)
{
	SlotInfo *r;
	int rl, rh;

	r = seq_slot(s, 2);

	if (_slotInRegister(LSLOT(r), Rint)) {
		rl = _slowSlotRegister(LSLOT(r), Rint, rread);
	}
	else {
		rl = slowSlotOffset(LSLOT(r), Rint, rread);
		op_movel_od(REG_fp, rl, REG_d1);
		rl = REG_d1;
	}

	if (_slotInRegister(HSLOT(r), Rint)) {
		rh = _slowSlotRegister(HSLOT(r), Rint, rread);
	}
	else {
		rh = slowSlotOffset(HSLOT(r), Rint, rread);
		op_movel_od(REG_fp, rh, REG_d0);
		rh = REG_d0;
	}

	/* Return long is a bit complicated since part of the source may
	   be the destination.  */
	if (rl == REG_d0 && rh == REG_d1) {
		op_exg_dd(rl, rh);
	}
	else {
		if (rh == REG_d1) {
			op_movel_dd(rh, REG_d0);
		}
		if (rl != REG_d1) {
			op_movel_dd(rl, REG_d1);
		}
		if (rh != REG_d0 && rh != REG_d1) {
			op_movel_dd(rh, REG_d0);
		}
	}
}

define_insn(returnarg_float, returnargf_xxR)
{
	int r;

	if (slotInRegister(2, Rfloat)) {
		r = rreg_float(2);
		if (r != REG_fp0)
			op_fsmovex_ff(r, REG_fp0);
	}
	else {
		r = rslot_float(2);
		assert(r >= -0x8000 && r < 0x8000);
		op_fsmoves_of(REG_fp, r, REG_fp0);
	}
}

define_insn(returnarg_double, returnargd_xxR)
{
	int r;

	if (slotInRegister(2, Rdouble)) {
		r = rreg_double(2);
		if (r != REG_fp0)
			op_fdmovex_ff(r, REG_fp0);
	}
	else {
		r = rslot_double(2);
		assert(r >= -0x8000 && r < 0x8000);
		op_fdmoved_of(REG_fp, r, REG_fp0);
	}
}
