/***************************************************************************

    drcbex86.c

    32-bit x86 back-end for the universal machine language.

****************************************************************************

    Copyright Aaron Giles
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are
    met:

        * Redistributions of source code must retain the above copyright
          notice, this list of conditions and the following disclaimer.
        * Redistributions in binary form must reproduce the above copyright
          notice, this list of conditions and the following disclaimer in
          the documentation and/or other materials provided with the
          distribution.
        * Neither the name 'MAME' nor the names of its contributors may be
          used to endorse or promote products derived from this software
          without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGE.

****************************************************************************

   Future improvements/changes:

    * Optimize to avoid unnecessary reloads
        - especially EDX for 64-bit operations
        - also FCMP/FLAGS has unnecessary PUSHF/POP EAX

    * Identify common pairs and optimize output

    * Convert SUB a,0,b to NEG

    * Optimize, e.g., and [r5],i0,$FF to use ebx as temporary register
        (avoid initial move) if i0 is not needed going forward

****************************************************************************

    ---------------
    ABI/conventions
    ---------------

    Registers:
        EAX        - volatile, function return value
        EBX        - non-volatile
        ECX        - volatile
        EDX        - volatile, function return value (upper 32 bits)
        ESI        - non-volatile
        EDI        - non-volatile
        EBP        - non-volatile

        FP stack   - volatile


    ---------------
    Execution model
    ---------------

    Registers:
        EAX        - scratch register
        EBX        - maps to I0 (low 32 bits)
        ECX        - scratch register
        EDX        - scratch register
        ESI        - maps to I1 (low 32 bits)
        EDI        - maps to I2 (low 32 bits)
        EBP        - maps to I3 (low 32 bits)

        FP stack   - scratch registers

    Entry point:
        Assumes 1 parameter passed, which is the codeptr of the code
        to execute once the environment is set up.

    Exit point:
        Assumes exit value is in EAX.

    Entry stack:
        [esp]      - return
        [esp+4]    - input parameter (entry handle)

    Runtime stack:
        [esp]      - param 0
        [esp+4]    - param 1
        [esp+8]    - param 2
        [esp+12]   - param 3
        [esp+16]   - param 4
        [esp+20]   - alignment
        [esp+24]   - alignment
        [esp+28]   - saved ebp
        [esp+32]   - saved edi
        [esp+36]   - saved esi
        [esp+40]   - saved ebx
        [esp+44]   - ret
        [esp+48]   - input parameter (entry handle)

**************************************************************************/

#include "emu.h"
#include "debugger.h"
#include "drcuml.h"
#include "drcbex86.h"

using namespace uml;
using namespace x86emit;


//**************************************************************************
//  DEBUGGING
//**************************************************************************

#define LOG_HASHJMPS		(0)



//**************************************************************************
//  CONSTANTS
//**************************************************************************

const UINT32 PTYPE_M	= 1 << parameter::PTYPE_MEMORY;
const UINT32 PTYPE_I	= 1 << parameter::PTYPE_IMMEDIATE;
const UINT32 PTYPE_R	= 1 << parameter::PTYPE_INT_REGISTER;
const UINT32 PTYPE_F	= 1 << parameter::PTYPE_FLOAT_REGISTER;
const UINT32 PTYPE_MI	= PTYPE_M | PTYPE_I;
const UINT32 PTYPE_RI	= PTYPE_R | PTYPE_I;
const UINT32 PTYPE_MR	= PTYPE_M | PTYPE_R;
const UINT32 PTYPE_MRI	= PTYPE_M | PTYPE_R | PTYPE_I;
const UINT32 PTYPE_MF	= PTYPE_M | PTYPE_F;



//**************************************************************************
//  MACROS
//**************************************************************************

#define X86_CONDITION(condition)		(condition_map[condition - uml::COND_Z])
#define X86_NOT_CONDITION(condition)	(condition_map[condition - uml::COND_Z] ^ 1)

#define assert_no_condition(inst)		assert((inst).condition() == uml::COND_ALWAYS)
#define assert_any_condition(inst)		assert((inst).condition() == uml::COND_ALWAYS || ((inst).condition() >= uml::COND_Z && (inst).condition() < uml::COND_MAX))
#define assert_no_flags(inst)			assert((inst).flags() == 0)
#define assert_flags(inst, valid)		assert(((inst).flags() & ~(valid)) == 0)



//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

drcbe_x86::opcode_generate_func drcbe_x86::s_opcode_table[OP_MAX];

// size-to-mask table
static const UINT64 size_to_mask[] = { 0, 0xff, 0xffff, 0, 0xffffffff, 0, 0, 0, U64(0xffffffffffffffff) };

// register mapping tables
static const UINT8 int_register_map[REG_I_COUNT] =
{
	REG_EBX, REG_ESI, REG_EDI, REG_EBP
};

// flags mapping tables
static UINT8 flags_map[0x1000];
static UINT32 flags_unmap[0x20];

// condition mapping table
static const UINT8 condition_map[uml::COND_MAX - uml::COND_Z] =
{
	x86emit::COND_Z,	// COND_Z = 0x80,    requires Z
	x86emit::COND_NZ,	// COND_NZ,          requires Z
	x86emit::COND_S,	// COND_S,           requires S
	x86emit::COND_NS,	// COND_NS,          requires S
	x86emit::COND_C,	// COND_C,           requires C
	x86emit::COND_NC,	// COND_NC,          requires C
	x86emit::COND_O,	// COND_V,           requires V
	x86emit::COND_NO,	// COND_NV,          requires V
	x86emit::COND_P,	// COND_U,           requires U
	x86emit::COND_NP,	// COND_NU,          requires U
	x86emit::COND_A,	// COND_A,           requires CZ
	x86emit::COND_BE,	// COND_BE,          requires CZ
	x86emit::COND_G,	// COND_G,           requires SVZ
	x86emit::COND_LE,	// COND_LE,          requires SVZ
	x86emit::COND_L,	// COND_L,           requires SV
	x86emit::COND_GE,	// COND_GE,          requires SV
};

// FPU control register mapping
static const UINT16 fp_control[4] =
{
	0x0e3f, 	// ROUND_TRUNC
	0x023f, 	// ROUND_ROUND
	0x0a3f, 	// ROUND_CEIL
	0x063f		// ROUND_FLOOR
};



//**************************************************************************
//  TABLES
//**************************************************************************

const drcbe_x86::opcode_table_entry drcbe_x86::s_opcode_table_source[] =
{
	// Compile-time opcodes
	{ uml::OP_HANDLE,  &drcbe_x86::op_handle },		// HANDLE  handle
	{ uml::OP_HASH,    &drcbe_x86::op_hash },		// HASH    mode,pc
	{ uml::OP_LABEL,   &drcbe_x86::op_label },		// LABEL   imm
	{ uml::OP_COMMENT, &drcbe_x86::op_comment },	// COMMENT string
	{ uml::OP_MAPVAR,  &drcbe_x86::op_mapvar },		// MAPVAR  mapvar,value

	// Control Flow Operations
	{ uml::OP_NOP,     &drcbe_x86::op_nop },		// NOP
	{ uml::OP_DEBUG,   &drcbe_x86::op_debug },		// DEBUG   pc
	{ uml::OP_EXIT,    &drcbe_x86::op_exit },		// EXIT    src1[,c]
	{ uml::OP_HASHJMP, &drcbe_x86::op_hashjmp },	// HASHJMP mode,pc,handle
	{ uml::OP_JMP,     &drcbe_x86::op_jmp },		// JMP     imm[,c]
	{ uml::OP_EXH,     &drcbe_x86::op_exh },		// EXH     handle,param[,c]
	{ uml::OP_CALLH,   &drcbe_x86::op_callh },		// CALLH   handle[,c]
	{ uml::OP_RET,     &drcbe_x86::op_ret },		// RET     [c]
	{ uml::OP_CALLC,   &drcbe_x86::op_callc },		// CALLC   func,ptr[,c]
	{ uml::OP_RECOVER, &drcbe_x86::op_recover },	// RECOVER dst,mapvar

	// Internal Register Operations
	{ uml::OP_SETFMOD, &drcbe_x86::op_setfmod },	// SETFMOD src
	{ uml::OP_GETFMOD, &drcbe_x86::op_getfmod },	// GETFMOD dst
	{ uml::OP_GETEXP,  &drcbe_x86::op_getexp },		// GETEXP  dst
	{ uml::OP_GETFLGS, &drcbe_x86::op_getflgs },	// GETFLGS dst[,f]
	{ uml::OP_SAVE,    &drcbe_x86::op_save },		// SAVE    dst
	{ uml::OP_RESTORE, &drcbe_x86::op_restore },	// RESTORE dst

	// Integer Operations
	{ uml::OP_LOAD,    &drcbe_x86::op_load },		// LOAD    dst,base,index,size
	{ uml::OP_LOADS,   &drcbe_x86::op_loads },		// LOADS   dst,base,index,size
	{ uml::OP_STORE,   &drcbe_x86::op_store },		// STORE   base,index,src,size
	{ uml::OP_READ,    &drcbe_x86::op_read },		// READ    dst,src1,spacesize
	{ uml::OP_READM,   &drcbe_x86::op_readm },		// READM   dst,src1,mask,spacesize
	{ uml::OP_WRITE,   &drcbe_x86::op_write },		// WRITE   dst,src1,spacesize
	{ uml::OP_WRITEM,  &drcbe_x86::op_writem },		// WRITEM  dst,src1,spacesize
	{ uml::OP_CARRY,   &drcbe_x86::op_carry },		// CARRY   src,bitnum
	{ uml::OP_SET,     &drcbe_x86::op_set },		// SET     dst,c
	{ uml::OP_MOV,     &drcbe_x86::op_mov },		// MOV     dst,src[,c]
	{ uml::OP_SEXT,    &drcbe_x86::op_sext },		// SEXT    dst,src
	{ uml::OP_ROLAND,  &drcbe_x86::op_roland },		// ROLAND  dst,src1,src2,src3
	{ uml::OP_ROLINS,  &drcbe_x86::op_rolins },		// ROLINS  dst,src1,src2,src3
	{ uml::OP_ADD,     &drcbe_x86::op_add },		// ADD     dst,src1,src2[,f]
	{ uml::OP_ADDC,    &drcbe_x86::op_addc },		// ADDC    dst,src1,src2[,f]
	{ uml::OP_SUB,     &drcbe_x86::op_sub },		// SUB     dst,src1,src2[,f]
	{ uml::OP_SUBB,    &drcbe_x86::op_subc },		// SUBB    dst,src1,src2[,f]
	{ uml::OP_CMP,     &drcbe_x86::op_cmp },		// CMP     src1,src2[,f]
	{ uml::OP_MULU,    &drcbe_x86::op_mulu },		// MULU    dst,edst,src1,src2[,f]
	{ uml::OP_MULS,    &drcbe_x86::op_muls },		// MULS    dst,edst,src1,src2[,f]
	{ uml::OP_DIVU,    &drcbe_x86::op_divu },		// DIVU    dst,edst,src1,src2[,f]
	{ uml::OP_DIVS,    &drcbe_x86::op_divs },		// DIVS    dst,edst,src1,src2[,f]
	{ uml::OP_AND,     &drcbe_x86::op_and },		// AND     dst,src1,src2[,f]
	{ uml::OP_TEST,    &drcbe_x86::op_test },		// TEST    src1,src2[,f]
	{ uml::OP_OR,      &drcbe_x86::op_or },			// OR      dst,src1,src2[,f]
	{ uml::OP_XOR,     &drcbe_x86::op_xor },		// XOR     dst,src1,src2[,f]
	{ uml::OP_LZCNT,   &drcbe_x86::op_lzcnt },		// LZCNT   dst,src[,f]
	{ uml::OP_BSWAP,   &drcbe_x86::op_bswap },		// BSWAP   dst,src
	{ uml::OP_SHL,     &drcbe_x86::op_shl },		// SHL     dst,src,count[,f]
	{ uml::OP_SHR,     &drcbe_x86::op_shr },		// SHR     dst,src,count[,f]
	{ uml::OP_SAR,     &drcbe_x86::op_sar },		// SAR     dst,src,count[,f]
	{ uml::OP_ROL,     &drcbe_x86::op_rol },		// ROL     dst,src,count[,f]
	{ uml::OP_ROLC,    &drcbe_x86::op_rolc },		// ROLC    dst,src,count[,f]
	{ uml::OP_ROR,     &drcbe_x86::op_ror },		// ROR     dst,src,count[,f]
	{ uml::OP_RORC,    &drcbe_x86::op_rorc },		// RORC    dst,src,count[,f]

	// Floating Point Operations
	{ uml::OP_FLOAD,   &drcbe_x86::op_fload },		// FLOAD   dst,base,index
	{ uml::OP_FSTORE,  &drcbe_x86::op_fstore },		// FSTORE  base,index,src
	{ uml::OP_FREAD,   &drcbe_x86::op_fread },		// FREAD   dst,space,src1
	{ uml::OP_FWRITE,  &drcbe_x86::op_fwrite },		// FWRITE  space,dst,src1
	{ uml::OP_FMOV,    &drcbe_x86::op_fmov },		// FMOV    dst,src1[,c]
	{ uml::OP_FTOINT,  &drcbe_x86::op_ftoint },		// FTOINT  dst,src1,size,round
	{ uml::OP_FFRINT,  &drcbe_x86::op_ffrint },		// FFRINT  dst,src1,size
	{ uml::OP_FFRFLT,  &drcbe_x86::op_ffrflt },		// FFRFLT  dst,src1,size
	{ uml::OP_FRNDS,   &drcbe_x86::op_frnds },		// FRNDS   dst,src1
	{ uml::OP_FADD,    &drcbe_x86::op_fadd },		// FADD    dst,src1,src2
	{ uml::OP_FSUB,    &drcbe_x86::op_fsub },		// FSUB    dst,src1,src2
	{ uml::OP_FCMP,    &drcbe_x86::op_fcmp },		// FCMP    src1,src2
	{ uml::OP_FMUL,    &drcbe_x86::op_fmul },		// FMUL    dst,src1,src2
	{ uml::OP_FDIV,    &drcbe_x86::op_fdiv },		// FDIV    dst,src1,src2
	{ uml::OP_FNEG,    &drcbe_x86::op_fneg },		// FNEG    dst,src1
	{ uml::OP_FABS,    &drcbe_x86::op_fabs },		// FABS    dst,src1
	{ uml::OP_FSQRT,   &drcbe_x86::op_fsqrt },		// FSQRT   dst,src1
	{ uml::OP_FRECIP,  &drcbe_x86::op_frecip },		// FRECIP  dst,src1
	{ uml::OP_FRSQRT,  &drcbe_x86::op_frsqrt }		// FRSQRT  dst,src1
};



//**************************************************************************
//  INLINE FUNCTIONS
//**************************************************************************

//-------------------------------------------------
//  param_normalize - convert a full parameter
//  into a reduced set
//-------------------------------------------------

drcbe_x86::be_parameter::be_parameter(drcbe_x86 &drcbe, const parameter &param, UINT32 allowed)
{
	int regnum;

	switch (param.type())
	{
		// immediates pass through
		case parameter::PTYPE_IMMEDIATE:
			assert(allowed & PTYPE_I);
			*this = param.immediate();
			break;

		// memory passes through
		case parameter::PTYPE_MEMORY:
			assert(allowed & PTYPE_M);
			*this = make_memory(param.memory());
			break;

		// if a register maps to a register, keep it as a register; otherwise map it to memory
		case parameter::PTYPE_INT_REGISTER:
			assert(allowed & PTYPE_R);
			assert(allowed & PTYPE_M);
			regnum = int_register_map[param.ireg() - REG_I0];
			if (regnum != 0)
				*this = make_ireg(regnum);
			else
				*this = make_memory(&drcbe.m_state.r[param.ireg() - REG_I0]);
			break;

		// if a register maps to a register, keep it as a register; otherwise map it to memory
		case parameter::PTYPE_FLOAT_REGISTER:
			assert(allowed & PTYPE_F);
			assert(allowed & PTYPE_M);
			*this = make_memory(&drcbe.m_state.f[param.freg() - REG_F0]);
			break;

		// everything else is unexpected
		default:
			fatalerror("Unexpected parameter type");
			break;
	}
}


//-------------------------------------------------
//  select_register - select a register to use,
//  avoiding conflicts with the optional
//  checkparam
//-------------------------------------------------

inline int drcbe_x86::be_parameter::select_register(int defreg) const
{
	if (m_type == PTYPE_INT_REGISTER || m_type == PTYPE_FLOAT_REGISTER || m_type == PTYPE_VECTOR_REGISTER)
		return m_value;
	return defreg;
}

inline int drcbe_x86::be_parameter::select_register(int defreg, const be_parameter &checkparam) const
{
	if (*this == checkparam)
		return defreg;
	return select_register(defreg);
}

inline int drcbe_x86::be_parameter::select_register(int defreg, const be_parameter &checkparam, const be_parameter &checkparam2) const
{
	if (*this == checkparam || *this == checkparam2)
		return defreg;
	return select_register(defreg);
}


//-------------------------------------------------
//  select_register - select a register to use,
//  avoiding conflicts with the optional
//  checkparam
//-------------------------------------------------

inline void drcbe_x86::normalize_commutative(be_parameter &inner, be_parameter &outer)
{
	// if the inner parameter is a memory operand, push it to the outer
	if (inner.is_memory())
	{
		be_parameter temp = inner;
		inner = outer;
		outer = temp;
	}

	// if the inner parameter is an immediate, push it to the outer
	if (inner.is_immediate())
	{
		be_parameter temp = inner;
		inner = outer;
		outer = temp;
	}
}


//-------------------------------------------------
//  emit_combine_z_flags - combine the Z flag from
//  two 32-bit operations
//-------------------------------------------------

inline void drcbe_x86::emit_combine_z_flags(x86code *&dst)
{
	// this assumes that the flags from the low 32-bit op are on the stack
	// and the flags from the high 32-bit op are live
	emit_pushf(dst);																	// pushf
	emit_mov_r32_m32(dst, REG_ECX, MBD(REG_ESP, 4));									// mov   ecx,[esp+4]
	emit_or_r32_imm(dst, REG_ECX, ~0x40);												// or    ecx,~0x40
	emit_and_m32_r32(dst, MBD(REG_ESP, 0), REG_ECX);									// and   [esp],ecx
	emit_popf(dst);																		// popf
	emit_lea_r32_m32(dst, REG_ESP, MBD(REG_ESP, 4));									// lea   esp,[esp+4]
}


//-------------------------------------------------
//  emit_combine_z_shl_flags - combine the Z
//  flags from two 32-bit shift left operations
//-------------------------------------------------

inline void drcbe_x86::emit_combine_z_shl_flags(x86code *&dst)
{
	// this assumes that the flags from the high 32-bit op are on the stack
	// and the flags from the low 32-bit op are live
	emit_pushf(dst);																	// pushf
	emit_pop_r32(dst, REG_ECX);															// pop   ecx
	emit_or_r32_imm(dst, REG_ECX, ~0x40);												// or    ecx,~0x40
	emit_and_m32_r32(dst, MBD(REG_ESP, 0), REG_ECX);									// and   [esp],ecx
	emit_popf(dst);																		// popf
}


//-------------------------------------------------
//  reset_last_upper_lower_reg - reset the last
//  upper/lower register state
//-------------------------------------------------

inline void drcbe_x86::reset_last_upper_lower_reg()
{
	m_last_lower_reg = REG_NONE;
	m_last_upper_reg = REG_NONE;
}


//-------------------------------------------------
//  set_last_lower_reg - note that we have just
//  loaded a lower register
//-------------------------------------------------

inline void drcbe_x86::set_last_lower_reg(x86code *&dst, const be_parameter &param, UINT8 reglo)
{
	if (param.is_memory())
	{
		m_last_lower_reg = reglo;
		m_last_lower_addr = (UINT32 *)((FPTR)param.memory());
		m_last_lower_pc = dst;
	}
}


//-------------------------------------------------
//  set_last_upper_reg - note that we have just
//  loaded an upper register
//-------------------------------------------------

inline void drcbe_x86::set_last_upper_reg(x86code *&dst, const be_parameter &param, UINT8 reghi)
{
	m_last_upper_reg = reghi;
	m_last_upper_addr = (param.is_int_register()) ? m_reghi[param.ireg()] : (UINT32 *)((FPTR)param.memory(4));
	m_last_upper_pc = dst;
}


//-------------------------------------------------
//  can_skip_lower_load - return true if we can
//  skip re-loading a lower half of a register
//-------------------------------------------------

inline bool drcbe_x86::can_skip_lower_load(x86code *&dst, UINT32 *memref, UINT8 reglo)
{
	return (dst == m_last_lower_pc && memref == m_last_lower_addr && reglo == m_last_lower_reg);
}


//-------------------------------------------------
//  can_skip_upper_load - return true if we can
//  skip re-loading an upper half of a register
//-------------------------------------------------

inline bool drcbe_x86::can_skip_upper_load(x86code *&dst, UINT32 *memref, UINT8 reghi)
{
	return (dst == m_last_upper_pc && memref == m_last_upper_addr && reghi == m_last_upper_reg);
}


//-------------------------------------------------
//  track_resolve_link - wrapper for resolve_link
//  that resets all register tracking info
//-------------------------------------------------

inline void drcbe_x86::track_resolve_link(x86code *&destptr, const emit_link &linkinfo)
{
	reset_last_upper_lower_reg();
	resolve_link(destptr, linkinfo);
}

#define resolve_link INVALID



//**************************************************************************
//  BACKEND CALLBACKS
//**************************************************************************

//-------------------------------------------------
//  drcbe_x86 - constructor
//-------------------------------------------------

drcbe_x86::drcbe_x86(drcuml_state &drcuml, device_t &device, drc_cache &cache, UINT32 flags, int modes, int addrbits, int ignorebits)
	: drcbe_interface(drcuml, cache, device),
	  m_hash(cache, modes, addrbits, ignorebits),
	  m_map(cache, 0),
	  m_labels(cache),
	  m_log(NULL),
	  m_logged_common(false),
	  m_sse3(false),
	  m_entry(NULL),
	  m_exit(NULL),
	  m_nocode(NULL),
	  m_save(NULL),
	  m_restore(NULL),
	  m_last_lower_reg(REG_NONE),
	  m_last_lower_pc(NULL),
	  m_last_lower_addr(NULL),
	  m_last_upper_reg(REG_NONE),
	  m_last_upper_pc(NULL),
	  m_last_upper_addr(NULL),
	  m_fptemp(0),
	  m_fpumode(0),
	  m_fmodesave(0),
	  m_stacksave(0),
	  m_hashstacksave(0),
	  m_reslo(0),
	  m_reshi(0),
	  m_fixup_label(FUNC(drcbe_x86::fixup_label), this),
	  m_fixup_exception(FUNC(drcbe_x86::fixup_exception), this)
{
	// compute hi pointers for each register
	for (int regnum = 0; regnum < ARRAY_LENGTH(int_register_map); regnum++)
		if (int_register_map[regnum] != 0)
		{
			m_reglo[int_register_map[regnum]] = &m_state.r[regnum].w.l;
			m_reghi[int_register_map[regnum]] = &m_state.r[regnum].w.h;
		}

	// build the flags map (static but it doesn't hurt to regenerate it)
	for (int entry = 0; entry < ARRAY_LENGTH(flags_map); entry++)
	{
		UINT8 flags = 0;
		if (entry & 0x001) flags |= FLAG_C;
		if (entry & 0x004) flags |= FLAG_U;
		if (entry & 0x040) flags |= FLAG_Z;
		if (entry & 0x080) flags |= FLAG_S;
		if (entry & 0x800) flags |= FLAG_V;
		flags_map[entry] = flags;
	}
	for (int entry = 0; entry < ARRAY_LENGTH(flags_unmap); entry++)
	{
		UINT32 flags = 0;
		if (entry & FLAG_C) flags |= 0x001;
		if (entry & FLAG_U) flags |= 0x004;
		if (entry & FLAG_Z) flags |= 0x040;
		if (entry & FLAG_S) flags |= 0x080;
		if (entry & FLAG_V) flags |= 0x800;
		flags_unmap[entry] = flags;
	}

	// build the opcode table (static but it doesn't hurt to regenerate it)
	for (int opnum = 0; opnum < ARRAY_LENGTH(s_opcode_table_source); opnum++)
		s_opcode_table[s_opcode_table_source[opnum].opcode] = s_opcode_table_source[opnum].func;

	// create the log
	if (flags & DRCUML_OPTION_LOG_NATIVE)
		m_log = x86log_create_context("drcbex86.asm");
}


//-------------------------------------------------
//  ~drcbe_x86 - destructor
//-------------------------------------------------

drcbe_x86::~drcbe_x86()
{
	// free the log context
	if (m_log != NULL)
		x86log_free_context(m_log);
}


//-------------------------------------------------
//  reset - reset back-end specific state
//-------------------------------------------------

void drcbe_x86::reset()
{
	// output a note to the log
	if (m_log != NULL)
		x86log_printf(m_log, "\n\n===========\nCACHE RESET\n===========\n\n");

	// generate a little bit of glue code to set up the environment
	drccodeptr *cachetop = m_cache.begin_codegen(500);
	if (cachetop == NULL)
		fatalerror("Out of cache space after a reset!");

	x86code *dst = (x86code *)*cachetop;

	// generate a simple CPUID stub
	UINT32 (*cpuid_ecx_stub)(void) = (UINT32 (*)(void))dst;
	emit_push_r32(dst, REG_EBX);														// push  ebx
	emit_mov_r32_imm(dst, REG_EAX, 1);													// mov   eax,1
	emit_cpuid(dst);																	// cpuid
	emit_mov_r32_r32(dst, REG_EAX, REG_ECX);											// mov   eax,ecx
	emit_pop_r32(dst, REG_EBX);															// pop   ebx
	emit_ret(dst);																		// ret

	// call it to determine if we have SSE3 support
	m_sse3 = (((*cpuid_ecx_stub)() & 1) != 0);

	// generate an entry point
	m_entry = (x86_entry_point_func)dst;
	emit_mov_r32_m32(dst, REG_EAX, MBD(REG_ESP, 4));									// mov   eax,[esp+4]
	emit_push_r32(dst, REG_EBX);														// push  ebx
	emit_push_r32(dst, REG_ESI);														// push  esi
	emit_push_r32(dst, REG_EDI);														// push  edi
	emit_push_r32(dst, REG_EBP);														// push  ebp
	emit_sub_r32_imm(dst, REG_ESP, 24);													// sub   esp,24
	emit_mov_m32_r32(dst, MABS(&m_hashstacksave), REG_ESP);						// mov   [hashstacksave],esp
	emit_sub_r32_imm(dst, REG_ESP, 4);													// sub   esp,4
	emit_mov_m32_r32(dst, MABS(&m_stacksave), REG_ESP);							// mov   [stacksave],esp
	emit_fstcw_m16(dst, MABS(&m_fpumode));											// fstcw [fpumode]
	emit_jmp_r32(dst, REG_EAX);															// jmp   eax
	if (m_log != NULL && !m_logged_common)
		x86log_disasm_code_range(m_log, "entry_point", (x86code *)m_entry, dst);

	// generate an exit point
	m_exit = dst;
	emit_fldcw_m16(dst, MABS(&m_fpumode));											// fldcw [fpumode]
	emit_mov_r32_m32(dst, REG_ESP, MABS(&m_hashstacksave));						// mov   esp,[hashstacksave]
	emit_add_r32_imm(dst, REG_ESP, 24);													// add   esp,24
	emit_pop_r32(dst, REG_EBP);															// pop   ebp
	emit_pop_r32(dst, REG_EDI);															// pop   edi
	emit_pop_r32(dst, REG_ESI);															// pop   esi
	emit_pop_r32(dst, REG_EBX);															// pop   ebx
	emit_ret(dst);																		// ret
	if (m_log != NULL && !m_logged_common)
		x86log_disasm_code_range(m_log, "exit_point", m_exit, dst);

	// generate a no code point
	m_nocode = dst;
	emit_ret(dst);																		// ret
	if (m_log != NULL && !m_logged_common)
		x86log_disasm_code_range(m_log, "nocode", m_nocode, dst);

	// generate a save subroutine
	m_save = dst;
	emit_pushf(dst);																	// pushf
	emit_pop_r32(dst, REG_EAX);															// pop    eax
	emit_and_r32_imm(dst, REG_EAX, 0x8c5);												// and    eax,0x8c5
	emit_mov_r8_m8(dst, REG_AL, MABSI(flags_map, REG_EAX));								// mov    al,[flags_map]
	emit_mov_m8_r8(dst, MBD(REG_ECX, offsetof(drcuml_machine_state, flags)), REG_AL);	// mov    state->flags,al
	emit_mov_r8_m8(dst, REG_AL, MABS(&m_state.fmod));								// mov    al,[fmod]
	emit_mov_m8_r8(dst, MBD(REG_ECX, offsetof(drcuml_machine_state, fmod)), REG_AL);	// mov    state->fmod,al
	emit_mov_r32_m32(dst, REG_EAX, MABS(&m_state.exp));							// mov    eax,[exp]
	emit_mov_m32_r32(dst, MBD(REG_ECX, offsetof(drcuml_machine_state, exp)), REG_EAX);	// mov    state->exp,eax
	for (int regnum = 0; regnum < ARRAY_LENGTH(m_state.r); regnum++)
	{
		FPTR regoffsl = (FPTR)&((drcuml_machine_state *)NULL)->r[regnum].w.l;
		FPTR regoffsh = (FPTR)&((drcuml_machine_state *)NULL)->r[regnum].w.h;
		if (int_register_map[regnum] != 0)
			emit_mov_m32_r32(dst, MBD(REG_ECX, regoffsl), int_register_map[regnum]);
		else
		{
			emit_mov_r32_m32(dst, REG_EAX, MABS(&m_state.r[regnum].w.l));
			emit_mov_m32_r32(dst, MBD(REG_ECX, regoffsl), REG_EAX);
		}
		emit_mov_r32_m32(dst, REG_EAX, MABS(&m_state.r[regnum].w.h));
		emit_mov_m32_r32(dst, MBD(REG_ECX, regoffsh), REG_EAX);
	}
	for (int regnum = 0; regnum < ARRAY_LENGTH(m_state.f); regnum++)
	{
		FPTR regoffsl = (FPTR)&((drcuml_machine_state *)NULL)->f[regnum].s.l;
		FPTR regoffsh = (FPTR)&((drcuml_machine_state *)NULL)->f[regnum].s.h;
		emit_mov_r32_m32(dst, REG_EAX, MABS(&m_state.f[regnum].s.l));
		emit_mov_m32_r32(dst, MBD(REG_ECX, regoffsl), REG_EAX);
		emit_mov_r32_m32(dst, REG_EAX, MABS(&m_state.f[regnum].s.h));
		emit_mov_m32_r32(dst, MBD(REG_ECX, regoffsh), REG_EAX);
	}
	emit_ret(dst);																		// ret
	if (m_log != NULL && !m_logged_common)
		x86log_disasm_code_range(m_log, "save", m_save, dst);

	// generate a restore subroutine
	m_restore = dst;
	for (int regnum = 0; regnum < ARRAY_LENGTH(m_state.r); regnum++)
	{
		FPTR regoffsl = (FPTR)&((drcuml_machine_state *)NULL)->r[regnum].w.l;
		FPTR regoffsh = (FPTR)&((drcuml_machine_state *)NULL)->r[regnum].w.h;
		if (int_register_map[regnum] != 0)
			emit_mov_r32_m32(dst, int_register_map[regnum], MBD(REG_ECX, regoffsl));
		else
		{
			emit_mov_r32_m32(dst, REG_EAX, MBD(REG_ECX, regoffsl));
			emit_mov_m32_r32(dst, MABS(&m_state.r[regnum].w.l), REG_EAX);
		}
		emit_mov_r32_m32(dst, REG_EAX, MBD(REG_ECX, regoffsh));
		emit_mov_m32_r32(dst, MABS(&m_state.r[regnum].w.h), REG_EAX);
	}
	for (int regnum = 0; regnum < ARRAY_LENGTH(m_state.f); regnum++)
	{
		FPTR regoffsl = (FPTR)&((drcuml_machine_state *)NULL)->f[regnum].s.l;
		FPTR regoffsh = (FPTR)&((drcuml_machine_state *)NULL)->f[regnum].s.h;
		emit_mov_r32_m32(dst, REG_EAX, MBD(REG_ECX, regoffsl));
		emit_mov_m32_r32(dst, MABS(&m_state.f[regnum].s.l), REG_EAX);
		emit_mov_r32_m32(dst, REG_EAX, MBD(REG_ECX, regoffsh));
		emit_mov_m32_r32(dst, MABS(&m_state.f[regnum].s.h), REG_EAX);
	}
	emit_movzx_r32_m8(dst, REG_EAX, MBD(REG_ECX, offsetof(drcuml_machine_state, fmod)));// movzx eax,state->fmod
	emit_and_r32_imm(dst, REG_EAX, 3);													// and    eax,3
	emit_mov_m8_r8(dst, MABS(&m_state.fmod), REG_AL);								// mov    [fmod],al
	emit_fldcw_m16(dst, MABSI(&fp_control[0], REG_EAX, 2));								// fldcw  fp_control[eax]
	emit_mov_r32_m32(dst, REG_EAX, MBD(REG_ECX, offsetof(drcuml_machine_state, exp)));	// mov    eax,state->exp
	emit_mov_m32_r32(dst, MABS(&m_state.exp), REG_EAX);							// mov    [exp],eax
	emit_movzx_r32_m8(dst, REG_EAX, MBD(REG_ECX, offsetof(drcuml_machine_state, flags)));// movzx eax,state->flags
	emit_push_m32(dst, MABSI(flags_unmap, REG_EAX, 4));									// push   flags_unmap[eax*4]
	emit_popf(dst);																		// popf
	emit_ret(dst);																		// ret
	if (m_log != NULL && !m_logged_common)
		x86log_disasm_code_range(m_log, "restore", m_restore, dst);

	// finish up codegen
	*cachetop = dst;
	m_cache.end_codegen();
	m_logged_common = true;

	// reset our hash tables
	m_hash.reset();
	m_hash.set_default_codeptr(m_nocode);
}


//-------------------------------------------------
//  drcbex86_execute - execute a block of code
//  referenced by the given handle
//-------------------------------------------------

int drcbe_x86::execute(code_handle &entry)
{
	// call our entry point which will jump to the destination
	return (*m_entry)((x86code *)entry.codeptr());
}


//-------------------------------------------------
//  drcbex86_generate - generate code
//-------------------------------------------------

void drcbe_x86::generate(drcuml_block &block, const instruction *instlist, UINT32 numinst)
{
	// tell all of our utility objects that a block is beginning
	m_hash.block_begin(block, instlist, numinst);
	m_labels.block_begin(block);
	m_map.block_begin(block);

	// begin codegen; fail if we can't
	drccodeptr *cachetop = m_cache.begin_codegen(numinst * 8 * 4);
	if (cachetop == NULL)
		block.abort();

	// compute the base by aligning the cache top to a cache line (assumed to be 64 bytes)
	x86code *base = (x86code *)(((FPTR)*cachetop + 63) & ~63);
	x86code *dst = base;

	// generate code
	astring tempstring;
	const char *blockname = NULL;
	for (int inum = 0; inum < numinst; inum++)
	{
		const instruction &inst = instlist[inum];
		assert(inst.opcode() < ARRAY_LENGTH(s_opcode_table));

		// add a comment
		if (m_log != NULL)
		{
			astring dasm;
			inst.disasm(dasm, &m_drcuml);
			x86log_add_comment(m_log, dst, "%s", dasm.cstr());
		}

		// extract a blockname
		if (blockname == NULL)
		{
			if (inst.opcode() == OP_HANDLE)
				blockname = inst.param(0).handle().string();
			else if (inst.opcode() == OP_HASH)
				blockname = tempstring.format("Code: mode=%d PC=%08X", (UINT32)inst.param(0).immediate(), (offs_t)inst.param(1).immediate());
		}

		// generate code
		(this->*s_opcode_table[inst.opcode()])(dst, inst);
	}

	// complete codegen
	*cachetop = (drccodeptr)dst;
	m_cache.end_codegen();

	// log it
	if (m_log != NULL)
		x86log_disasm_code_range(m_log, (blockname == NULL) ? "Unknown block" : blockname, base, m_cache.top());

	// tell all of our utility objects that the block is finished
	m_hash.block_end(block);
	m_labels.block_end(block);
	m_map.block_end(block);
}


//-------------------------------------------------
//  drcbex86_hash_exists - return true if the
//  given mode/pc exists in the hash table
//-------------------------------------------------

bool drcbe_x86::hash_exists(UINT32 mode, UINT32 pc)
{
	return m_hash.code_exists(mode, pc);
}


//-------------------------------------------------
//  drcbex86_get_info - return information about
//  the back-end implementation
//-------------------------------------------------

void drcbe_x86::get_info(drcbe_info &info)
{
	for (info.direct_iregs = 0; info.direct_iregs < REG_I_COUNT; info.direct_iregs++)
		if (int_register_map[info.direct_iregs] == 0)
			break;
	info.direct_fregs = 0;
}



//**************************************************************************
//  EMITTERS FOR 32-BIT OPERATIONS WITH PARAMETERS
//**************************************************************************

//-------------------------------------------------
//  emit_mov_r32_p32 - move a 32-bit parameter
//  into a register
//-------------------------------------------------

void drcbe_x86::emit_mov_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param)
{
	if (param.is_immediate())
	{
		if (param.immediate() == 0)
			emit_xor_r32_r32(dst, reg, reg);											// xor   reg,reg
		else
			emit_mov_r32_imm(dst, reg, param.immediate());									// mov   reg,param
	}
	else if (param.is_memory())
		emit_mov_r32_m32(dst, reg, MABS(param.memory()));									// mov   reg,[param]
	else if (param.is_int_register())
	{
		if (reg != param.ireg())
			emit_mov_r32_r32(dst, reg, param.ireg());									// mov   reg,param
	}
}


//-------------------------------------------------
//  emit_mov_r32_p32_keepflags - move a 32-bit
//  parameter into a register without affecting
//  any flags
//-------------------------------------------------

void drcbe_x86::emit_mov_r32_p32_keepflags(x86code *&dst, UINT8 reg, const be_parameter &param)
{
	if (param.is_immediate())
		emit_mov_r32_imm(dst, reg, param.immediate());										// mov   reg,param
	else if (param.is_memory())
	{
		if (!can_skip_lower_load(dst, (UINT32 *)((FPTR)param.memory()), reg))
			emit_mov_r32_m32(dst, reg, MABS(param.memory()));								// mov   reg,[param]
	}
	else if (param.is_int_register())
	{
		if (reg != param.ireg())
			emit_mov_r32_r32(dst, reg, param.ireg());									// mov   reg,param
	}
}


//-------------------------------------------------
//  emit_mov_m32_p32 - move a 32-bit parameter
//  into a memory location
//-------------------------------------------------

void drcbe_x86::emit_mov_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param)
{
	if (param.is_immediate())
		emit_mov_m32_imm(dst, memref, param.immediate());									// mov   [mem],param
	else if (param.is_memory())
	{
		if (!can_skip_lower_load(dst, (UINT32 *)((FPTR)param.memory()), REG_EAX))
			emit_mov_r32_m32(dst, REG_EAX, MABS(param.memory()));							// mov   eax,[param]
		emit_mov_m32_r32(dst, memref, REG_EAX);										// mov   [mem],eax
	}
	else if (param.is_int_register())
		emit_mov_m32_r32(dst, memref, param.ireg());									// mov   [mem],param
}


//-------------------------------------------------
//  emit_mov_p32_r32 - move a register into a
//  32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_mov_p32_r32(x86code *&dst, const be_parameter &param, UINT8 reg)
{
	assert(!param.is_immediate());
	if (param.is_memory())
	{
		emit_mov_m32_r32(dst, MABS(param.memory()), reg);									// mov   [param],reg
		set_last_lower_reg(dst, param, reg);
	}
	else if (param.is_int_register())
	{
		if (reg != param.ireg())
			emit_mov_r32_r32(dst, param.ireg(), reg);									// mov   param,reg
	}
}


//-------------------------------------------------
//  emit_add_r32_p32 - add operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_add_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() != 0 || param.immediate() != 0)
			emit_add_r32_imm(dst, reg, param.immediate());									// add   reg,param
	}
	else if (param.is_memory())
		emit_add_r32_m32(dst, reg, MABS(param.memory()));									// add   reg,[param]
	else if (param.is_int_register())
		emit_add_r32_r32(dst, reg, param.ireg());										// add   reg,param
}


//-------------------------------------------------
//  emit_add_m32_p32 - add operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_add_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() != 0 || param.immediate() != 0)
			emit_add_m32_imm(dst, memref, param.immediate());								// add   [dest],param
	}
	else
	{
		int reg = param.select_register(REG_EAX);
		emit_mov_r32_p32(dst, reg, param);										// mov   reg,param
		emit_add_m32_r32(dst, memref, reg);											// add   [dest],reg
	}
}


//-------------------------------------------------
//  emit_adc_r32_p32 - adc operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_adc_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
		emit_adc_r32_imm(dst, reg, param.immediate());										// adc   reg,param
	else if (param.is_memory())
		emit_adc_r32_m32(dst, reg, MABS(param.memory()));									// adc   reg,[param]
	else if (param.is_int_register())
		emit_adc_r32_r32(dst, reg, param.ireg());										// adc   reg,param
}


//-------------------------------------------------
//  emit_adc_m32_p32 - adc operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_adc_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
		emit_adc_m32_imm(dst, memref, param.immediate());									// adc   [dest],param
	else
	{
		int reg = param.select_register(REG_EAX);
		emit_mov_r32_p32_keepflags(dst, reg, param);								// mov   reg,param
		emit_adc_m32_r32(dst, memref, reg);											// adc   [dest],reg
	}
}


//-------------------------------------------------
//  emit_sub_r32_p32 - sub operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_sub_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() != 0 || param.immediate() != 0)
			emit_sub_r32_imm(dst, reg, param.immediate());									// sub   reg,param
	}
	else if (param.is_memory())
		emit_sub_r32_m32(dst, reg, MABS(param.memory()));									// sub   reg,[param]
	else if (param.is_int_register())
		emit_sub_r32_r32(dst, reg, param.ireg());										// sub   reg,param
}


//-------------------------------------------------
//  emit_sub_m32_p32 - sub operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_sub_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() != 0 || param.immediate() != 0)
			emit_sub_m32_imm(dst, memref, param.immediate());								// sub   [dest],param
	}
	else
	{
		int reg = param.select_register(REG_EAX);
		emit_mov_r32_p32(dst, reg, param);										// mov   reg,param
		emit_sub_m32_r32(dst, memref, reg);											// sub   [dest],reg
	}
}


//-------------------------------------------------
//  emit_sbb_r32_p32 - sbb operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_sbb_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
		emit_sbb_r32_imm(dst, reg, param.immediate());										// sbb   reg,param
	else if (param.is_memory())
		emit_sbb_r32_m32(dst, reg, MABS(param.memory()));									// sbb   reg,[param]
	else if (param.is_int_register())
		emit_sbb_r32_r32(dst, reg, param.ireg());										// sbb   reg,param
}


//-------------------------------------------------
//  emit_sbb_m32_p32 - sbb operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_sbb_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
		emit_sbb_m32_imm(dst, memref, param.immediate());									// sbb   [dest],param
	else
	{
		int reg = param.select_register(REG_EAX);
		emit_mov_r32_p32_keepflags(dst, reg, param);								// mov   reg,param
		emit_sbb_m32_r32(dst, memref, reg);											// sbb   [dest],reg
	}
}


//-------------------------------------------------
//  emit_cmp_r32_p32 - cmp operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_cmp_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
		emit_cmp_r32_imm(dst, reg, param.immediate());										// cmp   reg,param
	else if (param.is_memory())
		emit_cmp_r32_m32(dst, reg, MABS(param.memory()));									// cmp   reg,[param]
	else if (param.is_int_register())
		emit_cmp_r32_r32(dst, reg, param.ireg());										// cmp   reg,param
}


//-------------------------------------------------
//  emit_cmp_m32_p32 - cmp operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_cmp_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
		emit_cmp_m32_imm(dst, memref, param.immediate());									// cmp   [dest],param
	else
	{
		int reg = param.select_register(REG_EAX);
		emit_mov_r32_p32(dst, reg, param);										// mov   reg,param
		emit_cmp_m32_r32(dst, memref, reg);											// cmp   [dest],reg
	}
}


//-------------------------------------------------
//  emit_and_r32_p32 - and operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_and_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff)
			;// skip
		else if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			emit_xor_r32_r32(dst, reg, reg);											// xor   reg,reg
		else
			emit_and_r32_imm(dst, reg, param.immediate());									// and   reg,param
	}
	else if (param.is_memory())
		emit_and_r32_m32(dst, reg, MABS(param.memory()));									// and   reg,[param]
	else if (param.is_int_register())
		emit_and_r32_r32(dst, reg, param.ireg());										// and   reg,param
}


//-------------------------------------------------
//  emit_and_m32_p32 - and operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_and_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff)
			;// skip
		else if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			emit_mov_m32_imm(dst, memref, 0);										// mov   [dest],0
		else
			emit_and_m32_imm(dst, memref, param.immediate());								// and   [dest],param
	}
	else
	{
		int reg = param.select_register(REG_EAX);
		emit_mov_r32_p32(dst, reg, param);										// mov   reg,param
		emit_and_m32_r32(dst, memref, reg);											// and   [dest],reg
	}
}


//-------------------------------------------------
//  emit_test_r32_p32 - test operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_test_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
		emit_test_r32_imm(dst, reg, param.immediate());										// test   reg,param
	else if (param.is_memory())
		emit_test_m32_r32(dst, MABS(param.memory()), reg);								// test   [param],reg
	else if (param.is_int_register())
		emit_test_r32_r32(dst, reg, param.ireg());										// test   reg,param
}


//-------------------------------------------------
//  emit_test_m32_p32 - test operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_test_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
		emit_test_m32_imm(dst, memref, param.immediate());								// test  [dest],param
	else if (param.is_memory())
	{
		emit_mov_r32_p32(dst, REG_EAX, param);									// mov   reg,param
		emit_test_m32_r32(dst, memref, REG_EAX);										// test  [dest],reg
	}
	else if (param.is_int_register())
		emit_test_m32_r32(dst, memref, param.ireg());								// test  [dest],param
}


//-------------------------------------------------
//  emit_or_r32_p32 - or operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_or_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff)
			emit_mov_r32_imm(dst, reg, ~0);												// mov  reg,-1
		else
			emit_or_r32_imm(dst, reg, param.immediate());									// or   reg,param
	}
	else if (param.is_memory())
		emit_or_r32_m32(dst, reg, MABS(param.memory()));									// or   reg,[param]
	else if (param.is_int_register())
		emit_or_r32_r32(dst, reg, param.ireg());										// or   reg,param
}


//-------------------------------------------------
//  emit_or_m32_p32 - or operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_or_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff)
			emit_mov_m32_imm(dst, memref, ~0);										// mov   [dest],-1
		else
			emit_or_m32_imm(dst, memref, param.immediate());								// or   [dest],param
	}
	else
	{
		int reg = param.select_register(REG_EAX);
		emit_mov_r32_p32(dst, reg, param);										// mov   reg,param
		emit_or_m32_r32(dst, memref, reg);											// or   [dest],reg
	}
}


//-------------------------------------------------
//  emit_xor_r32_p32 - xor operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_xor_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff)
			emit_not_r32(dst, reg);														// not   reg
		else
			emit_xor_r32_imm(dst, reg, param.immediate());									// xor   reg,param
	}
	else if (param.is_memory())
		emit_xor_r32_m32(dst, reg, MABS(param.memory()));									// xor   reg,[param]
	else if (param.is_int_register())
		emit_xor_r32_r32(dst, reg, param.ireg());										// xor   reg,param
}


//-------------------------------------------------
//  emit_xor_m32_p32 - xor operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_xor_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff)
			emit_not_m32(dst, memref);												// not   [dest]
		else
			emit_xor_m32_imm(dst, memref, param.immediate());								// xor   [dest],param
	}
	else
	{
		int reg = param.select_register(REG_EAX);
		emit_mov_r32_p32(dst, reg, param);										// mov   reg,param
		emit_xor_m32_r32(dst, memref, reg);											// xor   [dest],reg
	}
}


//-------------------------------------------------
//  emit_shl_r32_p32 - shl operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_shl_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else
			emit_shl_r32_imm(dst, reg, param.immediate());									// shl   reg,param
	}
	else
	{
		emit_mov_r32_p32(dst, REG_ECX, param);									// mov   ecx,param
		emit_shl_r32_cl(dst, reg);														// shl   reg,cl
	}
}


//-------------------------------------------------
//  emit_shl_m32_p32 - shl operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_shl_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else
			emit_shl_m32_imm(dst, memref, param.immediate());								// shl   [dest],param
	}
	else
	{
		emit_mov_r32_p32(dst, REG_ECX, param);									// mov   ecx,param
		emit_shl_m32_cl(dst, memref);												// shl   [dest],cl
	}
}


//-------------------------------------------------
//  emit_shr_r32_p32 - shr operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_shr_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else
			emit_shr_r32_imm(dst, reg, param.immediate());									// shr   reg,param
	}
	else
	{
		emit_mov_r32_p32(dst, REG_ECX, param);									// mov   ecx,param
		emit_shr_r32_cl(dst, reg);														// shr   reg,cl
	}
}


//-------------------------------------------------
//  emit_shr_m32_p32 - shr operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_shr_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else
			emit_shr_m32_imm(dst, memref, param.immediate());								// shr   [dest],param
	}
	else
	{
		emit_mov_r32_p32(dst, REG_ECX, param);									// mov   ecx,param
		emit_shr_m32_cl(dst, memref);												// shr   [dest],cl
	}
}


//-------------------------------------------------
//  emit_sar_r32_p32 - sar operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_sar_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else
			emit_sar_r32_imm(dst, reg, param.immediate());									// sar   reg,param
	}
	else
	{
		emit_mov_r32_p32(dst, REG_ECX, param);									// mov   ecx,param
		emit_sar_r32_cl(dst, reg);														// sar   reg,cl
	}
}


//-------------------------------------------------
//  emit_sar_m32_p32 - sar operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_sar_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else
			emit_sar_m32_imm(dst, memref, param.immediate());								// sar   [dest],param
	}
	else
	{
		emit_mov_r32_p32(dst, REG_ECX, param);									// mov   ecx,param
		emit_sar_m32_cl(dst, memref);												// sar   [dest],cl
	}
}


//-------------------------------------------------
//  emit_rol_r32_p32 - rol operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_rol_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else
			emit_rol_r32_imm(dst, reg, param.immediate());									// rol   reg,param
	}
	else
	{
		emit_mov_r32_p32(dst, REG_ECX, param);									// mov   ecx,param
		emit_rol_r32_cl(dst, reg);														// rol   reg,cl
	}
}


//-------------------------------------------------
//  emit_rol_m32_p32 - rol operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_rol_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else
			emit_rol_m32_imm(dst, memref, param.immediate());								// rol   [dest],param
	}
	else
	{
		emit_mov_r32_p32(dst, REG_ECX, param);									// mov   ecx,param
		emit_rol_m32_cl(dst, memref);												// rol   [dest],cl
	}
}


//-------------------------------------------------
//  emit_ror_r32_p32 - ror operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_ror_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else
			emit_ror_r32_imm(dst, reg, param.immediate());									// ror   reg,param
	}
	else
	{
		emit_mov_r32_p32(dst, REG_ECX, param);									// mov   ecx,param
		emit_ror_r32_cl(dst, reg);														// ror   reg,cl
	}
}


//-------------------------------------------------
//  emit_ror_m32_p32 - ror operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_ror_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else
			emit_ror_m32_imm(dst, memref, param.immediate());								// ror   [dest],param
	}
	else
	{
		emit_mov_r32_p32(dst, REG_ECX, param);									// mov   ecx,param
		emit_ror_m32_cl(dst, memref);												// ror   [dest],cl
	}
}


//-------------------------------------------------
//  emit_rcl_r32_p32 - rcl operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_rcl_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else
			emit_rcl_r32_imm(dst, reg, param.immediate());									// rcl   reg,param
	}
	else
	{
		emit_mov_r32_p32_keepflags(dst, REG_ECX, param);							// mov   ecx,param
		emit_rcl_r32_cl(dst, reg);														// rcl   reg,cl
	}
}


//-------------------------------------------------
//  emit_rcl_m32_p32 - rcl operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_rcl_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else
			emit_rcl_m32_imm(dst, memref, param.immediate());								// rcl   [dest],param
	}
	else
	{
		emit_mov_r32_p32_keepflags(dst, REG_ECX, param);							// mov   ecx,param
		emit_rcl_m32_cl(dst, memref);												// rcl   [dest],cl
	}
}


//-------------------------------------------------
//  emit_rcr_r32_p32 - rcr operation to a 32-bit
//  register from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_rcr_r32_p32(x86code *&dst, UINT8 reg, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else
			emit_rcr_r32_imm(dst, reg, param.immediate());									// rcr   reg,param
	}
	else
	{
		emit_mov_r32_p32_keepflags(dst, REG_ECX, param);							// mov   ecx,param
		emit_rcr_r32_cl(dst, reg);														// rcr   reg,cl
	}
}


//-------------------------------------------------
//  emit_rcr_m32_p32 - rcr operation to a 32-bit
//  memory location from a 32-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_rcr_m32_p32(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else
			emit_rcr_m32_imm(dst, memref, param.immediate());								// rcr   [dest],param
	}
	else
	{
		emit_mov_r32_p32_keepflags(dst, REG_ECX, param);							// mov   ecx,param
		emit_rcr_m32_cl(dst, memref);												// rcr   [dest],cl
	}
}



//**************************************************************************
//  EMITTERS FOR 64-BIT OPERATIONS WITH PARAMETERS
//**************************************************************************

//-------------------------------------------------
//  emit_mov_r64_p64 - move a 64-bit parameter
//  into a pair of registers
//-------------------------------------------------

void drcbe_x86::emit_mov_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param)
{
	if (param.is_immediate())
	{
		if (reglo == REG_NONE)
			;
		else if ((UINT32)param.immediate() == 0)
			emit_xor_r32_r32(dst, reglo, reglo);										// xor   reglo,reglo
		else
			emit_mov_r32_imm(dst, reglo, param.immediate());									// mov   reglo,param
		if (reghi == REG_NONE)
			;
		else if ((UINT32)(param.immediate() >> 32) == 0)
			emit_xor_r32_r32(dst, reghi, reghi);										// xor   reghi,reghi
		else
			emit_mov_r32_imm(dst, reghi, param.immediate() >> 32);							// mov   reghi,param >> 32
	}
	else if (param.is_memory())
	{
		int skip_lower = can_skip_lower_load(dst, (UINT32 *)((FPTR)param.memory()), reglo);
		int skip_upper = can_skip_upper_load(dst, (UINT32 *)((FPTR)param.memory(4)), reghi);
		if (reglo != REG_NONE && !skip_lower)
			emit_mov_r32_m32(dst, reglo, MABS(param.memory()));							// mov   reglo,[param]
		if (reghi != REG_NONE && !skip_upper)
			emit_mov_r32_m32(dst, reghi, MABS(param.memory(4)));						// mov   reghi,[param+4]
	}
	else if (param.is_int_register())
	{
		int skip_upper = can_skip_upper_load(dst, m_reghi[param.ireg()], reghi);
		if (reglo != REG_NONE && reglo != param.ireg())
			emit_mov_r32_r32(dst, reglo, param.ireg());									// mov   reglo,param
		if (reghi != REG_NONE && !skip_upper)
			emit_mov_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()]));				// mov   reghi,reghi[param]
	}
}


//-------------------------------------------------
//  emit_mov_r64_p64_keepflags - move a 64-bit
//  parameter into a pair of registers without
//  affecting any flags
//-------------------------------------------------

void drcbe_x86::emit_mov_r64_p64_keepflags(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param)
{
	if (param.is_immediate())
	{
		if (reglo != REG_NONE)
			emit_mov_r32_imm(dst, reglo, param.immediate());									// mov   reglo,param
		if (reghi != REG_NONE)
			emit_mov_r32_imm(dst, reghi, param.immediate() >> 32);							// mov   reghi,param >> 32
	}
	else if (param.is_memory())
	{
		int skip_lower = can_skip_lower_load(dst, (UINT32 *)((FPTR)param.memory()), reglo);
		int skip_upper = can_skip_upper_load(dst, (UINT32 *)((FPTR)param.memory(4)), reghi);
		if (reglo != REG_NONE && !skip_lower)
			emit_mov_r32_m32(dst, reglo, MABS(param.memory()));							// mov   reglo,[param]
		if (reghi != REG_NONE && !skip_upper)
			emit_mov_r32_m32(dst, reghi, MABS(param.memory(4)));						// mov   reghi,[param+4]
	}
	else if (param.is_int_register())
	{
		int skip_upper = can_skip_upper_load(dst, m_reghi[param.ireg()], reghi);
		if (reglo != REG_NONE && reglo != param.ireg())
			emit_mov_r32_r32(dst, reglo, param.ireg());									// mov   reglo,param
		if (reghi != REG_NONE && !skip_upper)
			emit_mov_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()]));				// mov   reghi,reghi[param]
	}
}


//-------------------------------------------------
//  emit_mov_m64_p64 - move a 64-bit parameter
//  into a memory location
//-------------------------------------------------

void drcbe_x86::emit_mov_m64_p64(x86code *&dst, x86_memref memref, const be_parameter &param)
{
	if (param.is_immediate())
	{
		emit_mov_m32_imm(dst, memref + 0, param.immediate());								// mov   [mem],param
		emit_mov_m32_imm(dst, memref + 4, param.immediate() >> 32);						// mov   [mem],param >> 32
	}
	else if (param.is_memory())
	{
		int skip_lower = can_skip_lower_load(dst, (UINT32 *)((FPTR)param.memory()), REG_EAX);
		if (!skip_lower)
			emit_mov_r32_m32(dst, REG_EAX, MABS(param.memory()));							// mov   eax,[param]
		emit_mov_m32_r32(dst, memref + 0, REG_EAX);									// mov   [mem],eax
		emit_mov_r32_m32(dst, REG_EAX, MABS(param.memory(4)));							// mov   eax,[param+4]
		emit_mov_m32_r32(dst, memref + 4, REG_EAX);									// mov   [mem+4],eax
	}
	else if (param.is_int_register())
	{
		emit_mov_m32_r32(dst, memref + 0, param.ireg());								// mov   [mem],param
		emit_mov_r32_m32(dst, REG_EAX, MABS(m_reghi[param.ireg()]));				// mov   eax,[param.hi]
		emit_mov_m32_r32(dst, memref + 4, REG_EAX);									// mov   [mem+4],eax
	}
}


//-------------------------------------------------
//  emit_mov_p64_r64 - move a pair of registers
//  into a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_mov_p64_r64(x86code *&dst, const be_parameter &param, UINT8 reglo, UINT8 reghi)
{
	assert(!param.is_immediate());
	if (param.is_memory())
	{
		emit_mov_m32_r32(dst, MABS(param.memory()), reglo);								// mov   [param],reglo
		emit_mov_m32_r32(dst, MABS(param.memory(4)), reghi);							// mov   [param+4],reghi
	}
	else if (param.is_int_register())
	{
		if (reglo != param.ireg())
			emit_mov_r32_r32(dst, param.ireg(), reglo);									// mov   param,reglo
		emit_mov_m32_r32(dst, MABS(m_reghi[param.ireg()]), reghi);					// mov   reghi[param],reghi
	}
	set_last_lower_reg(dst, param, reglo);
	set_last_upper_reg(dst, param, reghi);
}


//-------------------------------------------------
//  emit_add_r64_p64 - add operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_add_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_memory())
	{
		emit_add_r32_m32(dst, reglo, MABS(param.memory()));								// add   reglo,[param]
		if (saveflags) emit_pushf(dst);													// pushf
		emit_adc_r32_m32(dst, reghi, MABS(param.memory(4)));							// adc   reghi,[param]
	}
	else if (param.is_immediate())
	{
		emit_add_r32_imm(dst, reglo, param.immediate());										// add   reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_adc_r32_imm(dst, reghi, param.immediate() >> 32);								// adc   reghi,param >> 32
	}
	else if (param.is_int_register())
	{
		emit_add_r32_r32(dst, reglo, param.ireg());										// add   reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_adc_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()]));					// adc   reghi,reghi[param]
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_add_m64_p64 - add operation to a 64-bit
//  memory location from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_add_m64_p64(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_immediate())
	{
		emit_add_m32_imm(dst, memref, param.immediate());									// add   [dest],param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_adc_m32_imm(dst, memref + 4, param.immediate() >> 32);						// adc   [dest+4],param >> 32
	}
	else
	{
		int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX;
		emit_mov_r64_p64(dst, reglo, REG_EDX, param);							// mov   edx:reglo,param
		emit_add_m32_r32(dst, memref, reglo);										// add   [dest],reglo
		if (saveflags) emit_pushf(dst);													// pushf
		emit_adc_m32_r32(dst, memref + 4, REG_EDX);									// adc   [dest+4],edx
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_adc_r64_p64 - adc operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_adc_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_memory())
	{
		emit_adc_r32_m32(dst, reglo, MABS(param.memory()));								// adc   reglo,[param]
		if (saveflags) emit_pushf(dst);													// pushf
		emit_adc_r32_m32(dst, reghi, MABS(param.memory(4)));							// adc   reghi,[param]
	}
	else if (param.is_immediate())
	{
		emit_adc_r32_imm(dst, reglo, param.immediate());										// adc   reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_adc_r32_imm(dst, reghi, param.immediate() >> 32);								// adc   reghi,param >> 32
	}
	else if (param.is_int_register())
	{
		emit_adc_r32_r32(dst, reglo, param.ireg());										// adc   reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_adc_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()]));					// adc   reghi,reghi[param]
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_adc_m64_p64 - adc operation to a 64-bit
//  memory location from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_adc_m64_p64(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_immediate())
	{
		emit_adc_m32_imm(dst, memref, param.immediate());									// adc   [dest],param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_adc_m32_imm(dst, memref + 4, param.immediate() >> 32);						// adc   [dest+4],param >> 32
	}
	else
	{
		int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX;
		emit_mov_r64_p64_keepflags(dst, reglo, REG_EDX, param);					// mov   edx:reglo,param
		emit_adc_m32_r32(dst, memref, reglo);										// adc   [dest],reglo
		if (saveflags) emit_pushf(dst);													// pushf
		emit_adc_m32_r32(dst, memref + 4, REG_EDX);									// adc   [dest+4],edx
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_sub_r64_p64 - sub operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_sub_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_memory())
	{
		emit_sub_r32_m32(dst, reglo, MABS(param.memory()));								// sub   reglo,[param]
		if (saveflags) emit_pushf(dst);													// pushf
		emit_sbb_r32_m32(dst, reghi, MABS(param.memory(4)));							// sbb   reghi,[param]
	}
	else if (param.is_immediate())
	{
		emit_sub_r32_imm(dst, reglo, param.immediate());										// sub   reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_sbb_r32_imm(dst, reghi, param.immediate() >> 32);								// sbb   reghi,param >> 32
	}
	else if (param.is_int_register())
	{
		emit_sub_r32_r32(dst, reglo, param.ireg());										// sub   reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_sbb_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()]));					// sbb   reghi,reghi[param]
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_sub_m64_p64 - sub operation to a 64-bit
//  memory location from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_sub_m64_p64(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_immediate())
	{
		emit_sub_m32_imm(dst, memref, param.immediate());									// sub   [dest],param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_sbb_m32_imm(dst, memref + 4, param.immediate() >> 32);						// sbb   [dest+4],param >> 32
	}
	else
	{
		int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX;
		emit_mov_r64_p64(dst, reglo, REG_EDX, param);							// mov   edx:reglo,param
		emit_sub_m32_r32(dst, memref, reglo);										// sub   [dest],reglo
		if (saveflags) emit_pushf(dst);													// pushf
		emit_sbb_m32_r32(dst, memref + 4, REG_EDX);									// sbb   [dest+4],edx
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_sbb_r64_p64 - sbb operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_sbb_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_memory())
	{
		emit_sbb_r32_m32(dst, reglo, MABS(param.memory()));								// sbb   reglo,[param]
		if (saveflags) emit_pushf(dst);													// pushf
		emit_sbb_r32_m32(dst, reghi, MABS(param.memory(4)));							// sbb   reghi,[param]
	}
	else if (param.is_immediate())
	{
		emit_sbb_r32_imm(dst, reglo, param.immediate());										// sbb   reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_sbb_r32_imm(dst, reghi, param.immediate() >> 32);								// sbb   reghi,param >> 32
	}
	else if (param.is_int_register())
	{
		emit_sbb_r32_r32(dst, reglo, param.ireg());										// sbb   reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_sbb_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()]));					// sbb   reghi,reghi[param]
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_sbb_m64_p64 - sbb operation to a 64-bit
//  memory location from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_sbb_m64_p64(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_immediate())
	{
		emit_sbb_m32_imm(dst, memref, param.immediate());									// sbb   [dest],param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_sbb_m32_imm(dst, memref + 4, param.immediate() >> 32);						// sbb   [dest+4],param >> 32
	}
	else
	{
		int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX;
		emit_mov_r64_p64_keepflags(dst, reglo, REG_EDX, param);					// mov   edx:reglo,param
		emit_sbb_m32_r32(dst, memref, reglo);										// sbb   [dest],reglo
		if (saveflags) emit_pushf(dst);													// pushf
		emit_sbb_m32_r32(dst, memref + 4, REG_EDX);									// sbb   [dest+4],edx
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_cmp_r64_p64 - sub operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_cmp_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = (inst.flags() != FLAG_Z && (inst.flags() & FLAG_Z) != 0);
	if (param.is_memory())
	{
		emit_sub_r32_m32(dst, reglo, MABS(param.memory()));								// sub   reglo,[param]
		if (saveflags) emit_pushf(dst);													// pushf
		emit_sbb_r32_m32(dst, reghi, MABS(param.memory(4)));							// sbb   reghi,[param]
	}
	else if (param.is_immediate())
	{
		emit_sub_r32_imm(dst, reglo, param.immediate());										// sub   reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_sbb_r32_imm(dst, reghi, param.immediate() >> 32);								// sbb   reghi,param >> 32
	}
	else if (param.is_int_register())
	{
		emit_sub_r32_r32(dst, reglo, param.ireg());										// sub   reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_sbb_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()]));					// sbb   reghi,reghi[param]
	}
	if (inst.flags() == FLAG_Z)
		emit_or_r32_r32(dst, reghi, reglo);												// or    reghi,reglo
	else if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_and_r64_p64 - and operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_and_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_memory())
	{
		emit_and_r32_m32(dst, reglo, MABS(param.memory()));								// and   reglo,[param]
		if (saveflags) emit_pushf(dst);													// pushf
		emit_and_r32_m32(dst, reghi, MABS(param.memory(4)));							// and   reghi,[param]
	}
	else if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff)
			;// skip
		else if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			emit_xor_r32_r32(dst, reglo, reglo);										// xor   reglo,reglo
		else
			emit_and_r32_imm(dst, reglo, param.immediate());									// and   reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0xffffffff)
			;// skip
		else if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0)
			emit_xor_r32_r32(dst, reghi, reghi);										// xor   reghi,reghi
		else
			emit_and_r32_imm(dst, reghi, param.immediate() >> 32);							// and   reghi,param >> 32
	}
	else if (param.is_int_register())
	{
		emit_and_r32_r32(dst, reglo, param.ireg());										// and   reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_and_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()]));					// and   reghi,reghi[param]
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_and_m64_p64 - and operation to a 64-bit
//  memory location from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_and_m64_p64(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff)
			;// skip
		else if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			emit_mov_m32_imm(dst, memref, 0);										// mov   [dest],0
		else
			emit_and_m32_imm(dst, memref, param.immediate());								// and   [dest],param
		if (saveflags) emit_pushf(dst);													// pushf
		if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0xffffffff)
			;// skip
		else if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0)
			emit_mov_m32_imm(dst, memref + 4, 0);									// mov   [dest+4],0
		else
			emit_and_m32_imm(dst, memref + 4, param.immediate() >> 32);					// and   [dest+4],param >> 32
	}
	else
	{
		int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX;
		emit_mov_r64_p64(dst, reglo, REG_EDX, param);							// mov   edx:reglo,param
		emit_and_m32_r32(dst, memref, reglo);										// and   [dest],reglo
		if (saveflags) emit_pushf(dst);													// pushf
		emit_and_m32_r32(dst, memref + 4, REG_EDX);									// and   [dest+4],edx
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_test_r64_p64 - test operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_test_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_memory())
	{
		emit_test_m32_r32(dst, MABS(param.memory()), reglo);								// test  [param],reglo
		if (saveflags) emit_pushf(dst);													// pushf
		emit_test_m32_r32(dst, MABS(param.memory(4)), reghi);							// test  [param],reghi
	}
	else if (param.is_immediate())
	{
		emit_test_r32_imm(dst, reglo, param.immediate());									// test  reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_test_r32_imm(dst, reghi, param.immediate() >> 32);								// test  reghi,param >> 32
	}
	else if (param.is_int_register())
	{
		emit_test_r32_r32(dst, reglo, param.ireg());									// test  reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_test_m32_r32(dst, MABS(m_reghi[param.ireg()]), reghi);				// test  reghi[param],reghi
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_test_m64_p64 - test operation to a 64-bit
//  memory location from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_test_m64_p64(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_immediate())
	{
		emit_test_m32_imm(dst, memref, param.immediate());								// test   [dest],param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_test_m32_imm(dst, memref + 4, param.immediate() >> 32);						// test   [dest+4],param >> 32
	}
	else
	{
		int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX;
		emit_mov_r64_p64(dst, reglo, REG_EDX, param);							// mov   edx:reglo,param
		emit_test_m32_r32(dst, memref, reglo);										// test  [dest],reglo
		if (saveflags) emit_pushf(dst);													// pushf
		emit_test_m32_r32(dst, memref + 4, REG_EDX);									// test  [dest+4],edx
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_or_r64_p64 - or operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_or_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_memory())
	{
		emit_or_r32_m32(dst, reglo, MABS(param.memory()));								// or    reglo,[param]
		if (saveflags) emit_pushf(dst);													// pushf
		emit_or_r32_m32(dst, reghi, MABS(param.memory(4)));							// or    reghi,[param]
	}
	else if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff)
			emit_mov_r32_imm(dst, reglo, ~0);											// mov   reglo,-1
		else
			emit_or_r32_imm(dst, reglo, param.immediate());									// or    reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0)
			;// skip
		else if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0xffffffff)
			emit_mov_r32_imm(dst, reghi, ~0);											// mov   reghi,-1
		else
			emit_or_r32_imm(dst, reghi, param.immediate() >> 32);							// or    reghi,param >> 32
	}
	else if (param.is_int_register())
	{
		emit_or_r32_r32(dst, reglo, param.ireg());										// or    reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_or_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()]));					// or    reghi,reghi[param]
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_or_m64_p64 - or operation to a 64-bit
//  memory location from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_or_m64_p64(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff)
			emit_mov_m32_imm(dst, memref, ~0);										// mov   [dest],-1
		else
			emit_or_m32_imm(dst, memref, param.immediate());								// or    [dest],param
		if (saveflags) emit_pushf(dst);													// pushf
		if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0)
			;// skip
		else if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0xffffffff)
			emit_mov_m32_imm(dst, memref + 4, ~0);									// mov   [dest+4],-1
		else
			emit_or_m32_imm(dst, memref + 4, param.immediate() >> 32);					// or    [dest+4],param >> 32
	}
	else
	{
		int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX;
		emit_mov_r64_p64(dst, reglo, REG_EDX, param);							// mov   edx:reglo,param
		emit_or_m32_r32(dst, memref, reglo);											// or    [dest],reglo
		if (saveflags) emit_pushf(dst);													// pushf
		emit_or_m32_r32(dst, memref + 4, REG_EDX);									// or    [dest+4],edx
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_xor_r64_p64 - xor operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_xor_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_memory())
	{
		emit_xor_r32_m32(dst, reglo, MABS(param.memory()));								// xor   reglo,[param]
		if (saveflags) emit_pushf(dst);													// pushf
		emit_xor_r32_m32(dst, reghi, MABS(param.memory(4)));							// xor   reghi,[param]
	}
	else if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff)
			emit_not_r32(dst, reglo);													// not   reglo
		else
			emit_xor_r32_imm(dst, reglo, param.immediate());									// xor   reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0)
			;// skip
		else if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0xffffffff)
			emit_not_r32(dst, reghi);													// not   reghi
		else
			emit_xor_r32_imm(dst, reghi, param.immediate() >> 32);							// xor   reghi,param >> 32
	}
	else if (param.is_int_register())
	{
		emit_xor_r32_r32(dst, reglo, param.ireg());										// xor   reglo,param
		if (saveflags) emit_pushf(dst);													// pushf
		emit_xor_r32_m32(dst, reghi, MABS(m_reghi[param.ireg()]));					// xor   reghi,reghi[param]
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_xor_m64_p64 - xor operation to a 64-bit
//  memory location from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_xor_m64_p64(x86code *&dst, x86_memref memref, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_immediate())
	{
		if (inst.flags() == 0 && (UINT32)param.immediate() == 0)
			;// skip
		else if (inst.flags() == 0 && (UINT32)param.immediate() == 0xffffffff)
			emit_not_m32(dst, memref);												// not   [dest]
		else
			emit_xor_m32_imm(dst, memref, param.immediate());								// xor   [dest],param
		if (saveflags) emit_pushf(dst);													// pushf
		if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0)
			;// skip
		else if (inst.flags() == 0 && (UINT32)(param.immediate() >> 32) == 0xffffffff)
			emit_not_m32(dst, memref + 4);											// not   [dest+4]
		else
			emit_xor_m32_imm(dst, memref + 4, param.immediate() >> 32);					// xor   [dest+4],param >> 32
	}
	else
	{
		int reglo = (param.is_int_register()) ? param.ireg() : REG_EAX;
		emit_mov_r64_p64(dst, reglo, REG_EDX, param);							// mov   edx:reglo,param
		emit_xor_m32_r32(dst, memref, reglo);										// xor   [dest],reglo
		if (saveflags) emit_pushf(dst);													// pushf
		emit_xor_m32_r32(dst, memref + 4, REG_EDX);									// xor   [dest+4],edx
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_shl_r64_p64 - shl operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_shl_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = (inst.flags() != 0);
	if (param.is_immediate())
	{
		int count = param.immediate() & 63;
		if (inst.flags() == 0 && count == 0)
			;// skip
		else
		{
			while (count >= 32)
			{
				if (inst.flags() != 0)
				{
					emit_shld_r32_r32_imm(dst, reghi, reglo, 31);						// shld  reghi,reglo,31
					emit_shl_r32_imm(dst, reglo, 31);									// shl   reglo,31
					count -= 31;
				}
				else
				{
					emit_mov_r32_r32(dst, reghi, reglo);								// mov   reghi,reglo
					emit_xor_r32_r32(dst, reglo, reglo);								// xor   reglo,reglo
					count -= 32;
				}
			}
			if (inst.flags() != 0 || count > 0)
			{
				emit_shld_r32_r32_imm(dst, reghi, reglo, count);						// shld  reghi,reglo,count
				if (saveflags) emit_pushf(dst);											// pushf
				emit_shl_r32_imm(dst, reglo, count);									// shl   reglo,count
			}
		}
	}
	else
	{
		emit_link skip1, skip2;
		emit_mov_r32_p32(dst, REG_ECX, param);									// mov   ecx,param
		emit_test_r32_imm(dst, REG_ECX, 0x20);											// test  ecx,0x20
		emit_jcc_short_link(dst, x86emit::COND_Z, skip1);								// jz    skip1
		if (inst.flags() != 0)
		{
			emit_sub_r32_imm(dst, REG_ECX, 31);											// sub   ecx,31
			emit_shld_r32_r32_imm(dst, reghi, reglo, 31);								// shld  reghi,reglo,31
			emit_shl_r32_imm(dst, reglo, 31);											// shl   reglo,31
			emit_test_r32_imm(dst, REG_ECX, 0x20);										// test  ecx,0x20
			emit_jcc_short_link(dst, x86emit::COND_Z, skip2);							// jz    skip2
			emit_sub_r32_imm(dst, REG_ECX, 31);											// sub   ecx,31
			emit_shld_r32_r32_imm(dst, reghi, reglo, 31);								// shld  reghi,reglo,31
			emit_shl_r32_imm(dst, reglo, 31);											// shl   reglo,31
			track_resolve_link(dst, skip2);											// skip2:
		}
		else
		{
			emit_mov_r32_r32(dst, reghi, reglo);										// mov   reghi,reglo
			emit_xor_r32_r32(dst, reglo, reglo);										// xor   reglo,reglo
		}
		track_resolve_link(dst, skip1);												// skip1:
		emit_shld_r32_r32_cl(dst, reghi, reglo);										// shld  reghi,reglo,cl
		if (saveflags) emit_pushf(dst);													// pushf
		emit_shl_r32_cl(dst, reglo);													// shl   reglo,cl
	}
	if (saveflags)
		emit_combine_z_shl_flags(dst);
}


//-------------------------------------------------
//  emit_shr_r64_p64 - shr operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_shr_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_immediate())
	{
		int count = param.immediate() & 63;
		if (inst.flags() == 0 && count == 0)
			;// skip
		else
		{
			while (count >= 32)
			{
				if (inst.flags() != 0)
				{
					emit_shrd_r32_r32_imm(dst, reglo, reghi, 31);						// shrd  reglo,reghi,31
					emit_shr_r32_imm(dst, reghi, 31);									// shr   reghi,31
					count -= 31;
				}
				else
				{
					emit_mov_r32_r32(dst, reglo, reghi);								// mov   reglo,reghi
					emit_xor_r32_r32(dst, reghi, reghi);								// xor   reghi,reghi
					count -= 32;
				}
			}
			if (inst.flags() != 0 || count > 0)
			{
				emit_shrd_r32_r32_imm(dst, reglo, reghi, count);						// shrd  reglo,reghi,count
				if (saveflags) emit_pushf(dst);											// pushf
				emit_shr_r32_imm(dst, reghi, count);									// shr   reghi,count
			}
		}
	}
	else
	{
		emit_link skip1, skip2;
		emit_mov_r32_p32(dst, REG_ECX, param);									// mov   ecx,param
		emit_test_r32_imm(dst, REG_ECX, 0x20);											// test  ecx,0x20
		emit_jcc_short_link(dst, x86emit::COND_Z, skip1);								// jz    skip1
		if (inst.flags() != 0)
		{
			emit_sub_r32_imm(dst, REG_ECX, 31);											// sub   ecx,31
			emit_shrd_r32_r32_imm(dst, reglo, reghi, 31);								// shrd  reglo,reghi,31
			emit_shr_r32_imm(dst, reghi, 31);											// shr   reghi,31
			emit_test_r32_imm(dst, REG_ECX, 0x20);										// test  ecx,0x20
			emit_jcc_short_link(dst, x86emit::COND_Z, skip2);							// jz    skip2
			emit_sub_r32_imm(dst, REG_ECX, 31);											// sub   ecx,31
			emit_shrd_r32_r32_imm(dst, reglo, reghi, 31);								// shrd  reglo,reghi,31
			emit_shr_r32_imm(dst, reghi, 31);											// shr   reghi,31
			track_resolve_link(dst, skip2);											// skip2:
		}
		else
		{
			emit_mov_r32_r32(dst, reglo, reghi);										// mov   reglo,reghi
			emit_xor_r32_r32(dst, reghi, reghi);										// xor   reghi,reghi
		}
		track_resolve_link(dst, skip1);												// skip1:
		emit_shrd_r32_r32_cl(dst, reglo, reghi);										// shrd  reglo,reghi,cl
		if (saveflags) emit_pushf(dst);													// pushf
		emit_shr_r32_cl(dst, reghi);													// shr   reghi,cl
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_sar_r64_p64 - sar operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_sar_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_immediate())
	{
		int count = param.immediate() & 63;
		if (inst.flags() == 0 && count == 0)
			;// skip
		else
		{
			while (count >= 32)
			{
				if (inst.flags() != 0)
				{
					emit_shrd_r32_r32_imm(dst, reglo, reghi, 31);						// shrd  reglo,reghi,31
					emit_sar_r32_imm(dst, reghi, 31);									// sar   reghi,31
					count -= 31;
				}
				else
				{
					emit_mov_r32_r32(dst, reglo, reghi);								// mov   reglo,reghi
					emit_sar_r32_imm(dst, reghi, 31);									// sar   reghi,31
					count -= 32;
				}
			}
			if (inst.flags() != 0 || count > 0)
			{
				emit_shrd_r32_r32_imm(dst, reglo, reghi, count);						// shrd  reglo,reghi,count
				if (saveflags) emit_pushf(dst);											// pushf
				emit_sar_r32_imm(dst, reghi, count);									// sar   reghi,count
			}
		}
	}
	else
	{
		emit_link skip1, skip2;
		emit_mov_r32_p32(dst, REG_ECX, param);									// mov   ecx,param
		emit_test_r32_imm(dst, REG_ECX, 0x20);											// test  ecx,0x20
		emit_jcc_short_link(dst, x86emit::COND_Z, skip1);								// jz    skip1
		if (inst.flags() != 0)
		{
			emit_sub_r32_imm(dst, REG_ECX, 31);											// sub   ecx,31
			emit_shrd_r32_r32_imm(dst, reglo, reghi, 31);								// shrd  reglo,reghi,31
			emit_sar_r32_imm(dst, reghi, 31);											// sar   reghi,31
			emit_test_r32_imm(dst, REG_ECX, 0x20);										// test  ecx,0x20
			emit_jcc_short_link(dst, x86emit::COND_Z, skip2);							// jz    skip
			emit_sub_r32_imm(dst, REG_ECX, 31);											// sub   ecx,31
			emit_shrd_r32_r32_imm(dst, reglo, reghi, 31);								// shrd  reglo,reghi,31
			emit_sar_r32_imm(dst, reghi, 31);											// sar   reghi,31
			track_resolve_link(dst, skip2);											// skip2:
		}
		else
		{
			emit_mov_r32_r32(dst, reglo, reghi);										// mov   reglo,reghi
			emit_sar_r32_imm(dst, reghi, 31);											// sar   reghi,31
		}
		track_resolve_link(dst, skip1);												// skip1:
		emit_shrd_r32_r32_cl(dst, reglo, reghi);										// shrd  reglo,reghi,cl
		if (saveflags) emit_pushf(dst);													// pushf
		emit_sar_r32_cl(dst, reghi);													// sar   reghi,cl
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_rol_r64_p64 - rol operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_rol_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_immediate())
	{
		int count = param.immediate() & 63;
		if (inst.flags() == 0 && count == 0)
			;// skip
		else
		{
			while (count >= 32)
			{
				if (inst.flags() != 0)
				{
					emit_mov_r32_r32(dst, REG_ECX, reglo);								// mov   ecx,reglo
					emit_shld_r32_r32_imm(dst, reglo, reghi, 31);						// shld  reglo,reghi,31
					emit_shld_r32_r32_imm(dst, reghi, REG_ECX, 31);						// shld  reghi,ecx,31
					count -= 31;
				}
				else
				{
					emit_xchg_r32_r32(dst, reghi, reglo);								// xchg  reghi,reglo
					count -= 32;
				}
			}
			if (inst.flags() != 0 || count > 0)
			{
				emit_mov_r32_r32(dst, REG_ECX, reglo);									// mov   ecx,reglo
				emit_shld_r32_r32_imm(dst, reglo, reghi, count);						// shld  reglo,reghi,count
				if (saveflags) emit_pushf(dst);											// pushf
				emit_shld_r32_r32_imm(dst, reghi, REG_ECX, count);						// shld  reghi,ecx,count
			}
		}
	}
	else
	{
		emit_link skip1, skip2;
		int tempreg = REG_EBX;
		emit_mov_m32_r32(dst, MBD(REG_ESP, -8), tempreg);                               // mov   [esp-8],ebx
		emit_mov_r32_p32(dst, REG_ECX, param);									// mov   ecx,param
		emit_test_r32_imm(dst, REG_ECX, 0x20);											// test  ecx,0x20
		emit_jcc_short_link(dst, x86emit::COND_Z, skip1);								// jz    skip1
		if (inst.flags() != 0)
		{
			emit_sub_r32_imm(dst, REG_ECX, 31);											// sub   ecx,31
			emit_mov_r32_r32(dst, tempreg, reglo);										// mov   ebx,reglo
			emit_shld_r32_r32_imm(dst, reglo, reghi, 31);								// shld  reglo,reghi,31
			emit_shld_r32_r32_imm(dst, reghi, tempreg, 31);								// shld  reghi,ebx,31
			emit_test_r32_imm(dst, REG_ECX, 0x20);										// test  ecx,0x20
			emit_jcc_short_link(dst, x86emit::COND_Z, skip2);							// jz    skip2
			emit_sub_r32_imm(dst, REG_ECX, 31);											// sub   ecx,31
			emit_mov_r32_r32(dst, tempreg, reglo);										// mov   ebx,reglo
			emit_shld_r32_r32_imm(dst, reglo, reghi, 31);								// shld  reglo,reghi,31
			emit_shld_r32_r32_imm(dst, reghi, tempreg, 31);								// shld  reghi,ebx,31
			track_resolve_link(dst, skip2);											// skip2:
		}
		else
			emit_xchg_r32_r32(dst, reghi, reglo);										// xchg  reghi,reglo
		track_resolve_link(dst, skip1);												// skip1:
		emit_mov_r32_r32(dst, tempreg, reglo);											// mov   ebx,reglo
		emit_shld_r32_r32_cl(dst, reglo, reghi);										// shld  reglo,reghi,cl
		if (saveflags) emit_pushf(dst);													// pushf
		emit_shld_r32_r32_cl(dst, reghi, tempreg);										// shld  reghi,ebx,cl
		emit_mov_r32_m32(dst, tempreg, MBD(REG_ESP, saveflags ? -4 : -8));              // mov   ebx,[esp-8]
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_ror_r64_p64 - ror operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_ror_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	if (param.is_immediate())
	{
		int count = param.immediate() & 63;
		if (inst.flags() == 0 && count == 0)
			;// skip
		else
		{
			while (count >= 32)
			{
				if (inst.flags() != 0)
				{
					emit_mov_r32_r32(dst, REG_ECX, reglo);								// mov   ecx,reglo
					emit_shrd_r32_r32_imm(dst, reglo, reghi, 31);						// shrd  reglo,reghi,31
					emit_shrd_r32_r32_imm(dst, reghi, REG_ECX, 31);						// shrd  reghi,ecx,31
					count -= 31;
				}
				else
				{
					emit_xchg_r32_r32(dst, reghi, reglo);								// xchg  reghi,reglo
					count -= 32;
				}
			}
			if (inst.flags() != 0 || count > 0)
			{
				emit_mov_r32_r32(dst, REG_ECX, reglo);									// mov   ecx,reglo
				emit_shrd_r32_r32_imm(dst, reglo, reghi, count);						// shrd  reglo,reghi,count
				if (saveflags) emit_pushf(dst);											// pushf
				emit_shrd_r32_r32_imm(dst, reghi, REG_ECX, count);						// shrd  reghi,ecx,count
			}
		}
	}
	else
	{
		emit_link skip1, skip2;
		int tempreg = REG_EBX;
		emit_mov_m32_r32(dst, MBD(REG_ESP, -8), tempreg);                               // mov   [esp-8],ebx
		emit_mov_r32_p32(dst, REG_ECX, param);									// mov   ecx,param
		emit_test_r32_imm(dst, REG_ECX, 0x20);											// test  ecx,0x20
		emit_jcc_short_link(dst, x86emit::COND_Z, skip1);								// jz    skip1
		if (inst.flags() != 0)
		{
			emit_sub_r32_imm(dst, REG_ECX, 31);											// sub   ecx,31
			emit_mov_r32_r32(dst, tempreg, reglo);										// mov   ebx,reglo
			emit_shrd_r32_r32_imm(dst, reglo, reghi, 31);								// shrd  reglo,reghi,31
			emit_shrd_r32_r32_imm(dst, reghi, tempreg, 31);								// shrd  reghi,ebx,31
			emit_test_r32_imm(dst, REG_ECX, 0x20);										// test  ecx,0x20
			emit_jcc_short_link(dst, x86emit::COND_Z, skip2);							// jz    skip2
			emit_sub_r32_imm(dst, REG_ECX, 31);											// sub   ecx,31
			emit_mov_r32_r32(dst, tempreg, reglo);										// mov   ebx,reglo
			emit_shrd_r32_r32_imm(dst, reglo, reghi, 31);								// shrd  reglo,reghi,31
			emit_shrd_r32_r32_imm(dst, reghi, tempreg, 31);								// shrd  reghi,ebx,31
			track_resolve_link(dst, skip2);											// skip2:
		}
		else
			emit_xchg_r32_r32(dst, reghi, reglo);										// xchg  reghi,reglo
		track_resolve_link(dst, skip1);												// skip1:
		emit_mov_r32_r32(dst, tempreg, reglo);											// mov   ebx,reglo
		emit_shrd_r32_r32_cl(dst, reglo, reghi);										// shrd  reglo,reghi,cl
		if (saveflags) emit_pushf(dst);													// pushf
		emit_shrd_r32_r32_cl(dst, reghi, tempreg);										// shrd  reghi,ebx,cl
		emit_mov_r32_m32(dst, tempreg, MBD(REG_ESP, saveflags ? -4 : -8));              // mov   ebx,[esp-8]
	}
	if (saveflags)
		emit_combine_z_flags(dst);
}


//-------------------------------------------------
//  emit_rcl_r64_p64 - rcl operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_rcl_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = ((inst.flags() & FLAG_Z) != 0);
	emit_link skipall, skiploop;
	x86code *loop;

	emit_mov_r32_p32_keepflags(dst, REG_ECX, param);								// mov   ecx,param
	if (!saveflags)
	{
		loop = dst;																	// loop:
		emit_jecxz_link(dst, skipall);													// jecxz skipall
		emit_lea_r32_m32(dst, REG_ECX, MBD(REG_ECX, -1));								// lea   ecx,[ecx-1]
		emit_rcl_r32_imm(dst, reglo, 1);												// rcl   reglo,1
		emit_rcl_r32_imm(dst, reghi, 1);												// rcl   reghi,1
		emit_jmp(dst, loop);															// jmp   loop
		track_resolve_link(dst, skipall);											// skipall:
	}
	else
	{
		emit_jecxz_link(dst, skipall);													// jecxz skipall
		emit_lea_r32_m32(dst, REG_ECX, MBD(REG_ECX, -1));								// lea   ecx,[ecx-1]
		loop = dst;																	// loop:
		emit_jecxz_link(dst, skiploop);												// jecxz skiploop
		emit_lea_r32_m32(dst, REG_ECX, MBD(REG_ECX, -1));								// lea   ecx,[ecx-1]
		emit_rcl_r32_imm(dst, reglo, 1);												// rcl   reglo,1
		emit_rcl_r32_imm(dst, reghi, 1);												// rcl   reghi,1
		emit_jmp(dst, loop);															// jmp   loop
		track_resolve_link(dst, skiploop);											// skiploop:
		emit_rcl_r32_imm(dst, reglo, 1);												// rcl   reglo,1
		emit_pushf(dst);																// pushf
		emit_rcl_r32_imm(dst, reghi, 1);												// rcl   reghi,1
		track_resolve_link(dst, skipall);											// skipall:
		emit_combine_z_flags(dst);
	}
}


//-------------------------------------------------
//  emit_rcr_r64_p64 - rcr operation to a 64-bit
//  pair of registers from a 64-bit parameter
//-------------------------------------------------

void drcbe_x86::emit_rcr_r64_p64(x86code *&dst, UINT8 reglo, UINT8 reghi, const be_parameter &param, const instruction &inst)
{
	int saveflags = (inst.flags() != 0);
	emit_link skipall, skiploop;
	x86code *loop;

	emit_mov_r32_p32_keepflags(dst, REG_ECX, param);								// mov   ecx,param
	if (!saveflags)
	{
		loop = dst;																	// loop:
		emit_jecxz_link(dst, skipall);													// jecxz skipall
		emit_lea_r32_m32(dst, REG_ECX, MBD(REG_ECX, -1));								// lea   ecx,[ecx-1]
		emit_rcr_r32_imm(dst, reghi, 1);												// rcr   reghi,1
		emit_rcr_r32_imm(dst, reglo, 1);												// rcr   reglo,1
		emit_jmp(dst, loop);															// jmp   loop
		track_resolve_link(dst, skipall);											// skipall:
	}
	else
	{
		emit_jecxz_link(dst, skipall);													// jecxz skipall
		emit_lea_r32_m32(dst, REG_ECX, MBD(REG_ECX, -1));								// lea   ecx,[ecx-1]
		loop = dst;																	// loop:
		emit_jecxz_link(dst, skiploop);												// jecxz skiploop
		emit_lea_r32_m32(dst, REG_ECX, MBD(REG_ECX, -1));								// lea   ecx,[ecx-1]
		emit_rcr_r32_imm(dst, reghi, 1);												// rcr   reghi,1
		emit_rcr_r32_imm(dst, reglo, 1);												// rcr   reglo,1
		emit_jmp(dst, loop);															// jmp   loop
		track_resolve_link(dst, skiploop);											// skiploop:
		emit_rcr_r32_imm(dst, reghi, 1);												// rcr   reghi,1
		emit_pushf(dst);																// pushf
		emit_rcr_r32_imm(dst, reglo, 1);												// rcr   reglo,1
		track_resolve_link(dst, skipall);											// skipall:
		emit_combine_z_shl_flags(dst);
	}
}



//**************************************************************************
//  EMITTERS FOR FLOATING POINT
//**************************************************************************

//-------------------------------------------------
//  emit_fld_p - load a floating point parameter
//  onto the stack
//-------------------------------------------------

void drcbe_x86::emit_fld_p(x86code *&dst, int size, const be_parameter &param)
{
	assert(param.is_memory());
	assert(size == 4 || size == 8);
	if (size == 4)
		emit_fld_m32(dst, MABS(param.memory()));
	else if (size == 8)
		emit_fld_m64(dst, MABS(param.memory()));
}


//-------------------------------------------------
//  emit_fstp_p - store a floating point parameter
//  from the stack and pop it
//-------------------------------------------------

void drcbe_x86::emit_fstp_p(x86code *&dst, int size, const be_parameter &param)
{
	assert(param.is_memory());
	assert(size == 4 || size == 8);
	if (size == 4)
		emit_fstp_m32(dst, MABS(param.memory()));
	else if (size == 8)
		emit_fstp_m64(dst, MABS(param.memory()));
}



//**************************************************************************
//  OUT-OF-BAND CODE FIXUP CALLBACKS
//**************************************************************************

//-------------------------------------------------
//  fixup_label - callback to fixup forward-
//  referenced labels
//-------------------------------------------------

void drcbe_x86::fixup_label(void *parameter, drccodeptr labelcodeptr)
{
	drccodeptr src = (drccodeptr)parameter;

	// find the end of the instruction
	if (src[0] == 0xe3)
	{
		src += 1 + 1;
		src[-1] = labelcodeptr - src;
	}
	else if (src[0] == 0xe9)
	{
		src += 1 + 4;
		((UINT32 *)src)[-1] = labelcodeptr - src;
	}
	else if (src[0] == 0x0f && (src[1] & 0xf0) == 0x80)
	{
		src += 2 + 4;
		((UINT32 *)src)[-1] = labelcodeptr - src;
	}
	else
		fatalerror("fixup_label called with invalid jmp source!");
}


//-------------------------------------------------
//  fixup_exception - callback to perform cleanup
//  and jump to an exception handler
//-------------------------------------------------

void drcbe_x86::fixup_exception(drccodeptr *codeptr, void *param1, void *param2)
{
	drccodeptr src = (drccodeptr)param1;
	const instruction &inst = *(const instruction *)param2;

	// normalize parameters
	const parameter &handp = inst.param(0);
	assert(handp.is_code_handle());
	be_parameter exp(*this, inst.param(1), PTYPE_MRI);

	// look up the handle target
	drccodeptr *targetptr = handp.handle().codeptr_addr();

	// first fixup the jump to get us here
	drccodeptr dst = *codeptr;
	((UINT32 *)src)[-1] = dst - src;

	// then store the exception parameter
	emit_mov_m32_p32(dst, MABS(&m_state.exp), exp);						// mov   [exp],exp

	// push the original return address on the stack
	emit_push_imm(dst, (FPTR)src);														// push  <return>
	if (*targetptr != NULL)
		emit_jmp(dst, *targetptr);														// jmp   *targetptr
	else
		emit_jmp_m32(dst, MABS(targetptr));											// jmp   [targetptr]

	*codeptr = dst;
}



//**************************************************************************
//  DEBUG HELPERS
//**************************************************************************

//-------------------------------------------------
//  debug_log_hashjmp - callback to handle
//  logging of hashjmps
//-------------------------------------------------

void drcbe_x86::debug_log_hashjmp(int mode, offs_t pc)
{
	printf("mode=%d PC=%08X\n", mode, pc);
}



//**************************************************************************
//  COMPILE-TIME OPCODES
//**************************************************************************

//-------------------------------------------------
//  op_handle - process a HANDLE opcode
//-------------------------------------------------

void drcbe_x86::op_handle(x86code *&dst, const instruction &inst)
{
	assert_no_condition(inst);
	assert_no_flags(inst);
	assert(inst.numparams() == 1);
	assert(inst.param(0).is_code_handle());

	reset_last_upper_lower_reg();

	// emit a jump around the stack adjust in case code falls through here
	emit_link skip;
	emit_jmp_short_link(dst, skip);													// jmp   skip

	// register the current pointer for the handle
	inst.param(0).handle().set_codeptr(dst);

	// by default, the handle points to prolog code that moves the stack pointer
	emit_lea_r32_m32(dst, REG_ESP, MBD(REG_ESP, -28));									// lea   rsp,[rsp-28]
	track_resolve_link(dst, skip);													// skip:
}


//-------------------------------------------------
//  op_hash - process a HASH opcode
//-------------------------------------------------

void drcbe_x86::op_hash(x86code *&dst, const instruction &inst)
{
	assert_no_condition(inst);
	assert_no_flags(inst);
	assert(inst.numparams() == 2);
	assert(inst.param(0).is_immediate());
	assert(inst.param(1).is_immediate());

	// register the current pointer for the mode/PC
	m_hash.set_codeptr(inst.param(0).immediate(), inst.param(1).immediate(), dst);
	reset_last_upper_lower_reg();
}


//-------------------------------------------------
//  op_label - process a LABEL opcode
//-------------------------------------------------

void drcbe_x86::op_label(x86code *&dst, const instruction &inst)
{
	assert_no_condition(inst);
	assert_no_flags(inst);
	assert(inst.numparams() == 1);
	assert(inst.param(0).is_code_label());

	// register the current pointer for the label
	m_labels.set_codeptr(inst.param(0).label(), dst);
	reset_last_upper_lower_reg();
}


//-------------------------------------------------
//  op_comment - process a COMMENT opcode
//-------------------------------------------------

void drcbe_x86::op_comment(x86code *&dst, const instruction &inst)
{
	assert_no_condition(inst);
	assert_no_flags(inst);
	assert(inst.numparams() == 1);
	assert(inst.param(0).is_string());

	// do nothing
}


//-------------------------------------------------
//  op_mapvar - process a MAPVAR opcode
//-------------------------------------------------

void drcbe_x86::op_mapvar(x86code *&dst, const instruction &inst)
{
	assert_no_condition(inst);
	assert_no_flags(inst);
	assert(inst.numparams() == 2);
	assert(inst.param(0).is_mapvar());
	assert(inst.param(1).is_immediate());

	// set the value of the specified mapvar
	m_map.set_value(dst, inst.param(0).mapvar(), inst.param(1).immediate());
}



//**************************************************************************
//  CONTROL FLOW OPCODES
//**************************************************************************

//-------------------------------------------------
//  op_nop - process a NOP opcode
//-------------------------------------------------

void drcbe_x86::op_nop(x86code *&dst, const instruction &inst)
{
	// nothing
}


//-------------------------------------------------
//  op_debug - process a DEBUG opcode
//-------------------------------------------------

void drcbe_x86::op_debug(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	if ((m_device.machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
	{
		// normalize parameters
		be_parameter pcp(*this, inst.param(0), PTYPE_MRI);

		// test and branch
		emit_test_m32_imm(dst, MABS(&m_device.machine().debug_flags), DEBUG_FLAG_CALL_HOOK);		// test  [debug_flags],DEBUG_FLAG_CALL_HOOK
		emit_link skip = { 0 };
		emit_jcc_short_link(dst, x86emit::COND_Z, skip);										// jz    skip

		// push the parameter
		emit_mov_m32_p32(dst, MBD(REG_ESP, 4), pcp);							// mov   [esp+4],pcp
		emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)&m_device);					// mov   [esp],device
		emit_call(dst, (x86code *)debugger_instruction_hook);							// call  debug_cpu_instruction_hook

		track_resolve_link(dst, skip);												// skip:
	}
}


//-------------------------------------------------
//  op_exit - process an EXIT opcode
//-------------------------------------------------

void drcbe_x86::op_exit(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter retp(*this, inst.param(0), PTYPE_MRI);

	// load the parameter into EAX
	emit_mov_r32_p32(dst, REG_EAX, retp);										// mov   eax,retp
	if (inst.condition() == uml::COND_ALWAYS)
		emit_jmp(dst, m_exit);													// jmp   exit
	else
		emit_jcc(dst, X86_CONDITION(inst.condition()), m_exit);					// jcc   exit
}


//-------------------------------------------------
//  op_hashjmp - process a HASHJMP opcode
//-------------------------------------------------

void drcbe_x86::op_hashjmp(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter modep(*this, inst.param(0), PTYPE_MRI);
	be_parameter pcp(*this, inst.param(1), PTYPE_MRI);
	const parameter &exp = inst.param(2);
	assert(exp.is_code_handle());

	if (LOG_HASHJMPS)
	{
		emit_mov_m32_p32(dst, MBD(REG_ESP, 4), pcp);
		emit_mov_m32_p32(dst, MBD(REG_ESP, 0), modep);
		emit_call(dst, (x86code *)debug_log_hashjmp);
	}

	// load the stack base one word early so we end up at the right spot after our call below
	emit_mov_r32_m32(dst, REG_ESP, MABS(&m_hashstacksave));						// mov   esp,[hashstacksave]

	// fixed mode cases
	if (modep.is_immediate() && m_hash.is_mode_populated(modep.immediate()))
	{
		// a straight immediate jump is direct, though we need the PC in EAX in case of failure
		if (pcp.is_immediate())
		{
			UINT32 l1val = (pcp.immediate() >> m_hash.l1shift()) & m_hash.l1mask();
			UINT32 l2val = (pcp.immediate() >> m_hash.l2shift()) & m_hash.l2mask();
			emit_call_m32(dst, MABS(&m_hash.base()[modep.immediate()][l1val][l2val]));	// call  hash[modep][l1val][l2val]
		}

		// a fixed mode but variable PC
		else
		{
			emit_mov_r32_p32(dst, REG_EAX, pcp);								// mov   eax,pcp
			emit_mov_r32_r32(dst, REG_EDX, REG_EAX);									// mov   edx,eax
			emit_shr_r32_imm(dst, REG_EDX, m_hash.l1shift());					// shr   edx,l1shift
			emit_and_r32_imm(dst, REG_EAX, m_hash.l2mask() << m_hash.l2shift());// and  eax,l2mask << l2shift
			emit_mov_r32_m32(dst, REG_EDX, MABSI(&m_hash.base()[modep.immediate()][0], REG_EDX, 4));
																						// mov   edx,hash[modep+edx*4]
			emit_call_m32(dst, MBISD(REG_EDX, REG_EAX, 4 >> m_hash.l2shift(), 0));// call  [edx+eax*shift]
		}
	}
	else
	{
		// variable mode
		int modereg = modep.select_register(REG_ECX);
		emit_mov_r32_p32(dst, modereg, modep);									// mov   modereg,modep
		emit_mov_r32_m32(dst, REG_ECX, MABSI(m_hash.base(), modereg, 4));			// mov   ecx,hash[modereg*4]

		// fixed PC
		if (pcp.is_immediate())
		{
			UINT32 l1val = (pcp.immediate() >> m_hash.l1shift()) & m_hash.l1mask();
			UINT32 l2val = (pcp.immediate() >> m_hash.l2shift()) & m_hash.l2mask();
			emit_mov_r32_m32(dst, REG_EDX, MBD(REG_ECX, l1val*4));						// mov   edx,[ecx+l1val*4]
			emit_call_m32(dst, MBD(REG_EDX, l2val*4));									// call  [l2val*4]
		}

		// variable PC
		else
		{
			emit_mov_r32_p32(dst, REG_EAX, pcp);								// mov   eax,pcp
			emit_mov_r32_r32(dst, REG_EDX, REG_EAX);									// mov   edx,eax
			emit_shr_r32_imm(dst, REG_EDX, m_hash.l1shift());					// shr   edx,l1shift
			emit_mov_r32_m32(dst, REG_EDX, MBISD(REG_ECX, REG_EDX, 4, 0));				// mov   edx,[ecx+edx*4]
			emit_and_r32_imm(dst, REG_EAX, m_hash.l2mask() << m_hash.l2shift());// and  eax,l2mask << l2shift
			emit_call_m32(dst, MBISD(REG_EDX, REG_EAX, 4 >> m_hash.l2shift(), 0));// call  [edx+eax*shift]
		}
	}

	// in all cases, if there is no code, we return here to generate the exception
	emit_mov_m32_p32(dst, MABS(&m_state.exp), pcp);						// mov   [exp],param
	emit_sub_r32_imm(dst, REG_ESP, 4);													// sub   esp,4
	emit_call_m32(dst, MABS(exp.handle().codeptr_addr()));												// call  [exp]
}


//-------------------------------------------------
//  op_jmp - process a JMP opcode
//-------------------------------------------------

void drcbe_x86::op_jmp(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	const parameter &labelp = inst.param(0);
	assert(labelp.is_code_label());

	// look up the jump target and jump there
	x86code *jmptarget = (x86code *)m_labels.get_codeptr(labelp.label(), m_fixup_label, dst);
	if (inst.condition() == uml::COND_ALWAYS)
		emit_jmp(dst, jmptarget);														// jmp   target
	else
		emit_jcc(dst, X86_CONDITION(inst.condition()), jmptarget);						// jcc   target
}


//-------------------------------------------------
//  op_exh - process an EXH opcode
//-------------------------------------------------

void drcbe_x86::op_exh(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	const parameter &handp = inst.param(0);
	assert(handp.is_code_handle());
	be_parameter exp(*this, inst.param(1), PTYPE_MRI);

	// look up the handle target
	drccodeptr *targetptr = handp.handle().codeptr_addr();

	// perform the exception processing inline if unconditional
	if (inst.condition() == uml::COND_ALWAYS)
	{
		emit_mov_m32_p32(dst, MABS(&m_state.exp), exp);					// mov   [exp],exp
		if (*targetptr != NULL)
			emit_call(dst, *targetptr);												// call  *targetptr
		else
			emit_call_m32(dst, MABS(targetptr));										// call  [targetptr]
	}

	// otherwise, jump to an out-of-band handler
	else
	{
		emit_jcc(dst, X86_CONDITION(inst.condition()), 0);								// jcc   exception
		m_cache.request_oob_codegen(m_fixup_exception, dst, &const_cast<instruction &>(inst));
	}
}


//-------------------------------------------------
//  op_callh - process a CALLH opcode
//-------------------------------------------------

void drcbe_x86::op_callh(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	const parameter &handp = inst.param(0);
	assert(handp.is_code_handle());

	// look up the handle target
	drccodeptr *targetptr = handp.handle().codeptr_addr();

	// skip if conditional
	emit_link skip = { 0 };
	if (inst.condition() != uml::COND_ALWAYS)
		emit_jcc_short_link(dst, X86_NOT_CONDITION(inst.condition()), skip);			// jcc   skip

	// jump through the handle; directly if a normal jump
	if (*targetptr != NULL)
		emit_call(dst, *targetptr);													// call  *targetptr
	else
		emit_call_m32(dst, MABS(targetptr));											// call  [targetptr]

	// resolve the conditional link
	if (inst.condition() != uml::COND_ALWAYS)
		track_resolve_link(dst, skip);												// skip:
}


//-------------------------------------------------
//  op_ret - process a RET opcode
//-------------------------------------------------

void drcbe_x86::op_ret(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_any_condition(inst);
	assert_no_flags(inst);
	assert(inst.numparams() == 0);

	// skip if conditional
	emit_link skip = { 0 };
	if (inst.condition() != uml::COND_ALWAYS)
		emit_jcc_short_link(dst, X86_NOT_CONDITION(inst.condition()), skip);			// jcc   skip

	// return
	emit_lea_r32_m32(dst, REG_ESP, MBD(REG_ESP, 28));									// lea   rsp,[rsp+28]
	emit_ret(dst);																		// ret

	// resolve the conditional link
	if (inst.condition() != uml::COND_ALWAYS)
		track_resolve_link(dst, skip);												// skip:
}


//-------------------------------------------------
//  op_callc - process a CALLC opcode
//-------------------------------------------------

void drcbe_x86::op_callc(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	const parameter &funcp = inst.param(0);
	assert(funcp.is_c_function());
	be_parameter paramp(*this, inst.param(1), PTYPE_M);

	// skip if conditional
	emit_link skip = { 0 };
	if (inst.condition() != uml::COND_ALWAYS)
		emit_jcc_short_link(dst, X86_NOT_CONDITION(inst.condition()), skip);			// jcc   skip

	// perform the call
	emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)paramp.memory());							// mov   [esp],paramp
	emit_call(dst, (x86code *)(FPTR)funcp.cfunc());										// call  funcp

	// resolve the conditional link
	if (inst.condition() != uml::COND_ALWAYS)
		track_resolve_link(dst, skip);												// skip:
}


//-------------------------------------------------
//  op_recover - process a RECOVER opcode
//-------------------------------------------------

void drcbe_x86::op_recover(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);

	// call the recovery code
	emit_mov_r32_m32(dst, REG_EAX, MABS(&m_stacksave));							// mov   eax,stacksave
	emit_mov_r32_m32(dst, REG_EAX, MBD(REG_EAX, -4));									// mov   eax,[eax-4]
	emit_sub_r32_imm(dst, REG_EAX, 1);													// sub   eax,1
	emit_mov_m32_imm(dst, MBD(REG_ESP, 8), inst.param(1).mapvar());						// mov   [esp+8],param1
	emit_mov_m32_r32(dst, MBD(REG_ESP, 4), REG_EAX);									// mov   [esp+4],eax
	emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)&m_map);							// mov   [esp],m_map
	emit_call(dst, (x86code *)&drc_map_variables::static_get_value);					// call  drcmap_get_value
	emit_mov_p32_r32(dst, dstp, REG_EAX);										// mov   dstp,eax
}



//**************************************************************************
//  INTERNAL REGISTER OPCODES
//**************************************************************************

//-------------------------------------------------
//  op_setfmod - process a SETFMOD opcode
//-------------------------------------------------

void drcbe_x86::op_setfmod(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter srcp(*this, inst.param(0), PTYPE_MRI);

	// immediate case
	if (srcp.is_immediate())
	{
		int value = srcp.immediate() & 3;
		emit_mov_m8_imm(dst, MABS(&m_state.fmod), value);					// mov   [fmod],srcp
		emit_fldcw_m16(dst, MABS(&fp_control[value]));							// fldcw fp_control[srcp]
	}

	// register/memory case
	else
	{
		emit_mov_r32_p32(dst, REG_EAX, srcp);									// mov   eax,srcp
		emit_and_r32_imm(dst, REG_EAX, 3);												// and   eax,3
		emit_mov_m8_r8(dst, MABS(&m_state.fmod), REG_AL);							// mov   [fmod],al
		emit_fldcw_m16(dst, MABSI(&fp_control[0], REG_EAX, 2));							// fldcw fp_control[eax]
	}
}


//-------------------------------------------------
//  op_getfmod - process a GETFMOD opcode
//-------------------------------------------------

void drcbe_x86::op_getfmod(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);

	// fetch the current mode and store to the destination
	if (dstp.is_int_register())
		emit_movzx_r32_m8(dst, dstp.ireg(), MABS(&m_state.fmod));					// movzx reg,[fmod]
	else
	{
		emit_movzx_r32_m8(dst, REG_EAX, MABS(&m_state.fmod));						// movzx eax,[fmod]
		emit_mov_m32_r32(dst, MABS(dstp.memory()), REG_EAX);								// mov   [dstp],eax
	}
}


//-------------------------------------------------
//  op_getexp - process a GETEXP opcode
//-------------------------------------------------

void drcbe_x86::op_getexp(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);

	// fetch the exception parameter and store to the destination
	if (dstp.is_int_register())
		emit_mov_r32_m32(dst, dstp.ireg(), MABS(&m_state.exp));					// mov   reg,[exp]
	else
	{
		emit_mov_r32_m32(dst, REG_EAX, MABS(&m_state.exp));						// mov   eax,[exp]
		emit_mov_m32_r32(dst, MABS(dstp.memory()), REG_EAX);								// mov   [dstp],eax
	}
}


//-------------------------------------------------
//  op_getflgs - process a GETFLGS opcode
//-------------------------------------------------

void drcbe_x86::op_getflgs(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter maskp(*this, inst.param(1), PTYPE_I);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX);

	// compute mask for flags
	UINT32 flagmask = 0;
	if (maskp.immediate() & FLAG_C) flagmask |= 0x001;
	if (maskp.immediate() & FLAG_V) flagmask |= 0x800;
	if (maskp.immediate() & FLAG_Z) flagmask |= 0x040;
	if (maskp.immediate() & FLAG_S) flagmask |= 0x080;
	if (maskp.immediate() & FLAG_U) flagmask |= 0x004;

	switch (maskp.immediate())
	{
		// single flags only
		case FLAG_C:
			emit_setcc_r8(dst, x86emit::COND_C, REG_AL);										// setc   al
			emit_movzx_r32_r8(dst, dstreg, REG_AL);									// movzx  dstreg,al
			break;

		case FLAG_V:
			emit_setcc_r8(dst, x86emit::COND_O, REG_AL);										// seto   al
			emit_movzx_r32_r8(dst, dstreg, REG_AL);									// movzx  dstreg,al
			emit_shl_r32_imm(dst, dstreg, 1);											// shl    dstreg,1
			break;

		case FLAG_Z:
			emit_setcc_r8(dst, x86emit::COND_Z, REG_AL);										// setz   al
			emit_movzx_r32_r8(dst, dstreg, REG_AL);									// movzx  dstreg,al
			emit_shl_r32_imm(dst, dstreg, 2);											// shl    dstreg,2
			break;

		case FLAG_S:
			emit_setcc_r8(dst, x86emit::COND_S, REG_AL);										// sets   al
			emit_movzx_r32_r8(dst, dstreg, REG_AL);									// movzx  dstreg,al
			emit_shl_r32_imm(dst, dstreg, 3);											// shl    dstreg,3
			break;

		case FLAG_U:
			emit_setcc_r8(dst, x86emit::COND_P, REG_AL);										// setp   al
			emit_movzx_r32_r8(dst, dstreg, REG_AL);									// movzx  dstreg,al
			emit_shl_r32_imm(dst, dstreg, 4);											// shl    dstreg,4
			break;

		// carry plus another flag
		case FLAG_C | FLAG_V:
			emit_setcc_r8(dst, x86emit::COND_C, REG_AL);										// setc   al
			emit_setcc_r8(dst, x86emit::COND_O, REG_CL);										// seto   cl
			emit_movzx_r32_r8(dst, REG_EAX, REG_AL);									// movzx  eax,al
			emit_movzx_r32_r8(dst, REG_ECX, REG_CL);									// movzx  ecx,al
			emit_lea_r32_m32(dst, dstreg, MBISD(REG_EAX, REG_ECX, 2, 0));				// lea    dstreg,[eax+ecx*2]
			break;

		case FLAG_C | FLAG_Z:
			emit_setcc_r8(dst, x86emit::COND_C, REG_AL);										// setc   al
			emit_setcc_r8(dst, x86emit::COND_Z, REG_CL);										// setz   cl
			emit_movzx_r32_r8(dst, REG_EAX, REG_AL);									// movzx  eax,al
			emit_movzx_r32_r8(dst, REG_ECX, REG_CL);									// movzx  ecx,al
			emit_lea_r32_m32(dst, dstreg, MBISD(REG_EAX, REG_ECX, 4, 0));				// lea    dstreg,[eax+ecx*4]
			break;

		case FLAG_C | FLAG_S:
			emit_setcc_r8(dst, x86emit::COND_C, REG_AL);										// setc   al
			emit_setcc_r8(dst, x86emit::COND_S, REG_CL);										// sets   cl
			emit_movzx_r32_r8(dst, REG_EAX, REG_AL);									// movzx  eax,al
			emit_movzx_r32_r8(dst, REG_ECX, REG_CL);									// movzx  ecx,al
			emit_lea_r32_m32(dst, dstreg, MBISD(REG_EAX, REG_ECX, 8, 0));				// lea    dstreg,[eax+ecx*8]
			break;

		// overflow plus another flag
		case FLAG_V | FLAG_Z:
			emit_setcc_r8(dst, x86emit::COND_O, REG_AL);										// seto   al
			emit_setcc_r8(dst, x86emit::COND_Z, REG_CL);										// setz   cl
			emit_movzx_r32_r8(dst, REG_EAX, REG_AL);									// movzx  eax,al
			emit_movzx_r32_r8(dst, REG_ECX, REG_CL);									// movzx  ecx,al
			emit_lea_r32_m32(dst, dstreg, MBISD(REG_EAX, REG_ECX, 2, 0));				// lea    dstreg,[eax+ecx*2]
			emit_shl_r32_imm(dst, dstreg, 1);											// shl    dstreg,1
			break;

		case FLAG_V | FLAG_S:
			emit_setcc_r8(dst, x86emit::COND_O, REG_AL);										// seto   al
			emit_setcc_r8(dst, x86emit::COND_S, REG_CL);										// sets   cl
			emit_movzx_r32_r8(dst, REG_EAX, REG_AL);									// movzx  eax,al
			emit_movzx_r32_r8(dst, REG_ECX, REG_CL);									// movzx  ecx,al
			emit_lea_r32_m32(dst, dstreg, MBISD(REG_EAX, REG_ECX, 4, 0));				// lea    dstreg,[eax+ecx*4]
			emit_shl_r32_imm(dst, dstreg, 1);											// shl    dstreg,1
			break;

		// zero plus another flag
		case FLAG_Z | FLAG_S:
			emit_setcc_r8(dst, x86emit::COND_Z, REG_AL);										// setz   al
			emit_setcc_r8(dst, x86emit::COND_S, REG_CL);										// sets   cl
			emit_movzx_r32_r8(dst, REG_EAX, REG_AL);									// movzx  eax,al
			emit_movzx_r32_r8(dst, REG_ECX, REG_CL);									// movzx  ecx,al
			emit_lea_r32_m32(dst, dstreg, MBISD(REG_EAX, REG_ECX, 2, 0));				// lea    dstreg,[eax+ecx*2]
			emit_shl_r32_imm(dst, dstreg, 2);											// shl    dstreg,2
			break;

		// default cases
		default:
			emit_pushf(dst);															// pushf
			emit_pop_r32(dst, REG_EAX);												// pop    eax
			emit_and_r32_imm(dst, REG_EAX, flagmask);									// and    eax,flagmask
			emit_movzx_r32_m8(dst, dstreg, MABSI(flags_map, REG_EAX));					// movzx  dstreg,[flags_map]
			break;
	}

	// store low 32 bits
	emit_mov_p32_r32(dst, dstp, dstreg);										// mov   dstp,dstreg

	// 64-bit form stores upper 32 bits
	if (inst.size() == 8)
	{
		// general case
		if (dstp.is_memory())
			emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0);							// mov   [dstp+4],0
		else if (dstp.is_int_register())
			emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0);					// mov   [reghi],0
	}
}


//-------------------------------------------------
//  op_save - process a SAVE opcode
//-------------------------------------------------

void drcbe_x86::op_save(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_M);

	// copy live state to the destination
	emit_mov_r32_imm(dst, REG_ECX, (FPTR)dstp.memory());										// mov    ecx,dstp
	emit_call(dst, m_save);														// call   save
}


//-------------------------------------------------
//  op_restore - process a RESTORE opcode
//-------------------------------------------------

void drcbe_x86::op_restore(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);

	// normalize parameters
	be_parameter srcp(*this, inst.param(0), PTYPE_M);

	// copy live state from the destination
	emit_mov_r32_imm(dst, REG_ECX, (FPTR)srcp.memory());										// mov    ecx,dstp
	emit_call(dst, m_restore);													// call   restore
}



//**************************************************************************
//  INTEGER OPERATIONS
//**************************************************************************

//-------------------------------------------------
//  op_load - process a LOAD opcode
//-------------------------------------------------

void drcbe_x86::op_load(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter basep(*this, inst.param(1), PTYPE_M);
	be_parameter indp(*this, inst.param(2), PTYPE_MRI);
	const parameter &scalesizep = inst.param(3);
	assert(scalesizep.is_size_scale());
	int scale = 1 << scalesizep.scale();
	int size = scalesizep.size();

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX);

	// immediate index
	if (indp.is_immediate())
	{
		if (size == SIZE_BYTE)
			emit_movzx_r32_m8(dst, dstreg, MABS(basep.memory(scale*indp.immediate())));		// movzx dstreg,[basep + scale*indp]
		else if (size == SIZE_WORD)
			emit_movzx_r32_m16(dst, dstreg, MABS(basep.memory(scale*indp.immediate())));		// movzx dstreg,[basep + scale*indp]
		else if (size == SIZE_DWORD)
			emit_mov_r32_m32(dst, dstreg, MABS(basep.memory(scale*indp.immediate())));		// mov   dstreg,[basep + scale*indp]
		else if (size == SIZE_QWORD)
		{
			emit_mov_r32_m32(dst, REG_EDX, MABS(basep.memory(scale*indp.immediate() + 4)));	// mov   edx,[basep + scale*indp + 4]
			emit_mov_r32_m32(dst, dstreg, MABS(basep.memory(scale*indp.immediate())));		// mov   dstreg,[basep + scale*indp]
		}
	}

	// other index
	else
	{
		int indreg = indp.select_register(REG_ECX);
		emit_mov_r32_p32(dst, indreg, indp);
		if (size == SIZE_BYTE)
			emit_movzx_r32_m8(dst, dstreg, MABSI(basep.memory(), indreg, scale));			// movzx dstreg,[basep + scale*indp]
		else if (size == SIZE_WORD)
			emit_movzx_r32_m16(dst, dstreg, MABSI(basep.memory(), indreg, scale));			// movzx dstreg,[basep + scale*indp]
		else if (size == SIZE_DWORD)
			emit_mov_r32_m32(dst, dstreg, MABSI(basep.memory(), indreg, scale));			// mov   dstreg,[basep + scale*indp]
		else if (size == SIZE_QWORD)
		{
			emit_mov_r32_m32(dst, REG_EDX, MABSI(basep.memory(4), indreg, scale));		// mov   edx,[basep + scale*indp + 4]
			emit_mov_r32_m32(dst, dstreg, MABSI(basep.memory(), indreg, scale));			// mov   dstreg,[basep + scale*indp]
		}
	}

	// store low 32 bits
	emit_mov_p32_r32(dst, dstp, dstreg);										// mov   dstp,dstreg

	// 64-bit form stores upper 32 bits
	if (inst.size() == 8)
	{
		// 1, 2, or 4-byte case
		if (size != SIZE_QWORD)
		{
			if (dstp.is_memory())
				emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0);						// mov   [dstp+4],0
			else if (dstp.is_int_register())
				emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0);				// mov   [reghi],0
		}

		// 8-byte case
		else
		{
			if (dstp.is_memory())
				emit_mov_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX);					// mov   [dstp+4],edx
			else if (dstp.is_int_register())
				emit_mov_m32_r32(dst, MABS(m_reghi[dstp.ireg()]), REG_EDX);		// mov   [reghi],edx
			set_last_upper_reg(dst, dstp, REG_EDX);
		}
	}
	set_last_lower_reg(dst, dstp, dstreg);
}


//-------------------------------------------------
//  op_loads - process a LOADS opcode
//-------------------------------------------------

void drcbe_x86::op_loads(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter basep(*this, inst.param(1), PTYPE_M);
	be_parameter indp(*this, inst.param(2), PTYPE_MRI);
	const parameter &scalesizep = inst.param(3);
	assert(scalesizep.is_size_scale());
	int scale = 1 << scalesizep.scale();
	int size = scalesizep.size();

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX);

	// immediate index
	if (indp.is_immediate())
	{
		if (size == SIZE_BYTE)
			emit_movsx_r32_m8(dst, dstreg, MABS(basep.memory(scale*indp.immediate())));		// movsx dstreg,[basep + scale*indp]
		else if (size == SIZE_WORD)
			emit_movsx_r32_m16(dst, dstreg, MABS(basep.memory(scale*indp.immediate())));		// movsx dstreg,[basep + scale*indp]
		else if (size == SIZE_DWORD)
			emit_mov_r32_m32(dst, dstreg, MABS(basep.memory(scale*indp.immediate())));		// mov   dstreg,[basep + scale*indp]
		else if (size == SIZE_QWORD)
		{
			emit_mov_r32_m32(dst, REG_EDX, MABS(basep.memory(scale*indp.immediate() + 4)));	// mov   edx,[basep + scale*indp + 4]
			emit_mov_r32_m32(dst, dstreg, MABS(basep.memory(scale*indp.immediate())));		// mov   dstreg,[basep + scale*indp]
		}
	}

	// other index
	else
	{
		int indreg = indp.select_register(REG_ECX);
		emit_mov_r32_p32(dst, indreg, indp);
		if (size == SIZE_BYTE)
			emit_movsx_r32_m8(dst, dstreg, MABSI(basep.memory(), indreg, scale));			// movsx dstreg,[basep + scale*indp]
		else if (size == SIZE_WORD)
			emit_movsx_r32_m16(dst, dstreg, MABSI(basep.memory(), indreg, scale));			// movsx dstreg,[basep + scale*indp]
		else if (size == SIZE_DWORD)
			emit_mov_r32_m32(dst, dstreg, MABSI(basep.memory(), indreg, scale));			// mov   dstreg,[basep + scale*indp]
		else if (size == SIZE_QWORD)
		{
			emit_mov_r32_m32(dst, REG_EDX, MABSI(basep.memory(4), indreg, scale));		// mov   edx,[basep + scale*indp + 4]
			emit_mov_r32_m32(dst, dstreg, MABSI(basep.memory(), indreg, scale));			// mov   dstreg,[basep + scale*indp]
		}
	}

	// store low 32 bits
	emit_mov_p32_r32(dst, dstp, dstreg);										// mov   dstp,dstreg

	// 64-bit form stores upper 32 bits
	if (inst.size() == 8)
	{
		emit_cdq(dst);																	// cdq
		if (dstp.is_memory())
			emit_mov_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX);						// mov   [dstp+4],edx
		else if (dstp.is_int_register())
			emit_mov_m32_r32(dst, MABS(m_reghi[dstp.ireg()]), REG_EDX);			// mov   [reghi],edx
		set_last_upper_reg(dst, dstp, REG_EDX);
	}
	set_last_lower_reg(dst, dstp, dstreg);
}


//-------------------------------------------------
//  op_store - process a STORE opcode
//-------------------------------------------------

void drcbe_x86::op_store(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter basep(*this, inst.param(0), PTYPE_M);
	be_parameter indp(*this, inst.param(1), PTYPE_MRI);
	be_parameter srcp(*this, inst.param(2), PTYPE_MRI);
	const parameter &scalesizep = inst.param(3);
	int scale = 1 << (scalesizep.scale());
	int size = scalesizep.size();

	// pick a source register for the general case
	int srcreg = srcp.select_register(REG_EAX);
	if (size == SIZE_BYTE && (srcreg & 4))
		srcreg = REG_EAX;

	// degenerate case: constant index
	if (indp.is_immediate())
	{
		// immediate source
		if (srcp.is_immediate())
		{
			if (size == SIZE_BYTE)
				emit_mov_m8_imm(dst, MABS(basep.memory(scale*indp.immediate())), srcp.immediate());	// mov   [basep + scale*indp],srcp
			else if (size == SIZE_WORD)
				emit_mov_m16_imm(dst, MABS(basep.memory(scale*indp.immediate())), srcp.immediate());	// mov   [basep + scale*indp],srcp
			else if (size == SIZE_DWORD)
				emit_mov_m32_imm(dst, MABS(basep.memory(scale*indp.immediate())), srcp.immediate());	// mov   [basep + scale*indp],srcp
			else if (size == SIZE_QWORD)
			{
				emit_mov_m32_imm(dst, MABS(basep.memory(scale*indp.immediate())), srcp.immediate());	// mov   [basep + scale*indp],srcp
				emit_mov_m32_imm(dst, MABS(basep.memory(scale*indp.immediate() + 4)), srcp.immediate() >> 32);
																						// mov   [basep + scale*indp + 4],srcp >> 32
			}
		}

		// variable source
		else
		{
			if (size != SIZE_QWORD)
				emit_mov_r32_p32(dst, srcreg, srcp);							// mov   srcreg,srcp
			else
				emit_mov_r64_p64(dst, srcreg, REG_EDX, srcp);					// mov   edx:srcreg,srcp
			if (size == SIZE_BYTE)
				emit_mov_m8_r8(dst, MABS(basep.memory(scale*indp.immediate())), srcreg);		// mov   [basep + scale*indp],srcreg
			else if (size == SIZE_WORD)
				emit_mov_m16_r16(dst, MABS(basep.memory(scale*indp.immediate())), srcreg);	// mov   [basep + scale*indp],srcreg
			else if (size == SIZE_DWORD)
				emit_mov_m32_r32(dst, MABS(basep.memory(scale*indp.immediate())), srcreg);	// mov   [basep + scale*indp],srcreg
			else if (size == SIZE_QWORD)
			{
				emit_mov_m32_r32(dst, MABS(basep.memory(scale*indp.immediate())), srcreg);	// mov   [basep + scale*indp],srcreg
				emit_mov_m32_r32(dst, MABS(basep.memory(scale*indp.immediate() + 4)), REG_EDX);	// mov   [basep + scale*indp + 4],edx
			}
		}
	}

	// normal case: variable index
	else
	{
		int indreg = indp.select_register(REG_ECX);
		emit_mov_r32_p32(dst, indreg, indp);									// mov   indreg,indp

		// immediate source
		if (srcp.is_immediate())
		{
			if (size == SIZE_BYTE)
				emit_mov_m8_imm(dst, MABSI(basep.memory(), indreg, scale), srcp.immediate());	// mov   [basep + 1*ecx],srcp
			else if (size == SIZE_WORD)
				emit_mov_m16_imm(dst, MABSI(basep.memory(), indreg, scale), srcp.immediate());	// mov   [basep + 2*ecx],srcp
			else if (size == SIZE_DWORD)
				emit_mov_m32_imm(dst, MABSI(basep.memory(), indreg, scale), srcp.immediate());	// mov   [basep + 4*ecx],srcp
			else if (size == SIZE_QWORD)
			{
				emit_mov_m32_imm(dst, MABSI(basep.memory(), indreg, scale), srcp.immediate());	// mov   [basep + 8*ecx],srcp
				emit_mov_m32_imm(dst, MABSI(basep.memory(4), indreg, scale), srcp.immediate() >> 32);
																						// mov   [basep + 8*ecx + 4],srcp >> 32
			}
		}

		// variable source
		else
		{
			if (size != SIZE_QWORD)
				emit_mov_r32_p32(dst, srcreg, srcp);							// mov   srcreg,srcp
			else
				emit_mov_r64_p64(dst, srcreg, REG_EDX, srcp);					// mov   edx:srcreg,srcp
			if (size == SIZE_BYTE)
				emit_mov_m8_r8(dst, MABSI(basep.memory(), indreg, scale), srcreg);			// mov   [basep + 1*ecx],srcreg
			else if (size == SIZE_WORD)
				emit_mov_m16_r16(dst, MABSI(basep.memory(), indreg, scale), srcreg);		// mov   [basep + 2*ecx],srcreg
			else if (size == SIZE_DWORD)
				emit_mov_m32_r32(dst, MABSI(basep.memory(), indreg, scale), srcreg);		// mov   [basep + 4*ecx],srcreg
			else if (size == SIZE_QWORD)
			{
				emit_mov_m32_r32(dst, MABSI(basep.memory(), indreg, scale), srcreg);		// mov   [basep + 8*ecx],srcreg
				emit_mov_m32_r32(dst, MABSI(basep.memory(4), indreg, scale), REG_EDX);	// mov   [basep + 8*ecx],edx
			}
		}
	}
}


//-------------------------------------------------
//  op_read - process a READ opcode
//-------------------------------------------------

void drcbe_x86::op_read(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter addrp(*this, inst.param(1), PTYPE_MRI);
	const parameter &spacesizep = inst.param(2);
	assert(spacesizep.is_size_space());

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX);

	// set up a call to the read byte handler
	emit_mov_m32_p32(dst, MBD(REG_ESP, 4), addrp);								// mov    [esp+4],addrp
	emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)m_space[spacesizep.space()]);	// mov    [esp],space
	if (spacesizep.size() == SIZE_BYTE)
	{
		emit_call(dst, (x86code *)m_accessors[spacesizep.space()].read_byte);
																						// call   read_byte
		emit_movzx_r32_r8(dst, dstreg, REG_AL);										// movzx  dstreg,al
	}
	else if (spacesizep.size() == SIZE_WORD)
	{
		emit_call(dst, (x86code *)m_accessors[spacesizep.space()].read_word);
																						// call   read_word
		emit_movzx_r32_r16(dst, dstreg, REG_AX);										// movzx  dstreg,ax
	}
	else if (spacesizep.size() == SIZE_DWORD)
	{
		emit_call(dst, (x86code *)m_accessors[spacesizep.space()].read_dword);
																						// call   read_dword
		emit_mov_r32_r32(dst, dstreg, REG_EAX);										// mov    dstreg,eax
	}
	else if (spacesizep.size() == SIZE_QWORD)
	{
		emit_call(dst, (x86code *)m_accessors[spacesizep.space()].read_qword);
																						// call   read_qword
		emit_mov_r32_r32(dst, dstreg, REG_EAX);										// mov    dstreg,eax
	}

	// store low 32 bits
	emit_mov_p32_r32(dst, dstp, dstreg);										// mov    dstp,dstreg

	// 64-bit form stores upper 32 bits
	if (inst.size() == 8)
	{
		// 1, 2, or 4-byte case
		if (spacesizep.size() != SIZE_QWORD)
		{
			if (dstp.is_memory())
				emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0);						// mov   [dstp+4],0
			else if (dstp.is_int_register())
				emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0);				// mov   [reghi],0
		}

		// 8-byte case
		else
		{
			if (dstp.is_memory())
				emit_mov_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX);					// mov   [dstp+4],edx
			else if (dstp.is_int_register())
				emit_mov_m32_r32(dst, MABS(m_reghi[dstp.ireg()]), REG_EDX);		// mov   [reghi],edx
		}
	}
}


//-------------------------------------------------
//  op_readm - process a READM opcode
//-------------------------------------------------

void drcbe_x86::op_readm(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter addrp(*this, inst.param(1), PTYPE_MRI);
	be_parameter maskp(*this, inst.param(2), PTYPE_MRI);
	const parameter &spacesizep = inst.param(3);
	assert(spacesizep.is_size_space());

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX);

	// set up a call to the read byte handler
	if (spacesizep.size() != SIZE_QWORD)
		emit_mov_m32_p32(dst, MBD(REG_ESP, 8), maskp);							// mov    [esp+8],maskp
	else
		emit_mov_m64_p64(dst, MBD(REG_ESP, 8), maskp);							// mov    [esp+8],maskp
	emit_mov_m32_p32(dst, MBD(REG_ESP, 4), addrp);								// mov    [esp+4],addrp
	emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)m_space[spacesizep.space()]);	// mov    [esp],space
	if (spacesizep.size() == SIZE_WORD)
	{
		emit_call(dst, (x86code *)m_accessors[spacesizep.space()].read_word_masked);
																						// call   read_word_masked
		emit_movzx_r32_r16(dst, dstreg, REG_AX);										// movzx  dstreg,ax
	}
	else if (spacesizep.size() == SIZE_DWORD)
	{
		emit_call(dst, (x86code *)m_accessors[spacesizep.space()].read_dword_masked);
																						// call   read_dword_masked
		emit_mov_r32_r32(dst, dstreg, REG_EAX);										// mov    dstreg,eax
	}
	else if (spacesizep.size() == SIZE_QWORD)
	{
		emit_call(dst, (x86code *)m_accessors[spacesizep.space()].read_qword_masked);
																						// call   read_qword_masked
		emit_mov_r32_r32(dst, dstreg, REG_EAX);										// mov    dstreg,eax
	}

	// store low 32 bits
	emit_mov_p32_r32(dst, dstp, dstreg);										// mov    dstp,dstreg

	// 64-bit form stores upper 32 bits
	if (inst.size() == 8)
	{
		// 1, 2, or 4-byte case
		if (spacesizep.size() != SIZE_QWORD)
		{
			if (dstp.is_memory())
				emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0);						// mov   [dstp+4],0
			else if (dstp.is_int_register())
				emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0);				// mov   [reghi],0
		}

		// 8-byte case
		else
		{
			if (dstp.is_memory())
				emit_mov_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX);					// mov   [dstp+4],edx
			else if (dstp.is_int_register())
				emit_mov_m32_r32(dst, MABS(m_reghi[dstp.ireg()]), REG_EDX);		// mov   [reghi],edx
		}
	}
}


//-------------------------------------------------
//  op_write - process a WRITE opcode
//-------------------------------------------------

void drcbe_x86::op_write(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter addrp(*this, inst.param(0), PTYPE_MRI);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
	const parameter &spacesizep = inst.param(2);
	assert(spacesizep.is_size_space());

	// set up a call to the write byte handler
	if (spacesizep.size() != SIZE_QWORD)
		emit_mov_m32_p32(dst, MBD(REG_ESP, 8), srcp);							// mov    [esp+8],srcp
	else
		emit_mov_m64_p64(dst, MBD(REG_ESP, 8), srcp);							// mov    [esp+8],srcp
	emit_mov_m32_p32(dst, MBD(REG_ESP, 4), addrp);								// mov    [esp+4],addrp
	emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)m_space[spacesizep.space()]);	// mov    [esp],space
	if (spacesizep.size() == SIZE_BYTE)
		emit_call(dst, (x86code *)m_accessors[spacesizep.space()].write_byte);
																						// call   write_byte
	else if (spacesizep.size() == SIZE_WORD)
		emit_call(dst, (x86code *)m_accessors[spacesizep.space()].write_word);
																						// call   write_word
	else if (spacesizep.size() == SIZE_DWORD)
		emit_call(dst, (x86code *)m_accessors[spacesizep.space()].write_dword);
																						// call   write_dword
	else if (spacesizep.size() == SIZE_QWORD)
		emit_call(dst, (x86code *)m_accessors[spacesizep.space()].write_qword);
																						// call   write_qword
}


//-------------------------------------------------
//  op_writem - process a WRITEM opcode
//-------------------------------------------------

void drcbe_x86::op_writem(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter addrp(*this, inst.param(0), PTYPE_MRI);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
	be_parameter maskp(*this, inst.param(2), PTYPE_MRI);
	const parameter &spacesizep = inst.param(3);
	assert(spacesizep.is_size_space());

	// set up a call to the write byte handler
	if (spacesizep.size() != SIZE_QWORD)
	{
		emit_mov_m32_p32(dst, MBD(REG_ESP, 12), maskp);						// mov    [esp+12],maskp
		emit_mov_m32_p32(dst, MBD(REG_ESP, 8), srcp);							// mov    [esp+8],srcp
	}
	else
	{
		emit_mov_m64_p64(dst, MBD(REG_ESP, 16), maskp);						// mov    [esp+16],maskp
		emit_mov_m64_p64(dst, MBD(REG_ESP, 8), srcp);							// mov    [esp+8],srcp
	}
	emit_mov_m32_p32(dst, MBD(REG_ESP, 4), addrp);								// mov    [esp+4],addrp
	emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)m_space[spacesizep.space()]);	// mov    [esp],space
	if (spacesizep.size() == SIZE_WORD)
		emit_call(dst, (x86code *)m_accessors[spacesizep.space()].write_word_masked);
																						// call   write_word_masked
	else if (spacesizep.size() == SIZE_DWORD)
		emit_call(dst, (x86code *)m_accessors[spacesizep.space()].write_dword_masked);
																						// call   write_dword_masked
	else if (spacesizep.size() == SIZE_QWORD)
		emit_call(dst, (x86code *)m_accessors[spacesizep.space()].write_qword_masked);
																						// call   write_qword_masked
}


//-------------------------------------------------
//  op_carry - process a CARRY opcode
//-------------------------------------------------

void drcbe_x86::op_carry(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C);

	// normalize parameters
	be_parameter srcp(*this, inst.param(0), PTYPE_MRI);
	be_parameter bitp(*this, inst.param(1), PTYPE_MRI);

	// degenerate case: source is immediate
	if (srcp.is_immediate() && bitp.is_immediate())
	{
		if (srcp.immediate() & ((UINT64)1 << bitp.immediate()))
			emit_stc(dst);
		else
			emit_clc(dst);
		}

	// load non-immediate bit numbers into a register
	if (!bitp.is_immediate())
	{
		emit_mov_r32_p32(dst, REG_ECX, bitp);
		emit_and_r32_imm(dst, REG_ECX, inst.size() * 8 - 1);
	}

	// 32-bit form
	if (inst.size() == 4)
	{
		if (bitp.is_immediate())
		{
			if (srcp.is_memory())
				emit_bt_m32_imm(dst, MABS(srcp.memory()), bitp.immediate());					// bt     [srcp],bitp
			else if (srcp.is_int_register())
				emit_bt_r32_imm(dst, srcp.ireg(), bitp.immediate());							// bt     srcp,bitp
		}
		else
		{
			if (srcp.is_memory())
				emit_bt_m32_r32(dst, MABS(srcp.memory()), REG_ECX);						// bt     [srcp],ecx
			else if (srcp.is_int_register())
				emit_bt_r32_r32(dst, srcp.ireg(), REG_ECX);								// bt     [srcp],ecx
		}
	}

	// 64-bit form
	else
	{
		if (bitp.is_immediate())
		{
			if (srcp.is_memory())
				emit_bt_m32_imm(dst, MABS(srcp.memory()), bitp.immediate());					// bt     [srcp],bitp
			else if (srcp.is_int_register() && bitp.immediate() < 32)
				emit_bt_r32_imm(dst, srcp.ireg(), bitp.immediate());							// bt     srcp,bitp
			else if (srcp.is_int_register() && bitp.immediate() >= 32)
				emit_bt_m32_imm(dst, MABS(m_reghi[srcp.ireg()]), bitp.immediate() - 32);	// bt     [srcp.hi],bitp
		}
		else
		{
			if (srcp.is_memory())
				emit_bt_m32_r32(dst, MABS(srcp.memory()), REG_ECX);						// bt     [srcp],ecx
			else if (srcp.is_int_register())
			{
				emit_mov_m32_r32(dst, MABS(m_reglo[srcp.ireg()]), srcp.ireg());		// mov    [srcp.lo],srcp
				emit_bt_m32_r32(dst, MABS(m_reglo[srcp.ireg()]), REG_ECX);			// bt     [srcp],ecx
			}
		}
	}
}


//-------------------------------------------------
//  op_set - process a SET opcode
//-------------------------------------------------

void drcbe_x86::op_set(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX);

	// set to AL
	emit_setcc_r8(dst, X86_CONDITION(inst.condition()), REG_AL);						// setcc  al
	emit_movzx_r32_r8(dst, dstreg, REG_AL);											// movzx  dstreg,al

	// store low 32 bits
	emit_mov_p32_r32(dst, dstp, dstreg);										// mov   dstp,dstreg

	// 64-bit form stores upper 32 bits
	if (inst.size() == 8)
	{
		// general case
		if (dstp.is_memory())
			emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0);							// mov   [dstp+4],0
		else if (dstp.is_int_register())
			emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0);					// mov   [reghi],0
	}
}


//-------------------------------------------------
//  op_mov - process a MOV opcode
//-------------------------------------------------

void drcbe_x86::op_mov(x86code *&dst, const instruction &inst)
{
	x86code *savedst = dst;

	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX);

	// always start with a jmp
	emit_link skip = { 0 };
	if (inst.condition() != uml::COND_ALWAYS)
		emit_jcc_short_link(dst, X86_NOT_CONDITION(inst.condition()), skip);			// jcc   skip

	// 32-bit form
	if (inst.size() == 4)
	{
		// register to memory
		if (dstp.is_memory() && srcp.is_int_register())
			emit_mov_m32_r32(dst, MABS(dstp.memory()), srcp.ireg());						// mov   [dstp],srcp

		// immediate to memory
		else if (dstp.is_memory() && srcp.is_immediate())
			emit_mov_m32_imm(dst, MABS(dstp.memory()), srcp.immediate());						// mov   [dstp],srcp

		// conditional memory to register
		else if (inst.condition() != uml::COND_ALWAYS && dstp.is_int_register() && srcp.is_memory())
		{
			dst = savedst;
			skip.target = NULL;
			emit_cmovcc_r32_m32(dst, X86_CONDITION(inst.condition()), dstp.ireg(), MABS(srcp.memory()));
																						// cmovcc dstp,[srcp]
		}

		// conditional register to register
		else if (inst.condition() != uml::COND_ALWAYS && dstp.is_int_register() && srcp.is_int_register())
		{
			dst = savedst;
			skip.target = NULL;
			emit_cmovcc_r32_r32(dst, X86_CONDITION(inst.condition()), dstp.ireg(), srcp.ireg());
																						// cmovcc dstp,srcp
		}

		// general case
		else
		{
			emit_mov_r32_p32_keepflags(dst, dstreg, srcp);						// mov   dstreg,srcp
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// register to memory
		if (dstp.is_memory() && srcp.is_int_register())
		{
			emit_mov_r32_m32(dst, REG_EAX, MABS(m_reghi[srcp.ireg()]));			// mov   eax,reghi[srcp]
			emit_mov_m32_r32(dst, MABS(dstp.memory()), srcp.ireg());						// mov   [dstp],srcp
			emit_mov_m32_r32(dst, MABS(dstp.memory(4)), REG_EAX);						// mov   [dstp+4],eax
		}

		// immediate to memory
		else if (dstp.is_memory() && srcp.is_immediate())
		{
			emit_mov_m32_imm(dst, MABS(dstp.memory()), srcp.immediate());						// mov   [dstp],srcp
			emit_mov_m32_imm(dst, MABS(dstp.memory(4)), srcp.immediate() >> 32);				// mov   [dstp+4],srcp >> 32
		}

		// general case
		else
		{
			emit_mov_r64_p64(dst, dstreg, REG_EDX, srcp);						// mov   edx:dstreg,srcp
			emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);						// mov   dstp,edx:dstreg
		}
	}

	// resolve the jump
	if (skip.target != NULL)
		track_resolve_link(dst, skip);
}


//-------------------------------------------------
//  op_sext - process a SEXT opcode
//-------------------------------------------------

void drcbe_x86::op_sext(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_S | FLAG_Z);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
	const parameter &sizep = inst.param(2);
	assert(sizep.is_size());

	// pick a target register for the general case
	int dstreg = (inst.size() == 8) ? REG_EAX : dstp.select_register(REG_EAX);

	// convert 8-bit source registers to EAX
	if (sizep.size() == SIZE_BYTE && srcp.is_int_register() && (srcp.ireg() & 4))
	{
		emit_mov_r32_r32(dst, REG_EAX, srcp.ireg());									// mov   eax,srcp
		srcp = be_parameter::make_ireg(REG_EAX);
	}

	// general case
	if (srcp.is_memory())
	{
		if (sizep.size() == SIZE_BYTE)
			emit_movsx_r32_m8(dst, dstreg, MABS(srcp.memory()));							// movsx dstreg,[srcp]
		else if (sizep.size() == SIZE_WORD)
			emit_movsx_r32_m16(dst, dstreg, MABS(srcp.memory()));							// movsx dstreg,[srcp]
		else if (sizep.size() == SIZE_DWORD)
			emit_mov_r32_m32(dst, dstreg, MABS(srcp.memory()));							// mov   dstreg,[srcp]
	}
	else if (srcp.is_int_register())
	{
		if (sizep.size() == SIZE_BYTE)
			emit_movsx_r32_r8(dst, dstreg, srcp.ireg());								// movsx dstreg,srcp
		else if (sizep.size() == SIZE_WORD)
			emit_movsx_r32_r16(dst, dstreg, srcp.ireg());								// movsx dstreg,srcp
		else if (sizep.size() == SIZE_DWORD && dstreg != srcp.ireg())
			emit_mov_r32_r32(dst, dstreg, srcp.ireg());									// mov   dstreg,srcp
	}
	if (inst.flags() != 0)
		emit_test_r32_r32(dst, dstreg, dstreg);										// test  dstreg,dstreg

	// 32-bit form: store the low 32 bits
	if (inst.size() == 4)
		emit_mov_p32_r32(dst, dstp, dstreg);									// mov   dstp,dstreg

	// 64-bit form: sign extend to 64 bits and store edx:eax
	else if (inst.size() == 8)
	{
		emit_cdq(dst);																	// cdq
		emit_mov_p64_r64(dst, dstp, REG_EAX, REG_EDX);							// mov   dstp,edx:eax
	}
}


//-------------------------------------------------
//  op_roland - process an ROLAND opcode
//-------------------------------------------------

void drcbe_x86::op_roland(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_S | FLAG_Z);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
	be_parameter shiftp(*this, inst.param(2), PTYPE_MRI);
	be_parameter maskp(*this, inst.param(3), PTYPE_MRI);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX, shiftp, maskp);

	// 32-bit form
	if (inst.size() == 4)
	{
		emit_mov_r32_p32(dst, dstreg, srcp);									// mov   dstreg,srcp
		emit_rol_r32_p32(dst, dstreg, shiftp, inst);							// rol   dstreg,shiftp
		emit_and_r32_p32(dst, dstreg, maskp, inst);							// and   dstreg,maskp
		emit_mov_p32_r32(dst, dstp, dstreg);									// mov   dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		emit_mov_r64_p64(dst, dstreg, REG_EDX, srcp);							// mov   edx:dstreg,srcp
		emit_rol_r64_p64(dst, dstreg, REG_EDX, shiftp, inst);					// rol   edx:dstreg,shiftp
		emit_and_r64_p64(dst, dstreg, REG_EDX, maskp, inst);					// and   edx:dstreg,maskp
		emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);							// mov   dstp,edx:dstreg
	}
}


//-------------------------------------------------
//  op_rolins - process an ROLINS opcode
//-------------------------------------------------

void drcbe_x86::op_rolins(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_S | FLAG_Z);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
	be_parameter shiftp(*this, inst.param(2), PTYPE_MRI);
	be_parameter maskp(*this, inst.param(3), PTYPE_MRI);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_ECX, shiftp, maskp);

	// 32-bit form
	if (inst.size() == 4)
	{
		emit_mov_r32_p32(dst, REG_EAX, srcp);									// mov   eax,srcp
		emit_rol_r32_p32(dst, REG_EAX, shiftp, inst);							// rol   eax,shiftp
		emit_mov_r32_p32(dst, dstreg, dstp);									// mov   dstreg,dstp
		if (maskp.is_immediate())
		{
			emit_and_r32_imm(dst, REG_EAX, maskp.immediate());								// and   eax,maskp
			emit_and_r32_imm(dst, dstreg, ~maskp.immediate());								// and   dstreg,~maskp
		}
		else
		{
			emit_mov_r32_p32(dst, REG_EDX, maskp);								// mov   edx,maskp
			emit_and_r32_r32(dst, REG_EAX, REG_EDX);									// and   eax,edx
			emit_not_r32(dst, REG_EDX);												// not   edx
			emit_and_r32_r32(dst, dstreg, REG_EDX);									// and   dstreg,edx
		}
		emit_or_r32_r32(dst, dstreg, REG_EAX);											// or    dstreg,eax
		emit_mov_p32_r32(dst, dstp, dstreg);									// mov   dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		emit_mov_r64_p64(dst, REG_EAX, REG_EDX, srcp);							// mov   edx:eax,srcp
		emit_rol_r64_p64(dst, REG_EAX, REG_EDX, shiftp, inst);					// rol   edx:eax,shiftp
		if (maskp.is_immediate())
		{
			emit_and_r32_imm(dst, REG_EAX, maskp.immediate());								// and   eax,maskp
			emit_and_r32_imm(dst, REG_EDX, maskp.immediate() >> 32);							// and   edx,maskp >> 32
			if (dstp.is_int_register())
			{
				emit_and_r32_imm(dst, dstp.ireg(), ~maskp.immediate());						// and   dstp.lo,~maskp
				emit_and_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), ~maskp.immediate() >> 32);// and   dstp.hi,~maskp >> 32
				emit_or_r32_r32(dst, dstp.ireg(), REG_EAX);								// or    dstp.lo,eax
				emit_or_m32_r32(dst, MABS(m_reghi[dstp.ireg()]), REG_EDX);			// or    dstp.hi,edx
			}
			else
			{
				emit_and_m32_imm(dst, MABS(dstp.memory()), ~maskp.immediate());					// and   dstp.lo,~maskp
				emit_and_m32_imm(dst, MABS(dstp.memory(4)), ~maskp.immediate() >> 32);		// and   dstp.hi,~maskp >> 32
				emit_or_m32_r32(dst, MABS(dstp.memory()), REG_EAX);						// or    dstp.lo,eax
				emit_or_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX);					// or    dstp.hi,edx
			}
		}
		else
		{
			int tempreg = REG_EBX;
			emit_mov_m32_r32(dst, MBD(REG_ESP, -8), tempreg);							// mov   [esp-8],ebx
			emit_mov_r64_p64(dst, tempreg, REG_ECX, maskp);					// mov   ecx:ebx,maskp
			emit_and_r32_r32(dst, REG_EAX, tempreg);									// and   eax,ebx
			emit_and_r32_r32(dst, REG_EDX, REG_ECX);									// and   edx,ecx
			emit_not_r32(dst, tempreg);												// not   ebx
			emit_not_r32(dst, REG_ECX);												// not   ecx
			if (dstp.is_int_register())
			{
				emit_and_r32_r32(dst, dstp.ireg(), tempreg);							// and   dstp.lo,ebx
				emit_and_m32_r32(dst, MABS(m_reghi[dstp.ireg()]), REG_ECX);		// and   dstp.hi,ecx
				emit_or_r32_r32(dst, dstp.ireg(), REG_EAX);								// or    dstp.lo,eax
				emit_or_m32_r32(dst, MABS(m_reghi[dstp.ireg()]), REG_EDX);			// or    dstp.hi,edx
			}
			else
			{
				emit_and_m32_r32(dst, MABS(dstp.memory()), tempreg);						// and   dstp.lo,ebx
				emit_and_m32_r32(dst, MABS(dstp.memory(4)), REG_ECX);					// and   dstp.hi,ecx
				emit_or_m32_r32(dst, MABS(dstp.memory()), REG_EAX);						// or    dstp.lo,eax
				emit_or_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX);					// or    dstp.hi,edx
			}
			emit_mov_r32_m32(dst, tempreg, MBD(REG_ESP, -8));							// mov   ebx,[esp-8]
		}
		if (inst.flags() == FLAG_Z)
			emit_or_r32_r32(dst, REG_EAX, REG_EDX);									// or    eax,edx
		else if (inst.flags() == FLAG_S)
			;// do nothing -- final OR will have the right result
		else if (inst.flags() == (FLAG_Z | FLAG_S))
		{
			emit_movzx_r32_r16(dst, REG_ECX, REG_AX);									// movzx ecx,ax
			emit_shr_r32_imm(dst, REG_EAX, 16);										// shr   eax,16
			emit_or_r32_r32(dst, REG_EDX, REG_ECX);									// or    edx,ecx
			emit_or_r32_r32(dst, REG_EDX, REG_EAX);									// or    edx,eax
		}
	}
}


//-------------------------------------------------
//  op_add - process a ADD opcode
//-------------------------------------------------

void drcbe_x86::op_add(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
	normalize_commutative(src1p, src2p);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_add_m32_p32(dst, MABS(dstp.memory()), src2p, inst);				// add   [dstp],src2p

		// reg = reg + imm
		else if (dstp.is_int_register() && src1p.is_int_register() && src2p.is_immediate() && inst.flags() == 0)
			emit_lea_r32_m32(dst, dstp.ireg(), MBD(src1p.ireg(), src2p.immediate()));			// lea   dstp,[src1p+src2p]

		// reg = reg + reg
		else if (dstp.is_int_register() && src1p.is_int_register() && src2p.is_int_register() && inst.flags() == 0)
			emit_lea_r32_m32(dst, dstp.ireg(), MBISD(src1p.ireg(), src2p.ireg(), 1, 0));	// lea   dstp,[src1p+src2p]

		// general case
		else
		{
			emit_mov_r32_p32(dst, dstreg, src1p);								// mov   dstreg,src1p
			emit_add_r32_p32(dst, dstreg, src2p, inst);						// add   dstreg,src2p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_add_m64_p64(dst, MABS(dstp.memory()), src2p, inst);				// add   [dstp],src2p

		// general case
		else
		{
			emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p);						// mov   dstreg:dstp,[src1p]
			emit_add_r64_p64(dst, dstreg, REG_EDX, src2p, inst);				// add   dstreg:dstp,src2p
			emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);						// mov   dstp,dstreg:eax
		}
	}
}


//-------------------------------------------------
//  op_addc - process a ADDC opcode
//-------------------------------------------------

void drcbe_x86::op_addc(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
	normalize_commutative(src1p, src2p);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_adc_m32_p32(dst, MABS(dstp.memory()), src2p, inst);				// adc   [dstp],src2p

		// general case
		else
		{
			emit_mov_r32_p32_keepflags(dst, dstreg, src1p);					// mov   dstreg,src1p
			emit_adc_r32_p32(dst, dstreg, src2p, inst);						// adc   dstreg,src2p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_adc_m64_p64(dst, MABS(dstp.memory()), src2p, inst);				// adc   [dstp],src2p

		// general case
		else
		{
			emit_mov_r64_p64_keepflags(dst, dstreg, REG_EDX, src1p);			// mov   dstreg:dstp,[src1p]
			emit_adc_r64_p64(dst, dstreg, REG_EDX, src2p, inst);				// adc   dstreg:dstp,src2p
			emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);						// mov   dstp,dstreg:eax
		}
	}
}


//-------------------------------------------------
//  op_sub - process a SUB opcode
//-------------------------------------------------

void drcbe_x86::op_sub(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_sub_m32_p32(dst, MABS(dstp.memory()), src2p, inst);				// sub   [dstp],src2p

		// reg = reg - imm
		else if (dstp.is_int_register() && src1p.is_int_register() && src2p.is_immediate() && inst.flags() == 0)
			emit_lea_r32_m32(dst, dstp.ireg(), MBD(src1p.ireg(), -src2p.immediate()));			// lea   dstp,[src1p-src2p]

		// general case
		else
		{
			emit_mov_r32_p32(dst, dstreg, src1p);								// mov   dstreg,src1p
			emit_sub_r32_p32(dst, dstreg, src2p, inst);						// sub   dstreg,src2p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_sub_m64_p64(dst, MABS(dstp.memory()), src2p, inst);				// sub   [dstp],src2p

		// general case
		else
		{
			emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p);						// mov   dstreg:dstp,[src1p]
			emit_sub_r64_p64(dst, dstreg, REG_EDX, src2p, inst);				// sub   dstreg:dstp,src2p
			emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);						// mov   dstp,dstreg:eax
		}
	}
}


//-------------------------------------------------
//  op_subc - process a SUBC opcode
//-------------------------------------------------

void drcbe_x86::op_subc(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_sbb_m32_p32(dst, MABS(dstp.memory()), src2p, inst);				// sbb   [dstp],src2p

		// general case
		else
		{
			emit_mov_r32_p32_keepflags(dst, dstreg, src1p);					// mov   dstreg,src1p
			emit_sbb_r32_p32(dst, dstreg, src2p, inst);						// sbb   dstreg,src2p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_sbb_m64_p64(dst, MABS(dstp.memory()), src2p, inst);				// sbb   [dstp],src2p

		// general case
		else
		{
			emit_mov_r64_p64_keepflags(dst, dstreg, REG_EDX, src1p);			// mov   dstreg:dstp,[src1p]
			emit_sbb_r64_p64(dst, dstreg, REG_EDX, src2p, inst);				// sbb   dstreg:dstp,src2p
			emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);						// mov   dstp,dstreg:eax
		}
	}
}


//-------------------------------------------------
//  op_cmp - process a CMP opcode
//-------------------------------------------------

void drcbe_x86::op_cmp(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter src1p(*this, inst.param(0), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(1), PTYPE_MRI);

	// pick a target register for the general case
	int src1reg = src1p.select_register(REG_EAX);

	// 32-bit form
	if (inst.size() == 4)
	{
		// memory versus anything
		if (src1p.is_memory())
			emit_cmp_m32_p32(dst, MABS(src1p.memory()), src2p, inst);				// cmp   [dstp],src2p

		// general case
		else
		{
			if (src1p.is_immediate())
				emit_mov_r32_imm(dst, src1reg, src1p.immediate());							// mov   src1reg,imm
			emit_cmp_r32_p32(dst, src1reg, src2p, inst);						// cmp   src1reg,src2p
		}
	}

	// 64-bit form
	else
	{
		// general case
		emit_mov_r64_p64(dst, REG_EAX, REG_EDX, src1p);						// mov   eax:dstp,[src1p]
		emit_cmp_r64_p64(dst, REG_EAX, REG_EDX, src2p, inst);					// cmp   eax:dstp,src2p
	}
}


//-------------------------------------------------
//  op_mulu - process a MULU opcode
//-------------------------------------------------

void drcbe_x86::op_mulu(x86code *&dst, const instruction &inst)
{
	UINT8 zsflags = inst.flags() & (FLAG_Z | FLAG_S);
	UINT8 vflag = inst.flags() & FLAG_V;

	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter edstp(*this, inst.param(1), PTYPE_MR);
	be_parameter src1p(*this, inst.param(2), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(3), PTYPE_MRI);
	normalize_commutative(src1p, src2p);
	bool compute_hi = (dstp != edstp);

	// 32-bit form
	if (inst.size() == 4)
	{
		// general case
		emit_mov_r32_p32(dst, REG_EAX, src1p);									// mov   eax,src1p
		if (src2p.is_memory())
			emit_mul_m32(dst, MABS(src2p.memory()));										// mul   [src2p]
		else if (src2p.is_int_register())
			emit_mul_r32(dst, src2p.ireg());											// mul   src2p
		else if (src2p.is_immediate())
		{
			emit_mov_r32_imm(dst, REG_EDX, src2p.immediate());								// mov   edx,src2p
			emit_mul_r32(dst, REG_EDX);												// mul   edx
		}
		emit_mov_p32_r32(dst, dstp, REG_EAX);									// mov   dstp,eax
		if (compute_hi)
			emit_mov_p32_r32(dst, edstp, REG_EDX);								// mov   edstp,edx

		// compute flags
		if (inst.flags() != 0)
		{
			if (zsflags != 0)
			{
				if (vflag)
					emit_pushf(dst);													// pushf
				if (compute_hi)
				{
					if (zsflags == FLAG_Z)
						emit_or_r32_r32(dst, REG_EDX, REG_EAX);						// or    edx,eax
					else if (zsflags == FLAG_S)
						emit_test_r32_r32(dst, REG_EDX, REG_EDX);						// test  edx,edx
					else
					{
						emit_movzx_r32_r16(dst, REG_ECX, REG_AX);						// movzx ecx,ax
						emit_shr_r32_imm(dst, REG_EAX, 16);							// shr   eax,16
						emit_or_r32_r32(dst, REG_EDX, REG_ECX);						// or    edx,ecx
						emit_or_r32_r32(dst, REG_EDX, REG_EAX);						// or    edx,eax
					}
				}
				else
					emit_test_r32_r32(dst, REG_EAX, REG_EAX);							// test  eax,eax

				// we rely on the fact that OF is cleared by all logical operations above
				if (vflag)
				{
					emit_pushf(dst);													// pushf
					emit_pop_r32(dst, REG_EAX);										// pop   eax
					emit_and_m32_imm(dst, MBD(REG_ESP, 0), ~0x84);						// and   [esp],~0x84
					emit_or_m32_r32(dst, MBD(REG_ESP, 0), REG_EAX);					// or    [esp],eax
					emit_popf(dst);													// popf
				}
			}
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// general case
		emit_mov_m32_imm(dst, MBD(REG_ESP, 24), inst.flags());							// mov   [esp+24],flags
		emit_mov_m64_p64(dst, MBD(REG_ESP, 16), src2p);						// mov   [esp+16],src2p
		emit_mov_m64_p64(dst, MBD(REG_ESP, 8), src1p);							// mov   [esp+8],src1p
		if (!compute_hi)
			emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reslo);				// mov   [esp+4],&reslo
		else
			emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reshi);				// mov   [esp+4],&reshi
		emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)&m_reslo);					// mov   [esp],&reslo
		emit_call(dst, (x86code *)dmulu);												// call  dmulu
		if (inst.flags() != 0)
			emit_push_m32(dst, MABSI(flags_unmap, REG_EAX, 4));							// push   flags_unmap[eax*4]
		emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reslo + 0));				// mov   eax,reslo.lo
		emit_mov_r32_m32(dst, REG_EDX, MABS((UINT32 *)&m_reslo + 1));				// mov   edx,reslo.hi
		emit_mov_p64_r64(dst, dstp, REG_EAX, REG_EDX);							// mov   dstp,edx:eax
		if (compute_hi)
		{
			emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reshi + 0));			// mov   eax,reshi.lo
			emit_mov_r32_m32(dst, REG_ECX, MABS((UINT32 *)&m_reshi + 1));			// mov   ecx,reshi.hi
			emit_mov_p64_r64(dst, edstp, REG_EAX, REG_ECX);					// mov   edstp,ecx:eax
		}
		if (inst.flags() != 0)
			emit_popf(dst);															// popf
	}
}


//-------------------------------------------------
//  op_muls - process a MULS opcode
//-------------------------------------------------

void drcbe_x86::op_muls(x86code *&dst, const instruction &inst)
{
	UINT8 zsflags = inst.flags() & (FLAG_Z | FLAG_S);
	UINT8 vflag =  inst.flags() & FLAG_V;

	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter edstp(*this, inst.param(1), PTYPE_MR);
	be_parameter src1p(*this, inst.param(2), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(3), PTYPE_MRI);
	normalize_commutative(src1p, src2p);
	bool compute_hi = (dstp != edstp);

	// 32-bit form
	if (inst.size() == 4)
	{
		// 32-bit destination with memory/immediate or register/immediate
		if (!compute_hi && !src1p.is_immediate() && src2p.is_immediate())
		{
			if (src1p.is_memory())
				emit_imul_r32_m32_imm(dst, REG_EAX, MABS(src1p.memory()), src2p.immediate());	// imul  eax,[src1p],src2p
			else if (src1p.is_int_register())
				emit_imul_r32_r32_imm(dst, REG_EAX, src1p.ireg(), src2p.immediate());			// imul  eax,src1p,src2p
			emit_mov_p32_r32(dst, dstp, REG_EAX);								// mov   dstp,eax
		}

		// 32-bit destination, general case
		else if (!compute_hi)
		{
			emit_mov_r32_p32(dst, REG_EAX, src1p);								// mov   eax,src1p
			if (src2p.is_memory())
				emit_imul_r32_m32(dst, REG_EAX, MABS(src2p.memory()));					// imul  eax,[src2p]
			else if (src2p.is_int_register())
				emit_imul_r32_r32(dst, REG_EAX, src2p.ireg());							// imul  eax,src2p
			emit_mov_p32_r32(dst, dstp, REG_EAX);								// mov   dstp,eax
		}

		// 64-bit destination, general case
		else
		{
			emit_mov_r32_p32(dst, REG_EAX, src1p);								// mov   eax,src1p
			if (src2p.is_memory())
				emit_imul_m32(dst, MABS(src2p.memory()));									// imul  [src2p]
			else if (src2p.is_int_register())
				emit_imul_r32(dst, src2p.ireg());										// imul  src2p
			else if (src2p.is_immediate())
			{
				emit_mov_r32_imm(dst, REG_EDX, src2p.immediate());							// mov   edx,src2p
				emit_imul_r32(dst, REG_EDX);											// imul  edx
			}
			emit_mov_p32_r32(dst, dstp, REG_EAX);								// mov   dstp,eax
			emit_mov_p32_r32(dst, edstp, REG_EDX);								// mov   edstp,edx
		}

		// compute flags
		if (inst.flags() != 0)
		{
			if (zsflags != 0)
			{
				if (vflag)
					emit_pushf(dst);													// pushf
				if (compute_hi)
				{
					if (inst.flags() == FLAG_Z)
						emit_or_r32_r32(dst, REG_EDX, REG_EAX);						// or    edx,eax
					else if (inst.flags() == FLAG_S)
						emit_test_r32_r32(dst, REG_EDX, REG_EDX);						// test  edx,edx
					else
					{
						emit_movzx_r32_r16(dst, REG_ECX, REG_AX);						// movzx ecx,ax
						emit_shr_r32_imm(dst, REG_EAX, 16);							// shr   eax,16
						emit_or_r32_r32(dst, REG_EDX, REG_ECX);						// or    edx,ecx
						emit_or_r32_r32(dst, REG_EDX, REG_EAX);						// or    edx,eax
					}
				}
				else
					emit_test_r32_r32(dst, REG_EAX, REG_EAX);							// test  eax,eax

				// we rely on the fact that OF is cleared by all logical operations above
				if (vflag)
				{
					emit_pushf(dst);													// pushf
					emit_pop_r32(dst, REG_EAX);										// pop   eax
					emit_and_m32_imm(dst, MBD(REG_ESP, 0), ~0x84);						// and   [esp],~0x84
					emit_or_m32_r32(dst, MBD(REG_ESP, 0), REG_EAX);					// or    [esp],eax
					emit_popf(dst);													// popf
				}
			}
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// general case
		emit_mov_m32_imm(dst, MBD(REG_ESP, 24), inst.flags());							// mov   [esp+24],flags
		emit_mov_m64_p64(dst, MBD(REG_ESP, 16), src2p);						// mov   [esp+16],src2p
		emit_mov_m64_p64(dst, MBD(REG_ESP, 8), src1p);							// mov   [esp+8],src1p
		if (!compute_hi)
			emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reslo);				// mov   [esp+4],&reslo
		else
			emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reshi);				// push  [esp+4],&reshi
		emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)&m_reslo);					// mov   [esp],&reslo
		emit_call(dst, (x86code *)dmuls);												// call  dmuls
		if (inst.flags() != 0)
			emit_push_m32(dst, MABSI(flags_unmap, REG_EAX, 4));							// push   flags_unmap[eax*4]
		emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reslo + 0));				// mov   eax,reslo.lo
		emit_mov_r32_m32(dst, REG_EDX, MABS((UINT32 *)&m_reslo + 1));				// mov   edx,reslo.hi
		emit_mov_p64_r64(dst, dstp, REG_EAX, REG_EDX);							// mov   dstp,edx:eax
		if (compute_hi)
		{
			emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reshi + 0));			// mov   eax,reshi.lo
			emit_mov_r32_m32(dst, REG_EDX, MABS((UINT32 *)&m_reshi + 1));			// mov   edx,reshi.hi
			emit_mov_p64_r64(dst, edstp, REG_EAX, REG_EDX);					// mov   edstp,edx:eax
		}
		if (inst.flags() != 0)
			emit_popf(dst);															// popf
	}
}


//-------------------------------------------------
//  op_divu - process a DIVU opcode
//-------------------------------------------------

void drcbe_x86::op_divu(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter edstp(*this, inst.param(1), PTYPE_MR);
	be_parameter src1p(*this, inst.param(2), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(3), PTYPE_MRI);
	bool compute_rem = (dstp != edstp);

	// 32-bit form
	if (inst.size() == 4)
	{
		// general case
		emit_mov_r32_p32(dst, REG_ECX, src2p);									// mov   ecx,src2p
		if (inst.flags() != 0)
		{
			emit_mov_r32_imm(dst, REG_EAX, 0xa0000000);								// mov   eax,0xa0000000
			emit_add_r32_r32(dst, REG_EAX, REG_EAX);									// add   eax,eax
		}
		emit_link skip;
		emit_jecxz_link(dst, skip);													// jecxz skip
		emit_mov_r32_p32(dst, REG_EAX, src1p);									// mov   eax,src1p
		emit_xor_r32_r32(dst, REG_EDX, REG_EDX);										// xor   edx,edx
		emit_div_r32(dst, REG_ECX);													// div   ecx
		emit_mov_p32_r32(dst, dstp, REG_EAX);									// mov   dstp,eax
		if (compute_rem)
			emit_mov_p32_r32(dst, edstp, REG_EDX);								// mov   edstp,edx
		if (inst.flags() != 0)
			emit_test_r32_r32(dst, REG_EAX, REG_EAX);									// test  eax,eax
		track_resolve_link(dst, skip);												// skip:
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// general case
		emit_mov_m64_p64(dst, MBD(REG_ESP, 16), src2p);						// mov   [esp+16],src2p
		emit_mov_m64_p64(dst, MBD(REG_ESP, 8), src1p);							// mov   [esp+8],src1p
		if (!compute_rem)
			emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reslo);				// mov   [esp+4],&reslo
		else
			emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reshi);				// push  [esp+4],&reshi
		emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)&m_reslo);					// mov   [esp],&reslo
		emit_call(dst, (x86code *)ddivu);												// call  ddivu
		if (inst.flags() != 0)
			emit_push_m32(dst, MABSI(flags_unmap, REG_EAX, 4));							// push   flags_unmap[eax*4]
		emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reslo + 0));				// mov   eax,reslo.lo
		emit_mov_r32_m32(dst, REG_EDX, MABS((UINT32 *)&m_reslo + 1));				// mov   edx,reslo.hi
		emit_mov_p64_r64(dst, dstp, REG_EAX, REG_EDX);							// mov   dstp,edx:eax
		if (compute_rem)
		{
			emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reshi + 0));			// mov   eax,reshi.lo
			emit_mov_r32_m32(dst, REG_EDX, MABS((UINT32 *)&m_reshi + 1));			// mov   edx,reshi.hi
			emit_mov_p64_r64(dst, edstp, REG_EAX, REG_EDX);					// mov   edstp,edx:eax
		}
		if (inst.flags() != 0)
			emit_popf(dst);															// popf
	}
}


//-------------------------------------------------
//  op_divs - process a DIVS opcode
//-------------------------------------------------

void drcbe_x86::op_divs(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter edstp(*this, inst.param(1), PTYPE_MR);
	be_parameter src1p(*this, inst.param(2), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(3), PTYPE_MRI);
	bool compute_rem = (dstp != edstp);

	// 32-bit form
	if (inst.size() == 4)
	{
		// general case
		emit_mov_r32_p32(dst, REG_ECX, src2p);									// mov   ecx,src2p
		if (inst.flags() != 0)
		{
			emit_mov_r32_imm(dst, REG_EAX, 0xa0000000);								// mov   eax,0xa0000000
			emit_add_r32_r32(dst, REG_EAX, REG_EAX);									// add   eax,eax
		}
		emit_link skip;
		emit_jecxz_link(dst, skip);													// jecxz skip
		emit_mov_r32_p32(dst, REG_EAX, src1p);									// mov   eax,src1p
		emit_cdq(dst);																	// cdq
		emit_idiv_r32(dst, REG_ECX);													// idiv  ecx
		emit_mov_p32_r32(dst, dstp, REG_EAX);									// mov   dstp,eax
		if (compute_rem)
			emit_mov_p32_r32(dst, edstp, REG_EDX);								// mov   edstp,edx
		if (inst.flags() != 0)
			emit_test_r32_r32(dst, REG_EAX, REG_EAX);									// test  eax,eax
		track_resolve_link(dst, skip);												// skip:
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// general case
		emit_mov_m64_p64(dst, MBD(REG_ESP, 16), src2p);						// mov   [esp+16],src2p
		emit_mov_m64_p64(dst, MBD(REG_ESP, 8), src1p);							// mov   [esp+8],src1p
		if (!compute_rem)
			emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reslo);				// mov   [esp+4],&reslo
		else
			emit_mov_m32_imm(dst, MBD(REG_ESP, 4), (FPTR)&m_reshi);				// push  [esp+4],&reshi
		emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)&m_reslo);					// mov   [esp],&reslo
		emit_call(dst, (x86code *)ddivs);												// call  ddivs
		if (inst.flags() != 0)
			emit_push_m32(dst, MABSI(flags_unmap, REG_EAX, 4));							// push   flags_unmap[eax*4]
		emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reslo + 0));				// mov   eax,reslo.lo
		emit_mov_r32_m32(dst, REG_EDX, MABS((UINT32 *)&m_reslo + 1));				// mov   edx,reslo.hi
		emit_mov_p64_r64(dst, dstp, REG_EAX, REG_EDX);							// mov   dstp,edx:eax
		if (compute_rem)
		{
			emit_mov_r32_m32(dst, REG_EAX, MABS((UINT32 *)&m_reshi + 0));			// mov   eax,reshi.lo
			emit_mov_r32_m32(dst, REG_EDX, MABS((UINT32 *)&m_reshi + 1));			// mov   edx,reshi.hi
			emit_mov_p64_r64(dst, edstp, REG_EAX, REG_EDX);					// mov   edstp,edx:eax
		}
		if (inst.flags() != 0)
			emit_popf(dst);															// popf
	}
}


//-------------------------------------------------
//  op_and - process a AND opcode
//-------------------------------------------------

void drcbe_x86::op_and(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
	normalize_commutative(src1p, src2p);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_and_m32_p32(dst, MABS(dstp.memory()), src2p, inst);				// and   [dstp],src2p

		// AND with immediate 0xff
		else if (src2p.is_immediate_value(0xff) && inst.flags() == 0)
		{
			if (src1p.is_int_register())
				emit_movzx_r32_r8(dst, dstreg, src1p.ireg());							// movzx dstreg,src1p
			else if (src1p.is_memory())
				emit_movzx_r32_m8(dst, dstreg, MABS(src1p.memory()));						// movzx dstreg,[src1p]
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}

		// AND with immediate 0xffff
		else if (src2p.is_immediate_value(0xffff) && inst.flags() == 0)
		{
			if (src1p.is_int_register())
				emit_movzx_r32_r16(dst, dstreg, src1p.ireg());							// movzx dstreg,src1p
			else if (src1p.is_memory())
				emit_movzx_r32_m16(dst, dstreg, MABS(src1p.memory()));					// movzx dstreg,[src1p]
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}

		// general case
		else
		{
			emit_mov_r32_p32(dst, dstreg, src1p);								// mov   dstreg,src1p
			emit_and_r32_p32(dst, dstreg, src2p, inst);						// and   dstreg,src2p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_and_m64_p64(dst, MABS(dstp.memory()), src2p, inst);				// and   [dstp],src2p

		// AND with immediate 0xff
		else if (src2p.is_immediate_value(0xff) && inst.flags() == 0)
		{
			if (src1p.is_int_register())
				emit_movzx_r32_r8(dst, dstreg, src1p.ireg());							// movzx dstreg,src1p
			else if (src1p.is_memory())
				emit_movzx_r32_m8(dst, dstreg, MABS(src1p.memory()));						// movzx dstreg,[src1p]
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
			if (dstp.is_int_register())
				emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0);				// mov   dsthi,0
			else if (dstp.is_memory())
				emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0);						// mov   dsthi,0
		}

		// AND with immediate 0xffff
		else if (src2p.is_immediate_value(0xffff) && inst.flags() == 0)
		{
			if (src1p.is_int_register())
				emit_movzx_r32_r16(dst, dstreg, src1p.ireg());							// movzx dstreg,src1p
			else if (src1p.is_memory())
				emit_movzx_r32_m16(dst, dstreg, MABS(src1p.memory()));					// movzx dstreg,[src1p]
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
			if (dstp.is_int_register())
				emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0);				// mov   dsthi,0
			else if (dstp.is_memory())
				emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0);						// mov   dsthi,0
		}

		// AND with immediate 0xffffffff
		else if (src2p.is_immediate_value(0xffffffff) && inst.flags() == 0)
		{
			emit_mov_r32_p32(dst, dstreg, src1p);								// mov   dstreg,src1p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
			if (dstp.is_int_register())
				emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0);				// mov   dsthi,0
			else if (dstp.is_memory())
				emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0);						// mov   dsthi,0
		}

		// AND with immediate 0xffffffff00000000
		else if (src2p.is_immediate_value(U64(0xffffffff00000000)) && inst.flags() == 0)
		{
			if (src1p != dstp)
			{
				emit_mov_r64_p64(dst, REG_NONE, REG_EDX, src1p);				// mov   dstreg,src1p
				emit_mov_p64_r64(dst, dstp, REG_NONE, REG_EDX);				// mov   dstp,dstreg
			}
			if (dstp.is_int_register())
				emit_xor_r32_r32(dst, dstp.ireg(), dstp.ireg());							// xor   dstlo,dstlo
			else if (dstp.is_memory())
				emit_mov_m32_imm(dst, MABS(dstp.memory()), 0);							// mov   dstlo,0
		}

		// AND with immediate <= 0xffffffff
		else if (src2p.is_immediate() && src2p.immediate() <= 0xffffffff && inst.flags() == 0)
		{
			emit_mov_r32_p32(dst, dstreg, src1p);								// mov   dstreg,src1p
			emit_and_r32_p32(dst, dstreg, src2p, inst);						// and   dstreg,src2p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
			if (dstp.is_int_register())
				emit_mov_m32_imm(dst, MABS(m_reghi[dstp.ireg()]), 0);				// mov   dsthi,0
			else if (dstp.is_memory())
				emit_mov_m32_imm(dst, MABS(dstp.memory(4)), 0);						// mov   dsthi,0
		}

		// general case
		else
		{
			emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p);						// mov   dstreg:dstp,[src1p]
			emit_and_r64_p64(dst, dstreg, REG_EDX, src2p, inst);				// and   dstreg:dstp,src2p
			emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);						// mov   dstp,dstreg:eax
		}
	}
}


//-------------------------------------------------
//  op_test - process a TEST opcode
//-------------------------------------------------

void drcbe_x86::op_test(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter src1p(*this, inst.param(0), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(1), PTYPE_MRI);
	normalize_commutative(src1p, src2p);

	// pick a target register for the general case
	int src1reg = src1p.select_register(REG_EAX);

	// 32-bit form
	if (inst.size() == 4)
	{
		// src1p in memory
		if (src1p.is_memory())
			emit_test_m32_p32(dst, MABS(src1p.memory()), src2p, inst);			// test  [src1p],src2p

		// general case
		else
		{
			emit_mov_r32_p32(dst, src1reg, src1p);								// mov   src1reg,src1p
			emit_test_r32_p32(dst, src1reg, src2p, inst);						// test  src1reg,src2p
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// src1p in memory
		if (src1p.is_memory())
			emit_test_m64_p64(dst, MABS(src1p.memory()), src2p, inst);			// test  [dstp],src2p

		// general case
		else
		{
			emit_mov_r64_p64(dst, src1reg, REG_EDX, src1p);					// mov   src1reg:dstp,[src1p]
			emit_test_r64_p64(dst, src1reg, REG_EDX, src2p, inst);				// test  src1reg:dstp,src2p
		}
	}
}


//-------------------------------------------------
//  op_or - process a OR opcode
//-------------------------------------------------

void drcbe_x86::op_or(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
	normalize_commutative(src1p, src2p);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_or_m32_p32(dst, MABS(dstp.memory()), src2p, inst);				// or    [dstp],src2p

		// general case
		else
		{
			emit_mov_r32_p32(dst, dstreg, src1p);								// mov   dstreg,src1p
			emit_or_r32_p32(dst, dstreg, src2p, inst);							// or    dstreg,src2p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_or_m64_p64(dst, MABS(dstp.memory()), src2p, inst);				// or    [dstp],src2p

		// general case
		else
		{
			emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p);						// mov   dstreg:dstp,[src1p]
			emit_or_r64_p64(dst, dstreg, REG_EDX, src2p, inst);				// or    dstreg:dstp,src2p
			emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);						// mov   dstp,dstreg:eax
		}
	}
}


//-------------------------------------------------
//  op_xor - process a XOR opcode
//-------------------------------------------------

void drcbe_x86::op_xor(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
	normalize_commutative(src1p, src2p);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_xor_m32_p32(dst, MABS(dstp.memory()), src2p, inst);				// xor   [dstp],src2p

		// general case
		else
		{
			emit_mov_r32_p32(dst, dstreg, src1p);								// mov   dstreg,src1p
			emit_xor_r32_p32(dst, dstreg, src2p, inst);						// xor   dstreg,src2p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_xor_m64_p64(dst, MABS(dstp.memory()), src2p, inst);				// xor   [dstp],src2p

		// general case
		else
		{
			emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p);						// mov   dstreg:dstp,[src1p]
			emit_xor_r64_p64(dst, dstreg, REG_EDX, src2p, inst);				// xor   dstreg:dstp,src2p
			emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);						// mov   dstp,dstreg:eax
		}
	}
}


//-------------------------------------------------
//  op_lzcnt - process a LZCNT opcode
//-------------------------------------------------

void drcbe_x86::op_lzcnt(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX);

	// 32-bit form
	if (inst.size() == 4)
	{
		emit_mov_r32_p32(dst, dstreg, srcp);									// mov   dstreg,src1p
		emit_mov_r32_imm(dst, REG_ECX, 32 ^ 31);										// mov   ecx,32 ^ 31
		emit_bsr_r32_r32(dst, dstreg, dstreg);											// bsr   dstreg,dstreg
		emit_cmovcc_r32_r32(dst, x86emit::COND_Z, dstreg, REG_ECX);								// cmovz dstreg,ecx
		emit_xor_r32_imm(dst, dstreg, 31);												// xor   dstreg,31
		emit_mov_p32_r32(dst, dstp, dstreg);									// mov   dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		emit_mov_r64_p64(dst, REG_EDX, dstreg, srcp);							// mov   dstreg:edx,srcp
		emit_bsr_r32_r32(dst, dstreg, dstreg);											// bsr   dstreg,dstreg
		emit_link skip;
		emit_jcc_short_link(dst, x86emit::COND_NZ, skip);										// jnz   skip
		emit_mov_r32_imm(dst, REG_ECX, 32 ^ 31);										// mov   ecx,32 ^ 31
		emit_bsr_r32_r32(dst, dstreg, REG_EDX);										// bsr   dstreg,edx
		emit_cmovcc_r32_r32(dst, x86emit::COND_Z, dstreg, REG_ECX);								// cmovz dstreg,ecx
		emit_add_r32_imm(dst, REG_ECX, 32);											// add   ecx,32
		track_resolve_link(dst, skip);												// skip:
		emit_xor_r32_r32(dst, REG_EDX, REG_EDX);										// xor   edx,edx
		emit_xor_r32_imm(dst, dstreg, 31);												// xor   dstreg,31
		emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);							// mov   dstp,edx:dstreg
	}
}


//-------------------------------------------------
//  op_bswap - process a BSWAP opcode
//-------------------------------------------------

void drcbe_x86::op_bswap(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX);

	// 32-bit form
	if (inst.size() == 4)
	{
		emit_mov_r32_p32(dst, dstreg, srcp);									// mov   dstreg,src1p
		emit_bswap_r32(dst, dstreg);													// bswap dstreg
		if (inst.flags() != 0)
			emit_test_r32_r32(dst, dstreg, dstreg);									// test  dstreg,dstreg
		emit_mov_p32_r32(dst, dstp, dstreg);									// mov   dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		emit_mov_r64_p64(dst, REG_EDX, dstreg, srcp);							// mov   dstreg:edx,srcp
		emit_bswap_r32(dst, dstreg);													// bswap dstreg
		emit_bswap_r32(dst, REG_EDX);													// bswap edx
		emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);							// mov   dstp,edx:dstreg
		if (inst.flags() == FLAG_Z)
			emit_or_r32_r32(dst, REG_EDX, dstreg);										// or    edx,eax
		else if (inst.flags() == FLAG_S)
			emit_test_r32_r32(dst, REG_EDX, REG_EDX);									// test  edx,edx
		else
		{
			emit_movzx_r32_r16(dst, REG_ECX, dstreg);									// movzx ecx,dstreg
			emit_or_r32_r32(dst, REG_EDX, REG_ECX);									// or    edx,ecx
			emit_mov_r32_r32(dst, REG_ECX, dstreg);									// mov   ecx,dstreg
			emit_shr_r32_imm(dst, REG_ECX, 16);										// shr   ecx,16
			emit_or_r32_r32(dst, REG_EDX, REG_ECX);									// or    edx,ecx
		}
	}
}


//-------------------------------------------------
//  op_shl - process a SHL opcode
//-------------------------------------------------

void drcbe_x86::op_shl(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_shl_m32_p32(dst, MABS(dstp.memory()), src2p, inst);				// shl   [dstp],src2p

		// general case
		else
		{
			emit_mov_r32_p32(dst, dstreg, src1p);								// mov   dstreg,src1p
			emit_shl_r32_p32(dst, dstreg, src2p, inst);						// shl   dstreg,src2p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// general case
		emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p);							// mov   dstreg:dstp,[src1p]
		emit_shl_r64_p64(dst, dstreg, REG_EDX, src2p, inst);					// shl   dstreg:dstp,src2p
		emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);							// mov   dstp,dstreg:eax
	}
}


//-------------------------------------------------
//  op_shr - process a SHR opcode
//-------------------------------------------------

void drcbe_x86::op_shr(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_shr_m32_p32(dst, MABS(dstp.memory()), src2p, inst);				// shr   [dstp],src2p

		// general case
		else
		{
			emit_mov_r32_p32(dst, dstreg, src1p);								// mov   dstreg,src1p
			emit_shr_r32_p32(dst, dstreg, src2p, inst);						// shr   dstreg,src2p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// general case
		emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p);							// mov   dstreg:dstp,[src1p]
		emit_shr_r64_p64(dst, dstreg, REG_EDX, src2p, inst);					// shr   dstreg:dstp,src2p
		emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);							// mov   dstp,dstreg:eax
	}
}


//-------------------------------------------------
//  op_sar - process a SAR opcode
//-------------------------------------------------

void drcbe_x86::op_sar(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_sar_m32_p32(dst, MABS(dstp.memory()), src2p, inst);				// sar   [dstp],src2p

		// general case
		else
		{
			emit_mov_r32_p32(dst, dstreg, src1p);								// mov   dstreg,src1p
			emit_sar_r32_p32(dst, dstreg, src2p, inst);						// sar   dstreg,src2p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// general case
		emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p);							// mov   dstreg:dstp,[src1p]
		emit_sar_r64_p64(dst, dstreg, REG_EDX, src2p, inst);					// sar   dstreg:dstp,src2p
		emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);							// mov   dstp,dstreg:eax
	}
}


//-------------------------------------------------
//  op_rol - process a rol opcode
//-------------------------------------------------

void drcbe_x86::op_rol(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_rol_m32_p32(dst, MABS(dstp.memory()), src2p, inst);				// rol   [dstp],src2p

		// general case
		else
		{
			emit_mov_r32_p32(dst, dstreg, src1p);								// mov   dstreg,src1p
			emit_rol_r32_p32(dst, dstreg, src2p, inst);						// rol   dstreg,src2p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// general case
		emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p);							// mov   dstreg:dstp,[src1p]
		emit_rol_r64_p64(dst, dstreg, REG_EDX, src2p, inst);					// rol   dstreg:dstp,src2p
		emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);							// mov   dstp,dstreg:eax
	}
}


//-------------------------------------------------
//  op_ror - process a ROR opcode
//-------------------------------------------------

void drcbe_x86::op_ror(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_ror_m32_p32(dst, MABS(dstp.memory()), src2p, inst);				// ror   [dstp],src2p

		// general case
		else
		{
			emit_mov_r32_p32(dst, dstreg, src1p);								// mov   dstreg,src1p
			emit_ror_r32_p32(dst, dstreg, src2p, inst);						// ror   dstreg,src2p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// general case
		emit_mov_r64_p64(dst, dstreg, REG_EDX, src1p);							// mov   dstreg:dstp,[src1p]
		emit_ror_r64_p64(dst, dstreg, REG_EDX, src2p, inst);					// ror   dstreg:dstp,src2p
		emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);							// mov   dstp,dstreg:eax
	}
}


//-------------------------------------------------
//  op_rolc - process a ROLC opcode
//-------------------------------------------------

void drcbe_x86::op_rolc(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_rcl_m32_p32(dst, MABS(dstp.memory()), src2p, inst);				// rcl   [dstp],src2p

		// general case
		else
		{
			emit_mov_r32_p32_keepflags(dst, dstreg, src1p);					// mov   dstreg,src1p
			emit_rcl_r32_p32(dst, dstreg, src2p, inst);						// rcl   dstreg,src2p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// general case
		emit_mov_r64_p64_keepflags(dst, dstreg, REG_EDX, src1p);				// mov   dstreg:dstp,[src1p]
		emit_rcl_r64_p64(dst, dstreg, REG_EDX, src2p, inst);					// rcl   dstreg:dstp,src2p
		emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);							// mov   dstp,dstreg:eax
	}
}


//-------------------------------------------------
//  op_rorc - process a RORC opcode
//-------------------------------------------------

void drcbe_x86::op_rorc(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);

	// pick a target register for the general case
	int dstreg = dstp.select_register(REG_EAX, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			emit_rcr_m32_p32(dst, MABS(dstp.memory()), src2p, inst);				// rcr   [dstp],src2p

		// general case
		else
		{
			emit_mov_r32_p32_keepflags(dst, dstreg, src1p);					// mov   dstreg,src1p
			emit_rcr_r32_p32(dst, dstreg, src2p, inst);						// rcr   dstreg,src2p
			emit_mov_p32_r32(dst, dstp, dstreg);								// mov   dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// general case
		emit_mov_r64_p64_keepflags(dst, dstreg, REG_EDX, src1p);				// mov   dstreg:dstp,[src1p]
		emit_rcr_r64_p64(dst, dstreg, REG_EDX, src2p, inst);					// rcr   dstreg:dstp,src2p
		emit_mov_p64_r64(dst, dstp, dstreg, REG_EDX);							// mov   dstp,dstreg:eax
	}
}



//**************************************************************************
//  FLOATING POINT OPERATIONS
//**************************************************************************

//-------------------------------------------------
//  op_fload - process a FLOAD opcode
//-------------------------------------------------

void drcbe_x86::op_fload(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter basep(*this, inst.param(1), PTYPE_M);
	be_parameter indp(*this, inst.param(2), PTYPE_MRI);

	// immediate index
	if (indp.is_immediate())
	{
		emit_mov_r32_m32(dst, REG_EAX, MABS(basep.memory(4*indp.immediate())));				// mov   eax,[basep + 4*indp]
		if (inst.size() == 8)
			emit_mov_r32_m32(dst, REG_EDX, MABS(basep.memory(4 + 4*indp.immediate())));		// mov   edx,[basep + 4*indp + 4]
	}

	// other index
	else
	{
		int indreg = indp.select_register(REG_ECX);
		emit_mov_r32_p32(dst, indreg, indp);
		emit_mov_r32_m32(dst, REG_EAX, MABSI(basep.memory(), indreg, 4));					// mov   eax,[basep + 4*indp]
		if (inst.size() == 8)
			emit_mov_r32_m32(dst, REG_EDX, MABSI(basep.memory(4), indreg, 4));			// mov   edx,[basep + 4*indp + 4]
	}

	// general case
	emit_mov_m32_r32(dst, MABS(dstp.memory()), REG_EAX);									// mov   [dstp],eax
	if (inst.size() == 8)
		emit_mov_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX);							// mov   [dstp + 4],edx
}


//-------------------------------------------------
//  op_fstore - process a FSTORE opcode
//-------------------------------------------------

void drcbe_x86::op_fstore(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter basep(*this, inst.param(0), PTYPE_M);
	be_parameter indp(*this, inst.param(1), PTYPE_MRI);
	be_parameter srcp(*this, inst.param(2), PTYPE_MF);

	// general case
	emit_mov_r32_m32(dst, REG_EAX, MABS(srcp.memory()));									// mov   eax,[srcp]
	if (inst.size() == 8)
		emit_mov_r32_m32(dst, REG_EDX, MABS(srcp.memory(4)));							// mov   edx,[srcp + 4]

	// immediate index
	if (indp.is_immediate())
	{
		emit_mov_m32_r32(dst, MABS(basep.memory(4*indp.immediate())), REG_EAX);				// mov   [basep + 4*indp],eax
		if (inst.size() == 8)
			emit_mov_m32_r32(dst, MABS(basep.memory(4 + 4*indp.immediate())), REG_EDX);		// mov   [basep + 4*indp + 4],edx
	}

	// other index
	else
	{
		int indreg = indp.select_register(REG_ECX);
		emit_mov_r32_p32(dst, indreg, indp);
		emit_mov_m32_r32(dst, MABSI(basep.memory(), indreg, 4), REG_EAX);					// mov   [basep + 4*indp],eax
		if (inst.size() == 8)
			emit_mov_m32_r32(dst, MABSI(basep.memory(4), indreg, 4), REG_EDX);			// mov   [basep + 4*indp + 4],edx
	}
}


//-------------------------------------------------
//  op_fread - process a FREAD opcode
//-------------------------------------------------

void drcbe_x86::op_fread(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter addrp(*this, inst.param(1), PTYPE_MRI);
	const parameter &spacep = inst.param(2);
	assert(spacep.is_size_space());
	assert((1 << spacep.size()) == inst.size());

	// set up a call to the read dword/qword handler
	emit_mov_m32_p32(dst, MBD(REG_ESP, 4), addrp);								// mov    [esp+4],addrp
	emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)m_space[spacep.space()]);		// mov    [esp],space
	if (inst.size() == 4)
		emit_call(dst, (x86code *)m_accessors[spacep.space()].read_dword);	// call   read_dword
	else if (inst.size() == 8)
		emit_call(dst, (x86code *)m_accessors[spacep.space()].read_qword);	// call   read_qword

	// store result
	if (inst.size() == 4)
		emit_mov_p32_r32(dst, dstp, REG_EAX);									// mov   dstp,eax
	else if (inst.size() == 8)
		emit_mov_p64_r64(dst, dstp, REG_EAX, REG_EDX);							// mov   dstp,edx:eax
}


//-------------------------------------------------
//  op_fwrite - process a FWRITE opcode
//-------------------------------------------------

void drcbe_x86::op_fwrite(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter addrp(*this, inst.param(0), PTYPE_MRI);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);
	const parameter &spacep = inst.param(2);
	assert(spacep.is_size_space());
	assert((1 << spacep.size()) == inst.size());

	// set up a call to the write dword/qword handler
	if (inst.size() == 4)
		emit_mov_m32_p32(dst, MBD(REG_ESP, 8), srcp);							// mov    [esp+8],srcp
	else if (inst.size() == 8)
		emit_mov_m64_p64(dst, MBD(REG_ESP, 8), srcp);							// mov    [esp+8],srcp
	emit_mov_m32_p32(dst, MBD(REG_ESP, 4), addrp);								// mov    [esp+4],addrp
	emit_mov_m32_imm(dst, MBD(REG_ESP, 0), (FPTR)m_space[spacep.space()]);		// mov    [esp],space
	if (inst.size() == 4)
		emit_call(dst, (x86code *)m_accessors[spacep.space()].write_dword);	// call   write_dword
	else if (inst.size() == 8)
		emit_call(dst, (x86code *)m_accessors[spacep.space()].write_qword);	// call   write_qword
}


//-------------------------------------------------
//  op_fmov - process a FMOV opcode
//-------------------------------------------------

void drcbe_x86::op_fmov(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);

	// always start with a jmp
	emit_link skip = { 0 };
	if (inst.condition() != uml::COND_ALWAYS)
		emit_jcc_short_link(dst, X86_NOT_CONDITION(inst.condition()), skip);			// jcc   skip

	// general case
	emit_mov_r32_m32(dst, REG_EAX, MABS(srcp.memory()));									// mov   eax,[srcp]
	if (inst.size() == 8)
		emit_mov_r32_m32(dst, REG_EDX, MABS(srcp.memory(4)));							// mov   edx,[srcp + 4]
	emit_mov_m32_r32(dst, MABS(dstp.memory()), REG_EAX);									// mov   [dstp],eax
	if (inst.size() == 8)
		emit_mov_m32_r32(dst, MABS(dstp.memory(4)), REG_EDX);							// mov   [dstp + 4],edx

	// resolve the jump
	if (skip.target != NULL)
		track_resolve_link(dst, skip);												// skip:
}


//-------------------------------------------------
//  op_ftoint - process a FTOINT opcode
//-------------------------------------------------

void drcbe_x86::op_ftoint(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);
	const parameter &sizep = inst.param(2);
	assert(sizep.is_size());
	const parameter &roundp = inst.param(3);
	assert(roundp.is_rounding());

	// set rounding mode if necessary
	if (roundp.rounding() != ROUND_DEFAULT && (!m_sse3 || roundp.rounding() != ROUND_TRUNC))
	{
		emit_fstcw_m16(dst, MABS(&m_fmodesave));									// fstcw [fmodesave]
		emit_fldcw_m16(dst, MABS(&fp_control[roundp.rounding()]));							// fldcw fpcontrol[roundp]
	}

	// general case
	emit_fld_p(dst, inst.size(), srcp);												// fld   srcp

	// 4-byte integer case
	if (sizep.size() == SIZE_DWORD)
	{
		if (dstp.is_memory())
		{
			if (!m_sse3 || roundp.rounding() != ROUND_TRUNC)
				emit_fistp_m32(dst, MABS(dstp.memory()));									// fistp [dstp]
			else
				emit_fisttp_m32(dst, MABS(dstp.memory()));								// fisttp [dstp]
		}
		else if (dstp.is_int_register())
		{
			if (!m_sse3 || roundp.rounding() != ROUND_TRUNC)
				emit_fistp_m32(dst, MABS(m_reglo[dstp.ireg()]));					// fistp reglo[dstp]
			else
				emit_fisttp_m32(dst, MABS(m_reglo[dstp.ireg()]));					// fisttp reglo[dstp]
			emit_mov_r32_m32(dst, dstp.ireg(), MABS(m_reglo[dstp.ireg()]));			// mov   dstp,reglo[dstp]
		}
	}

	// 8-byte integer case
	else if (sizep.size() == SIZE_QWORD)
	{
		if (dstp.is_memory())
		{
			if (!m_sse3 || roundp.rounding() != ROUND_TRUNC)
				emit_fistp_m64(dst, MABS(dstp.memory()));									// fistp [dstp]
			else
				emit_fisttp_m64(dst, MABS(dstp.memory()));								// fisttp [dstp]
		}
		else if (dstp.is_int_register())
		{
			if (!m_sse3 || roundp.rounding() != ROUND_TRUNC)
				emit_fistp_m64(dst, MABS(m_reglo[dstp.ireg()]));					// fistp reglo[dstp]
			else
				emit_fisttp_m64(dst, MABS(m_reglo[dstp.ireg()]));					// fisttp reglo[dstp]
			emit_mov_r32_m32(dst, dstp.ireg(), MABS(m_reglo[dstp.ireg()]));			// mov   dstp,reglo[dstp]
		}
	}

	// restore control word and proceed
	if (roundp.rounding() != ROUND_DEFAULT && (!m_sse3 || roundp.rounding() != ROUND_TRUNC))
		emit_fldcw_m16(dst, MABS(&m_fmodesave));									// fldcw [fmodesave]
}


//-------------------------------------------------
//  op_ffrint - process a FFRINT opcode
//-------------------------------------------------

void drcbe_x86::op_ffrint(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
	const parameter &sizep = inst.param(2);
	assert(sizep.is_size());

	// 4-byte integer case
	if (sizep.size() == SIZE_DWORD)
	{
		if (srcp.is_immediate())
		{
			emit_mov_m32_imm(dst, MABS(&m_fptemp), srcp.immediate());					// mov   [fptemp],srcp
			emit_fild_m32(dst, MABS(&m_fptemp));									// fild  [fptemp]
		}
		else if (srcp.is_memory())
			emit_fild_m32(dst, MABS(srcp.memory()));										// fild  [srcp]
		else if (srcp.is_int_register())
		{
			emit_mov_m32_r32(dst, MABS(m_reglo[srcp.ireg()]), srcp.ireg());			// mov   reglo[srcp],srcp
			emit_fild_m32(dst, MABS(m_reglo[srcp.ireg()]));						// fild  reglo[srcp]
		}
	}

	// 8-bit integer case
	else if (sizep.size() == SIZE_QWORD)
	{
		if (srcp.is_immediate())
		{
			emit_mov_m32_imm(dst, MABS(&m_fptemp), srcp.immediate());					// mov   [fptemp],srcp
			emit_mov_m32_imm(dst, MABS((UINT8 *)&m_fptemp + 4), srcp.immediate());		// mov   [fptemp+4],srcp
			emit_fild_m64(dst, MABS(&m_fptemp));									// fild  [fptemp]
		}
		else if (srcp.is_memory())
			emit_fild_m64(dst, MABS(srcp.memory()));										// fild  [srcp]
		else if (srcp.is_int_register())
		{
			emit_mov_m32_r32(dst, MABS(m_reglo[srcp.ireg()]), srcp.ireg());			// mov   reglo[srcp],srcp
			emit_fild_m64(dst, MABS(m_reglo[srcp.ireg()]));						// fild  reglo[srcp]
		}
	}

	// store the result and be done
	emit_fstp_p(dst, inst.size(), dstp);												// fstp  [dstp]
}


//-------------------------------------------------
//  op_ffrflt - process a FFRFLT opcode
//-------------------------------------------------

void drcbe_x86::op_ffrflt(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);
	const parameter &sizep = inst.param(2);
	assert(sizep.is_size());

	// general case
	if (sizep.size() == SIZE_DWORD)
		emit_fld_m32(dst, MABS(srcp.memory()));											// fld   [srcp]
	else if (sizep.size() == SIZE_QWORD)
		emit_fld_m64(dst, MABS(srcp.memory()));											// fld   [srcp]
	emit_fstp_p(dst, inst.size(), dstp);												// fstp  dstp
}


//-------------------------------------------------
//  op_frnds - process a FRNDS opcode
//-------------------------------------------------

void drcbe_x86::op_frnds(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);

	// general case
	emit_fld_p(dst, inst.size(), srcp);												// fld   srcp
	emit_fstp_m32(dst, MABS(&m_fptemp));											// fstp  [fptemp]
	emit_fld_m32(dst, MABS(&m_fptemp));											// fld   [fptemp]
	emit_fstp_p(dst, inst.size(), dstp);												// fstp  [dstp]
}


//-------------------------------------------------
//  op_fadd - process a FADD opcode
//-------------------------------------------------

void drcbe_x86::op_fadd(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter src1p(*this, inst.param(1), PTYPE_MF);
	be_parameter src2p(*this, inst.param(2), PTYPE_MF);
	normalize_commutative(src1p, src2p);

	// general case
	emit_fld_p(dst, inst.size(), src1p);												// fld   src1p
	emit_fld_p(dst, inst.size(), src2p);												// fld   src2p
	emit_faddp(dst);																	// faddp
	emit_fstp_p(dst, inst.size(), dstp);												// fstp  dstp
}


//-------------------------------------------------
//  op_fsub - process a FSUB opcode
//-------------------------------------------------

void drcbe_x86::op_fsub(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter src1p(*this, inst.param(1), PTYPE_MF);
	be_parameter src2p(*this, inst.param(2), PTYPE_MF);

	// general case
	emit_fld_p(dst, inst.size(), src1p);												// fld   src1p
	emit_fld_p(dst, inst.size(), src2p);												// fld   src2p
	emit_fsubp(dst);																	// fsubp
	emit_fstp_p(dst, inst.size(), dstp);												// fstp  dstp
}


//-------------------------------------------------
//  op_fcmp - process a FCMP opcode
//-------------------------------------------------

void drcbe_x86::op_fcmp(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_Z | FLAG_U);

	// normalize parameters
	be_parameter src1p(*this, inst.param(0), PTYPE_MF);
	be_parameter src2p(*this, inst.param(1), PTYPE_MF);

	// general case
	emit_fld_p(dst, inst.size(), src2p);												// fld   src2p
	emit_fld_p(dst, inst.size(), src1p);												// fld   src1p
	emit_fcompp(dst);																	// fcompp
	emit_fstsw_ax(dst);																// fnstsw ax
	emit_sahf(dst);																	// sahf
}


//-------------------------------------------------
//  op_fmul - process a FMUL opcode
//-------------------------------------------------

void drcbe_x86::op_fmul(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter src1p(*this, inst.param(1), PTYPE_MF);
	be_parameter src2p(*this, inst.param(2), PTYPE_MF);
	normalize_commutative(src1p, src2p);

	// general case
	emit_fld_p(dst, inst.size(), src1p);												// fld   src1p
	emit_fld_p(dst, inst.size(), src2p);												// fld   src2p
	emit_fmulp(dst);																	// fmulp
	emit_fstp_p(dst, inst.size(), dstp);												// fstp  dstp
}


//-------------------------------------------------
//  op_fdiv - process a FDIV opcode
//-------------------------------------------------

void drcbe_x86::op_fdiv(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter src1p(*this, inst.param(1), PTYPE_MF);
	be_parameter src2p(*this, inst.param(2), PTYPE_MF);

	// general case
	emit_fld_p(dst, inst.size(), src1p);												// fld   src1p
	emit_fld_p(dst, inst.size(), src2p);												// fld   src2p
	emit_fdivp(dst);																	// fdivp
	emit_fstp_p(dst, inst.size(), dstp);												// fstp  dstp
}


//-------------------------------------------------
//  op_fneg - process a FNEG opcode
//-------------------------------------------------

void drcbe_x86::op_fneg(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);

	// general case
	emit_fld_p(dst, inst.size(), srcp);												// fld   srcp
	emit_fchs(dst);																	// fchs
	emit_fstp_p(dst, inst.size(), dstp);												// fstp  dstp
}


//-------------------------------------------------
//  op_fabs - process a FABS opcode
//-------------------------------------------------

void drcbe_x86::op_fabs(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);

	// general case
	emit_fld_p(dst, inst.size(), srcp);												// fld   srcp
	emit_fabs(dst);																	// fabs
	emit_fstp_p(dst, inst.size(), dstp);												// fstp  dstp
}


//-------------------------------------------------
//  op_fsqrt - process a FSQRT opcode
//-------------------------------------------------

void drcbe_x86::op_fsqrt(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);

	// general case
	emit_fld_p(dst, inst.size(), srcp);												// fld   srcp
	emit_fsqrt(dst);																	// fsqrt
	emit_fstp_p(dst, inst.size(), dstp);												// fstp  dstp
}


//-------------------------------------------------
//  op_frecip - process a FRECIP opcode
//-------------------------------------------------

void drcbe_x86::op_frecip(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);

	// general case
	emit_fld1(dst);																	// fld1
	emit_fld_p(dst, inst.size(), srcp);												// fld   srcp
	emit_fdivp(dst);																	// fdivp
	emit_fstp_p(dst, inst.size(), dstp);												// fstp  dstp
}


//-------------------------------------------------
//  op_frsqrt - process a FRSQRT opcode
//-------------------------------------------------

void drcbe_x86::op_frsqrt(x86code *&dst, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);

	// general case
	emit_fld1(dst);																	// fld1
	emit_fld_p(dst, inst.size(), srcp);												// fld   srcp
	emit_fsqrt(dst);																	// fsqrt
	emit_fdivp(dst);																	// fdivp
	emit_fstp_p(dst, inst.size(), dstp);												// fstp  dstp
}



//**************************************************************************
//  MISCELLAENOUS FUNCTIONS
//**************************************************************************

//-------------------------------------------------
//  dmulu - perform a double-wide unsigned multiply
//-------------------------------------------------

int drcbe_x86::dmulu(UINT64 &dstlo, UINT64 &dsthi, UINT64 src1, UINT64 src2, int flags)
{
	// shortcut if we don't care about the high bits or the flags
	if (&dstlo == &dsthi && flags == 0)
	{
		dstlo = src1 * src2;
		return 0;
	}

	// fetch source values
	UINT64 a = src1;
	UINT64 b = src2;
	if (a == 0 || b == 0)
	{
		dsthi = dstlo = 0;
		return FLAG_Z;
	}

	// compute high and low parts first
	UINT64 lo = (UINT64)(UINT32)(a >> 0)  * (UINT64)(UINT32)(b >> 0);
	UINT64 hi = (UINT64)(UINT32)(a >> 32) * (UINT64)(UINT32)(b >> 32);

	// compute middle parts
	UINT64 prevlo = lo;
	UINT64 temp = (UINT64)(UINT32)(a >> 32)  * (UINT64)(UINT32)(b >> 0);
	lo += temp << 32;
	hi += (temp >> 32) + (lo < prevlo);

	prevlo = lo;
	temp = (UINT64)(UINT32)(a >> 0)  * (UINT64)(UINT32)(b >> 32);
	lo += temp << 32;
	hi += (temp >> 32) + (lo < prevlo);

	// store the results
	dsthi = hi;
	dstlo = lo;
	return ((hi >> 60) & FLAG_S) | ((dsthi != 0) << 1);
}


//-------------------------------------------------
//  dmuls - perform a double-wide signed multiply
//-------------------------------------------------

int drcbe_x86::dmuls(UINT64 &dstlo, UINT64 &dsthi, INT64 src1, INT64 src2, int flags)
{
	UINT64 lo, hi, prevlo;
	UINT64 a, b, temp;

	// shortcut if we don't care about the high bits or the flags
	if (&dstlo == &dsthi && flags == 0)
	{
		dstlo = src1 * src2;
		return 0;
	}

	// fetch absolute source values
	a = src1; if ((INT64)a < 0) a = -a;
	b = src2; if ((INT64)b < 0) b = -b;
	if (a == 0 || b == 0)
	{
		dsthi = dstlo = 0;
		return FLAG_Z;
	}

	// compute high and low parts first
	lo = (UINT64)(UINT32)(a >> 0)  * (UINT64)(UINT32)(b >> 0);
	hi = (UINT64)(UINT32)(a >> 32) * (UINT64)(UINT32)(b >> 32);

	// compute middle parts
	prevlo = lo;
	temp = (UINT64)(UINT32)(a >> 32)  * (UINT64)(UINT32)(b >> 0);
	lo += temp << 32;
	hi += (temp >> 32) + (lo < prevlo);

	prevlo = lo;
	temp = (UINT64)(UINT32)(a >> 0)  * (UINT64)(UINT32)(b >> 32);
	lo += temp << 32;
	hi += (temp >> 32) + (lo < prevlo);

	// adjust for signage
	if ((INT64)(src1 ^ src2) < 0)
	{
		hi = ~hi + (lo == 0);
		lo = ~lo + 1;
	}

	// store the results
	dsthi = hi;
	dstlo = lo;
	return ((hi >> 60) & FLAG_S) | ((dsthi != ((INT64)lo >> 63)) << 1);
}


//-------------------------------------------------
//  ddivu - perform a double-wide unsigned divide
//-------------------------------------------------

int drcbe_x86::ddivu(UINT64 &dstlo, UINT64 &dsthi, UINT64 src1, UINT64 src2)
{
	// do nothing if src2 == 0
	if (src2 == 0)
		return FLAG_V;

	// shortcut if no remainder
	dstlo = src1 / src2;
	if (dstlo != dsthi)
		dsthi = src1 % src2;
	return ((dstlo == 0) << 2) | ((dstlo >> 60) & FLAG_S);
}


//-------------------------------------------------
//  ddivs - perform a double-wide signed divide
//-------------------------------------------------

int drcbe_x86::ddivs(UINT64 &dstlo, UINT64 &dsthi, INT64 src1, INT64 src2)
{
	// do nothing if src2 == 0
	if (src2 == 0)
		return FLAG_V;

	// shortcut if no remainder
	dstlo = src1 / src2;
	if (dstlo != dsthi)
		dsthi = src1 % src2;
	return ((dstlo == 0) << 2) | ((dstlo >> 60) & FLAG_S);
}
