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

    uml.c

    Universal machine language definitions and classes.

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

    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:

    * UML optimizer:
        - constant folding

    * Write a back-end validator:
        - checks all combinations of memory/register/immediate on all params
        - checks behavior of all opcodes

    * Extend registers to 16? Depends on if PPC can use them

    * Support for FPU exceptions

    * New instructions?
        - FCOPYI, ICOPYF
            copy raw between float and integer registers

        - VALID opcode_desc,handle,param
            checksum/compare code referenced by opcode_desc; if not
            matching, generate exception with handle,param

        - RECALL handle
            change code at caller to call handle in the future

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

#include "emu.h"
#include "drcuml.h"
#include "drcumlsh.h"
#include <setjmp.h>

using namespace uml;



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

#define VALIDATE_BACKEND		(0)
#define LOG_SIMPLIFICATIONS		(0)



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

// opcode validation condition/flag valid bitmasks
#define OPFLAGS_NONE	0x00
#define OPFLAGS_C		FLAG_C
#define OPFLAGS_SZ		(FLAG_S | FLAG_Z)
#define OPFLAGS_SZC		(FLAG_S | FLAG_Z | FLAG_C)
#define OPFLAGS_SZV		(FLAG_S | FLAG_Z | FLAG_V)
#define OPFLAGS_SZVC	(FLAG_S | FLAG_Z | FLAG_V | FLAG_C)
#define OPFLAGS_UZC		(FLAG_U | FLAG_Z | FLAG_C)
#define OPFLAGS_ALL		0x1f
#define OPFLAGS_P1		0x81
#define OPFLAGS_P2		0x82
#define OPFLAGS_P3		0x83
#define OPFLAGS_P4		0x84

// parameter input/output states
#define PIO_IN			0x01
#define PIO_OUT			0x02
#define PIO_INOUT		(PIO_IN | PIO_OUT)

// parameter sizes
#define PSIZE_4			SIZE_DWORD
#define PSIZE_8			SIZE_QWORD
#define PSIZE_OP		0x80
#define PSIZE_P1		0x81
#define PSIZE_P2		0x82
#define PSIZE_P3		0x83
#define PSIZE_P4		0x84

// basic parameter types
#define PTYPES_NONE		0
#define PTYPES_IMM		(1 << parameter::PTYPE_IMMEDIATE)
#define PTYPES_IREG		(1 << parameter::PTYPE_INT_REGISTER)
#define PTYPES_FREG		(1 << parameter::PTYPE_FLOAT_REGISTER)
#define PTYPES_VREG		(1 << parameter::PTYPE_VECTOR_REGISTER)
#define PTYPES_MVAR		(1 << parameter::PTYPE_MAPVAR)
#define PTYPES_MEM		(1 << parameter::PTYPE_MEMORY)
#define PTYPES_SIZE		(1 << parameter::PTYPE_SIZE)
#define PTYPES_SCSIZE	(1 << parameter::PTYPE_SIZE_SCALE)
#define PTYPES_SPSIZE	(1 << parameter::PTYPE_SIZE_SPACE)
#define PTYPES_HANDLE	(1 << parameter::PTYPE_CODE_HANDLE)
#define PTYPES_LABEL	(1 << parameter::PTYPE_CODE_LABEL)
#define PTYPES_CFUNC	(1 << parameter::PTYPE_C_FUNCTION)
#define PTYPES_ROUND	(1 << parameter::PTYPE_ROUNDING)
#define PTYPES_STR		(1 << parameter::PTYPE_STRING)

// special parameter types
#define PTYPES_PTR		(PTYPES_MEM | 0x1000)
#define PTYPES_STATE	(PTYPES_MEM | 0x2000)

// combinations of types
#define PTYPES_IRM		(PTYPES_IREG | PTYPES_MEM)
#define PTYPES_FRM		(PTYPES_FREG | PTYPES_MEM)
#define PTYPES_IMV		(PTYPES_IMM | PTYPES_MVAR)
#define PTYPES_IANY		(PTYPES_IRM | PTYPES_IMV)
#define PTYPES_FANY		(PTYPES_FRM)



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

// macro to simplify the table
#define PINFO(inout, size, types)									{ PIO_##inout, PSIZE_##size, PTYPES_##types }
#define OPINFO0(op,str,sizes,cond,iflag,oflag,mflag)				{ OP_##op, str, sizes, cond, OPFLAGS_##iflag, OPFLAGS_##oflag, OPFLAGS_##mflag, { { 0 } } },
#define OPINFO1(op,str,sizes,cond,iflag,oflag,mflag,p0)				{ OP_##op, str, sizes, cond, OPFLAGS_##iflag, OPFLAGS_##oflag, OPFLAGS_##mflag, { p0 } },
#define OPINFO2(op,str,sizes,cond,iflag,oflag,mflag,p0,p1)			{ OP_##op, str, sizes, cond, OPFLAGS_##iflag, OPFLAGS_##oflag, OPFLAGS_##mflag, { p0, p1 } },
#define OPINFO3(op,str,sizes,cond,iflag,oflag,mflag,p0,p1,p2)		{ OP_##op, str, sizes, cond, OPFLAGS_##iflag, OPFLAGS_##oflag, OPFLAGS_##mflag, { p0, p1, p2 } },
#define OPINFO4(op,str,sizes,cond,iflag,oflag,mflag,p0,p1,p2,p3)	{ OP_##op, str, sizes, cond, OPFLAGS_##iflag, OPFLAGS_##oflag, OPFLAGS_##mflag, { p0, p1, p2, p3 } },

// opcode validation table
const opcode_info instruction::s_opcode_info_table[OP_MAX] =
{
	OPINFO0(INVALID, "invalid",  4,   false, NONE, NONE, NONE)

	// Compile-time opcodes
	OPINFO1(HANDLE,  "handle",   4,   false, NONE, NONE, NONE, PINFO(IN, OP, HANDLE))
	OPINFO2(HASH,    "hash",     4,   false, NONE, NONE, NONE, PINFO(IN, OP, IMV), PINFO(IN, OP, IMV))
	OPINFO1(LABEL,   "label",    4,   false, NONE, NONE, NONE, PINFO(IN, OP, LABEL))
	OPINFO1(COMMENT, "comment",  4,   false, NONE, NONE, NONE, PINFO(IN, OP, STR))
	OPINFO2(MAPVAR,  "mapvar",   4,   false, NONE, NONE, NONE, PINFO(OUT, OP, MVAR), PINFO(IN, OP, IMV))

	// Control Flow Operations
	OPINFO0(NOP,     "nop",      4,   false, NONE, NONE, NONE)
	OPINFO1(DEBUG,   "debug",    4,   false, NONE, NONE, ALL,  PINFO(IN, OP, IANY))
	OPINFO1(EXIT,    "exit",     4,   true,  NONE, NONE, ALL,  PINFO(IN, OP, IANY))
	OPINFO3(HASHJMP, "hashjmp",  4,   false, NONE, NONE, ALL,  PINFO(IN, OP, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, HANDLE))
	OPINFO1(JMP,     "jmp",      4,   true,  NONE, NONE, NONE, PINFO(IN, OP, LABEL))
	OPINFO2(EXH,     "exh",      4,   true,  NONE, NONE, ALL,  PINFO(IN, OP, HANDLE), PINFO(IN, OP, IANY))
	OPINFO1(CALLH,   "callh",    4,   true,  NONE, NONE, ALL,  PINFO(IN, OP, HANDLE))
	OPINFO0(RET,     "ret",      4,   true,  NONE, NONE, ALL)
	OPINFO2(CALLC,   "callc",    4,   true,  NONE, NONE, ALL,  PINFO(IN, OP, CFUNC), PINFO(IN, OP, PTR))
	OPINFO2(RECOVER, "recover",  4,   false, NONE, NONE, ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, MVAR))

	// Internal Register Operations
	OPINFO1(SETFMOD, "setfmod",  4,   false, NONE, NONE, ALL,  PINFO(IN, OP, IANY))
	OPINFO1(GETFMOD, "getfmod",  4,   false, NONE, NONE, ALL,  PINFO(OUT, OP, IRM))
	OPINFO1(GETEXP,  "getexp",   4,   false, NONE, NONE, ALL,  PINFO(OUT, OP, IRM))
	OPINFO2(GETFLGS, "getflgs",  4,   false, P2,   NONE, ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IMV))
	OPINFO1(SAVE,    "save",     4,   false, ALL,  NONE, ALL,  PINFO(OUT, OP, STATE))
	OPINFO1(RESTORE, "restore",  4,   false, NONE, ALL,  ALL,  PINFO(IN, OP, STATE))

	// Integer Operations
	OPINFO4(LOAD,    "!load",    4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, PTR), PINFO(IN, 4, IANY), PINFO(IN, OP, SCSIZE))
	OPINFO4(LOADS,   "!loads",   4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, PTR), PINFO(IN, 4, IANY), PINFO(IN, OP, SCSIZE))
	OPINFO4(STORE,   "!store",   4|8, false, NONE, NONE, ALL,  PINFO(IN, OP, PTR), PINFO(IN, 4, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, SCSIZE))
	OPINFO3(READ,    "!read",    4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, IRM), PINFO(IN, 4, IANY), PINFO(IN, OP, SPSIZE))
	OPINFO4(READM,   "!readm",   4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, IRM), PINFO(IN, 4, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, SPSIZE))
	OPINFO3(WRITE,   "!write",   4|8, false, NONE, NONE, ALL,  PINFO(IN, 4, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, SPSIZE))
	OPINFO4(WRITEM,  "!writem",  4|8, false, NONE, NONE, ALL,  PINFO(IN, 4, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, SPSIZE))
	OPINFO2(CARRY,   "!carry",   4|8, false, NONE, C,    ALL,  PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO1(SET,     "!set",     4|8, true,  NONE, NONE, ALL,  PINFO(OUT, OP, IRM))
	OPINFO2(MOV,     "!mov",     4|8, true,  NONE, NONE, NONE, PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY))
	OPINFO3(SEXT,    "!sext",    4|8, false, NONE, SZ,   ALL,  PINFO(OUT, OP, IRM), PINFO(IN, P3, IANY), PINFO(IN, OP, SIZE))
	OPINFO4(ROLAND,  "!roland",  4|8, false, NONE, SZ,   ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO4(ROLINS,  "!rolins",  4|8, false, NONE, SZ,   ALL,  PINFO(INOUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO3(ADD,     "!add",     4|8, false, NONE, SZVC, ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO3(ADDC,    "!addc",    4|8, false, C,    SZVC, ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO3(SUB,     "!sub",     4|8, false, NONE, SZVC, ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO3(SUBB,    "!subb",    4|8, false, C,    SZVC, ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO2(CMP,     "!cmp",     4|8, false, NONE, SZVC, ALL,  PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO4(MULU,    "!mulu",    4|8, false, NONE, SZV,  ALL,  PINFO(OUT, OP, IRM), PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO4(MULS,    "!muls",    4|8, false, NONE, SZV,  ALL,  PINFO(OUT, OP, IRM), PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO4(DIVU,    "!divu",    4|8, false, NONE, SZV,  ALL,  PINFO(OUT, OP, IRM), PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO4(DIVS,    "!divs",    4|8, false, NONE, SZV,  ALL,  PINFO(OUT, OP, IRM), PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO3(AND,     "!and",     4|8, false, NONE, SZ,   ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO2(TEST,    "!test",    4|8, false, NONE, SZ,   ALL,  PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO3(OR,      "!or",      4|8, false, NONE, SZ,   ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO3(XOR,     "!xor",     4|8, false, NONE, SZ,   ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO2(LZCNT,   "!lzcnt",   4|8, false, NONE, SZ,   ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY))
	OPINFO2(BSWAP,   "!bswap",   4|8, false, NONE, SZ,   ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY))
	OPINFO3(SHL,     "!shl",     4|8, false, NONE, SZC,  ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO3(SHR,     "!shr",     4|8, false, NONE, SZC,  ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO3(SAR,     "!sar",     4|8, false, NONE, SZC,  ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO3(ROL,     "!rol",     4|8, false, NONE, SZC,  ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO3(ROLC,    "!rolc",    4|8, false, C,    SZC,  ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO3(ROR,     "!ror",     4|8, false, NONE, SZC,  ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))
	OPINFO3(RORC,    "!rorc",    4|8, false, C,    SZC,  ALL,  PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY))

	// Floating Point Operations
	OPINFO3(FLOAD,   "f#load",   4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, FRM), PINFO(IN, OP, PTR), PINFO(IN, 4, IANY))
	OPINFO3(FSTORE,  "f#store",  4|8, false, NONE, NONE, ALL,  PINFO(IN, OP, PTR), PINFO(IN, 4, IANY), PINFO(IN, OP, FRM))
	OPINFO3(FREAD,   "f#read",   4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, FRM), PINFO(IN, 4, IANY), PINFO(IN, OP, SPSIZE))
	OPINFO3(FWRITE,  "f#write",  4|8, false, NONE, NONE, ALL,  PINFO(IN, 4, IANY), PINFO(IN, OP, FANY), PINFO(IN, OP, SPSIZE))
	OPINFO2(FMOV,    "f#mov",    4|8, true,  NONE, NONE, NONE, PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY))
	OPINFO4(FTOINT,  "f#toint",  4|8, false, NONE, NONE, ALL,  PINFO(OUT, P3, IRM), PINFO(IN, OP, FANY), PINFO(IN, OP, SIZE), PINFO(IN, OP, ROUND))
	OPINFO3(FFRINT,  "f#frint",  4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, FRM), PINFO(IN, P3, IANY), PINFO(IN, OP, SIZE))
	OPINFO3(FFRFLT,  "f#frflt",  4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, FRM), PINFO(IN, P3, FANY), PINFO(IN, OP, SIZE))
	OPINFO2(FRNDS,   "f#rnds",     8, false, NONE, NONE, ALL,  PINFO(OUT, OP, FRM), PINFO(IN, P3, FANY))
	OPINFO3(FADD,    "f#add",    4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY), PINFO(IN, OP, FANY))
	OPINFO3(FSUB,    "f#sub",    4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY), PINFO(IN, OP, FANY))
	OPINFO2(FCMP,    "f#cmp",    4|8, false, NONE, UZC,  ALL,  PINFO(IN, OP, FANY), PINFO(IN, OP, FANY))
	OPINFO3(FMUL,    "f#mul",    4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY), PINFO(IN, OP, FANY))
	OPINFO3(FDIV,    "f#div",    4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY), PINFO(IN, OP, FANY))
	OPINFO2(FNEG,    "f#neg",    4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY))
	OPINFO2(FABS,    "f#abs",    4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY))
	OPINFO2(FSQRT,   "f#sqrt",   4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY))
	OPINFO2(FRECIP,  "f#recip",  4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY))
	OPINFO2(FRSQRT,  "f#rsqrt",  4|8, false, NONE, NONE, ALL,  PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY))
};



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

//-------------------------------------------------
//  rol32 - perform a 32-bit left rotate
//-------------------------------------------------

inline UINT32 rol32(UINT32 source, UINT8 count)
{
	count &= 31;
	return (source << count) | (source >> (32 - count));
}


//-------------------------------------------------
//  rol64 - perform a 64-bit left rotate
//-------------------------------------------------

inline UINT64 rol64(UINT64 source, UINT8 count)
{
	count &= 63;
	return (source << count) | (source >> (64 - count));
}



//**************************************************************************
//  UML CODE HANDLE
//**************************************************************************

//-------------------------------------------------
//  code_handle - constructor
//-------------------------------------------------

uml::code_handle::code_handle(drcuml_state &drcuml, const char *name)
	: m_code(reinterpret_cast<drccodeptr *>(drcuml.cache().alloc_near(sizeof(drccodeptr)))),
	  m_string(name),
	  m_next(NULL),
	  m_drcuml(drcuml)
{
	if (m_code == NULL)
		throw std::bad_alloc();
	*m_code = NULL;
}


//-------------------------------------------------
//  set_codeptr - set a new code pointer
//-------------------------------------------------

void uml::code_handle::set_codeptr(drccodeptr code)
{
	assert(*m_code == NULL);
	assert_in_cache(m_drcuml.cache(), code);
	*m_code = code;
}



//**************************************************************************
//  UML INSTRUCTION
//**************************************************************************

//-------------------------------------------------
//  instruction - constructor
//-------------------------------------------------

uml::instruction::instruction()
	: m_opcode(OP_INVALID),
	  m_condition(COND_ALWAYS),
	  m_flags(0),
	  m_size(4),
	  m_numparams(0)
{
}


//-------------------------------------------------
//  configure - configure an opcode with no
//  parameters
//-------------------------------------------------

void uml::instruction::configure(opcode_t op, UINT8 size, condition_t condition)
{
	// fill in the instruction
	m_opcode = (opcode_t)(UINT8)op;
	m_size = size;
	m_condition = condition;
	m_flags = 0;
	m_numparams = 0;

	// validate
	validate();
}


//-------------------------------------------------
//  configure - configure an opcode with 1
//  parameter
//-------------------------------------------------

void uml::instruction::configure(opcode_t op, UINT8 size, parameter p0, condition_t condition)
{
	// fill in the instruction
	m_opcode = (opcode_t)(UINT8)op;
	m_size = size;
	m_condition = condition;
	m_flags = 0;
	m_numparams = 1;
	m_param[0] = p0;

	// validate
	validate();
}


//-------------------------------------------------
//  configure - configure an opcode with 2
//  parameters
//-------------------------------------------------

void uml::instruction::configure(opcode_t op, UINT8 size, parameter p0, parameter p1, condition_t condition)
{
	// fill in the instruction
	m_opcode = (opcode_t)(UINT8)op;
	m_size = size;
	m_condition = condition;
	m_flags = 0;
	m_numparams = 2;
	m_param[0] = p0;
	m_param[1] = p1;

	// validate
	validate();
}


//-------------------------------------------------
//  configure - configure an opcode with 3
//  parameters
//-------------------------------------------------

void uml::instruction::configure(opcode_t op, UINT8 size, parameter p0, parameter p1, parameter p2, condition_t condition)
{
	// fill in the instruction
	m_opcode = (opcode_t)(UINT8)op;
	m_size = size;
	m_condition = condition;
	m_flags = 0;
	m_numparams = 3;
	m_param[0] = p0;
	m_param[1] = p1;
	m_param[2] = p2;

	// validate
	validate();
}


//-------------------------------------------------
//  configure - configure an opcode with 4
//  parameters
//-------------------------------------------------

void uml::instruction::configure(opcode_t op, UINT8 size, parameter p0, parameter p1, parameter p2, parameter p3, condition_t condition)
{
	// fill in the instruction
	m_opcode = (opcode_t)(UINT8)op;
	m_size = size;
	m_condition = condition;
	m_flags = 0;
	m_numparams = 4;
	m_param[0] = p0;
	m_param[1] = p1;
	m_param[2] = p2;
	m_param[3] = p3;

	// validate
	validate();
}


//-------------------------------------------------
//  simplify - simplify instructions that have
//  immediate values we can evaluate at compile
//  time
//-------------------------------------------------

void uml::instruction::simplify()
{
	// can't simplify if flags are in play
	if (m_flags != 0)
		return;

	static const UINT64 instsizemask[] = { 0, 0, 0, 0, 0xffffffff, 0, 0, 0, U64(0xffffffffffffffff) };
	static const UINT64 paramsizemask[] = { 0xff, 0xffff, 0xffffffff, U64(0xffffffffffffffff) };

	// loop until we've simplified all we can
	opcode_t origop;
	do
	{
		// switch off the opcode
		origop = m_opcode;
		switch (m_opcode)
		{
			// READM: convert to READ if the mask is wide open
			case OP_READM:
				if (m_param[2].is_immediate_value(paramsizemask[m_param[3].size()]))
				{
					m_opcode = OP_READ;
					m_numparams = 2;
					m_param[2] = m_param[3];
				}
				break;

			// WRITEM: convert to WRITE if the mask is wide open
			case OP_WRITEM:
				if (m_param[2].is_immediate_value(paramsizemask[m_param[3].size()]))
				{
					m_opcode = OP_WRITE;
					m_numparams = 2;
					m_param[2] = m_param[3];
				}
				break;

			// SET: convert to MOV if constant condition
			case OP_SET:
				if (m_condition == COND_ALWAYS)
					convert_to_mov_immediate(1);
				break;

			// MOV: convert to NOP if move-to-self
			case OP_MOV:
				if (m_param[0] == m_param[1])
					nop();
				break;

			// SEXT: convert immediates to MOV
			case OP_SEXT:
				if (m_param[1].is_immediate())
					switch (m_param[2].size())
					{
						case SIZE_BYTE:		convert_to_mov_immediate((INT8)m_param[1].immediate());		break;
						case SIZE_WORD:		convert_to_mov_immediate((INT16)m_param[1].immediate());	break;
						case SIZE_DWORD:	convert_to_mov_immediate((INT32)m_param[1].immediate());	break;
						case SIZE_QWORD:	convert_to_mov_immediate((INT64)m_param[1].immediate());	break;
						case SIZE_DQWORD:	fatalerror("Invalid SEXT target size");						break;
					}
				break;

			// ROLAND: convert to MOV if all immediate, or to ROL or AND if one is not needed, or to SHL/SHR if the mask is right
			case OP_ROLAND:
				if (m_param[1].is_immediate() && m_param[2].is_immediate() && m_param[3].is_immediate())
				{
					assert(m_size == 4 || m_size == 8);
					if (m_size == 4)
						convert_to_mov_immediate(rol32(m_param[1].immediate(), m_param[2].immediate()) & m_param[3].immediate());
					else
						convert_to_mov_immediate(rol64(m_param[1].immediate(), m_param[2].immediate()) & m_param[3].immediate());
				}
				else if (m_param[2].is_immediate_value(0))
				{
					m_opcode = OP_AND;
					m_numparams = 3;
					m_param[2] = m_param[3];
				}
				else if (m_param[3].is_immediate_value(instsizemask[m_size]))
				{
					m_opcode = OP_ROL;
					m_numparams = 3;
				}
				else if (m_param[2].is_immediate() && m_param[3].is_immediate_value((U64(0xffffffffffffffff) << m_param[2].immediate()) & instsizemask[m_size]))
				{
					m_opcode = OP_SHL;
					m_numparams = 3;
				}
				else if (m_param[2].is_immediate() && m_param[3].is_immediate_value(instsizemask[m_size] >> (8 * m_size - m_param[2].immediate())))
				{
					m_opcode = OP_SHR;
					m_numparams = 3;
					m_param[2] = 8 * m_size - m_param[2].immediate();
				}
				break;

			// ROLINS: convert to ROLAND if the mask is full
			case OP_ROLINS:
				if (m_param[3].is_immediate_value(instsizemask[m_size]))
					m_opcode = OP_ROLAND;
				break;

			// ADD: convert to MOV if immediate, or if adding 0
			case OP_ADD:
				if (m_param[1].is_immediate() && m_param[2].is_immediate())
					convert_to_mov_immediate(m_param[1].immediate() + m_param[2].immediate());
				else if (m_param[1].is_immediate_value(0))
					convert_to_mov_param(2);
				else if (m_param[2].is_immediate_value(0))
					convert_to_mov_param(1);
				break;

			// SUB: convert to MOV if immediate, or if subtracting 0
			case OP_SUB:
				if (m_param[1].is_immediate() && m_param[2].is_immediate())
					convert_to_mov_immediate(m_param[1].immediate() - m_param[2].immediate());
				else if (m_param[2].is_immediate_value(0))
					convert_to_mov_param(1);
				break;

			// CMP: no-op if no flags needed, compare i0 to i0 if the parameters are equal
			case OP_CMP:
				if (m_flags == 0)
					nop();
				else if (m_param[0] == m_param[1])
					cmp(I0, I0);
				break;

			// MULU: convert simple form to MOV if immediate, or if multiplying by 0
			case OP_MULU:
				if (m_param[0] == m_param[1])
				{
					if (m_param[2].is_immediate_value(0) || m_param[3].is_immediate_value(0))
						convert_to_mov_immediate(0);
					else if (m_param[2].is_immediate() && m_param[3].is_immediate())
					{
						if (m_size == 4)
							convert_to_mov_immediate((UINT32)((UINT32)m_param[1].immediate() * (UINT32)m_param[2].immediate()));
						else if (m_size == 8)
							convert_to_mov_immediate((UINT64)((UINT64)m_param[1].immediate() * (UINT64)m_param[2].immediate()));
					}
				}
				break;

			// MULS: convert simple form to MOV if immediate, or if multiplying by 0
			case OP_MULS:
				if (m_param[0] == m_param[1])
				{
					if (m_param[2].is_immediate_value(0) || m_param[3].is_immediate_value(0))
						convert_to_mov_immediate(0);
					else if (m_param[2].is_immediate() && m_param[3].is_immediate())
					{
						if (m_size == 4)
							convert_to_mov_immediate((INT32)((INT32)m_param[1].immediate() * (INT32)m_param[2].immediate()));
						else if (m_size == 8)
							convert_to_mov_immediate((INT64)((INT64)m_param[1].immediate() * (INT64)m_param[2].immediate()));
					}
				}
				break;

			// DIVU: convert simple form to MOV if immediate, or if dividing with 0
			case OP_DIVU:
				if (m_param[0] == m_param[1] && !m_param[3].is_immediate_value(0))
				{
					if (m_param[2].is_immediate_value(0))
						convert_to_mov_immediate(0);
					else if (m_param[2].is_immediate() && m_param[3].is_immediate())
					{
						if (m_size == 4)
							convert_to_mov_immediate((UINT32)((UINT32)m_param[1].immediate() / (UINT32)m_param[2].immediate()));
						else if (m_size == 8)
							convert_to_mov_immediate((UINT64)((UINT64)m_param[1].immediate() / (UINT64)m_param[2].immediate()));
					}
				}
				break;

			// DIVS: convert simple form to MOV if immediate, or if dividing with 0
			case OP_DIVS:
				if (m_param[0] == m_param[1] && !m_param[3].is_immediate_value(0))
				{
					if (m_param[2].is_immediate_value(0))
						convert_to_mov_immediate(0);
					else if (m_param[2].is_immediate() && m_param[3].is_immediate())
					{
						if (m_size == 4)
							convert_to_mov_immediate((INT32)((INT32)m_param[1].immediate() / (INT32)m_param[2].immediate()));
						else if (m_size == 8)
							convert_to_mov_immediate((INT64)((INT64)m_param[1].immediate() / (INT64)m_param[2].immediate()));
					}
				}
				break;

			// AND: convert to MOV if immediate, or if anding against 0 or 0xffffffff
			case OP_AND:
				if (m_param[1].is_immediate_value(0) || m_param[2].is_immediate_value(0))
					convert_to_mov_immediate(0);
				else if (m_param[1].is_immediate() && m_param[2].is_immediate())
					convert_to_mov_immediate(m_param[1].immediate() & m_param[2].immediate());
				else if (m_param[1].is_immediate_value(instsizemask[m_size]))
					convert_to_mov_param(2);
				else if (m_param[2].is_immediate_value(instsizemask[m_size]))
					convert_to_mov_param(1);
				break;

			// TEST: no-op if no flags needed
			case OP_TEST:
				if (m_flags == 0)
					nop();
				break;

			// OR: convert to MOV if immediate, or if oring against 0 or 0xffffffff
			case OP_OR:
				if (m_param[1].is_immediate_value(instsizemask[m_size]) || m_param[2].is_immediate_value(instsizemask[m_size]))
					convert_to_mov_immediate(instsizemask[m_size]);
				else if (m_param[1].is_immediate() && m_param[2].is_immediate())
					convert_to_mov_immediate(m_param[1].immediate() | m_param[2].immediate());
				else if (m_param[1].is_immediate_value(0))
					convert_to_mov_param(2);
				else if (m_param[2].is_immediate_value(0))
					convert_to_mov_param(1);
				break;

			// XOR: convert to MOV if immediate, or if xoring against 0
			case OP_XOR:
				if (m_param[1].is_immediate() && m_param[2].is_immediate())
					convert_to_mov_immediate(m_param[1].immediate() ^ m_param[2].immediate());
				else if (m_param[1].is_immediate_value(0))
					convert_to_mov_param(2);
				else if (m_param[2].is_immediate_value(0))
					convert_to_mov_param(1);
				break;

			// LZCNT: convert to MOV if immediate
			case OP_LZCNT:
				if (m_param[1].is_immediate())
				{
					if (m_size == 4)
						convert_to_mov_immediate(count_leading_zeros(m_param[1].immediate()));
					else if (m_size == 8)
					{
						if ((m_param[1].immediate() >> 32) == 0)
							convert_to_mov_immediate(32 + count_leading_zeros(m_param[1].immediate()));
						else
							convert_to_mov_immediate(count_leading_zeros(m_param[1].immediate() >> 32));
					}
				}
				break;

			// BSWAP: convert to MOV if immediate
			case OP_BSWAP:
				if (m_param[1].is_immediate())
				{
					if (m_size == 4)
						convert_to_mov_immediate(FLIPENDIAN_INT32(m_param[1].immediate()));
					else if (m_size == 8)
						convert_to_mov_immediate(FLIPENDIAN_INT64(m_param[1].immediate()));
				}
				break;

			// SHL: convert to MOV if immediate or shifting by 0
			case OP_SHL:
				if (m_param[1].is_immediate() && m_param[2].is_immediate())
					convert_to_mov_immediate(m_param[1].immediate() << m_param[2].immediate());
				else if (m_param[2].is_immediate_value(0))
					convert_to_mov_param(1);
				break;

			// SHR: convert to MOV if immediate or shifting by 0
			case OP_SHR:
				if (m_param[1].is_immediate() && m_param[2].is_immediate())
				{
					if (m_size == 4)
						convert_to_mov_immediate((UINT32)m_param[1].immediate() >> m_param[2].immediate());
					else if (m_size == 8)
						convert_to_mov_immediate((UINT64)m_param[1].immediate() >> m_param[2].immediate());
				}
				else if (m_param[2].is_immediate_value(0))
					convert_to_mov_param(1);
				break;

			// SAR: convert to MOV if immediate or shifting by 0
			case OP_SAR:
				if (m_param[1].is_immediate() && m_param[2].is_immediate())
				{
					if (m_size == 4)
						convert_to_mov_immediate((INT32)m_param[1].immediate() >> m_param[2].immediate());
					else if (m_size == 8)
						convert_to_mov_immediate((INT64)m_param[1].immediate() >> m_param[2].immediate());
				}
				else if (m_param[2].is_immediate_value(0))
					convert_to_mov_param(1);
				break;

			// ROL: convert to NOP if immediate or rotating by 0
			case OP_ROL:
				if (m_param[1].is_immediate() && m_param[2].is_immediate())
				{
					if (m_size == 4)
						convert_to_mov_immediate(rol32(m_param[1].immediate(), m_param[2].immediate()));
					else if (m_size == 8)
						convert_to_mov_immediate(rol64(m_param[1].immediate(), m_param[2].immediate()));
				}
				else if (m_param[2].is_immediate_value(0))
					convert_to_mov_param(1);
				break;

			// ROR: convert to NOP if immediate or rotating by 0
			case OP_ROR:
				if (m_param[1].is_immediate() && m_param[2].is_immediate())
				{
					if (m_size == 4)
						convert_to_mov_immediate(rol32(m_param[1].immediate(), 32 - m_param[2].immediate()));
					else if (m_size == 8)
						convert_to_mov_immediate(rol64(m_param[1].immediate(), 64 - m_param[2].immediate()));
				}
				else if (m_param[2].is_immediate_value(0))
					convert_to_mov_param(1);
				break;

			// FMOV: convert to NOP if move-to-self
			case OP_FMOV:
				if (m_param[0] == m_param[1])
					nop();
				break;

			default:
				break;
		}

	/*
        if (LOG_SIMPLIFICATIONS && memcmp(&orig, inst, sizeof(orig)) != 0)
        {
            astring disasm1, disasm2;
            orig.disasm(disasm1, block->drcuml);
            inst->disasm(disasm2, block->drcuml);
            mame_printf_debug("Simplified: %-50.50s -> %s\n", disasm1.cstr(), disasm2.cstr());
        }
    */

		// loop until we stop changing opcodes
	} while (m_opcode != origop);
}


//-------------------------------------------------
//  validate - verify that the instruction created
//  meets all requirements
//-------------------------------------------------

void uml::instruction::validate()
{
#ifdef MAME_DEBUG
	const opcode_info &opinfo = s_opcode_info_table[m_opcode];
	assert(opinfo.opcode == m_opcode);

	// validate raw information
	assert(m_opcode != OP_INVALID && m_opcode < OP_MAX);
	assert(m_size == 1 || m_size == 2 || m_size == 4 || m_size == 8);

	// validate against opcode limits
	assert((opinfo.sizes & m_size) != 0);
	assert(m_condition == COND_ALWAYS || opinfo.condition);

	// validate each parameter
	for (int pnum = 0; pnum < m_numparams; pnum++)
	{
		// ensure the type is valid
		const parameter &param = m_param[pnum];
		assert((opinfo.param[pnum].typemask >> param.type()) & 1);
	}

	// make sure we aren't missing any parameters
	if (m_numparams < ARRAY_LENGTH(opinfo.param))
		assert(opinfo.param[m_numparams].typemask == 0);
#endif
}


//-------------------------------------------------
//  input_flags - return the effective input flags
//  based on any conditions encoded in an
//  instruction
//-------------------------------------------------

UINT8 uml::instruction::input_flags() const
{
	static const UINT8 flags_for_condition[] =
	{
		FLAG_Z,						// COND_Z
		FLAG_Z,						// COND_NZ
		FLAG_S,						// COND_S
		FLAG_S,						// COND_NS
		FLAG_C,						// COND_C
		FLAG_C,						// COND_NC
		FLAG_V,						// COND_V
		FLAG_V,						// COND_NV
		FLAG_U,						// COND_U
		FLAG_U,						// COND_NU
		FLAG_C | FLAG_Z,			// COND_A
		FLAG_C | FLAG_Z,			// COND_BE
		FLAG_S | FLAG_V | FLAG_Z,	// COND_G
		FLAG_S | FLAG_V | FLAG_Z,	// COND_LE
		FLAG_S | FLAG_V,			// COND_L
		FLAG_S | FLAG_V				// COND_GE
	};

	UINT8 flags = s_opcode_info_table[m_opcode].inflags;
	if (flags & 0x80)
		flags = m_param[flags - OPFLAGS_P1].immediate() & OPFLAGS_ALL;
	if (m_condition != COND_ALWAYS)
		flags |= flags_for_condition[m_condition & 0x0f];
	return flags;
}


//-------------------------------------------------
//  output_flags - return the effective output
//  flags based on any conditions encoded in an
//  instruction
//-------------------------------------------------

UINT8 uml::instruction::output_flags() const
{
	UINT8 flags = s_opcode_info_table[m_opcode].outflags;
	if (flags & 0x80)
		flags = m_param[flags - OPFLAGS_P1].immediate() & OPFLAGS_ALL;
	return flags;
}


//-------------------------------------------------
//  modified_flags - return the effective output
//  flags based on any conditions encoded in an
//  instruction
//-------------------------------------------------

UINT8 uml::instruction::modified_flags() const
{
	return s_opcode_info_table[m_opcode].modflags;
}


//-------------------------------------------------
//  disasm - disassemble an instruction to the
//  given buffer
//-------------------------------------------------

const char *uml::instruction::disasm(astring &buffer, drcuml_state *drcuml) const
{
	static const char *const conditions[] = { "z", "nz", "s", "ns", "c", "nc", "v", "nv", "u", "nu", "a", "be", "g", "le", "l", "ge" };
	static const char *const pound_size[] = { "?", "?", "?", "?", "s", "?", "?", "?", "d" };
	static const char *const bang_size[] = { "?", "b", "h", "?", "", "?", "?", "?", "d" };
	static const char *const fmods[] = { "trunc", "round", "ceil", "floor", "default" };
	static const char *const spaces[] = { "program", "data", "io", "3", "4", "5", "6", "7" };
	static const char *const sizes[] = { "byte", "word", "dword", "qword" };

	const opcode_info &opinfo = s_opcode_info_table[m_opcode];

	assert(m_opcode != OP_INVALID && m_opcode < OP_MAX);

	// start with the raw mnemonic and substitute sizes
	buffer.reset();
	for (const char *opsrc = opinfo.mnemonic; *opsrc != 0; opsrc++)
		if (*opsrc == '!')
			buffer.catprintf("%s", bang_size[m_size]);
		else if (*opsrc == '#')
			buffer.catprintf("%s", pound_size[m_size]);
		else
			buffer.cat(*opsrc);

	// pad to 8 spaces
	int pad = 8 - buffer.len();
	for (int ch = 0; ch < pad; ch++)
		buffer.cat(' ');

	// iterate through parameters
	for (int pnum = 0; pnum < m_numparams; pnum++)
	{
		const parameter &param = m_param[pnum];

		// start with a comma for all except the first parameter
		if (pnum != 0)
			buffer.cat(',');

		// ouput based on type
		switch (param.type())
		{
			// immediates have several special cases
			case parameter::PTYPE_IMMEDIATE:
				{
					// determine the size of the immediate
					int size;
					switch (opinfo.param[pnum].size)
					{
						case PSIZE_4:	size = 4; break;
						case PSIZE_8:	size = 8; break;
						case PSIZE_P1:	size = 1 << m_param[0].size(); break;
						case PSIZE_P2:	size = 1 << m_param[1].size(); break;
						case PSIZE_P3:	size = 1 << m_param[2].size(); break;
						case PSIZE_P4:	size = 1 << m_param[3].size(); break;
						default:
						case PSIZE_OP:	size = m_size; break;
					}

					// truncate to size
					UINT64 value = param.immediate();
					if (size == 1) value = (UINT8)value;
					if (size == 2) value = (UINT16)value;
					if (size == 4) value = (UINT32)value;
					if ((UINT32)value == value)
						buffer.catprintf("$%X", (UINT32)value);
					else
						buffer.catprintf("$%X%08X", (UINT32)(value >> 32), (UINT32)value);
				}
				break;

			// immediates have several special cases
			case parameter::PTYPE_SIZE:
				buffer.catprintf("%s", sizes[param.size()]);
				break;

			// size + address space immediate
			case parameter::PTYPE_SIZE_SPACE:
				buffer.catprintf("%s_%s", spaces[param.space()], sizes[param.size()]);
				break;

			// size + scale immediate
			case parameter::PTYPE_SIZE_SCALE:
				{
					int scale = param.scale();
					int size  = param.size();
					if (scale == size)
						buffer.catprintf("%s", sizes[size]);
					else
						buffer.catprintf("%s_x%d", sizes[size], 1 << scale);
				}
				break;

			// fmod immediate
			case parameter::PTYPE_ROUNDING:
				buffer.catprintf("%s", fmods[param.rounding()]);
				break;

			// integer registers
			case parameter::PTYPE_INT_REGISTER:
				buffer.catprintf("i%d", param.ireg() - REG_I0);
				break;

			// floating point registers
			case parameter::PTYPE_FLOAT_REGISTER:
				buffer.catprintf("f%d", param.freg() - REG_F0);
				break;

			// map variables
			case parameter::PTYPE_MAPVAR:
				buffer.catprintf("m%d", param.mapvar() - MAPVAR_M0);
				break;

			// memory
			case parameter::PTYPE_MEMORY:
			{
				const char *symbol;
				UINT32 symoffset;

				// symbol
				if (drcuml != NULL && (symbol = drcuml->symbol_find(param.memory(), &symoffset)) != NULL)
				{
					if (symoffset == 0)
						buffer.catprintf("[%s]", symbol);
					else
						buffer.catprintf("[%s+$%X]", symbol, symoffset);
				}

				// cache memory
				else if (drcuml != NULL && drcuml->cache().contains_pointer(param.memory()))
					buffer.catprintf("[+$%X]", (UINT32)(FPTR)((drccodeptr)param.memory() - drcuml->cache().near()));

				// general memory
				else
					buffer.catprintf("[[$%p]]", param.memory());
				break;
			}

			// string pointer
			case parameter::PTYPE_STRING:
				buffer.catprintf("%s", (const char *)(FPTR)param.string());
				break;

			// handle pointer
			case parameter::PTYPE_CODE_HANDLE:
				buffer.catprintf("%s", param.handle().string());
				break;

			default:
				buffer.catprintf("???");
				break;
		}
	}

	// if there's a condition, append it
	if (m_condition != COND_ALWAYS)
		buffer.catprintf(",%s", conditions[m_condition & 0x0f]);

	// if there are flags, append them
	if (m_flags != 0)
	{
		buffer.cat(',');
		if (m_flags & FLAG_U)
			buffer.cat('U');
		if (m_flags & FLAG_S)
			buffer.cat('S');
		if (m_flags & FLAG_Z)
			buffer.cat('Z');
		if (m_flags & FLAG_V)
			buffer.cat('V');
		if (m_flags & FLAG_C)
			buffer.cat('C');
	}
	return buffer;
}
