diff options
29 files changed, 23886 insertions, 11218 deletions
diff --git a/.gitattributes b/.gitattributes index e2e00f22e37..00d5bb9874e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -60,8 +60,20 @@ src/emu/cpu/cp1610/1610dasm.c svneol=native#text/plain src/emu/cpu/cp1610/cp1610.c svneol=native#text/plain src/emu/cpu/cp1610/cp1610.h svneol=native#text/plain src/emu/cpu/cpu.mak svneol=native#text/plain +src/emu/cpu/drcbec.c svneol=native#text/plain +src/emu/cpu/drcbeut.c svneol=native#text/plain +src/emu/cpu/drcbeut.h svneol=native#text/plain +src/emu/cpu/drcbex64.c svneol=native#text/plain +src/emu/cpu/drcbex86.c svneol=native#text/plain +src/emu/cpu/drccache.c svneol=native#text/plain +src/emu/cpu/drccache.h svneol=native#text/plain src/emu/cpu/drcfe.c svneol=native#text/plain src/emu/cpu/drcfe.h svneol=native#text/plain +src/emu/cpu/drcuml.c svneol=native#text/plain +src/emu/cpu/drcuml.h svneol=native#text/plain +src/emu/cpu/drcumld.c svneol=native#text/plain +src/emu/cpu/drcumld.h svneol=native#text/plain +src/emu/cpu/drcumlsh.h svneol=native#text/plain src/emu/cpu/dsp32/dsp32.c svneol=native#text/plain src/emu/cpu/dsp32/dsp32.h svneol=native#text/plain src/emu/cpu/dsp32/dsp32dis.c svneol=native#text/plain @@ -262,8 +274,6 @@ src/emu/cpu/minx/minxopcf.h svneol=native#text/plain src/emu/cpu/minx/minxops.h svneol=native#text/plain src/emu/cpu/mips/dismips.c svneol=native#text/plain src/emu/cpu/mips/dismips.mak svneol=native#text/plain -src/emu/cpu/mips/mdrc64.c svneol=native#text/plain -src/emu/cpu/mips/mdrcold.c svneol=native#text/plain src/emu/cpu/mips/mips3.c svneol=native#text/plain src/emu/cpu/mips/mips3.h svneol=native#text/plain src/emu/cpu/mips/mips3com.c svneol=native#text/plain @@ -48,6 +48,7 @@ CROSS_BUILD_OSD = $(OSD) endif + #------------------------------------------------- # specify OS target, which further differentiates # the underlying OS; supported values are: @@ -105,8 +106,10 @@ endif # uncomment next line to include the internal profiler # PROFILER = 1 -# uncomment next line to use DRC MIPS3 engine -X86_MIPS3_DRC = 1 +# uncomment the force the universal DRC to always use the C backend +# you may need to do this if your target architecture does not have +# a native backend +# FORCE_DRC_C_BACKEND = 1 # uncomment next line to use DRC PowerPC engine X86_PPC_DRC = 1 diff --git a/src/emu/cpu/cpu.mak b/src/emu/cpu/cpu.mak index cabf78f27dc..589169f9238 100644 --- a/src/emu/cpu/cpu.mak +++ b/src/emu/cpu/cpu.mak @@ -18,27 +18,59 @@ CPUOBJ = $(EMUOBJ)/cpu # Dynamic recompiler objects #------------------------------------------------- +DRCOBJ = \ + $(CPUOBJ)/drcbec.o \ + $(CPUOBJ)/drcbeut.o \ + $(CPUOBJ)/drccache.o \ + $(CPUOBJ)/drcfe.o \ + $(CPUOBJ)/drcuml.o \ + $(CPUOBJ)/drcumld.o \ + +DRCDEPS = \ + $(CPUSRC)/drcbeut.h \ + $(CPUSRC)/drccache.h \ + $(CPUSRC)/drcfe.h \ + $(CPUSRC)/drcuml.h \ + $(CPUSRC)/drcumld.h \ + $(CPUSRC)/drcumlsh.h \ + +# fixme - need to make this work for other target architectures (PPC) + +ifndef FORCE_DRC_C_BACKEND ifdef PTR64 -DRCOBJ = $(CPUOBJ)/x64drc.o $(CPUOBJ)/x86log.o $(CPUOBJ)/drcfe.o +DRCOBJ += \ + $(CPUOBJ)/drcbex64.o \ + $(CPUOBJ)/x64drc.o \ + $(CPUOBJ)/x86log.o \ -DRCDEPS = $(CPUSRC)/x86emit.h \ - $(CPUSRC)/x64drc.c \ - $(CPUSRC)/x64drc.h \ +DRCDEPS += \ + $(CPUSRC)/x86emit.h \ + $(CPUSRC)/x64drc.c \ + $(CPUSRC)/x64drc.h \ -$(DRCOBJ): $(DRCDEPS) +DEFS += -DNATIVE_DRC=drcbe_x64_be_interface else -DRCOBJ = $(CPUOBJ)/x86drc.o $(CPUOBJ)/x86log.o $(CPUOBJ)/drcfe.o +DRCOBJ += \ + $(CPUOBJ)/drcbex86.o \ + $(CPUOBJ)/x86drc.o \ + $(CPUOBJ)/x86log.o \ -DRCDEPS = $(CPUSRC)/x86emit.h \ - $(CPUSRC)/x86drc.c \ - $(CPUSRC)/x86drc.h \ +DRCDEPS += \ + $(CPUSRC)/x86emit.h \ + $(CPUSRC)/x86drc.c \ + $(CPUSRC)/x86drc.h \ -$(DRCOBJ): $(DRCDEPS) +DEFS += -DNATIVE_DRC=drcbe_x86_be_interface endif +endif + + +$(DRCOBJ): $(DRCDEPS) + #------------------------------------------------- @@ -793,23 +825,11 @@ CPUDEFS += -DHAS_RM7000=$(if $(filter RM7000,$(CPUS)),1,0) ifneq ($(filter R4600 R4650 R4700 R5000 QED5271 RM7000,$(CPUS)),) OBJDIRS += $(CPUOBJ)/mips -CPUOBJS += $(CPUOBJ)/mips/mips3com.o +CPUOBJS += $(CPUOBJ)/mips/mips3com.o $(CPUOBJ)/mips/mips3drc.o $(CPUOBJ)/mips/mips3fe.o $(DRCOBJ) DBGOBJS += $(CPUOBJ)/mips/mips3dsm.o - -ifdef X86_MIPS3_DRC -CPUOBJS += $(CPUOBJ)/mips/mips3drc.o $(CPUOBJ)/mips/mips3fe.o $(DRCOBJ) -else -CPUOBJS += $(CPUOBJ)/mips/mips3.o endif -endif - -$(CPUOBJ)/mips/mips3.o: $(CPUSRC)/mips/mips3.c \ - $(CPUSRC)/mips/mips3.h \ - $(CPUSRC)/mips/mips3com.h $(CPUOBJ)/mips/mips3drc.o: $(CPUSRC)/mips/mips3drc.c \ - $(CPUSRC)/mips/mdrcold.c \ - $(CPUSRC)/mips/mdrc64.c \ $(CPUSRC)/mips/mips3.h \ $(CPUSRC)/mips/mips3com.h \ $(CPUSRC)/mips/mips3fe.h \ diff --git a/src/emu/cpu/drcbec.c b/src/emu/cpu/drcbec.c new file mode 100644 index 00000000000..930bb2b9c30 --- /dev/null +++ b/src/emu/cpu/drcbec.c @@ -0,0 +1,1806 @@ +/*************************************************************************** + + drcbec.c + + Interpreted C core back-end for the universal machine language. + + Copyright Aaron Giles + Released for general non-commercial use under the MAME license + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include "drcuml.h" +#include "drcbeut.h" +#include "eminline.h" +#include "debugger.h" +#include <math.h> + +#ifdef _MSC_VER +#include <float.h> +#endif + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* define a bit to match each possible condition, starting at bit 12 */ +#define ZBIT (0x1000 << (DRCUML_COND_Z & 15)) +#define NZBIT (0x1000 << (DRCUML_COND_NZ & 15)) +#define SBIT (0x1000 << (DRCUML_COND_S & 15)) +#define NSBIT (0x1000 << (DRCUML_COND_NS & 15)) +#define CBIT (0x1000 << (DRCUML_COND_C & 15)) +#define NCBIT (0x1000 << (DRCUML_COND_NC & 15)) +#define VBIT (0x1000 << (DRCUML_COND_V & 15)) +#define NVBIT (0x1000 << (DRCUML_COND_NV & 15)) +#define UBIT (0x1000 << (DRCUML_COND_U & 15)) +#define NUBIT (0x1000 << (DRCUML_COND_NU & 15)) +#define ABIT (0x1000 << (DRCUML_COND_A & 15)) +#define AEBIT (0x1000 << (DRCUML_COND_AE & 15)) +#define BBIT (0x1000 << (DRCUML_COND_B & 15)) +#define BEBIT (0x1000 << (DRCUML_COND_BE & 15)) +#define GBIT (0x1000 << (DRCUML_COND_G & 15)) +#define GEBIT (0x1000 << (DRCUML_COND_GE & 15)) +#define LBIT (0x1000 << (DRCUML_COND_L & 15)) +#define LEBIT (0x1000 << (DRCUML_COND_LE & 15)) + + + +/*************************************************************************** + MACROS +***************************************************************************/ + +/* opcode format: + + bits 31..28 == number of words following the opcode itself (0-15) + bits 27..12 == bitmask specify which condition code we care about + bits 11.. 2 == opcode + bit 1 == flags/condition summary (0 if no condition/flags, 1 otherwise) + bit 0 == operation size (0=32-bit, 1=64-bit) +*/ + +/* build a short opcode from the raw opcode and size */ +#define MAKE_OPCODE_SHORT(op, size, condflags) \ + ((((size) == 8) << 0) | (((condflags) != 0) << 1) | ((op) << 2)) + +/* build a full opcode from the raw opcode, size, condition/flags, and immediate count */ +#define MAKE_OPCODE_FULL(op, size, condflags, pwords) \ + (MAKE_OPCODE_SHORT(op, size, condflags) | ((condflags & 0x80) ? (0x1000 << ((condflags) & 15)) : 0) | ((pwords) << 28)) + +/* extract various parts of the opcode */ +#define OPCODE_GET_SHORT(op) ((op) & 0xfff) +#define OPCODE_PASS_CONDITION(op,f) (((op) & condition_map[f]) != 0) +#define OPCODE_FAIL_CONDITION(op,f) (((op) & condition_map[f]) == 0) +#define OPCODE_GET_PWORDS(op) ((op) >> 28) + +/* shorthand for accessing parameters in the instruction stream */ +#define PARAM0 (*inst[0].puint32) +#define PARAM1 (*inst[1].puint32) +#define PARAM2 (*inst[2].puint32) +#define PARAM3 (*inst[3].puint32) + +#define DPARAM0 (*inst[0].puint64) +#define DPARAM1 (*inst[1].puint64) +#define DPARAM2 (*inst[2].puint64) +#define DPARAM3 (*inst[3].puint64) + +#define FSPARAM0 (*inst[0].pfloat) +#define FSPARAM1 (*inst[1].pfloat) +#define FSPARAM2 (*inst[2].pfloat) +#define FSPARAM3 (*inst[3].pfloat) + +#define FDPARAM0 (*inst[0].pdouble) +#define FDPARAM1 (*inst[1].pdouble) +#define FDPARAM2 (*inst[2].pdouble) +#define FDPARAM3 (*inst[3].pdouble) + +/* compute C and V flags for 32-bit add/subtract */ +#define FLAGS32_C_ADD(a,b) ((UINT32)~(a) < (UINT32)(b)) +#define FLAGS32_C_SUB(a,b) ((UINT32)(b) > (UINT32)(a)) +#define FLAGS32_V_SUB(r,a,b) (((((a) ^ (b)) & ((a) ^ (r))) >> 30) & DRCUML_FLAG_V) +#define FLAGS32_V_ADD(r,a,b) (((~((a) ^ (b)) & ((a) ^ (r))) >> 30) & DRCUML_FLAG_V) + +/* compute N and Z flags for 32-bit operations */ +#define FLAGS32_NZ(v) ((((v) >> 28) & DRCUML_FLAG_S) | (((UINT32)(v) == 0) << 2)) +#define FLAGS32_NZCV_ADD(r,a,b) (FLAGS32_NZ(r) | FLAGS32_C_ADD(a,b) | FLAGS32_V_ADD(r,a,b)) +#define FLAGS32_NZCV_SUB(r,a,b) (FLAGS32_NZ(r) | FLAGS32_C_SUB(a,b) | FLAGS32_V_SUB(r,a,b)) + +/* compute C and V flags for 64-bit add/subtract */ +#define FLAGS64_C_ADD(a,b) ((UINT64)~(a) < (UINT64)(b)) +#define FLAGS64_C_SUB(a,b) ((UINT64)(b) > (UINT64)(a)) +#define FLAGS64_V_SUB(r,a,b) (((((a) ^ (b)) & ((a) ^ (r))) >> 62) & DRCUML_FLAG_V) +#define FLAGS64_V_ADD(r,a,b) (((~((a) ^ (b)) & ((a) ^ (r))) >> 62) & DRCUML_FLAG_V) + +/* compute N and Z flags for 64-bit operations */ +#define FLAGS64_NZ(v) ((((v) >> 60) & DRCUML_FLAG_S) | (((UINT64)(v) == 0) << 2)) +#define FLAGS64_NZCV_ADD(r,a,b) (FLAGS64_NZ(r) | FLAGS64_C_ADD(a,b) | FLAGS64_V_ADD(r,a,b)) +#define FLAGS64_NZCV_SUB(r,a,b) (FLAGS64_NZ(r) | FLAGS64_C_SUB(a,b) | FLAGS64_V_SUB(r,a,b)) + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* internal backend-specific state */ +struct _drcbe_state +{ + drcuml_state * drcuml; /* pointer back to our owner */ + drccache * cache; /* pointer to the cache */ + drcuml_machine_state state; /* state of the machine */ + drchash_state * hash; /* hash table state */ + drcmap_state * map; /* code map */ + drclabel_list * labels; /* label list */ +}; + + +/* union to simplify accessing data via the instruction stream */ +typedef union _drcbec_instruction drcbec_instruction; +union _drcbec_instruction +{ + UINT32 i; + void * v; + char * c; + UINT8 * puint8; + INT8 * pint8; + UINT16 * puint16; + INT16 * pint16; + UINT32 * puint32; + INT32 * pint32; + UINT64 * puint64; + INT64 * pint64; + float * pfloat; + double * pdouble; + void (*cfunc)(void *); + const drcuml_codehandle *handle; + const drcbec_instruction *inst; + const drcbec_instruction **pinst; +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* primary back-end callbacks */ +static drcbe_state *drcbec_alloc(drcuml_state *drcuml, drccache *cache, UINT32 flags, int modes, int addrbits, int ignorebits); +static void drcbec_free(drcbe_state *drcbe); +static void drcbec_reset(drcbe_state *drcbe); +static int drcbec_execute(drcbe_state *state, drcuml_codehandle *entry); +static void drcbec_generate(drcbe_state *drcbe, drcuml_block *block, const drcuml_instruction *instlist, UINT32 numinst); +static int drcbec_hash_exists(drcbe_state *state, UINT32 mode, UINT32 pc); + +/* private helper functions */ +static void output_parameter(drcbe_state *drcbe, drcbec_instruction **dstptr, void **immedptr, int size, int immedsize, const drcuml_parameter *param); +static void fixup_label(void *parameter, drccodeptr labelcodeptr); +static int dmulu(UINT64 *dstlo, UINT64 *dsthi, UINT64 src1, UINT64 src2, int flags); +static int dmuls(UINT64 *dstlo, UINT64 *dsthi, INT64 src1, INT64 src2, int flags); + + + +/*************************************************************************** + TABLES +***************************************************************************/ + +static const UINT32 condition_map[] = +{ + /* ..... */ NCBIT | NVBIT | NZBIT | NSBIT | NUBIT | ABIT | AEBIT | GBIT | GEBIT, + /* ....C */ CBIT | NVBIT | NZBIT | NSBIT | NUBIT | BEBIT | BBIT | GBIT | GEBIT, + /* ...V. */ NCBIT | VBIT | NZBIT | NSBIT | NUBIT | ABIT | AEBIT | LEBIT | LBIT, + /* ...VC */ CBIT | VBIT | NZBIT | NSBIT | NUBIT | BEBIT | BBIT | LEBIT | LBIT, + /* ..Z.. */ NCBIT | NVBIT | ZBIT | NSBIT | NUBIT | BEBIT | AEBIT | LEBIT | GEBIT, + /* ..Z.C */ CBIT | NVBIT | ZBIT | NSBIT | NUBIT | BEBIT | BBIT | LEBIT | GEBIT, + /* ..ZV. */ NCBIT | VBIT | ZBIT | NSBIT | NUBIT | BEBIT | AEBIT | LEBIT | LBIT, + /* ..ZVC */ CBIT | VBIT | ZBIT | NSBIT | NUBIT | BEBIT | BBIT | LEBIT | LBIT, + /* .S... */ NCBIT | NVBIT | NZBIT | SBIT | NUBIT | ABIT | AEBIT | LEBIT | LBIT, + /* .S..C */ CBIT | NVBIT | NZBIT | SBIT | NUBIT | BEBIT | BBIT | LEBIT | LBIT, + /* .S.V. */ NCBIT | VBIT | NZBIT | SBIT | NUBIT | ABIT | AEBIT | GBIT | GEBIT, + /* .S.VC */ CBIT | VBIT | NZBIT | SBIT | NUBIT | BEBIT | BBIT | GBIT | GEBIT, + /* .SZ.. */ NCBIT | NVBIT | ZBIT | SBIT | NUBIT | BEBIT | AEBIT | LEBIT | LBIT, + /* .SZ.C */ CBIT | NVBIT | ZBIT | SBIT | NUBIT | BEBIT | BBIT | LEBIT | LBIT, + /* .SZV. */ NCBIT | VBIT | ZBIT | SBIT | NUBIT | BEBIT | AEBIT | LEBIT | GEBIT, + /* .SZVC */ CBIT | VBIT | ZBIT | SBIT | NUBIT | BEBIT | BBIT | LEBIT | GEBIT, + /* U.... */ NCBIT | NVBIT | NZBIT | NSBIT | UBIT | ABIT | AEBIT | GBIT | GEBIT, + /* U...C */ CBIT | NVBIT | NZBIT | NSBIT | UBIT | BEBIT | BBIT | GBIT | GEBIT, + /* U..V. */ NCBIT | VBIT | NZBIT | NSBIT | UBIT | ABIT | AEBIT | LEBIT | LBIT, + /* U..VC */ CBIT | VBIT | NZBIT | NSBIT | UBIT | BEBIT | BBIT | LEBIT | LBIT, + /* U.Z.. */ NCBIT | NVBIT | ZBIT | NSBIT | UBIT | BEBIT | AEBIT | LEBIT | GEBIT, + /* U.Z.C */ CBIT | NVBIT | ZBIT | NSBIT | UBIT | BEBIT | BBIT | LEBIT | GEBIT, + /* U.ZV. */ NCBIT | VBIT | ZBIT | NSBIT | UBIT | BEBIT | AEBIT | LEBIT | LBIT, + /* U.ZVC */ CBIT | VBIT | ZBIT | NSBIT | UBIT | BEBIT | BBIT | LEBIT | LBIT, + /* US... */ NCBIT | NVBIT | NZBIT | SBIT | UBIT | ABIT | AEBIT | LEBIT | LBIT, + /* US..C */ CBIT | NVBIT | NZBIT | SBIT | UBIT | BEBIT | BBIT | LEBIT | LBIT, + /* US.V. */ NCBIT | VBIT | NZBIT | SBIT | UBIT | ABIT | AEBIT | GBIT | GEBIT, + /* US.VC */ CBIT | VBIT | NZBIT | SBIT | UBIT | BEBIT | BBIT | GBIT | GEBIT, + /* USZ.. */ NCBIT | NVBIT | ZBIT | SBIT | UBIT | BEBIT | AEBIT | LEBIT | LBIT, + /* USZ.C */ CBIT | NVBIT | ZBIT | SBIT | UBIT | BEBIT | BBIT | LEBIT | LBIT, + /* USZV. */ NCBIT | VBIT | ZBIT | SBIT | UBIT | BEBIT | AEBIT | LEBIT | GEBIT, + /* USZVC */ CBIT | VBIT | ZBIT | SBIT | UBIT | BEBIT | BBIT | LEBIT | GEBIT +}; + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +static UINT64 immediate_zero = 0; + +const drcbe_interface drcbe_c_be_interface = +{ + drcbec_alloc, + drcbec_free, + drcbec_reset, + drcbec_execute, + drcbec_generate, + drcbec_hash_exists +}; + + + +/*************************************************************************** + BACKEND CALLBACKS +***************************************************************************/ + +/*------------------------------------------------- + drcbec_alloc - allocate back-end-specific + state +-------------------------------------------------*/ + +static drcbe_state *drcbec_alloc(drcuml_state *drcuml, drccache *cache, UINT32 flags, int modes, int addrbits, int ignorebits) +{ + /* allocate space in the cache for our state */ + drcbe_state *drcbe = drccache_memory_alloc(cache, sizeof(*drcbe)); + if (drcbe == NULL) + return NULL; + memset(drcbe, 0, sizeof(*drcbe)); + + /* remember our pointers */ + drcbe->drcuml = drcuml; + drcbe->cache = cache; + + /* allocate hash tables */ + drcbe->hash = drchash_alloc(cache, modes, addrbits, ignorebits); + if (drcbe->hash == NULL) + return NULL; + + /* allocate code map */ + drcbe->map = drcmap_alloc(cache, 0); + if (drcbe->map == NULL) + return NULL; + + /* allocate a label tracker */ + drcbe->labels = drclabel_list_alloc(cache); + if (drcbe->labels == NULL) + return NULL; + + return drcbe; +} + + +/*------------------------------------------------- + drcbec_free - free back-end specific state +-------------------------------------------------*/ + +static void drcbec_free(drcbe_state *drcbe) +{ +} + + +/*------------------------------------------------- + drcbec_reset - reset back-end specific state +-------------------------------------------------*/ + +static void drcbec_reset(drcbe_state *drcbe) +{ + /* reset our hash tables */ + drchash_reset(drcbe->hash); + drchash_set_default_codeptr(drcbe->hash, NULL); +} + + +/*------------------------------------------------- + drcbec_generate - generate code +-------------------------------------------------*/ + +static void drcbec_generate(drcbe_state *drcbe, drcuml_block *block, const drcuml_instruction *instlist, UINT32 numinst) +{ + drccodeptr *cachetop; + drcbec_instruction *base; + drcbec_instruction *dst; + int inum; + + /* tell all of our utility objects that a block is beginning */ + drchash_block_begin(drcbe->hash, block, instlist, numinst); + drclabel_block_begin(drcbe->labels, block); + drcmap_block_begin(drcbe->map, block); + + /* begin codegen; fail if we can't */ + cachetop = drccache_begin_codegen(drcbe->cache, numinst * sizeof(drcbec_instruction) * 4); + if (cachetop == NULL) + drcuml_block_abort(block); + + /* compute the base by aligning the cache top to an even multiple of drcbec_instruction */ + base = (drcbec_instruction *)(((FPTR)*cachetop + sizeof(drcbec_instruction) - 1) & ~(sizeof(drcbec_instruction) - 1)); + dst = base; + + /* generate code by copying the instructions and extracting immediates */ + for (inum = 0; inum < numinst; inum++) + { + const drcuml_instruction *inst = &instlist[inum]; + drcuml_instruction modified_inst; + int immedsize, immedbytes, immedwords, pnum; + void *immed; + + /* handle most instructions generally, but a few special cases */ + switch (inst->opcode) + { + /* when we hit a HANDLE opcode, register the current pointer for the handle */ + case DRCUML_OP_HANDLE: + assert(inst->numparams == 1); + assert(inst->param[0].type == DRCUML_PTYPE_MEMORY); + drcuml_handle_set_codeptr((drcuml_codehandle *)(FPTR)inst->param[0].value, (drccodeptr)dst); + break; + + /* when we hit a HASH opcode, register the current pointer for the mode/PC */ + case DRCUML_OP_HASH: + /* we already verified the parameter count and types above */ + drchash_set_codeptr(drcbe->hash, inst->param[0].value, inst->param[1].value, (drccodeptr)dst); + break; + + /* when we hit a LABEL opcode, register the current pointer for the label */ + case DRCUML_OP_LABEL: + assert(inst->numparams == 1); + assert(inst->param[0].type == DRCUML_PTYPE_IMMEDIATE); + drclabel_set_codeptr(drcbe->labels, inst->param[0].value, (drccodeptr)dst); + break; + + /* ignore COMMENT opcodes */ + case DRCUML_OP_COMMENT: + break; + + /* when we hit a MAPVAR opcode, log the change for the current PC */ + case DRCUML_OP_MAPVAR: + assert(inst->numparams == 2); + assert(inst->param[0].type == DRCUML_PTYPE_MAPVAR); + assert(inst->param[1].type == DRCUML_PTYPE_IMMEDIATE); + drcmap_set_value(drcbe->map, (drccodeptr)dst, inst->param[0].value, inst->param[1].value); + break; + + /* JMP instructions need to resolve their labels */ + case DRCUML_OP_JMP: + assert(inst->numparams == 1); + assert(inst->param[0].type == DRCUML_PTYPE_IMMEDIATE); + (dst++)->i = MAKE_OPCODE_FULL(inst->opcode, inst->size, inst->condflags, 1); + dst->inst = (drcbec_instruction *)drclabel_get_codeptr(drcbe->labels, inst->param[0].value, fixup_label, dst); + dst++; + break; + + /* generically handle everything else */ + default: + + /* for RECOVER opcodes, we need to fixup the second parameter into an immediate */ + if (inst->opcode == DRCUML_OP_RECOVER) + { + assert(inst->param[1].type == DRCUML_PTYPE_MAPVAR); + modified_inst = *inst; + modified_inst.param[1].type = DRCUML_PTYPE_IMMEDIATE; + inst = &modified_inst; + } + + /* determine the maximum size of an immediate; mostly this is just the instruction size */ + immedsize = inst->size; + if (inst->opcode == DRCUML_OP_FFRFD || inst->opcode == DRCUML_OP_FFRI8) + immedsize = 8; + + /* count how many bytes of immediates we need */ + immedbytes = 0; + for (pnum = 0; pnum < inst->numparams; pnum++) + if (inst->param[pnum].type == DRCUML_PTYPE_MAPVAR || + (inst->param[pnum].type == DRCUML_PTYPE_IMMEDIATE && inst->param[pnum].value != 0)) + immedbytes += immedsize; + + /* compute how many instruction words we need for that */ + immedwords = (immedbytes + sizeof(drcbec_instruction) - 1) / sizeof(drcbec_instruction); + + /* first item is the opcode, size, condition flags and length */ + (dst++)->i = MAKE_OPCODE_FULL(inst->opcode, inst->size, inst->condflags, inst->numparams + immedwords); + + /* immediates start after parameters */ + immed = dst + inst->numparams; + + /* output each of the parameters */ + for (pnum = 0; pnum < inst->numparams; pnum++) + output_parameter(drcbe, &dst, &immed, inst->size, immedsize, &inst->param[pnum]); + + /* point past the end of the immediates */ + dst += immedwords; + break; + } + } + + /* complete codegen */ + *cachetop = (drccodeptr)dst; + drccache_end_codegen(drcbe->cache); + + /* tell all of our utility objects that the block is finished */ + drchash_block_end(drcbe->hash, block); + drclabel_block_end(drcbe->labels, block); + drcmap_block_end(drcbe->map, block); +} + + +/*------------------------------------------------- + drcbec_hash_exists - return true if the + given mode/pc exists in the hash table +-------------------------------------------------*/ + +static int drcbec_hash_exists(drcbe_state *state, UINT32 mode, UINT32 pc) +{ + return drchash_code_exists(state->hash, mode, pc); +} + + + +/*************************************************************************** + BACK-END EXECUTION +***************************************************************************/ + +/*------------------------------------------------- + drcbec_execute - execute a block of code + registered at the given mode/pc +-------------------------------------------------*/ + +static int drcbec_execute(drcbe_state *drcbe, drcuml_codehandle *entry) +{ + const drcbec_instruction *callstack[32]; + const drcbec_instruction *newinst; + const drcbec_instruction *inst; + UINT32 temp32; + UINT64 temp64; + int shift; + UINT8 flags = 0; + UINT8 sp = 0; + + /* get the entry point */ + inst = (const drcbec_instruction *)drcuml_handle_codeptr(entry); + assert_in_cache(drcbe->cache, inst); + + /* loop while we have cycles */ + while (TRUE) + { + UINT32 opcode = (inst++)->i; + switch (OPCODE_GET_SHORT(opcode)) + { + /* ----------------------- Control Flow Operations ----------------------- */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_HANDLE, 4, 0): /* HANDLE handle */ + case MAKE_OPCODE_SHORT(DRCUML_OP_HASH, 4, 0): /* HASH mode,pc */ + case MAKE_OPCODE_SHORT(DRCUML_OP_LABEL, 4, 0): /* LABEL imm */ + case MAKE_OPCODE_SHORT(DRCUML_OP_COMMENT, 4, 0): /* COMMENT string */ + case MAKE_OPCODE_SHORT(DRCUML_OP_MAPVAR, 4, 0): /* MAPVAR mapvar,value */ + + /* these opcodes should be processed at compile-time only */ + fatalerror("Unexpected opcode"); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_DEBUG, 4, 0): /* DEBUG pc */ + CALL_DEBUGGER(PARAM0); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_HASHJMP, 4, 0): /* HASHJMP mode,pc,handle */ + sp = 0; + newinst = (const drcbec_instruction *)drchash_get_codeptr(drcbe->hash, PARAM0, PARAM1); + if (newinst == NULL) + { + assert(sp < ARRAY_LENGTH(callstack)); + drcbe->state.exp.l = PARAM1; + newinst = (const drcbec_instruction *)drcuml_handle_codeptr(inst[2].handle); + callstack[sp++] = inst; + } + assert_in_cache(drcbe->cache, newinst); + inst = newinst; + continue; + + case MAKE_OPCODE_SHORT(DRCUML_OP_EXIT, 4, 1): /* EXIT src1[,c] */ + if (OPCODE_FAIL_CONDITION(opcode, flags)) + break; + /* fall through... */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_EXIT, 4, 0): + return PARAM0; + + case MAKE_OPCODE_SHORT(DRCUML_OP_JMP, 4, 1): /* JMP imm[,c] */ + if (OPCODE_FAIL_CONDITION(opcode, flags)) + break; + /* fall through... */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_JMP, 4, 0): + newinst = inst[0].inst; + assert_in_cache(drcbe->cache, newinst); + inst = newinst; + continue; + + case MAKE_OPCODE_SHORT(DRCUML_OP_CALLH, 4, 1): /* CALLH handle[,c] */ + if (OPCODE_FAIL_CONDITION(opcode, flags)) + break; + /* fall through... */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_CALLH, 4, 0): + assert(sp < ARRAY_LENGTH(callstack)); + newinst = (const drcbec_instruction *)drcuml_handle_codeptr(inst[0].handle); + assert_in_cache(drcbe->cache, newinst); + callstack[sp++] = inst + OPCODE_GET_PWORDS(opcode); + inst = newinst; + continue; + + case MAKE_OPCODE_SHORT(DRCUML_OP_RET, 4, 1): /* RET [c] */ + if (OPCODE_FAIL_CONDITION(opcode, flags)) + break; + /* fall through... */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_RET, 4, 0): + assert(sp > 0); + newinst = callstack[--sp]; + assert_in_cache(drcbe->cache, newinst); + inst = newinst; + continue; + + case MAKE_OPCODE_SHORT(DRCUML_OP_EXH, 4, 1): /* EXH handle,param[,c] */ + if (OPCODE_FAIL_CONDITION(opcode, flags)) + break; + /* fall through... */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_EXH, 4, 0): + assert(sp < ARRAY_LENGTH(callstack)); + newinst = (const drcbec_instruction *)drcuml_handle_codeptr(inst[0].handle); + assert_in_cache(drcbe->cache, newinst); + drcbe->state.exp.l = PARAM1; + callstack[sp++] = inst; + inst = newinst; + continue; + + case MAKE_OPCODE_SHORT(DRCUML_OP_CALLC, 4, 1): /* CALLC func,ptr[,c] */ + if (OPCODE_FAIL_CONDITION(opcode, flags)) + break; + /* fall through... */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_CALLC, 4, 0): + (*inst[0].cfunc)(inst[1].v); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_RECOVER, 4, 0): /* RECOVER dst,mapvar */ + assert(sp > 0); + PARAM0 = drcmap_get_value(drcbe->map, (drccodeptr)callstack[0], PARAM1); + break; + + + /* ----------------------- Internal Register Operations ----------------------- */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_SETFMOD, 4, 0): /* SETFMOD src */ + drcbe->state.fmod = PARAM0; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_GETFMOD, 4, 0): /* GETFMOD dst */ + PARAM0 = drcbe->state.fmod; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_GETEXP, 4, 0): /* GETEXP dst */ + PARAM0 = drcbe->state.exp.l; + break; + + + /* ----------------------- 32-Bit Integer Operations ----------------------- */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_LOAD1U, 4, 0): /* LOAD1U dst,base,index */ + PARAM0 = inst[1].puint8[PARAM2]; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_LOAD1S, 4, 0): /* LOAD1S dst,base,index */ + PARAM0 = inst[1].pint8[PARAM2]; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_LOAD2U, 4, 0): /* LOAD2U dst,base,index */ + PARAM0 = inst[1].puint16[PARAM2]; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_LOAD2S, 4, 0): /* LOAD2S dst,base,index */ + PARAM0 = inst[1].pint16[PARAM2]; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_LOAD4U, 4, 0): /* LOAD4U dst,base,index */ + PARAM0 = inst[1].puint32[PARAM2]; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_STORE1, 4, 0): /* STORE1 dst,base,index */ + inst[0].puint8[PARAM1] = PARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_STORE2, 4, 0): /* STORE2 dst,base,index */ + inst[0].puint16[PARAM1] = PARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_STORE4, 4, 0): /* STORE4 dst,base,index */ + inst[0].puint32[PARAM1] = PARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ1U, 4, 0): /* READ1U dst,space,src1 */ + PARAM0 = (UINT8)(*active_address_space[PARAM1].accessors->read_byte)(PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ1S, 4, 0): /* READ1S dst,space,src1 */ + PARAM0 = (INT8)(*active_address_space[PARAM1].accessors->read_byte)(PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ2U, 4, 0): /* READ2U dst,space,src1 */ + PARAM0 = (UINT16)(*active_address_space[PARAM1].accessors->read_word)(PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ2S, 4, 0): /* READ2S dst,space,src1 */ + PARAM0 = (INT16)(*active_address_space[PARAM1].accessors->read_word)(PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ2M, 4, 0): /* READ2M dst,space,src1,mask */ + PARAM0 = (UINT16)(*active_address_space[PARAM1].accessors->read_word_masked)(PARAM2, PARAM3); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ4U, 4, 0): /* READ4U dst,space,src1 */ + PARAM0 = (UINT32)(*active_address_space[PARAM1].accessors->read_dword)(PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ4M, 4, 0): /* READ4M dst,space,src1,mask */ + PARAM0 = (UINT32)(*active_address_space[PARAM1].accessors->read_dword_masked)(PARAM2, PARAM3); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_WRITE1, 4, 0): /* WRITE1 space,dst,src1 */ + (*active_address_space[PARAM0].accessors->write_byte)(PARAM1, PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_WRITE2, 4, 0): /* WRITE2 space,dst,src1 */ + (*active_address_space[PARAM0].accessors->write_word)(PARAM1, PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_WRIT2M, 4, 0): /* WRIT2M space,dst,mask,src1 */ + (*active_address_space[PARAM0].accessors->write_word_masked)(PARAM1, PARAM2, PARAM3); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_WRITE4, 4, 0): /* WRITE4 space,dst,src1 */ + (*active_address_space[PARAM0].accessors->write_dword)(PARAM1, PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_WRIT4M, 4, 0): /* WRIT4M space,dst,mask,src1 */ + (*active_address_space[PARAM0].accessors->write_dword_masked)(PARAM1, PARAM2, PARAM3); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FLAGS, 4, 0): /* FLAGS dst,mask,table */ + PARAM0 = (PARAM0 & ~PARAM1) | (inst[2].puint32[flags & 0x1f] & PARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_MOV, 4, 1): /* MOV dst,src[,c] */ + if (OPCODE_FAIL_CONDITION(opcode, flags)) + break; + /* fall through... */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_MOV, 4, 0): + PARAM0 = PARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ZEXT1, 4, 0): /* ZEXT1 dst,src */ + PARAM0 = (UINT8)PARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ZEXT2, 4, 0): /* ZEXT2 dst,src */ + PARAM0 = (UINT16)PARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SEXT1, 4, 0): /* SEXT1 dst,src */ + PARAM0 = (INT8)PARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SEXT2, 4, 0): /* SEXT2 dst,src */ + PARAM0 = (INT16)PARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ADD, 4, 0): /* ADD dst,src1,src2[,f] */ + PARAM0 = PARAM1 + PARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ADD, 4, 1): + temp32 = PARAM1 + PARAM2; + flags = FLAGS32_NZCV_ADD(temp32, PARAM1, PARAM2); + PARAM0 = temp32; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ADDC, 4, 0): /* ADDC dst,src1,src2[,f] */ + PARAM0 = PARAM1 + PARAM2 + (flags & DRCUML_FLAG_C); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ADDC, 4, 1): + temp32 = PARAM1 + PARAM2 + (flags & DRCUML_FLAG_C); + flags = FLAGS32_NZCV_ADD(temp32, PARAM1, PARAM2); + PARAM0 = temp32; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SUB, 4, 0): /* SUB dst,src1,src2[,f] */ + PARAM0 = PARAM1 - PARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SUB, 4, 1): + temp32 = PARAM1 - PARAM2; + flags = FLAGS32_NZCV_SUB(temp32, PARAM1, PARAM2); + PARAM0 = temp32; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SUBB, 4, 0): /* SUBB dst,src1,src2[,f] */ + PARAM0 = PARAM1 - PARAM2 - (flags & DRCUML_FLAG_C); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SUBB, 4, 1): + temp32 = PARAM1 - PARAM2 - (flags & DRCUML_FLAG_C); + flags = FLAGS32_NZCV_SUB(temp32, PARAM1, PARAM2); + PARAM0 = temp32; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_CMP, 4, 1): /* CMP src1,src2[,f] */ + temp32 = PARAM0 - PARAM1; + flags = FLAGS32_NZCV_SUB(temp32, PARAM0, PARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_MULU, 4, 0): /* MULU dst,edst,src1,src2[,f] */ + temp64 = (UINT64)(UINT32)PARAM2 * (UINT64)(UINT32)PARAM3; + PARAM1 = temp64 >> 32; + PARAM0 = (UINT32)temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_MULU, 4, 1): + temp64 = (UINT64)(UINT32)PARAM2 * (UINT64)(UINT32)PARAM3; + flags = FLAGS64_NZ(temp64); + PARAM1 = temp64 >> 32; + PARAM0 = (UINT32)temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_MULS, 4, 0): /* MULS dst,edst,src1,src2[,f] */ + temp64 = (INT64)(INT32)PARAM2 * (INT64)(INT32)PARAM3; + PARAM1 = temp64 >> 32; + PARAM0 = (UINT32)temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_MULS, 4, 1): + temp64 = (INT64)(INT32)PARAM2 * (INT64)(INT32)PARAM3; + flags = FLAGS64_NZ(temp64); + PARAM1 = temp64 >> 32; + PARAM0 = (UINT32)temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_DIVU, 4, 0): /* DIVU dst,edst,src1,src2[,f] */ + if (PARAM3 != 0) + { + temp32 = (UINT32)PARAM2 / (UINT32)PARAM3; + PARAM1 = (UINT32)PARAM2 % (UINT32)PARAM3; + PARAM0 = temp32; + } + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_DIVU, 4, 1): + if (PARAM3 != 0) + { + temp32 = (UINT32)PARAM2 / (UINT32)PARAM3; + PARAM1 = (UINT32)PARAM2 % (UINT32)PARAM3; + flags = FLAGS32_NZ(temp32); + PARAM0 = temp32; + } + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_DIVS, 4, 0): /* DIVS dst,edst,src1,src2[,f] */ + if (PARAM3 != 0) + { + temp32 = (INT32)PARAM2 / (INT32)PARAM3; + PARAM1 = (INT32)PARAM2 % (INT32)PARAM3; + PARAM0 = temp32; + } + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_DIVS, 4, 1): + if (PARAM3 != 0) + { + temp32 = (INT32)PARAM2 / (INT32)PARAM3; + PARAM1 = (INT32)PARAM2 % (INT32)PARAM3; + flags = FLAGS32_NZ(temp32); + PARAM0 = temp32; + } + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_AND, 4, 0): /* AND dst,src1,src2[,f] */ + PARAM0 = PARAM1 & PARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_AND, 4, 1): + temp32 = PARAM1 & PARAM2; + flags = FLAGS32_NZ(temp32); + PARAM0 = temp32; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_TEST, 4, 1): /* TEST src1,src2[,f] */ + temp32 = PARAM0 & PARAM1; + flags = FLAGS32_NZ(temp32); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_OR, 4, 0): /* OR dst,src1,src2[,f] */ + PARAM0 = PARAM1 | PARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_OR, 4, 1): + temp32 = PARAM1 | PARAM2; + flags = FLAGS32_NZ(temp32); + PARAM0 = temp32; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_XOR, 4, 0): /* XOR dst,src1,src2[,f] */ + PARAM0 = PARAM1 ^ PARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_XOR, 4, 1): + temp32 = PARAM1 ^ PARAM2; + flags = FLAGS32_NZ(temp32); + PARAM0 = temp32; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SHL, 4, 0): /* SHL dst,src,count[,f] */ + PARAM0 = PARAM1 << (PARAM2 & 31); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SHL, 4, 1): + shift = PARAM2 & 31; + temp32 = PARAM1 << shift; + flags = FLAGS32_NZ(temp32); + if (shift != 0) flags |= ((PARAM1 << (shift - 1)) >> 31) & DRCUML_FLAG_C; + PARAM0 = temp32; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SHR, 4, 0): /* SHR dst,src,count[,f] */ + PARAM0 = PARAM1 >> (PARAM2 & 31); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SHR, 4, 1): + shift = PARAM2 & 31; + temp32 = PARAM1 >> shift; + flags = FLAGS32_NZ(temp32); + if (shift != 0) flags |= (PARAM1 >> (shift - 1)) & DRCUML_FLAG_C; + PARAM0 = temp32; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SAR, 4, 0): /* SAR dst,src,count[,f] */ + PARAM0 = (INT32)PARAM1 >> (PARAM2 & 31); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SAR, 4, 1): + shift = PARAM2 & 31; + temp32 = (INT32)PARAM1 >> shift; + flags = FLAGS32_NZ(temp32); + if (shift != 0) flags |= (PARAM1 >> (shift - 1)) & DRCUML_FLAG_C; + PARAM0 = temp32; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ROL, 4, 0): /* ROL dst,src,count[,f] */ + shift = PARAM2 & 31; + PARAM0 = (PARAM1 << shift) | (PARAM1 >> ((32 - shift) & 31)); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ROL, 4, 1): + shift = PARAM2 & 31; + temp32 = (PARAM1 << shift) | (PARAM1 >> ((32 - shift) & 31)); + flags = FLAGS32_NZ(temp32); + if (shift != 0) flags |= ((PARAM1 << (shift - 1)) >> 31) & DRCUML_FLAG_C; + PARAM0 = temp32; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ROLC, 4, 0): /* ROLC dst,src,count[,f] */ + shift = PARAM2 & 31; + if (shift > 1) + PARAM0 = (PARAM1 << shift) | ((flags & DRCUML_FLAG_C) << (shift - 1)) | (PARAM1 >> (33 - shift)); + else if (shift == 1) + PARAM0 = (PARAM1 << shift) | (flags & DRCUML_FLAG_C); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ROLC, 4, 1): + shift = PARAM2 & 31; + if (shift > 1) + temp32 = (PARAM1 << shift) | ((flags & DRCUML_FLAG_C) << (shift - 1)) | (PARAM1 >> (33 - shift)); + else if (shift == 1) + temp32 = (PARAM1 << shift) | (flags & DRCUML_FLAG_C); + else + temp32 = PARAM1; + flags = FLAGS32_NZ(temp32); + if (shift != 0) flags |= ((PARAM1 << (shift - 1)) >> 31) & DRCUML_FLAG_C; + PARAM0 = temp32; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ROR, 4, 0): /* ROR dst,src,count[,f] */ + shift = PARAM2 & 31; + PARAM0 = (PARAM1 >> shift) | (PARAM1 << ((32 - shift) & 31)); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ROR, 4, 1): + shift = PARAM2 & 31; + temp32 = (PARAM1 >> shift) | (PARAM1 << ((32 - shift) & 31)); + flags = FLAGS32_NZ(temp32); + if (shift != 0) flags |= (PARAM1 >> (shift - 1)) & DRCUML_FLAG_C; + PARAM0 = temp32; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_RORC, 4, 0): /* RORC dst,src,count[,f] */ + shift = PARAM2 & 31; + if (shift > 1) + PARAM0 = (PARAM1 >> shift) | (((flags & DRCUML_FLAG_C) << 31) >> (shift - 1)) | (PARAM1 << (33 - shift)); + else if (shift == 1) + PARAM0 = (PARAM1 >> shift) | ((flags & DRCUML_FLAG_C) << 31); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_RORC, 4, 1): + shift = PARAM2 & 31; + if (shift > 1) + temp32 = (PARAM1 >> shift) | (((flags & DRCUML_FLAG_C) << 31) >> (shift - 1)) | (PARAM1 << (33 - shift)); + else if (shift == 1) + temp32 = (PARAM1 >> shift) | ((flags & DRCUML_FLAG_C) << 31); + else + temp32 = PARAM1; + flags = FLAGS32_NZ(temp32); + if (shift != 0) flags |= (PARAM1 >> (shift - 1)) & DRCUML_FLAG_C; + PARAM0 = temp32; + break; + + + /* ----------------------- 64-Bit Integer Operations ----------------------- */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_LOAD1U, 8, 0): /* DLOAD1U dst,base,index */ + DPARAM0 = inst[1].puint8[PARAM2]; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_LOAD1S, 8, 0): /* DLOAD1S dst,base,index */ + DPARAM0 = inst[1].pint8[PARAM2]; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_LOAD2U, 8, 0): /* DLOAD2U dst,base,index */ + DPARAM0 = inst[1].puint16[PARAM2]; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_LOAD2S, 8, 0): /* DLOAD2S dst,base,index */ + DPARAM0 = inst[1].pint16[PARAM2]; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_LOAD4U, 8, 0): /* DLOAD4U dst,base,index */ + DPARAM0 = inst[1].puint32[PARAM2]; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_LOAD4S, 8, 0): /* DLOAD4S dst,base,index */ + DPARAM0 = inst[1].pint32[PARAM2]; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_LOAD8U, 8, 0): /* DLOAD8U dst,base,index */ + DPARAM0 = inst[1].puint64[PARAM2]; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_STORE1, 8, 0): /* DSTORE1 dst,base,index */ + inst[0].puint8[PARAM1] = DPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_STORE2, 8, 0): /* DSTORE2 dst,base,index */ + inst[0].puint16[PARAM1] = DPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_STORE4, 8, 0): /* DSTORE4 dst,base,index */ + inst[0].puint32[PARAM1] = DPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_STORE8, 8, 0): /* DSTORE8 dst,base,index */ + inst[0].puint64[PARAM1] = DPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ1U, 8, 0): /* DREAD1U dst,space,src1 */ + DPARAM0 = (UINT8)(*active_address_space[PARAM1].accessors->read_byte)(PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ1S, 8, 0): /* DREAD1S dst,space,src1 */ + DPARAM0 = (INT8)(*active_address_space[PARAM1].accessors->read_byte)(PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ2U, 8, 0): /* DREAD2U dst,space,src1 */ + DPARAM0 = (UINT16)(*active_address_space[PARAM1].accessors->read_word)(PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ2S, 8, 0): /* DREAD2S dst,space,src1 */ + DPARAM0 = (INT16)(*active_address_space[PARAM1].accessors->read_word)(PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ2M, 8, 0): /* DREAD2M dst,space,src1,mask */ + DPARAM0 = (UINT16)(*active_address_space[PARAM1].accessors->read_word_masked)(PARAM2, DPARAM3); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ4U, 8, 0): /* DREAD4U dst,space,src1 */ + DPARAM0 = (UINT32)(*active_address_space[PARAM1].accessors->read_dword)(PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ4S, 8, 0): /* DREAD4S dst,space,src1 */ + DPARAM0 = (INT32)(*active_address_space[PARAM1].accessors->read_dword)(PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ4M, 8, 0): /* DREAD4M dst,space,src1,mask */ + DPARAM0 = (UINT32)(*active_address_space[PARAM1].accessors->read_dword_masked)(PARAM2, DPARAM3); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ8U, 8, 0): /* DREAD8U dst,space,src1 */ + DPARAM0 = (UINT64)(*active_address_space[PARAM1].accessors->read_qword)(PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_READ8M, 8, 0): /* DREAD8M dst,space,src1,mask */ + DPARAM0 = (UINT64)(*active_address_space[PARAM1].accessors->read_qword_masked)(PARAM2, DPARAM3); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_WRITE1, 8, 0): /* DWRITE1 space,dst,src1 */ + (*active_address_space[PARAM0].accessors->write_byte)(PARAM1, DPARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_WRITE2, 8, 0): /* DWRITE2 space,dst,src1 */ + (*active_address_space[PARAM0].accessors->write_word)(PARAM1, DPARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_WRIT2M, 8, 0): /* DWRIT2M space,dst,mask,src1 */ + (*active_address_space[PARAM0].accessors->write_word_masked)(PARAM1, DPARAM3, DPARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_WRITE4, 8, 0): /* DWRITE4 space,dst,src1 */ + (*active_address_space[PARAM0].accessors->write_dword)(PARAM1, DPARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_WRIT4M, 8, 0): /* DWRIT4M space,dst,mask,src1 */ + (*active_address_space[PARAM0].accessors->write_dword_masked)(PARAM1, DPARAM3, DPARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_WRITE8, 8, 0): /* DWRITE8 space,dst,src1 */ + (*active_address_space[PARAM0].accessors->write_qword)(PARAM1, DPARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_WRIT8M, 8, 0): /* DWRIT8M space,dst,mask,src1 */ + (*active_address_space[PARAM0].accessors->write_qword_masked)(PARAM1, DPARAM3, DPARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FLAGS, 8, 0): /* DFLAGS dst,mask,table */ + DPARAM0 = (DPARAM0 & ~DPARAM1) | (inst[2].puint64[flags & 0x0f] & DPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_MOV, 8, 1): /* DMOV dst,src[,c] */ + if (OPCODE_FAIL_CONDITION(opcode, flags)) + break; + /* fall through... */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_MOV, 8, 0): + DPARAM0 = DPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ZEXT1, 8, 0): /* DZEXT1 dst,src */ + DPARAM0 = (UINT8)DPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ZEXT2, 8, 0): /* DZEXT2 dst,src */ + DPARAM0 = (UINT16)DPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ZEXT4, 8, 0): /* DZEXT4 dst,src */ + DPARAM0 = (UINT32)DPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SEXT1, 8, 0): /* DSEXT1 dst,src */ + DPARAM0 = (INT8)DPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SEXT2, 8, 0): /* DSEXT2 dst,src */ + DPARAM0 = (INT16)DPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SEXT4, 8, 0): /* DSEXT4 dst,src */ + DPARAM0 = (INT32)DPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ADD, 8, 0): /* DADD dst,src1,src2[,f] */ + DPARAM0 = DPARAM1 + DPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ADD, 8, 1): + temp64 = DPARAM1 + DPARAM2; + flags = FLAGS64_NZCV_ADD(temp64, DPARAM1, DPARAM2); + DPARAM0 = temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ADDC, 8, 0): /* DADDC dst,src1,src2[,f] */ + DPARAM0 = DPARAM1 + DPARAM2 + (flags & DRCUML_FLAG_C); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ADDC, 8, 1): + temp64 = DPARAM1 + DPARAM2 + (flags & DRCUML_FLAG_C); + flags = FLAGS64_NZCV_ADD(temp64, DPARAM1, DPARAM2); + DPARAM0 = temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SUB, 8, 0): /* DSUB dst,src1,src2[,f] */ + DPARAM0 = DPARAM1 - DPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SUB, 8, 1): + temp64 = DPARAM1 - DPARAM2; + flags = FLAGS64_NZCV_SUB(temp64, DPARAM1, DPARAM2); + DPARAM0 = temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SUBB, 8, 0): /* DSUBB dst,src1,src2[,f] */ + DPARAM0 = DPARAM1 - DPARAM2 - (flags & DRCUML_FLAG_C); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SUBB, 8, 1): + temp64 = DPARAM1 - DPARAM2 - (flags & DRCUML_FLAG_C); + flags = FLAGS64_NZCV_SUB(temp64, DPARAM1, DPARAM2); + DPARAM0 = temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_CMP, 8, 1): /* DCMP src1,src2[,f] */ + temp64 = DPARAM0 - DPARAM1; + flags = FLAGS64_NZCV_SUB(temp64, DPARAM0, DPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_MULU, 8, 0): /* DMULU dst,edst,src1,src2[,f] */ + dmulu(inst[0].puint64, inst[1].puint64, DPARAM2, DPARAM3, FALSE); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_MULU, 8, 1): + flags = dmulu(inst[0].puint64, inst[1].puint64, DPARAM2, DPARAM3, TRUE); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_MULS, 8, 0): /* DMULS dst,edst,src1,src2[,f] */ + dmuls(inst[0].puint64, inst[1].puint64, DPARAM2, DPARAM3, FALSE); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_MULS, 8, 1): + flags = dmuls(inst[0].puint64, inst[1].puint64, DPARAM2, DPARAM3, TRUE); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_DIVU, 8, 0): /* DDIVU dst,edst,src1,src2[,f] */ + if (DPARAM3 != 0) + { + temp64 = (UINT64)DPARAM2 / (UINT64)DPARAM3; + DPARAM1 = (UINT64)DPARAM2 % (UINT64)DPARAM3; + DPARAM0 = temp64; + } + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_DIVU, 8, 1): + if (DPARAM3 != 0) + { + temp64 = (UINT64)DPARAM2 / (UINT64)DPARAM3; + DPARAM1 = (UINT64)DPARAM2 % (UINT64)DPARAM3; + flags = FLAGS64_NZ(temp64); + DPARAM0 = temp64; + } + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_DIVS, 8, 0): /* DDIVS dst,edst,src1,src2[,f] */ + if (DPARAM3 != 0) + { + temp64 = (INT64)DPARAM2 / (INT64)DPARAM3; + DPARAM1 = (INT64)DPARAM2 % (INT64)DPARAM3; + DPARAM0 = temp64; + } + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_DIVS, 8, 1): + if (DPARAM3 != 0) + { + temp64 = (INT64)DPARAM2 / (INT64)DPARAM3; + DPARAM1 = (INT64)DPARAM2 % (INT64)DPARAM3; + flags = FLAGS64_NZ(temp64); + DPARAM0 = temp64; + } + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_AND, 8, 0): /* DAND dst,src1,src2[,f] */ + DPARAM0 = DPARAM1 & DPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_AND, 8, 1): + temp64 = DPARAM1 & DPARAM2; + flags = FLAGS64_NZ(temp64); + DPARAM0 = temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_TEST, 8, 1): /* DTEST src1,src2[,f] */ + temp64 = DPARAM1 & DPARAM2; + flags = FLAGS64_NZ(temp64); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_OR, 8, 0): /* DOR dst,src1,src2[,f] */ + DPARAM0 = DPARAM1 | DPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_OR, 8, 1): + temp64 = DPARAM1 | DPARAM2; + flags = FLAGS64_NZ(temp64); + DPARAM0 = temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_XOR, 8, 0): /* DXOR dst,src1,src2[,f] */ + DPARAM0 = DPARAM1 ^ DPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_XOR, 8, 1): + temp64 = DPARAM1 ^ DPARAM2; + flags = FLAGS64_NZ(temp64); + DPARAM0 = temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SHL, 8, 0): /* DSHL dst,src,count[,f] */ + DPARAM0 = DPARAM1 << (DPARAM2 & 63); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SHL, 8, 1): + shift = DPARAM2 & 63; + temp64 = DPARAM1 << shift; + flags = FLAGS64_NZ(temp64); + if (shift != 0) flags |= ((DPARAM1 << (shift - 1)) >> 63) & DRCUML_FLAG_C; + DPARAM0 = temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SHR, 8, 0): /* DSHR dst,src,count[,f] */ + DPARAM0 = DPARAM1 >> (DPARAM2 & 63); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SHR, 8, 1): + shift = DPARAM2 & 63; + temp64 = DPARAM1 >> shift; + flags = FLAGS64_NZ(temp64); + if (shift != 0) flags |= (DPARAM1 >> (shift - 1)) & DRCUML_FLAG_C; + DPARAM0 = temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SAR, 8, 0): /* DSAR dst,src,count[,f] */ + DPARAM0 = (INT64)DPARAM1 >> (DPARAM2 & 63); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_SAR, 8, 1): + shift = DPARAM2 & 63; + temp64 = (INT32)DPARAM1 >> shift; + flags = FLAGS64_NZ(temp64); + if (shift != 0) flags |= (DPARAM1 >> (shift - 1)) & DRCUML_FLAG_C; + DPARAM0 = temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ROL, 8, 0): /* DROL dst,src,count[,f] */ + shift = DPARAM2 & 31; + DPARAM0 = (DPARAM1 << shift) | (DPARAM1 >> ((64 - shift) & 63)); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ROL, 8, 1): + shift = DPARAM2 & 63; + temp64 = (DPARAM1 << shift) | (DPARAM1 >> ((64 - shift) & 63)); + flags = FLAGS64_NZ(temp64); + if (shift != 0) flags |= ((DPARAM1 << (shift - 1)) >> 63) & DRCUML_FLAG_C; + DPARAM0 = temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ROLC, 8, 0): /* DROLC dst,src,count[,f] */ + shift = DPARAM2 & 63; + if (shift > 1) + DPARAM0 = (DPARAM1 << shift) | ((flags & DRCUML_FLAG_C) << (shift - 1)) | (DPARAM1 >> (65 - shift)); + else if (shift == 1) + DPARAM0 = (DPARAM1 << shift) | (flags & DRCUML_FLAG_C); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ROLC, 8, 1): + shift = DPARAM2 & 63; + if (shift > 1) + temp64 = (DPARAM1 << shift) | ((flags & DRCUML_FLAG_C) << (shift - 1)) | (DPARAM1 >> (65 - shift)); + else if (shift == 1) + temp64 = (DPARAM1 << shift) | (flags & DRCUML_FLAG_C); + else + temp64 = DPARAM1; + flags = FLAGS64_NZ(temp64); + if (shift != 0) flags |= ((DPARAM1 << (shift - 1)) >> 63) & DRCUML_FLAG_C; + DPARAM0 = temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ROR, 8, 0): /* DROR dst,src,count[,f] */ + shift = DPARAM2 & 63; + DPARAM0 = (DPARAM1 >> shift) | (DPARAM1 << ((64 - shift) & 63)); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_ROR, 8, 1): + shift = DPARAM2 & 63; + temp64 = (DPARAM1 >> shift) | (DPARAM1 << ((64 - shift) & 63)); + flags = FLAGS64_NZ(temp64); + if (shift != 0) flags |= (DPARAM1 >> (shift - 1)) & DRCUML_FLAG_C; + DPARAM0 = temp64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_RORC, 8, 0): /* DRORC dst,src,count[,f] */ + shift = DPARAM2 & 63; + if (shift > 1) + DPARAM0 = (DPARAM1 >> shift) | ((((UINT64)flags & DRCUML_FLAG_C) << 63) >> (shift - 1)) | (DPARAM1 << (65 - shift)); + else if (shift == 1) + DPARAM0 = (DPARAM1 >> shift) | (((UINT64)flags & DRCUML_FLAG_C) << 63); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_RORC, 8, 1): + shift = DPARAM2 & 63; + if (shift > 1) + temp64 = (DPARAM1 >> shift) | ((((UINT64)flags & DRCUML_FLAG_C) << 63) >> (shift - 1)) | (DPARAM1 << (65 - shift)); + else if (shift == 1) + temp64 = (DPARAM1 >> shift) | (((UINT64)flags & DRCUML_FLAG_C) << 63); + else + temp64 = DPARAM1; + flags = FLAGS64_NZ(temp64); + if (shift != 0) flags |= (DPARAM1 >> (shift - 1)) & DRCUML_FLAG_C; + DPARAM0 = temp64; + break; + + + /* ----------------------- 32-Bit Floating Point Operations ----------------------- */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_FLOAD, 4, 0): /* FSLOAD dst,base,index */ + FSPARAM0 = inst[1].pfloat[PARAM2]; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FSTORE, 4, 0): /* FSSTORE dst,base,index */ + inst[0].pfloat[PARAM1] = FSPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FREAD, 4, 0): /* FSREAD dst,space,src1 */ + PARAM0 = (UINT32)(*active_address_space[PARAM1].accessors->read_dword)(PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FWRITE, 4, 0): /* FSWRITE space,dst,src1 */ + (*active_address_space[PARAM0].accessors->write_dword)(PARAM1, PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FMOV, 4, 1): /* FSMOV dst,src[,c] */ + if (OPCODE_FAIL_CONDITION(opcode, flags)) + break; + /* fall through... */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_FMOV, 4, 0): + FSPARAM0 = FSPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI4, 4, 0): /* FSTOI4 dst,src1 */ + *inst[0].pint32 = FSPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI4T, 4, 0): /* FSTOI4T dst,src1 */ + if (FSPARAM1 >= 0) + *inst[0].pint32 = floor(FSPARAM1); + else + *inst[0].pint32 = ceil(FSPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI4R, 4, 0): /* FSTOI4R dst,src1 */ + if (FSPARAM1 >= 0) + *inst[0].pint32 = floor(FSPARAM1 + 0.5f); + else + *inst[0].pint32 = ceil(FSPARAM1 - 0.5f); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI4F, 4, 0): /* FSTOI4F dst,src1 */ + *inst[0].pint32 = floor(FSPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI4C, 4, 0): /* FSTOI4C dst,src1 */ + *inst[0].pint32 = ceil(FSPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI8, 4, 0): /* FSTOI8 dst,src1 */ + *inst[0].pint64 = FSPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI8T, 4, 0): /* FSTOI8T dst,src1 */ + if (FSPARAM1 >= 0) + *inst[0].pint64 = floor(FSPARAM1); + else + *inst[0].pint64 = ceil(FSPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI8R, 4, 0): /* FSTOI8R dst,src1 */ + if (FSPARAM1 >= 0) + *inst[0].pint64 = floor(FSPARAM1 + 0.5f); + else + *inst[0].pint64 = ceil(FSPARAM1 - 0.5f); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI8F, 4, 0): /* FSTOI8F dst,src1 */ + *inst[0].pint64 = floor(FSPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI8C, 4, 0): /* FSTOI8C dst,src1 */ + *inst[0].pint64 = ceil(FSPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FFRFD, 4, 0): /* FSFRFD dst,src1 */ + FSPARAM0 = FDPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FFRI4, 4, 0): /* FSFRI4 dst,src1 */ + FSPARAM0 = *inst[1].pint32; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FFRI8, 4, 0): /* FSFRI8 dst,src1 */ + FSPARAM0 = *inst[1].pint64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FADD, 4, 0): /* FSADD dst,src1,src2 */ + FSPARAM0 = FSPARAM1 + FSPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FSUB, 4, 0): /* FSSUB dst,src1,src2 */ + FSPARAM0 = FSPARAM1 - FSPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FCMP, 4, 1): /* FSCMP src1,src2 */ + if (_isnan(FSPARAM0) || _isnan(FSPARAM1)) + flags = DRCUML_FLAG_U; + else + flags = (FSPARAM0 < FSPARAM1) | ((FSPARAM0 == FSPARAM1) << 2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FMUL, 4, 0): /* FSMUL dst,src1,src2 */ + FSPARAM0 = FSPARAM1 * FSPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FDIV, 4, 0): /* FSDIV dst,src1,src2 */ + FSPARAM0 = FSPARAM1 / FSPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FNEG, 4, 0): /* FSNEG dst,src1 */ + FSPARAM0 = -FSPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FABS, 4, 0): /* FSABS dst,src1 */ + FSPARAM0 = fabs(FSPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FSQRT, 4, 0): /* FSSQRT dst,src1 */ + FSPARAM0 = sqrt(FSPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FRECIP, 4, 0): /* FSRECIP dst,src1 */ + FSPARAM0 = 1.0f / FSPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FRSQRT, 4, 0): /* FSRSQRT dst,src1 */ + FSPARAM0 = 1.0f / sqrt(FSPARAM1); + break; + + + /* ----------------------- 64-Bit Floating Point Operations ----------------------- */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_FLOAD, 8, 0): /* FDLOAD dst,base,index */ + FDPARAM0 = inst[1].pdouble[PARAM2]; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FSTORE, 8, 0): /* FDSTORE dst,base,index */ + inst[0].pdouble[PARAM1] = FDPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FREAD, 8, 0): /* FDREAD dst,space,src1 */ + PARAM0 = (UINT64)(*active_address_space[PARAM1].accessors->read_qword)(PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FWRITE, 8, 0): /* FDWRITE space,dst,src1 */ + (*active_address_space[PARAM0].accessors->write_qword)(PARAM1, PARAM2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FMOV, 8, 1): /* FDMOV dst,src[,c] */ + if (OPCODE_FAIL_CONDITION(opcode, flags)) + break; + /* fall through... */ + + case MAKE_OPCODE_SHORT(DRCUML_OP_FMOV, 8, 0): + FDPARAM0 = FDPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI4, 8, 0): /* FDTOI4 dst,src1 */ + *inst[0].pint32 = FDPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI4T, 8, 0): /* FDTOI4T dst,src1 */ + if (FDPARAM1 >= 0) + *inst[0].pint32 = floor(FDPARAM1); + else + *inst[0].pint32 = ceil(FDPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI4R, 8, 0): /* FDTOI4R dst,src1 */ + if (FDPARAM1 >= 0) + *inst[0].pint32 = floor(FDPARAM1 + 0.5); + else + *inst[0].pint32 = ceil(FDPARAM1 - 0.5); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI4F, 8, 0): /* FDTOI4F dst,src1 */ + *inst[0].pint32 = floor(FDPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI4C, 8, 0): /* FDTOI4C dst,src1 */ + *inst[0].pint32 = ceil(FDPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI8, 8, 0): /* FDTOI8 dst,src1 */ + *inst[0].pint64 = FDPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI8T, 8, 0): /* FDTOI8T dst,src1 */ + if (FDPARAM1 >= 0) + *inst[0].pint64 = floor(FDPARAM1); + else + *inst[0].pint64 = ceil(FDPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI8R, 8, 0): /* FDTOI8R dst,src1 */ + if (FDPARAM1 >= 0) + *inst[0].pint64 = floor(FDPARAM1 + 0.5); + else + *inst[0].pint64 = ceil(FDPARAM1 - 0.5); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI8F, 8, 0): /* FDTOI8F dst,src1 */ + *inst[0].pint64 = floor(FDPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FTOI8C, 8, 0): /* FDTOI8C dst,src1 */ + *inst[0].pint64 = ceil(FDPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FFRFS, 8, 0): /* FDFRFS dst,src1 */ + FDPARAM0 = FSPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FFRI4, 8, 0): /* FDFRI4 dst,src1 */ + FDPARAM0 = *inst[1].pint32; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FFRI8, 8, 0): /* FDFRI8 dst,src1 */ + FDPARAM0 = *inst[1].pint64; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FADD, 8, 0): /* FDADD dst,src1,src2 */ + FDPARAM0 = FDPARAM1 + FDPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FSUB, 8, 0): /* FDSUB dst,src1,src2 */ + FDPARAM0 = FDPARAM1 - FDPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FCMP, 8, 1): /* FDCMP src1,src2 */ + if (_isnan(FDPARAM0) || _isnan(FDPARAM1)) + flags = DRCUML_FLAG_U; + else + flags = (FDPARAM0 < FDPARAM1) | ((FDPARAM0 == FDPARAM1) << 2); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FMUL, 8, 0): /* FDMUL dst,src1,src2 */ + FDPARAM0 = FDPARAM1 * FDPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FDIV, 8, 0): /* FDDIV dst,src1,src2 */ + FDPARAM0 = FDPARAM1 / FDPARAM2; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FNEG, 8, 0): /* FDNEG dst,src1 */ + FDPARAM0 = -FDPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FABS, 8, 0): /* FDABS dst,src1 */ + FDPARAM0 = fabs(FDPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FSQRT, 8, 0): /* FDSQRT dst,src1 */ + FDPARAM0 = sqrt(FDPARAM1); + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FRECIP, 8, 0): /* FDRECIP dst,src1 */ + FDPARAM0 = 1.0 / FDPARAM1; + break; + + case MAKE_OPCODE_SHORT(DRCUML_OP_FRSQRT, 8, 0): /* FDRSQRT dst,src1 */ + FDPARAM0 = 1.0 / sqrt(FDPARAM1); + break; + + default: + fatalerror("Unexpected opcode!"); + break; + } + + /* advance past the parameters and immediates */ + inst += OPCODE_GET_PWORDS(opcode); + } + + return 0; +} + + + +/*************************************************************************** + PRIVATE HELPER FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + output_parameter - output a parameter +-------------------------------------------------*/ + +static void output_parameter(drcbe_state *drcbe, drcbec_instruction **dstptr, void **immedptr, int size, int immedsize, const drcuml_parameter *param) +{ + drcbec_instruction *dst = *dstptr; + void *immed = *immedptr; + drcuml_parameter temp_param; + + switch (param->type) + { + /* convert mapvars to immediates */ + case DRCUML_PTYPE_MAPVAR: + temp_param.value = drcmap_get_last_value(drcbe->map, param->value); + param = &temp_param; + /* fall through to immediate case */ + + /* immediates store a pointer to the immediate data, which is stored at the end of the instruction */ + case DRCUML_PTYPE_IMMEDIATE: + if (param->value == 0) + (dst++)->v = &immediate_zero; + else + { + (dst++)->v = immed; + if (immedsize == 4) + *(UINT32 *)immed = (UINT32)param->value; + else + *(UINT64 *)immed = (UINT64)param->value; + immed = (UINT8 *)immed + immedsize; + } + break; + + /* int registers point to the appropriate part of the integer register state */ + case DRCUML_PTYPE_INT_REGISTER: + if (size == 4) + (dst++)->puint32 = &drcbe->state.r[param->value - DRCUML_REG_I0].l; + else + (dst++)->puint64 = &drcbe->state.r[param->value - DRCUML_REG_I0].d; + break; + + /* float registers point to the appropriate part of the floating point register state */ + case DRCUML_PTYPE_FLOAT_REGISTER: + if (size == 4) + (dst++)->pfloat = &drcbe->state.f[param->value - DRCUML_REG_F0].l; + else + (dst++)->pdouble = &drcbe->state.f[param->value - DRCUML_REG_F0].d; + break; + + /* memory just points to the memory */ + case DRCUML_PTYPE_MEMORY: + (dst++)->v = (void *)(FPTR)param->value; + break; + + default: + fatalerror("Unexpected param->type"); + break; + } + + *dstptr = dst; + *immedptr = immed; +} + + +/*------------------------------------------------- + fixup_label - callback to fixup forward- + referenced labels +-------------------------------------------------*/ + +static void fixup_label(void *parameter, drccodeptr labelcodeptr) +{ + drcbec_instruction *dst = parameter; + dst->inst = (drcbec_instruction *)labelcodeptr; +} + + +/*------------------------------------------------- + dmulu - perform a double-wide unsigned multiply +-------------------------------------------------*/ + +static int dmulu(UINT64 *dstlo, UINT64 *dsthi, UINT64 src1, UINT64 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) + { + *dstlo = src1 * src2; + return 0; + } + + /* fetch source values */ + a = src1; + b = src2; + if (a == 0 || b == 0) + { + *dsthi = *dstlo = 0; + return DRCUML_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); + + /* store the results */ + *dsthi = hi; + *dstlo = lo; + return ((hi >> 60) & DRCUML_FLAG_S); +} + + +/*------------------------------------------------- + dmuls - perform a double-wide signed multiply +-------------------------------------------------*/ + +static int 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) + { + *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 DRCUML_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) & DRCUML_FLAG_S); +} diff --git a/src/emu/cpu/drcbeut.c b/src/emu/cpu/drcbeut.c new file mode 100644 index 00000000000..462ec118f11 --- /dev/null +++ b/src/emu/cpu/drcbeut.c @@ -0,0 +1,673 @@ +/*************************************************************************** + + drcbeut.c + + Utility functions for dynamic recompiling backends. + + Copyright Aaron Giles + Released for general non-commercial use under the MAME license + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include "drcbeut.h" + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* structure holding information about a single label */ +typedef struct _drcmap_entry drcmap_entry; +struct _drcmap_entry +{ + drcmap_entry * next; /* pointer to next map entry */ + drccodeptr codeptr; /* pointer to the relevant code */ + UINT32 mapvar; /* map variable id */ + UINT32 newval; /* value of the variable starting at codeptr */ +}; + + +/* structure describing the state of the code map */ +struct _drcmap_state +{ + drccache * cache; /* pointer to the cache */ + UINT64 uniquevalue; /* unique value used to find the table */ + drcmap_entry * head; /* head of the live list */ + drcmap_entry ** tailptr; /* pointer to tail of the live list */ + UINT32 numvalues; /* number of values in the list */ + UINT32 mapvalue[DRCUML_MAPVAR_END - DRCUML_MAPVAR_M0]; /* array of current values */ +}; + + +/* structure holding information about a single label */ +typedef struct _drclabel drclabel; +struct _drclabel +{ + drclabel * next; /* pointer to next label */ + drcuml_codelabel label; /* the label specified */ + drccodeptr codeptr; /* pointer to the relevant code */ +}; + + +/* structure holding a live list of labels */ +struct _drclabel_list +{ + drccache * cache; /* pointer to the cache */ + drclabel * head; /* head of the live list */ +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static void label_list_reset(drclabel_list *list, int fatal_on_leftovers); +static drclabel *label_find_or_allocate(drclabel_list *list, drcuml_codelabel label); +static void label_oob_callback(drccodeptr *codeptr, void *param1, void *param2, void *param3); + + + +/*************************************************************************** + HASH TABLE MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + drchash_alloc - allocate memory in the cache + for the hash table tracker (it auto-frees + with the cache) +-------------------------------------------------*/ + +drchash_state *drchash_alloc(drccache *cache, int modes, int addrbits, int ignorebits) +{ + int effaddrbits = addrbits - ignorebits; + drchash_state *drchash; + + /* allocate permanent state from the cache */ + drchash = drccache_memory_alloc(cache, sizeof(*drchash) + modes * sizeof(drchash->base[0])); + if (drchash == NULL) + return NULL; + memset(drchash, 0, sizeof(*drchash) + modes * sizeof(drchash->base[0])); + + /* copy in parameters */ + drchash->cache = cache; + drchash->modes = modes; + + /* compute the sizes of the tables */ + drchash->l1bits = effaddrbits / 2; + drchash->l2bits = effaddrbits - drchash->l1bits; + drchash->l1shift = ignorebits + drchash->l2bits; + drchash->l2shift = ignorebits; + drchash->l1mask = (1 << drchash->l1bits) - 1; + drchash->l2mask = (1 << drchash->l2bits) - 1; + + /* reset the hash table, which allocates any subsequent tables */ + if (!drchash_reset(drchash)) + return NULL; + + return drchash; +} + + +/*------------------------------------------------- + drchash_reset - flush existing hash tables and + create new ones +-------------------------------------------------*/ + +int drchash_reset(drchash_state *drchash) +{ + int modenum, entry; + + /* allocate an empty l2 hash table */ + drchash->emptyl2 = drccache_memory_alloc_temporary(drchash->cache, sizeof(drccodeptr) << drchash->l2bits); + if (drchash->emptyl2 == NULL) + return FALSE; + + /* populate it with pointers to the recompile_exit code */ + for (entry = 0; entry < (1 << drchash->l2bits); entry++) + drchash->emptyl2[entry] = drchash->nocodeptr; + + /* allocate an empty l1 hash table */ + drchash->emptyl1 = drccache_memory_alloc_temporary(drchash->cache, sizeof(drccodeptr *) << drchash->l1bits); + if (drchash->emptyl1 == NULL) + return FALSE; + + /* populate it with pointers to the empty l2 table */ + for (entry = 0; entry < (1 << drchash->l1bits); entry++) + drchash->emptyl1[entry] = drchash->emptyl2; + + /* reset the hash tables */ + for (modenum = 0; modenum < drchash->modes; modenum++) + drchash->base[modenum] = drchash->emptyl1; + + return TRUE; +} + + +/*------------------------------------------------- + drchash_block_begin - note the beginning of a + block +-------------------------------------------------*/ + +void drchash_block_begin(drchash_state *drchash, drcuml_block *block, const drcuml_instruction *instlist, UINT32 numinst) +{ + int inum; + + /* before generating code, pre-allocate any hash entries; we do this by setting dummy hash values */ + for (inum = 0; inum < numinst; inum++) + { + const drcuml_instruction *inst = &instlist[inum]; + + /* if the opcode is a hash, verify that it makes sense and then set a NULL entry */ + if (inst->opcode == DRCUML_OP_HASH) + { + assert(inst->numparams == 2); + assert(inst->param[0].type == DRCUML_PTYPE_IMMEDIATE); + assert(inst->param[1].type == DRCUML_PTYPE_IMMEDIATE); + + /* if we fail to allocate, we must abort the block */ + if (!drchash_set_codeptr(drchash, inst->param[0].value, inst->param[1].value, NULL)) + drcuml_block_abort(block); + } + + /* if the opcode is a hashjmp to a fixed location, make sure we preallocate the tables */ + if (inst->opcode == DRCUML_OP_HASHJMP && inst->param[0].type == DRCUML_PTYPE_IMMEDIATE && inst->param[1].type == DRCUML_PTYPE_IMMEDIATE) + { + /* if we fail to allocate, we must abort the block */ + drccodeptr code = drchash_get_codeptr(drchash, inst->param[0].value, inst->param[1].value); + if (!drchash_set_codeptr(drchash, inst->param[0].value, inst->param[1].value, code)) + drcuml_block_abort(block); + } + } +} + + +/*------------------------------------------------- + drchash_block_end - note the end of a block +-------------------------------------------------*/ + +void drchash_block_end(drchash_state *drchash, drcuml_block *block) +{ + /* nothing to do here, yet */ +} + + +/*------------------------------------------------- + drchash_set_default_codeptr - change the + default codeptr +-------------------------------------------------*/ + +void drchash_set_default_codeptr(drchash_state *drchash, drccodeptr nocodeptr) +{ + drccodeptr old = drchash->nocodeptr; + int modenum, l1entry, l2entry; + + /* nothing to do if the same */ + if (old == nocodeptr) + return; + drchash->nocodeptr = nocodeptr; + + /* update the empty L2 table first */ + for (l2entry = 0; l2entry < (1 << drchash->l2bits); l2entry++) + drchash->emptyl2[l2entry] = nocodeptr; + + /* now scan all existing hashtables for entries */ + for (modenum = 0; modenum < drchash->modes; modenum++) + if (drchash->base[modenum] != drchash->emptyl1) + for (l1entry = 0; l1entry < (1 << drchash->l1bits); l1entry++) + if (drchash->base[modenum][l1entry] != drchash->emptyl2) + for (l2entry = 0; l2entry < (1 << drchash->l2bits); l2entry++) + if (drchash->base[modenum][l1entry][l2entry] == old) + drchash->base[modenum][l1entry][l2entry] = nocodeptr; +} + + +/*------------------------------------------------- + drchash_set_codeptr - set the codeptr for the + given mode/pc +-------------------------------------------------*/ + +int drchash_set_codeptr(drchash_state *drchash, UINT32 mode, UINT32 pc, drccodeptr code) +{ + UINT32 l1 = (pc >> drchash->l1shift) & drchash->l1mask; + UINT32 l2 = (pc >> drchash->l2shift) & drchash->l2mask; + + assert(mode < drchash->modes); + + /* copy-on-write for the l1 hash table */ + if (drchash->base[mode] == drchash->emptyl1) + { + drccodeptr **newtable = drccache_memory_alloc_temporary(drchash->cache, sizeof(drccodeptr *) << drchash->l1bits); + if (newtable == NULL) + return FALSE; + memcpy(newtable, drchash->emptyl1, sizeof(drccodeptr *) << drchash->l1bits); + drchash->base[mode] = newtable; + } + + /* copy-on-write for the l2 hash table */ + if (drchash->base[mode][l1] == drchash->emptyl2) + { + drccodeptr *newtable = drccache_memory_alloc_temporary(drchash->cache, sizeof(drccodeptr) << drchash->l2bits); + if (newtable == NULL) + return FALSE; + memcpy(newtable, drchash->emptyl2, sizeof(drccodeptr) << drchash->l2bits); + drchash->base[mode][l1] = newtable; + } + + /* set the new entry */ + drchash->base[mode][l1][l2] = code; + return TRUE; +} + + + +/*************************************************************************** + CODE MAP MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + drcmap_alloc - allocate memory in the cache + for the code mapper (it auto-frees with the + cache) +-------------------------------------------------*/ + +drcmap_state *drcmap_alloc(drccache *cache, UINT64 uniquevalue) +{ + drcmap_state *drcmap; + + /* allocate permanent state from the cache */ + drcmap = drccache_memory_alloc(cache, sizeof(*drcmap)); + if (drcmap == NULL) + return NULL; + memset(drcmap, 0, sizeof(*drcmap)); + + /* remember the cache */ + drcmap->cache = cache; + drcmap->tailptr = &drcmap->head; + + return drcmap; +} + + +/*------------------------------------------------- + drcmap_block_begin - note the beginning of a + block +-------------------------------------------------*/ + +void drcmap_block_begin(drcmap_state *drcmap, drcuml_block *block) +{ + /* release any remaining live entries */ + while (drcmap->head != NULL) + { + drcmap_entry *entry = drcmap->head; + drcmap->head = entry->next; + drccache_memory_free(drcmap->cache, entry, sizeof(*entry)); + } + + /* reset the tailptr and count */ + drcmap->tailptr = &drcmap->head; + drcmap->numvalues = 0; +} + + +/*------------------------------------------------- + drcmap_block_end - note the end of a block +-------------------------------------------------*/ + +void drcmap_block_end(drcmap_state *drcmap, drcuml_block *block) +{ + UINT32 curvalue[DRCUML_MAPVAR_END - DRCUML_MAPVAR_M0]; + UINT8 changed[DRCUML_MAPVAR_END - DRCUML_MAPVAR_M0]; + drcmap_entry *entry; + drccodeptr lastptr; + drccodeptr *top; + UINT32 *dest; + + /* only process if we have data */ + if (drcmap->head == NULL) + return; + + /* begin "code generation" aligned to an 8-byte boundary */ + top = drccache_begin_codegen(drcmap->cache, sizeof(UINT64) + sizeof(UINT32) + 2 * sizeof(UINT32) * drcmap->numvalues); + if (top == NULL) + drcuml_block_abort(block); + dest = (UINT32 *)(((FPTR)*top + 7) & ~7); + + /* store the cookie first */ + *(UINT64 *)dest = drcmap->uniquevalue; + dest += 2; + + /* get the pointer to the first item and store an initial backwards offset */ + lastptr = drcmap->head->codeptr; + *dest = (drccodeptr)dest - lastptr; + dest++; + + /* reset the current values */ + memset(curvalue, 0, sizeof(curvalue)); + memset(changed, 0, sizeof(changed)); + + /* now iterate over entries and store them */ + for (entry = drcmap->head; entry != NULL; entry = entry->next) + { + /* update the current value of the variable and detect changes */ + if (curvalue[entry->mapvar] != entry->newval) + { + curvalue[entry->mapvar] = entry->newval; + changed[entry->mapvar] = TRUE; + } + + /* if the next code pointer is different, or if we're at the end, flush changes */ + if (entry->next == NULL || entry->next->codeptr != entry->codeptr) + { + UINT32 codedelta = entry->codeptr - lastptr; + UINT32 varmask = 0; + int numchanged; + int varnum; + + /* build a mask of changed variables */ + for (numchanged = varnum = 0; varnum < ARRAY_LENGTH(changed); varnum++) + if (changed[varnum]) + { + changed[varnum] = FALSE; + varmask |= 1 << varnum; + numchanged++; + } + + /* if nothing really changed, skip it */ + if (numchanged == 0) + continue; + + /* first word is a code delta plus mask of changed variables */ + while (codedelta > 0xffff) + { + *dest++ = 0xffff << 16; + codedelta -= 0xffff; + } + *dest++ = (codedelta << 16) | (varmask << 4) | numchanged; + + /* now output updated variable values */ + for (varnum = 0; varnum < ARRAY_LENGTH(changed); varnum++) + if ((varmask >> varnum) & 1) + *dest++ = curvalue[varnum]; + + /* remember our lastptr */ + lastptr = entry->codeptr; + } + } + + /* add a terminator */ + *dest++ = 0; + + /* complete codegen */ + *top = (drccodeptr)dest; + drccache_end_codegen(drcmap->cache); +} + + +/*------------------------------------------------- + drcmap_set_value - set a map value for the + given code pointer +-------------------------------------------------*/ + +void drcmap_set_value(drcmap_state *drcmap, drccodeptr codebase, UINT32 mapvar, UINT32 newvalue) +{ + drcmap_entry *entry; + + assert(mapvar >= DRCUML_MAPVAR_M0 && mapvar < DRCUML_MAPVAR_END); + + /* if this value isn't different, skip it */ + if (drcmap->mapvalue[mapvar - DRCUML_MAPVAR_M0] == newvalue) + return; + + /* allocate a new entry and fill it in */ + entry = drccache_memory_alloc(drcmap->cache, sizeof(*entry)); + entry->next = NULL; + entry->codeptr = codebase; + entry->mapvar = mapvar - DRCUML_MAPVAR_M0; + entry->newval = newvalue; + + /* hook us into the end of the list */ + *drcmap->tailptr = entry; + drcmap->tailptr = &entry->next; + + /* update our state in the table as well */ + drcmap->mapvalue[mapvar - DRCUML_MAPVAR_M0] = newvalue; + + /* and increment the count */ + drcmap->numvalues++; +} + + +/*------------------------------------------------- + drcmap_get_value - return a map value for the + given code pointer +-------------------------------------------------*/ + +UINT32 drcmap_get_value(drcmap_state *drcmap, drccodeptr codebase, UINT32 mapvar) +{ + UINT64 *endscan = (UINT64 *)drccache_top(drcmap->cache); + UINT32 varmask = 0x10 << mapvar; + drccodeptr curcode; + UINT32 result = 0; + UINT64 *curscan; + UINT32 *data; + + assert(mapvar >= DRCUML_MAPVAR_M0 && mapvar < DRCUML_MAPVAR_END); + mapvar -= DRCUML_MAPVAR_M0; + + /* get an aligned pointer to start scanning */ + curscan = (UINT64 *)(((FPTR)codebase | 7) + 1); + + /* look for the signature */ + while (curscan < endscan && *curscan++ != drcmap->uniquevalue) ; + assert(curscan < endscan); + if (curscan >= endscan) + return 0; + + /* switch to 32-bit pointers for processing the rest */ + data = (UINT32 *)curscan; + + /* first get the 32-bit starting offset to the code */ + curcode = (drccodeptr)data - *data; + data++; + + /* now loop until we advance past our target */ + while (TRUE) + { + UINT32 controlword = *data++; + + /* a 0 is a terminator */ + if (controlword == 0) + break; + + /* update the codeptr; if this puts us past the end, we're done */ + curcode += (controlword >> 16) & 0xffff; + if (curcode > codebase) + break; + + /* if our mapvar has changed, process this word */ + if ((controlword & varmask) != 0) + { + int dataoffs = 0; + UINT32 skipmask; + + /* count how many words precede the one we care about */ + for (skipmask = (controlword & (varmask - 1)) >> 4; skipmask != 0; skipmask = skipmask & (skipmask - 1)) + dataoffs++; + + /* fetch the one we want */ + result = data[dataoffs]; + } + + /* low 4 bits contain the total number of words of data */ + data += controlword & 0x0f; + } + return result; +} + + +/*------------------------------------------------- + drcmap_get_last_value - return the most + recently set map value +-------------------------------------------------*/ + +UINT32 drcmap_get_last_value(drcmap_state *drcmap, UINT32 mapvar) +{ + assert(mapvar >= DRCUML_MAPVAR_M0 && mapvar < DRCUML_MAPVAR_END); + return drcmap->mapvalue[mapvar - DRCUML_MAPVAR_M0]; +} + + + +/*************************************************************************** + LABEL MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + drclabel_list_alloc - allocate a label + list within the cache (it auto-frees with the + cache) +-------------------------------------------------*/ + +drclabel_list *drclabel_list_alloc(drccache *cache) +{ + drclabel_list *list; + + /* allocate permanent state from the cache */ + list = drccache_memory_alloc(cache, sizeof(*list)); + if (list == NULL) + return NULL; + memset(list, 0, sizeof(*list)); + + /* remember the cache */ + list->cache = cache; + + return list; +} + + +/*------------------------------------------------- + drclabel_block_begin - note the beginning of + a block +-------------------------------------------------*/ + +void drclabel_block_begin(drclabel_list *list, drcuml_block *block) +{ + /* make sure the label list is clear, but don't fatalerror */ + label_list_reset(list, FALSE); +} + + +/*------------------------------------------------- + drclabel_block_end - note the end of a block +-------------------------------------------------*/ + +void drclabel_block_end(drclabel_list *list, drcuml_block *block) +{ + /* make sure the label list is clear, and fatalerror if we missed anything */ + label_list_reset(list, TRUE); +} + + +/*------------------------------------------------- + drclabel_get_codeptr - find or allocate a new + label; returns NULL and requests an OOB + callback if undefined +-------------------------------------------------*/ + +drccodeptr drclabel_get_codeptr(drclabel_list *list, drcuml_codelabel label, drclabel_fixup_func fixup, void *param) +{ + drclabel *curlabel = label_find_or_allocate(list, label); + + /* if no code pointer, request an OOB callback */ + if (curlabel->codeptr == NULL && fixup != NULL) + drccache_request_oob_codegen(list->cache, label_oob_callback, curlabel, fixup, param); + + return curlabel->codeptr; +} + + +/*------------------------------------------------- + drclabel_set_codeptr - set the pointer to a new + label +-------------------------------------------------*/ + +void drclabel_set_codeptr(drclabel_list *list, drcuml_codelabel label, drccodeptr codeptr) +{ + /* set the code pointer */ + drclabel *curlabel = label_find_or_allocate(list, label); + assert(curlabel->codeptr == NULL); + curlabel->codeptr = codeptr; +} + + + +/*************************************************************************** + LABEL MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + label_list_reset - reset a label + list (add all entries to the free list) +-------------------------------------------------*/ + +static void label_list_reset(drclabel_list *list, int fatal_on_leftovers) +{ + /* loop until out of labels */ + while (list->head != NULL) + { + /* remove from the list */ + drclabel *label = list->head; + list->head = label->next; + + /* fatal if we were a leftover */ + if (fatal_on_leftovers && label->codeptr == NULL) + fatalerror("Label %08X never defined!", label->label); + + /* free the label */ + drccache_memory_free(list->cache, label, sizeof(*label)); + } +} + + +/*------------------------------------------------- + label_find_or_allocate - look up a label and + allocate a new one if not found +-------------------------------------------------*/ + +static drclabel *label_find_or_allocate(drclabel_list *list, drcuml_codelabel label) +{ + drclabel *curlabel; + + /* find the label, or else allocate a new one */ + for (curlabel = list->head; curlabel != NULL; curlabel = curlabel->next) + if (curlabel->label == label) + break; + + /* if none found, allocate */ + if (curlabel == NULL) + { + curlabel = drccache_memory_alloc(list->cache, sizeof(*curlabel)); + curlabel->next = list->head; + curlabel->label = label; + curlabel->codeptr = NULL; + list->head = curlabel; + } + + return curlabel; +} + + +/*------------------------------------------------- + label_oob_callback - out-of-band codegen + callback for labels +-------------------------------------------------*/ + +static void label_oob_callback(drccodeptr *codeptr, void *param1, void *param2, void *param3) +{ + drclabel *label = param1; + drclabel_fixup_func callback = param2; + + (*callback)(param3, label->codeptr); +} diff --git a/src/emu/cpu/drcbeut.h b/src/emu/cpu/drcbeut.h new file mode 100644 index 00000000000..0bcf0a2a646 --- /dev/null +++ b/src/emu/cpu/drcbeut.h @@ -0,0 +1,192 @@ +/*************************************************************************** + + drcbeut.h + + Utility functions for dynamic recompiling backends. + + Copyright Aaron Giles + Released for general non-commercial use under the MAME license + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __DRCBEUT_H__ +#define __DRCBEUT_H__ + +#include "drcuml.h" + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* callback function for forward-referenced labels */ +typedef void (*drclabel_fixup_func)(void *parameter, drccodeptr labelcodeptr); + + +/* opaque structure representing a managed list of labels */ +typedef struct _drclabel_list drclabel_list; + + +/* opaque structure representing a managed code map */ +typedef struct _drcmap_state drcmap_state; + + +/* information about the hash tables used by the UML and backend */ +typedef struct _drchash_state drchash_state; +struct _drchash_state +{ + drccache * cache; /* cache where allocations come from */ + int modes; /* number of modes supported */ + + drccodeptr nocodeptr; /* pointer to code which will handle missing entries */ + + UINT8 l1bits; /* bits worth of entries in l1 hash tables */ + UINT8 l1shift; /* shift to apply to the PC to get the l1 hash entry */ + offs_t l1mask; /* mask to apply after shifting */ + UINT8 l2bits; /* bits worth of entries in l2 hash tables */ + UINT8 l2shift; /* shift to apply to the PC to get the l2 hash entry */ + offs_t l2mask; /* mask to apply after shifting */ + + drccodeptr ** emptyl1; /* pointer to empty l1 hash table */ + drccodeptr * emptyl2; /* pointer to empty l2 hash table */ + + drccodeptr ** base[1]; /* pointer to the l1 table for each mode */ +}; + + +/* an integer register, with low/high parts */ +typedef union _drcuml_ireg drcuml_ireg; +union _drcuml_ireg +{ +#ifdef LSB_FIRST + UINT32 l,h; /* 32-bit low, high parts of the register */ +#else + UINT32 h,l; /* 32-bit low, high parts of the register */ +#endif + UINT64 d; /* 64-bit full register */ +}; + + +/* an floating-point register, with low/high parts */ +typedef union _drcuml_freg drcuml_freg; +union _drcuml_freg +{ +#ifdef LSB_FIRST + float l,unused; /* 32-bit low, high parts of the register */ +#else + float unused,l; /* 32-bit low, high parts of the register */ +#endif + double d; /* 64-bit full register */ +}; + + +/* the collected machine state of a system */ +typedef struct _drcuml_machine_state drcuml_machine_state; +struct _drcuml_machine_state +{ + drcuml_ireg r[DRCUML_REG_I_END - DRCUML_REG_I0]; /* integer registers */ + drcuml_freg f[DRCUML_REG_F_END - DRCUML_REG_F0]; /* floating-point registers */ + drcuml_ireg exp; /* exception parameter register */ + UINT8 fmod; /* fmod (floating-point mode) register */ +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* ----- hash table management ----- */ + +/* allocate memory in the cache for the hash table tracker (it auto-frees with the cache) */ +drchash_state *drchash_alloc(drccache *cache, int modes, int addrbits, int ignorebits); + +/* flush existing hash tables and create new ones */ +int drchash_reset(drchash_state *drchash); + +/* note the beginning of a block */ +void drchash_block_begin(drchash_state *drchash, drcuml_block *block, const drcuml_instruction *instlist, UINT32 numinst); + +/* note the end of a block */ +void drchash_block_end(drchash_state *drchash, drcuml_block *block); + +/* set the default codeptr for any empty hash entries (defaults to NULL) */ +void drchash_set_default_codeptr(drchash_state *drchash, drccodeptr code); + +/* set the codeptr for the given mode/pc */ +int drchash_set_codeptr(drchash_state *drchash, UINT32 mode, UINT32 pc, drccodeptr code); + + + +/* ----- code map management ----- */ + +/* allocate memory in the cache for the code mapper (it auto-frees with the cache) */ +drcmap_state *drcmap_alloc(drccache *cache, UINT64 uniquevalue); + +/* note the beginning of a block */ +void drcmap_block_begin(drcmap_state *drcmap, drcuml_block *block); + +/* note the end of a block */ +void drcmap_block_end(drcmap_state *drcmap, drcuml_block *block); + +/* set a map value for the given code pointer */ +void drcmap_set_value(drcmap_state *drcmap, drccodeptr codebase, UINT32 mapvar, UINT32 newvalue); + +/* return a map value for the given code pointer */ +UINT32 drcmap_get_value(drcmap_state *drcmap, drccodeptr codebase, UINT32 mapvar); + +/* return the most recently set map value */ +UINT32 drcmap_get_last_value(drcmap_state *drcmap, UINT32 mapvar); + + + +/* ----- label management ----- */ + +/* allocate a label list within the cache (it auto-frees with the cache) */ +drclabel_list *drclabel_list_alloc(drccache *cache); + +/* note the beginning of a block */ +void drclabel_block_begin(drclabel_list *drcmap, drcuml_block *block); + +/* note the end of a block */ +void drclabel_block_end(drclabel_list *drcmap, drcuml_block *block); + +/* find or allocate a new label; returns NULL and requests an OOB callback if undefined */ +drccodeptr drclabel_get_codeptr(drclabel_list *list, drcuml_codelabel label, drclabel_fixup_func fixup, void *param); + +/* set the pointer to a new label */ +void drclabel_set_codeptr(drclabel_list *list, drcuml_codelabel label, drccodeptr codeptr); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + drchash_get_codeptr - return the codeptr + allocated for the given mode/pc +-------------------------------------------------*/ + +INLINE drccodeptr drchash_get_codeptr(drchash_state *drchash, UINT32 mode, UINT32 pc) +{ + assert(mode < drchash->modes); + return drchash->base[mode][(pc >> drchash->l1shift) & drchash->l1mask][(pc >> drchash->l2shift) & drchash->l2mask]; +} + + +/*------------------------------------------------- + drchash_code_exists - return TRUE if there is + a matching hash entry for the given mode/pc +-------------------------------------------------*/ + +INLINE int drchash_code_exists(drchash_state *drchash, UINT32 mode, UINT32 pc) +{ + return (drchash_get_codeptr(drchash, mode, pc) != drchash->nocodeptr); +} + + +#endif diff --git a/src/emu/cpu/drcbex64.c b/src/emu/cpu/drcbex64.c new file mode 100644 index 00000000000..156556419bb --- /dev/null +++ b/src/emu/cpu/drcbex64.c @@ -0,0 +1,7680 @@ +/*************************************************************************** + + drcbex64.c + + 64-bit x64 back-end for the universal machine language. + + Copyright Aaron Giles + Released for general non-commercial use under the MAME license + Visit http://mamedev.org for licensing and usage restrictions. + +**************************************************************************** + + Future improvements/changes: + + * Add support for FP registers + + * Optimize to avoid unnecessary reloads + + * Identify common pairs and optimize output + +**************************************************************************** + + ------------------------- + ABI/conventions (Windows) + ------------------------- + + Registers: + RAX - volatile, function return value + RBX - non-volatile + RCX - volatile, integer function parameter 1 + RDX - volatile, integer function parameter 2 + RSI - non-volatile + RDI - non-volatile + RBP - non-volatile + R8 - volatile, integer function parameter 3 + R9 - volatile, integer function parameter 4 + R10 - volatile + R11 - volatile, scratch immediate storage + R12 - non-volatile + R13 - non-volatile + R14 - non-volatile + R15 - non-volatile + + XMM0 - volatile, FP function parameter 1 + XMM1 - volatile, FP function parameter 2 + XMM2 - volatile, FP function parameter 3 + XMM3 - volatile, FP function parameter 4 + XMM4 - volatile + XMM5 - volatile + XMM6 - non-volatile + XMM7 - non-volatile + XMM8 - non-volatile + XMM9 - non-volatile + XMM10 - non-volatile + XMM11 - non-volatile + XMM12 - non-volatile + XMM13 - non-volatile + XMM14 - non-volatile + XMM15 - non-volatile + + + ----------------------------- + ABI/conventions (Linux/MacOS) + ----------------------------- + + Registers: + RAX - volatile, function return value + RBX - non-volatile + RCX - volatile, integer function parameter 4 + RDX - volatile, integer function parameter 3 + RSI - volatile, integer function parameter 2 + RDI - volatile, integer function parameter 1 + RBP - non-volatile + R8 - volatile, integer function parameter 5 + R9 - volatile, integer function parameter 6 + R10 - volatile + R11 - volatile, scratch immediate storage + R12 - non-volatile + R13 - non-volatile + R14 - non-volatile + R15 - non-volatile + + XMM0 - volatile, FP function parameter 1 + XMM1 - volatile, FP function parameter 2 + XMM2 - volatile, FP function parameter 3 + XMM3 - volatile, FP function parameter 4 + XMM4 - volatile + XMM5 - volatile + XMM6 - volatile + XMM7 - volatile + XMM8 - volatile + XMM9 - volatile + XMM10 - volatile + XMM11 - volatile + XMM12 - volatile + XMM13 - volatile + XMM14 - volatile + XMM15 - volatile + + + --------------- + Execution model + --------------- + + Registers (Windows): + RAX - scratch register + RBX - maps to I0 + RCX - scratch register + RDX - scratch register + RSI - maps to I1 + RDI - maps to I2 + RBP - pointer to code cache + R8 - scratch register + R9 - scratch register + R10 - scratch register + R11 - scratch register + R12 - maps to I3 + R13 - maps to I4 + R14 - maps to I5 + R15 - maps to I6 + + Registers (Linux/MacOS): + RAX - scratch register + RBX - maps to I0 + RCX - scratch register + RDX - scratch register + RSI - unused + RDI - unused + RBP - pointer to code cache + R8 - scratch register + R9 - scratch register + R10 - scratch register + R11 - scratch register + R12 - maps to I1 + R13 - maps to I2 + R14 - maps to I3 + R15 - maps to I4 + + 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 RAX. + + Entry stack: + [rsp] - return + + Runtime stack: + [rsp] - r9 home + [rsp+8] - r8 home + [rsp+16] - rdx home + [rsp+24] - rcx home + [rsp+40] - saved r15 + [rsp+48] - saved r14 + [rsp+56] - saved r13 + [rsp+64] - saved r12 + [rsp+72] - saved ebp + [rsp+80] - saved edi + [rsp+88] - saved esi + [rsp+96] - saved ebx + [rsp+104] - ret + +***************************************************************************/ + +#include "drcuml.h" +#include "drcumld.h" +#include "drcbeut.h" +#include "debugger.h" +#include "x86emit.h" +#undef REG_SP +#include "x86log.h" +#include <math.h> + + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define LOG_HASHJMPS (0) + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define PTYPE_M (1 << DRCUML_PTYPE_MEMORY) +#define PTYPE_I (1 << DRCUML_PTYPE_IMMEDIATE) +#define PTYPE_R (1 << DRCUML_PTYPE_INT_REGISTER) +#define PTYPE_F (1 << DRCUML_PTYPE_FLOAT_REGISTER) +#define PTYPE_MI (PTYPE_M | PTYPE_I) +#define PTYPE_RI (PTYPE_R | PTYPE_I) +#define PTYPE_MR (PTYPE_M | PTYPE_R) +#define PTYPE_MRI (PTYPE_M | PTYPE_R | PTYPE_I) +#define PTYPE_MF (PTYPE_M | PTYPE_F) + +#ifdef X64_WINDOWS_ABI + +#define REG_PARAM1 REG_RCX +#define REG_PARAM2 REG_RDX +#define REG_PARAM3 REG_R8 +#define REG_PARAM4 REG_R9 + +#else + +#define REG_PARAM1 REG_RDI +#define REG_PARAM2 REG_RSI +#define REG_PARAM3 REG_RDX +#define REG_PARAM4 REG_RCX + +#endif + + + +/*************************************************************************** + MACROS +***************************************************************************/ + +#define X86_CONDITION(condflags) (condition_map[condflags - DRCUML_COND_Z]) +#define X86_NOT_CONDITION(condflags) (condition_map[condflags - DRCUML_COND_Z] ^ 1) + +#undef MABS +#define MABS(drcbe, ptr) MBD(REG_RBP, offset_from_rbp(drcbe, (FPTR)(ptr))) + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* entry point */ +typedef UINT32 (*x86_entry_point_func)(UINT8 *rbpvalue, x86code *entry); + +/* opcode handler */ +typedef x86code *(*opcode_generate_func)(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); + + +/* opcode table entry */ +typedef struct _opcode_table_entry opcode_table_entry; +struct _opcode_table_entry +{ + drcuml_opcode opcode; /* opcode in question */ + opcode_generate_func func; /* function pointer to the work */ +}; + + +/* internal backend-specific state */ +struct _drcbe_state +{ + drcuml_state * drcuml; /* pointer back to our owner */ + drccache * cache; /* pointer to the cache */ + drcuml_machine_state state; /* state of the machine */ + drchash_state * hash; /* hash table state */ + drcmap_state * map; /* code map */ + drclabel_list * labels; /* label list */ + + x86_entry_point_func entry; /* entry point */ + x86code * exit; /* exit point */ + x86code * nocode; /* nocode handler */ + + x86code * mame_debug_hook; /* debugger callback */ + x86code * debug_log_hashjmp; /* hashjmp debugging */ + x86code * drcmap_get_value; /* map lookup helper */ + data_accessors accessors[ADDRESS_SPACES];/* memory accessors */ + + UINT32 ssemode; /* saved SSE mode */ + UINT32 ssemodesave; /* temporary location for saving */ + UINT32 ssecontrol[4]; /* copy of the sse_control array */ + UINT32 * absmask32; /* absolute value mask (32-bit) */ + UINT64 * absmask64; /* absolute value mask (32-bit) */ + + void * stacksave; /* saved stack pointer */ + void * hashstacksave; /* saved stack pointer for hashjmp */ + + UINT8 * rbpvalue; /* value of RBP */ + UINT8 flagsmap[0x1000]; /* flags map */ + + x86log_context * log; /* logging */ +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* primary back-end callbacks */ +static drcbe_state *drcbex64_alloc(drcuml_state *drcuml, drccache *cache, UINT32 flags, int modes, int addrbits, int ignorebits); +static void drcbex64_free(drcbe_state *drcbe); +static void drcbex64_reset(drcbe_state *drcbe); +static int drcbex64_execute(drcbe_state *drcbe, drcuml_codehandle *entry); +static void drcbex64_generate(drcbe_state *drcbe, drcuml_block *block, const drcuml_instruction *instlist, UINT32 numinst); +static int drcbex64_hash_exists(drcbe_state *drcbe, UINT32 mode, UINT32 pc); + +/* private helper functions */ +static void fixup_label(void *parameter, drccodeptr labelcodeptr); + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +/* globally-accessible interface to the backend */ +const drcbe_interface drcbe_x64_be_interface = +{ + drcbex64_alloc, + drcbex64_free, + drcbex64_reset, + drcbex64_execute, + drcbex64_generate, + drcbex64_hash_exists +}; + +/* opcode table */ +static opcode_generate_func opcode_table[DRCUML_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 UINT8 int_register_map[DRCUML_REG_I_END - DRCUML_REG_I0] = +{ +#ifdef X64_WINDOWS_ABI + REG_RBX, REG_RSI, REG_RDI, REG_R12, REG_R13, REG_R14, REG_R15 +#else + REG_RBX, REG_R12, REG_R13, REG_R14, REG_R15 +#endif +}; + +static UINT8 float_register_map[DRCUML_REG_F_END - DRCUML_REG_F0] = +{ + 0 +}; + +/* condition mapping table */ +static UINT8 condition_map[DRCUML_COND_MAX - DRCUML_COND_Z] = +{ + COND_Z, /* DRCUML_COND_Z = 0x80, requires Z */ + COND_NZ, /* DRCUML_COND_NZ, requires Z */ + COND_S, /* DRCUML_COND_S, requires S */ + COND_NS, /* DRCUML_COND_NS, requires S */ + COND_C, /* DRCUML_COND_C, requires C */ + COND_NC, /* DRCUML_COND_NC, requires C */ + COND_O, /* DRCUML_COND_V, requires V */ + COND_NO, /* DRCUML_COND_NV, requires V */ + COND_P, /* DRCUML_COND_U, requires U */ + COND_NP, /* DRCUML_COND_NU, requires U */ + COND_A, /* DRCUML_COND_A, requires CZ */ + COND_BE, /* DRCUML_COND_BE, requires CZ */ + COND_G, /* DRCUML_COND_G, requires SVZ */ + COND_LE, /* DRCUML_COND_LE, requires SVZ */ + COND_L, /* DRCUML_COND_L, requires SV */ + COND_GE, /* DRCUML_COND_GE, requires SV */ +}; + + + +/*************************************************************************** + TABLES +***************************************************************************/ + +static x86code *op_handle(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_hash(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_label(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_comment(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_mapvar(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); + +static x86code *op_debug(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_exit(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_hashjmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_jmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_exh(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_callh(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ret(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_callc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_recover(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); + +static x86code *op_setfmod(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_getfmod(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_getexp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); + +static x86code *op_load1u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_load1s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_load2u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_load2s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_load4u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_load4s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_load8u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_store1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_store2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_store4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_store8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read1u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read1s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read2u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read2s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read2m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read4u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read4s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read4m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read8u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read8m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_write1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_write2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_writ2m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_write4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_writ4m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_write8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_writ8m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_flags(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_mov(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_zext1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_zext2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_zext4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_sext1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_sext2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_sext4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_add(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_addc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_sub(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_subc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_cmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_mulu(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_muls(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_divu(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_divs(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_and(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_test(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_or(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_xor(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_shl(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_shr(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_sar(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ror(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_rol(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_rorc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_rolc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); + +static x86code *op_fload(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fstore(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fread(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fwrite(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fmov(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi4t(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi4r(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi4f(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi4c(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi8t(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi8r(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi8f(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi8c(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ffrfs(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ffrfd(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ffri4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ffri8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fadd(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fsub(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fcmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fmul(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fdiv(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fneg(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fabs(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fsqrt(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_frecip(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_frsqrt(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); + +static const opcode_table_entry opcode_table_source[] = +{ + /* Compile-time opcodes */ + { DRCUML_OP_HANDLE, op_handle }, /* HANDLE handle */ + { DRCUML_OP_HASH, op_hash }, /* HASH mode,pc */ + { DRCUML_OP_LABEL, op_label }, /* LABEL imm */ + { DRCUML_OP_COMMENT, op_comment }, /* COMMENT string */ + { DRCUML_OP_MAPVAR, op_mapvar }, /* MAPVAR mapvar,value */ + + /* Control Flow Operations */ + { DRCUML_OP_DEBUG, op_debug }, /* DEBUG pc */ + { DRCUML_OP_EXIT, op_exit }, /* EXIT src1[,c] */ + { DRCUML_OP_HASHJMP, op_hashjmp }, /* HASHJMP mode,pc,handle */ + { DRCUML_OP_JMP, op_jmp }, /* JMP imm[,c] */ + { DRCUML_OP_EXH, op_exh }, /* EXH handle,param[,c] */ + { DRCUML_OP_CALLH, op_callh }, /* CALLH handle[,c] */ + { DRCUML_OP_RET, op_ret }, /* RET [c] */ + { DRCUML_OP_CALLC, op_callc }, /* CALLC func,ptr[,c] */ + { DRCUML_OP_RECOVER, op_recover }, /* RECOVER dst,mapvar */ + + /* Internal Register Operations */ + { DRCUML_OP_SETFMOD, op_setfmod }, /* SETFMOD src */ + { DRCUML_OP_GETFMOD, op_getfmod }, /* GETFMOD dst */ + { DRCUML_OP_GETEXP, op_getexp }, /* GETEXP dst,index */ + + /* Integer Operations */ + { DRCUML_OP_LOAD1U, op_load1u }, /* LOAD1U dst,base,index */ + { DRCUML_OP_LOAD1S, op_load1s }, /* LOAD1S dst,base,index */ + { DRCUML_OP_LOAD2U, op_load2u }, /* LOAD2U dst,base,index */ + { DRCUML_OP_LOAD2S, op_load2s }, /* LOAD2S dst,base,index */ + { DRCUML_OP_LOAD4U, op_load4u }, /* LOAD4U dst,base,index */ + { DRCUML_OP_LOAD4S, op_load4s }, /* LOAD4S dst,base,index */ + { DRCUML_OP_LOAD8U, op_load8u }, /* LOAD8U dst,base,index */ + { DRCUML_OP_STORE1, op_store1 }, /* STORE1 base,index,src */ + { DRCUML_OP_STORE2, op_store2 }, /* STORE2 base,index,src */ + { DRCUML_OP_STORE4, op_store4 }, /* STORE4 base,index,src */ + { DRCUML_OP_STORE8, op_store8 }, /* STORE8 base,index,src */ + { DRCUML_OP_READ1U, op_read1u }, /* READ1U dst,space,src1 */ + { DRCUML_OP_READ1S, op_read1s }, /* READ1S dst,space,src1 */ + { DRCUML_OP_READ2U, op_read2u }, /* READ2U dst,space,src1 */ + { DRCUML_OP_READ2S, op_read2s }, /* READ2S dst,space,src1 */ + { DRCUML_OP_READ2M, op_read2m }, /* READ2M dst,space,src1,mask */ + { DRCUML_OP_READ4U, op_read4u }, /* READ4U dst,space,src1 */ + { DRCUML_OP_READ4S, op_read4s }, /* READ4S dst,space,src1 */ + { DRCUML_OP_READ4M, op_read4m }, /* READ4M dst,space,src1,mask */ + { DRCUML_OP_READ8U, op_read8u }, /* READ8U dst,space,src1 */ + { DRCUML_OP_READ8M, op_read8m }, /* READ8M dst,space,src1,mask */ + { DRCUML_OP_WRITE1, op_write1 }, /* WRITE1 space,dst,src1 */ + { DRCUML_OP_WRITE2, op_write2 }, /* WRITE2 space,dst,src1 */ + { DRCUML_OP_WRIT2M, op_writ2m }, /* WRIT2M space,dst,src1 */ + { DRCUML_OP_WRITE4, op_write4 }, /* WRITE4 space,dst,src1 */ + { DRCUML_OP_WRIT4M, op_writ4m }, /* WRIT4M space,dst,mask,src1 */ + { DRCUML_OP_WRITE8, op_write8 }, /* WRITE8 space,dst,src1 */ + { DRCUML_OP_WRIT8M, op_writ8m }, /* WRIT8M space,dst,mask,src1 */ + { DRCUML_OP_FLAGS, op_flags }, /* FLAGS dst,mask,table */ + { DRCUML_OP_MOV, op_mov }, /* MOV dst,src[,c] */ + { DRCUML_OP_ZEXT1, op_zext1 }, /* ZEXT1 dst,src */ + { DRCUML_OP_ZEXT2, op_zext2 }, /* ZEXT2 dst,src */ + { DRCUML_OP_ZEXT4, op_zext4 }, /* ZEXT4 dst,src */ + { DRCUML_OP_SEXT1, op_sext1 }, /* SEXT1 dst,src */ + { DRCUML_OP_SEXT2, op_sext2 }, /* SEXT2 dst,src */ + { DRCUML_OP_SEXT4, op_sext4 }, /* SEXT4 dst,src */ + { DRCUML_OP_ADD, op_add }, /* ADD dst,src1,src2[,f] */ + { DRCUML_OP_ADDC, op_addc }, /* ADDC dst,src1,src2[,f] */ + { DRCUML_OP_SUB, op_sub }, /* SUB dst,src1,src2[,f] */ + { DRCUML_OP_SUBB, op_subc }, /* SUBB dst,src1,src2[,f] */ + { DRCUML_OP_CMP, op_cmp }, /* CMP src1,src2[,f] */ + { DRCUML_OP_MULU, op_mulu }, /* MULU dst,edst,src1,src2[,f] */ + { DRCUML_OP_MULS, op_muls }, /* MULS dst,edst,src1,src2[,f] */ + { DRCUML_OP_DIVU, op_divu }, /* DIVU dst,edst,src1,src2[,f] */ + { DRCUML_OP_DIVS, op_divs }, /* DIVS dst,edst,src1,src2[,f] */ + { DRCUML_OP_AND, op_and }, /* AND dst,src1,src2[,f] */ + { DRCUML_OP_TEST, op_test }, /* TEST src1,src2[,f] */ + { DRCUML_OP_OR, op_or }, /* OR dst,src1,src2[,f] */ + { DRCUML_OP_XOR, op_xor }, /* XOR dst,src1,src2[,f] */ + { DRCUML_OP_SHL, op_shl }, /* SHL dst,src,count[,f] */ + { DRCUML_OP_SHR, op_shr }, /* SHR dst,src,count[,f] */ + { DRCUML_OP_SAR, op_sar }, /* SAR dst,src,count[,f] */ + { DRCUML_OP_ROL, op_rol }, /* ROL dst,src,count[,f] */ + { DRCUML_OP_ROLC, op_rolc }, /* ROLC dst,src,count[,f] */ + { DRCUML_OP_ROR, op_ror }, /* ROR dst,src,count[,f] */ + { DRCUML_OP_RORC, op_rorc }, /* RORC dst,src,count[,f] */ + + /* Floating Point Operations */ + { DRCUML_OP_FLOAD, op_fload }, /* FLOAD dst,base,index */ + { DRCUML_OP_FSTORE, op_fstore }, /* FSTORE base,index,src */ + { DRCUML_OP_FREAD, op_fread }, /* FREAD dst,space,src1 */ + { DRCUML_OP_FWRITE, op_fwrite }, /* FWRITE space,dst,src1 */ + { DRCUML_OP_FMOV, op_fmov }, /* FMOV dst,src1[,c] */ + { DRCUML_OP_FTOI4, op_ftoi4 }, /* FTOI4 dst,src1 */ + { DRCUML_OP_FTOI4T, op_ftoi4t }, /* FTOI4T dst,src1 */ + { DRCUML_OP_FTOI4R, op_ftoi4r }, /* FTOI4R dst,src1 */ + { DRCUML_OP_FTOI4F, op_ftoi4f }, /* FTOI4F dst,src1 */ + { DRCUML_OP_FTOI4C, op_ftoi4c }, /* FTOI4C dst,src1 */ + { DRCUML_OP_FTOI8, op_ftoi8 }, /* FTOI8 dst,src1 */ + { DRCUML_OP_FTOI8T, op_ftoi8t }, /* FTOI8T dst,src1 */ + { DRCUML_OP_FTOI8R, op_ftoi8r }, /* FTOI8R dst,src1 */ + { DRCUML_OP_FTOI8F, op_ftoi8f }, /* FTOI8F dst,src1 */ + { DRCUML_OP_FTOI8C, op_ftoi8c }, /* FTOI8C dst,src1 */ + { DRCUML_OP_FFRFS, op_ffrfs }, /* FFRFS dst,src1 */ + { DRCUML_OP_FFRFD, op_ffrfd }, /* FFRFD dst,src1 */ + { DRCUML_OP_FFRI4, op_ffri4 }, /* FFRI4 dst,src1 */ + { DRCUML_OP_FFRI8, op_ffri8 }, /* FFRI8 dst,src1 */ + { DRCUML_OP_FADD, op_fadd }, /* FADD dst,src1,src2 */ + { DRCUML_OP_FSUB, op_fsub }, /* FSUB dst,src1,src2 */ + { DRCUML_OP_FCMP, op_fcmp }, /* FCMP src1,src2 */ + { DRCUML_OP_FMUL, op_fmul }, /* FMUL dst,src1,src2 */ + { DRCUML_OP_FDIV, op_fdiv }, /* FDIV dst,src1,src2 */ + { DRCUML_OP_FNEG, op_fneg }, /* FNEG dst,src1 */ + { DRCUML_OP_FABS, op_fabs }, /* FABS dst,src1 */ + { DRCUML_OP_FSQRT, op_fsqrt }, /* FSQRT dst,src1 */ + { DRCUML_OP_FRECIP, op_frecip }, /* FRECIP dst,src1 */ + { DRCUML_OP_FRSQRT, op_frsqrt } /* FRSQRT dst,src1 */ +}; + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + param_select_register - select a register + to use, avoiding conflicts with the optional + checkparam +-------------------------------------------------*/ + +INLINE int param_select_register(int defreg, const drcuml_parameter *param, const drcuml_parameter *checkparam) +{ + if ((param->type == DRCUML_PTYPE_INT_REGISTER || param->type == DRCUML_PTYPE_FLOAT_REGISTER) && (checkparam == NULL || param->type != checkparam->type || param->value != checkparam->value)) + return param->value; + return defreg; +} + + +/*------------------------------------------------- + offset_from_rbp - return the verified offset + from rbp +-------------------------------------------------*/ + +INLINE INT32 offset_from_rbp(drcbe_state *drcbe, FPTR ptr) +{ + INT64 delta = (UINT8 *)ptr - drcbe->rbpvalue; + assert_always((INT32)delta == delta, "offset_from_rbp: delta out of range"); + return (INT32)delta; +} + + +/*------------------------------------------------- + short_immediate - true if the given immediate + fits as a signed 32-bit value +-------------------------------------------------*/ + +INLINE int short_immediate(INT64 immediate) +{ + return (INT32)immediate == immediate; +} + + +/*------------------------------------------------- + get_base_register_and_offset - determine right + base register and offset to access the given + target address +-------------------------------------------------*/ + +INLINE int get_base_register_and_offset(drcbe_state *drcbe, x86code **dst, FPTR target, UINT8 reg, INT32 *offset) +{ + INT64 delta = (UINT8 *)target - drcbe->rbpvalue; + if (short_immediate(delta)) + { + *offset = delta; + return REG_RBP; + } + else + { + *offset = 0; + emit_mov_r64_imm(dst, reg, target); // mov reg,target + return reg; + } +} + + +/*------------------------------------------------- + emit_smart_call_r64 - generate a call either + directly or via a call through pointer +-------------------------------------------------*/ + +INLINE void emit_smart_call_r64(drcbe_state *drcbe, x86code **dst, x86code *target, UINT8 reg) +{ + INT64 delta = target - (*dst + 5); + if (short_immediate(delta)) + emit_call(dst, target); // call target + else + { + emit_mov_r64_imm(dst, reg, (FPTR)target); // mov reg,target + emit_call_r64(dst, reg); // call reg + } +} + + +/*------------------------------------------------- + emit_smart_call_m64 - generate a call either + directly or via a call through pointer +-------------------------------------------------*/ + +INLINE void emit_smart_call_m64(drcbe_state *drcbe, x86code **dst, x86code **target) +{ + INT64 delta = *target - (*dst + 5); + if (short_immediate(delta)) + emit_call(dst, *target); // call *target + else + emit_call_m64(dst, MABS(drcbe, target)); // call [target] +} + + + +/*************************************************************************** + BACKEND CALLBACKS +***************************************************************************/ + +/*------------------------------------------------- + drcbex64_alloc - allocate back-end-specific + state +-------------------------------------------------*/ + +static drcbe_state *drcbex64_alloc(drcuml_state *drcuml, drccache *cache, UINT32 flags, int modes, int addrbits, int ignorebits) +{ + /* SSE control register mapping */ + static const UINT32 sse_control[4] = + { + 0xffc0, /* DRCUML_FMOD_TRUNC */ + 0x9fc0, /* DRCUML_FMOD_ROUND */ + 0xdfc0, /* DRCUML_FMOD_CEIL */ + 0xbfc0 /* DRCUML_FMOD_FLOOR */ + }; + drcbe_state *drcbe; + int opnum, entry; + + /* allocate space in the cache for our state */ + drcbe = drccache_memory_alloc_near(cache, sizeof(*drcbe)); + if (drcbe == NULL) + return NULL; + memset(drcbe, 0, sizeof(*drcbe)); + + /* remember our pointers */ + drcbe->drcuml = drcuml; + drcbe->cache = cache; + drcbe->rbpvalue = drccache_near(cache) + 0x80; + + /* build up necessary arrays */ + memcpy(drcbe->ssecontrol, sse_control, sizeof(drcbe->ssecontrol)); + drcbe->absmask32 = drccache_memory_alloc_near(cache, 16*2 + 15); + drcbe->absmask32 = (void *)(((FPTR)drcbe->absmask32 + 15) & ~15); + drcbe->absmask32[0] = drcbe->absmask32[1] = drcbe->absmask32[2] = drcbe->absmask32[3] = 0x7fffffff; + drcbe->absmask64 = (UINT64 *)&drcbe->absmask32[4]; + drcbe->absmask64[0] = drcbe->absmask64[1] = U64(0x7fffffffffffffff); + + /* get pointers to C functions we need to call */ +#ifdef ENABLE_DEBUGGER + drcbe->mame_debug_hook = (x86code *)mame_debug_hook; +#endif +#if LOG_HASHJMPS + drcbe->debug_log_hashjmp = (x86code *)debug_log_hashjmp; +#endif + drcbe->drcmap_get_value = (x86code *)drcmap_get_value; + + /* allocate hash tables */ + drcbe->hash = drchash_alloc(cache, modes, addrbits, ignorebits); + if (drcbe->hash == NULL) + return NULL; + + /* allocate code map */ + drcbe->map = drcmap_alloc(cache, 0); + if (drcbe->map == NULL) + return NULL; + + /* allocate a label tracker */ + drcbe->labels = drclabel_list_alloc(cache); + if (drcbe->labels == NULL) + return NULL; + + /* build the opcode table (static but it doesn't hurt to regenerate it) */ + for (opnum = 0; opnum < ARRAY_LENGTH(opcode_table_source); opnum++) + opcode_table[opcode_table_source[opnum].opcode] = opcode_table_source[opnum].func; + + /* build the flags map */ + for (entry = 0; entry < ARRAY_LENGTH(drcbe->flagsmap); entry++) + { + UINT8 flags = 0; + if (entry & 0x001) flags |= DRCUML_FLAG_C; + if (entry & 0x004) flags |= DRCUML_FLAG_U; + if (entry & 0x040) flags |= DRCUML_FLAG_Z; + if (entry & 0x080) flags |= DRCUML_FLAG_S; + if (entry & 0x800) flags |= DRCUML_FLAG_V; + drcbe->flagsmap[entry] = flags; + } + + /* create the log */ + if (flags & DRCUML_OPTION_LOG_NATIVE) + drcbe->log = x86log_create_context("drcbex64.asm"); + + return drcbe; +} + + +/*------------------------------------------------- + drcbex64_free - free back-end specific state +-------------------------------------------------*/ + +static void drcbex64_free(drcbe_state *drcbe) +{ + /* free the log context */ + if (drcbe->log != NULL) + x86log_free_context(drcbe->log); +} + + +/*------------------------------------------------- + drcbex64_reset - reset back-end specific state +-------------------------------------------------*/ + +static void drcbex64_reset(drcbe_state *drcbe) +{ + x86code **dst; + int spacenum; + + /* output a note to the log */ + if (drcbe->log != NULL) + x86log_printf(drcbe->log, "\n\n===========\nCACHE RESET\n===========\n\n"); + + /* fetch the accessors now that things are ready to go */ + for (spacenum = 0; spacenum < ADDRESS_SPACES; spacenum++) + if (active_address_space[spacenum].accessors != NULL) + drcbe->accessors[spacenum] = *active_address_space[spacenum].accessors; + + /* generate a little bit of glue code to set up the environment */ + dst = (x86code **)drccache_begin_codegen(drcbe->cache, 500); + if (dst == NULL) + fatalerror("Out of cache space after a reset!"); + + /* generate an entry point */ + drcbe->entry = (x86_entry_point_func)*dst; + emit_push_r64(dst, REG_RBX); // push rbx + emit_push_r64(dst, REG_RSI); // push rsi + emit_push_r64(dst, REG_RDI); // push rdi + emit_push_r64(dst, REG_RBP); // push rbp + emit_push_r64(dst, REG_R12); // push r12 + emit_push_r64(dst, REG_R13); // push r13 + emit_push_r64(dst, REG_R14); // push r14 + emit_push_r64(dst, REG_R15); // push r15 + emit_mov_r64_r64(dst, REG_RBP, REG_RCX); // mov rbp,rcx + emit_sub_r64_imm(dst, REG_RSP, 32); // sub rsp,32 + emit_mov_m64_r64(dst, MABS(drcbe, &drcbe->hashstacksave), REG_RSP); // mov [hashstacksave],rsp + emit_sub_r64_imm(dst, REG_RSP, 8); // sub rsp,8 + emit_mov_m64_r64(dst, MABS(drcbe, &drcbe->stacksave), REG_RSP); // mov [stacksave],rsp + emit_stmxcsr_m32(dst, MABS(drcbe, &drcbe->ssemode)); // stmxcsr [ssemode] + emit_jmp_r64(dst, REG_RDX); // jmp rdx + if (drcbe->log != NULL) + x86log_disasm_code_range(drcbe->log, "entry_point", (x86code *)drcbe->entry, *dst); + + /* generate an exit point */ + drcbe->exit = *dst; + emit_ldmxcsr_m32(dst, MABS(drcbe, &drcbe->ssemode)); // ldmxcsr [ssemode] + emit_mov_r64_m64(dst, REG_RSP, MABS(drcbe, &drcbe->hashstacksave)); // mov rsp,[hashstacksave] + emit_add_r64_imm(dst, REG_RSP, 32); // add rsp,32 + emit_pop_r64(dst, REG_R15); // pop r15 + emit_pop_r64(dst, REG_R14); // pop r14 + emit_pop_r64(dst, REG_R13); // pop r13 + emit_pop_r64(dst, REG_R12); // pop r12 + emit_pop_r64(dst, REG_RBP); // pop rbp + emit_pop_r64(dst, REG_RDI); // pop rdi + emit_pop_r64(dst, REG_RSI); // pop rsi + emit_pop_r64(dst, REG_RBX); // pop rbx + emit_ret(dst); // ret + if (drcbe->log != NULL) + x86log_disasm_code_range(drcbe->log, "exit_point", drcbe->exit, *dst); + + /* generate a no code point */ + drcbe->nocode = *dst; + emit_ret(dst); // ret + if (drcbe->log != NULL) + x86log_disasm_code_range(drcbe->log, "nocode", drcbe->nocode, *dst); + + /* finish up codegen */ + drccache_end_codegen(drcbe->cache); + + /* reset our hash tables */ + drchash_reset(drcbe->hash); + drchash_set_default_codeptr(drcbe->hash, drcbe->nocode); +} + + +/*------------------------------------------------- + drcbex64_generate - generate code +-------------------------------------------------*/ + +static void drcbex64_generate(drcbe_state *drcbe, drcuml_block *block, const drcuml_instruction *instlist, UINT32 numinst) +{ + const char *blockname = NULL; + char blockbuffer[100]; + drccodeptr *cachetop; + x86code *base; + x86code *dst; + int inum; + + /* tell all of our utility objects that a block is beginning */ + drchash_block_begin(drcbe->hash, block, instlist, numinst); + drclabel_block_begin(drcbe->labels, block); + drcmap_block_begin(drcbe->map, block); + + /* begin codegen; fail if we can't */ + cachetop = drccache_begin_codegen(drcbe->cache, numinst * 8 * 4); + if (cachetop == NULL) + drcuml_block_abort(block); + + /* compute the base by aligning the cache top to a cache line (assumed to be 64 bytes) */ + base = (x86code *)(((FPTR)*cachetop + 63) & ~63); + dst = base; + + /* generate code */ + for (inum = 0; inum < numinst; inum++) + { + const drcuml_instruction *inst = &instlist[inum]; + assert(inst->opcode < ARRAY_LENGTH(opcode_table)); + + /* add a comment */ + if (drcbe->log != NULL) + { + char dasm[100]; + drcuml_disasm(inst, dasm); + x86log_add_comment(drcbe->log, dst, "%s", dasm); + } + + /* extract a blockname */ + if (blockname == NULL) + { + if (inst->opcode == DRCUML_OP_HANDLE) + blockname = drcuml_handle_name((drcuml_codehandle *)(FPTR)inst->param[0].value); + else if (inst->opcode == DRCUML_OP_HASH) + { + sprintf(blockbuffer, "Code: mode=%d PC=%08X", (UINT32)inst->param[0].value, (offs_t)inst->param[1].value); + blockname = blockbuffer; + } + } + + /* generate code */ + dst = (*opcode_table[inst->opcode])(drcbe, dst, inst); + } + + /* complete codegen */ + *cachetop = (drccodeptr)dst; + drccache_end_codegen(drcbe->cache); + + /* log it */ + if (drcbe->log != NULL) + x86log_disasm_code_range(drcbe->log, (blockname == NULL) ? "Unknown block" : blockname, base, drccache_top(drcbe->cache)); + + /* tell all of our utility objects that the block is finished */ + drchash_block_end(drcbe->hash, block); + drclabel_block_end(drcbe->labels, block); + drcmap_block_end(drcbe->map, block); +} + + +/*------------------------------------------------- + drcbex64_hash_exists - return true if the + given mode/pc exists in the hash table +-------------------------------------------------*/ + +static int drcbex64_hash_exists(drcbe_state *drcbe, UINT32 mode, UINT32 pc) +{ + return drchash_code_exists(drcbe->hash, mode, pc); +} + + +/*------------------------------------------------- + drcbec_execute - execute a block of code + registered at the given mode/pc +-------------------------------------------------*/ + +static int drcbex64_execute(drcbe_state *drcbe, drcuml_codehandle *entry) +{ + /* call our entry point which will jump to the destination */ + return (*drcbe->entry)(drcbe->rbpvalue, (x86code *)drcuml_handle_codeptr(entry)); +} + + + +/*************************************************************************** + COMPILE HELPERS +***************************************************************************/ + +/*------------------------------------------------- + param_normalize - convert a full parameter + into a reduced set +-------------------------------------------------*/ + +static void param_normalize(drcbe_state *drcbe, const drcuml_parameter *src, drcuml_parameter *dest, UINT32 allowed) +{ + int regnum; + + switch (src->type) + { + /* immediates pass through */ + case DRCUML_PTYPE_IMMEDIATE: + assert(allowed & PTYPE_I); + dest->type = DRCUML_PTYPE_IMMEDIATE; + dest->value = src->value; + break; + + /* mapvars are converted to immediates with their current value */ + case DRCUML_PTYPE_MAPVAR: + assert(allowed & PTYPE_I); + dest->type = DRCUML_PTYPE_IMMEDIATE; + dest->value = drcmap_get_last_value(drcbe->map, src->value); + break; + + /* memory passes through */ + case DRCUML_PTYPE_MEMORY: + assert(allowed & PTYPE_M); + dest->type = DRCUML_PTYPE_MEMORY; + dest->value = (FPTR)src->value; + break; + + /* if a register maps to a register, keep it as a register; otherwise map it to memory */ + case DRCUML_PTYPE_INT_REGISTER: + assert(allowed & PTYPE_R); + assert(allowed & PTYPE_M); + regnum = int_register_map[src->value - DRCUML_REG_I0]; + if (regnum != 0) + { + dest->type = DRCUML_PTYPE_INT_REGISTER; + dest->value = regnum; + } + else + { + dest->type = DRCUML_PTYPE_MEMORY; + dest->value = (FPTR)&drcbe->state.r[src->value - DRCUML_REG_I0]; + } + break; + + /* if a register maps to a register, keep it as a register; otherwise map it to memory */ + case DRCUML_PTYPE_FLOAT_REGISTER: + assert(allowed & PTYPE_F); + assert(allowed & PTYPE_M); + regnum = float_register_map[src->value - DRCUML_REG_F0]; + if (regnum != 0) + { + dest->type = DRCUML_PTYPE_FLOAT_REGISTER; + dest->value = regnum; + } + else + { + dest->type = DRCUML_PTYPE_MEMORY; + dest->value = (FPTR)&drcbe->state.f[src->value - DRCUML_REG_F0]; + } + break; + + /* everything else is unexpected */ + default: + fatalerror("Unexpected parameter type"); + break; + } +} + + +/*------------------------------------------------- + param_normalize_1 - normalize a single + parameter instruction +-------------------------------------------------*/ + +static void param_normalize_1(drcbe_state *drcbe, const drcuml_instruction *inst, drcuml_parameter *dest0, UINT32 allowed0) +{ + assert(inst->numparams == 1); + param_normalize(drcbe, &inst->param[0], dest0, allowed0); +} + + +/*------------------------------------------------- + param_normalize_2 - normalize a 2 + parameter instruction +-------------------------------------------------*/ + +static void param_normalize_2(drcbe_state *drcbe, const drcuml_instruction *inst, drcuml_parameter *dest0, UINT32 allowed0, drcuml_parameter *dest1, UINT32 allowed1) +{ + assert(inst->numparams == 2); + param_normalize(drcbe, &inst->param[0], dest0, allowed0); + param_normalize(drcbe, &inst->param[1], dest1, allowed1); +} + + +/*------------------------------------------------- + param_normalize_2_commutative - normalize a 2 + parameter instruction, shuffling the + parameters on the assumption that the two + parameters can be swapped +-------------------------------------------------*/ + +static void param_normalize_2_commutative(drcbe_state *drcbe, const drcuml_instruction *inst, drcuml_parameter *dest0, UINT32 allowed0, drcuml_parameter *dest1, UINT32 allowed1) +{ + param_normalize_2(drcbe, inst, dest0, allowed0, dest1, allowed1); + + /* if the inner parameter is a memory operand, push it to the outer */ + if (dest0->type == DRCUML_PTYPE_MEMORY) + { + drcuml_parameter temp = *dest0; + *dest0 = *dest1; + *dest1 = temp; + } + + /* if the inner parameter is an immediate, push it to the outer */ + if (dest0->type == DRCUML_PTYPE_IMMEDIATE) + { + drcuml_parameter temp = *dest0; + *dest0 = *dest1; + *dest1 = temp; + } +} + + +/*------------------------------------------------- + param_normalize_3 - normalize a 3 + parameter instruction +-------------------------------------------------*/ + +static void param_normalize_3(drcbe_state *drcbe, const drcuml_instruction *inst, drcuml_parameter *dest0, UINT32 allowed0, drcuml_parameter *dest1, UINT32 allowed1, drcuml_parameter *dest2, UINT32 allowed2) +{ + assert(inst->numparams == 3); + param_normalize(drcbe, &inst->param[0], dest0, allowed0); + param_normalize(drcbe, &inst->param[1], dest1, allowed1); + param_normalize(drcbe, &inst->param[2], dest2, allowed2); +} + + +/*------------------------------------------------- + param_normalize_3_commutative - normalize a 3 + parameter instruction, shuffling the + parameters on the assumption that the last + 2 can be swapped +-------------------------------------------------*/ + +static void param_normalize_3_commutative(drcbe_state *drcbe, const drcuml_instruction *inst, drcuml_parameter *dest0, UINT32 allowed0, drcuml_parameter *dest1, UINT32 allowed1, drcuml_parameter *dest2, UINT32 allowed2) +{ + param_normalize_3(drcbe, inst, dest0, allowed0, dest1, allowed1, dest2, allowed2); + + /* if the inner parameter is a memory operand, push it to the outer */ + if (dest1->type == DRCUML_PTYPE_MEMORY) + { + drcuml_parameter temp = *dest1; + *dest1 = *dest2; + *dest2 = temp; + } + + /* if the inner parameter is an immediate, push it to the outer */ + if (dest1->type == DRCUML_PTYPE_IMMEDIATE) + { + drcuml_parameter temp = *dest1; + *dest1 = *dest2; + *dest2 = temp; + } + + /* if the destination and outer parameters are equal, move the outer to the inner */ + if (dest0->type == dest2->type && dest0->value == dest2->value && dest0->type != DRCUML_PTYPE_IMMEDIATE) + { + drcuml_parameter temp = *dest1; + *dest1 = *dest2; + *dest2 = temp; + } +} + + +/*------------------------------------------------- + param_normalize_4 - normalize a 4 + parameter instruction +-------------------------------------------------*/ + +static void param_normalize_4(drcbe_state *drcbe, const drcuml_instruction *inst, drcuml_parameter *dest0, UINT32 allowed0, drcuml_parameter *dest1, UINT32 allowed1, drcuml_parameter *dest2, UINT32 allowed2, drcuml_parameter *dest3, UINT32 allowed3) +{ + assert(inst->numparams == 4); + param_normalize(drcbe, &inst->param[0], dest0, allowed0); + param_normalize(drcbe, &inst->param[1], dest1, allowed1); + param_normalize(drcbe, &inst->param[2], dest2, allowed2); + param_normalize(drcbe, &inst->param[3], dest3, allowed3); +} + + +/*------------------------------------------------- + param_normalize_4_commutative - normalize a 4 + parameter instruction, shuffling the + parameters on the assumption that the last + 2 can be swapped +-------------------------------------------------*/ + +static void param_normalize_4_commutative(drcbe_state *drcbe, const drcuml_instruction *inst, drcuml_parameter *dest0, UINT32 allowed0, drcuml_parameter *dest1, UINT32 allowed1, drcuml_parameter *dest2, UINT32 allowed2, drcuml_parameter *dest3, UINT32 allowed3) +{ + param_normalize_4(drcbe, inst, dest0, allowed0, dest1, allowed1, dest2, allowed2, dest3, allowed3); + + /* if the inner parameter is a memory operand, push it to the outer */ + if (dest2->type == DRCUML_PTYPE_MEMORY) + { + drcuml_parameter temp = *dest2; + *dest2 = *dest3; + *dest3 = temp; + } + + /* if the inner parameter is an immediate, push it to the outer */ + if (dest2->type == DRCUML_PTYPE_IMMEDIATE) + { + drcuml_parameter temp = *dest2; + *dest2 = *dest3; + *dest3 = temp; + } + + /* if the destination and outer parameters are equal, move the outer to the inner */ + if (dest0->type == dest3->type && dest0->value == dest3->value && dest0->type != DRCUML_PTYPE_IMMEDIATE) + { + drcuml_parameter temp = *dest2; + *dest2 = *dest3; + *dest3 = temp; + } +} + + + +/*************************************************************************** + EMITTERS FOR 32-BIT OPERATIONS WITH PARAMETERS +***************************************************************************/ + +/*------------------------------------------------- + emit_mov_r32_p32 - move a 32-bit parameter + into a register +-------------------------------------------------*/ + +static void emit_mov_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (param->value == 0) + emit_xor_r32_r32(dst, reg, reg); // xor reg,reg + else + emit_mov_r32_imm(dst, reg, param->value); // mov reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_mov_r32_m32(dst, reg, MABS(drcbe, param->value)); // mov reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + if (reg != param->value) + emit_mov_r32_r32(dst, reg, param->value); // mov reg,param + } +} + + +/*------------------------------------------------- + emit_mov_m32_p32 - move a 32-bit parameter + into a memory location +-------------------------------------------------*/ + +static void emit_mov_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m32_imm(dst, MEMPARAMS, param->value); // mov [mem],param + else if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_mov_r32_m32(dst, REG_EAX, MABS(drcbe, param->value)); // mov eax,[param] + emit_mov_m32_r32(dst, MEMPARAMS, REG_EAX); // mov [mem],eax + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_r32(dst, MEMPARAMS, param->value); // mov [mem],param +} + + +/*------------------------------------------------- + emit_mov_p32_r32 - move a register into a + 32-bit parameter +-------------------------------------------------*/ + +static void emit_mov_p32_r32(drcbe_state *drcbe, x86code **dst, const drcuml_parameter *param, UINT8 reg) +{ + assert(param->type != DRCUML_PTYPE_IMMEDIATE); + if (param->type == DRCUML_PTYPE_MEMORY) + emit_mov_m32_r32(dst, MABS(drcbe, param->value), reg); // mov [param],reg + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + if (reg != param->value) + emit_mov_r32_r32(dst, param->value, reg); // mov param,reg + } +} + + +/*------------------------------------------------- + emit_add_r32_p32 - add operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_add_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_add_r32_imm(dst, reg, param->value); // add reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_add_r32_m32(dst, reg, MABS(drcbe, param->value)); // add reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_add_r32_r32(dst, reg, param->value); // add reg,param +} + + +/*------------------------------------------------- + emit_add_m32_p32 - add operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_add_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_add_m32_imm(dst, MEMPARAMS, param->value); // add [dest],param + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_add_m32_r32(dst, MEMPARAMS, reg); // add [dest],reg + } +} + + +/*------------------------------------------------- + emit_adc_r32_p32 - adc operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_adc_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_adc_r32_imm(dst, reg, param->value); // adc reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_adc_r32_m32(dst, reg, MABS(drcbe, param->value)); // adc reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_adc_r32_r32(dst, reg, param->value); // adc reg,param +} + + +/*------------------------------------------------- + emit_adc_m32_p32 - adc operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_adc_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_adc_m32_imm(dst, MEMPARAMS, param->value); // adc [dest],param + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_adc_m32_r32(dst, MEMPARAMS, reg); // adc [dest],reg + } +} + + +/*------------------------------------------------- + emit_sub_r32_p32 - sub operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_sub_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_sub_r32_imm(dst, reg, param->value); // sub reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_sub_r32_m32(dst, reg, MABS(drcbe, param->value)); // sub reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_sub_r32_r32(dst, reg, param->value); // sub reg,param +} + + +/*------------------------------------------------- + emit_sub_m32_p32 - sub operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_sub_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_sub_m32_imm(dst, MEMPARAMS, param->value); // sub [dest],param + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_sub_m32_r32(dst, MEMPARAMS, reg); // sub [dest],reg + } +} + + +/*------------------------------------------------- + emit_sbb_r32_p32 - sbb operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_sbb_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_sbb_r32_imm(dst, reg, param->value); // sbb reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_sbb_r32_m32(dst, reg, MABS(drcbe, param->value)); // sbb reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_sbb_r32_r32(dst, reg, param->value); // sbb reg,param +} + + +/*------------------------------------------------- + emit_sbb_m32_p32 - sbb operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_sbb_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_sbb_m32_imm(dst, MEMPARAMS, param->value); // sbb [dest],param + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_sbb_m32_r32(dst, MEMPARAMS, reg); // sbb [dest],reg + } +} + + +/*------------------------------------------------- + emit_cmp_r32_p32 - cmp operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_cmp_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + emit_cmp_r32_imm(dst, reg, param->value); // cmp reg,param + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_cmp_r32_m32(dst, reg, MABS(drcbe, param->value)); // cmp reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_cmp_r32_r32(dst, reg, param->value); // cmp reg,param +} + + +/*------------------------------------------------- + emit_cmp_m32_p32 - cmp operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_cmp_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + emit_cmp_m32_imm(dst, MEMPARAMS, param->value); // cmp [dest],param + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_cmp_m32_r32(dst, MEMPARAMS, reg); // cmp [dest],reg + } +} + + +/*------------------------------------------------- + emit_and_r32_p32 - and operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_and_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0xffffffff) + /* skip */; + else if (inst->condflags != 0 && (UINT32)param->value == 0) + emit_xor_r32_r32(dst, reg, reg); // xor reg,reg + else + emit_and_r32_imm(dst, reg, param->value); // and reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_and_r32_m32(dst, reg, MABS(drcbe, param->value)); // and reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_and_r32_r32(dst, reg, param->value); // and reg,param +} + + +/*------------------------------------------------- + emit_and_m32_p32 - and operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_and_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0xffffffff) + /* skip */; + else if (inst->condflags != 0 && (UINT32)param->value == 0) + emit_mov_m32_imm(dst, MEMPARAMS, 0); // mov [dest],0 + else + emit_and_m32_imm(dst, MEMPARAMS, param->value); // and [dest],param + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_and_m32_r32(dst, MEMPARAMS, reg); // and [dest],reg + } +} + + +/*------------------------------------------------- + emit_test_r32_p32 - test operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_test_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + emit_test_r32_imm(dst, reg, param->value); // test reg,param + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_test_m32_r32(dst, MABS(drcbe, param->value), reg); // test [param],reg + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_test_r32_r32(dst, reg, param->value); // test reg,param +} + + +/*------------------------------------------------- + emit_test_m32_p32 - test operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_test_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + emit_test_m32_imm(dst, MEMPARAMS, param->value); // test [dest],param + else if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_mov_r32_p32(drcbe, dst, REG_EAX, param); // mov reg,param + emit_test_m32_r32(dst, MEMPARAMS, REG_EAX); // test [dest],reg + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_test_m32_r32(dst, MEMPARAMS, param->value); // test [dest],param +} + + +/*------------------------------------------------- + emit_or_r32_p32 - or operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_or_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else if (inst->condflags != 0 && (UINT32)param->value == 0xffffffff) + emit_mov_r32_imm(dst, reg, -1); // mov reg,-1 + else + emit_or_r32_imm(dst, reg, param->value); // or reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_or_r32_m32(dst, reg, MABS(drcbe, param->value)); // or reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_or_r32_r32(dst, reg, param->value); // or reg,param +} + + +/*------------------------------------------------- + emit_or_m32_p32 - or operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_or_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else if (inst->condflags != 0 && (UINT32)param->value == 0xffffffff) + emit_mov_m32_imm(dst, MEMPARAMS, -1); // mov [dest],-1 + else + emit_or_m32_imm(dst, MEMPARAMS, param->value); // or [dest],param + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_or_m32_r32(dst, MEMPARAMS, reg); // or [dest],reg + } +} + + +/*------------------------------------------------- + emit_xor_r32_p32 - xor operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_xor_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else if (inst->condflags != 0 && (UINT32)param->value == 0xffffffff) + emit_not_r32(dst, reg); // not reg + else + emit_xor_r32_imm(dst, reg, param->value); // xor reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_xor_r32_m32(dst, reg, MABS(drcbe, param->value)); // xor reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_xor_r32_r32(dst, reg, param->value); // xor reg,param +} + + +/*------------------------------------------------- + emit_xor_m32_p32 - xor operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_xor_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else if (inst->condflags != 0 && (UINT32)param->value == 0xffffffff) + emit_not_m32(dst, MEMPARAMS); // not [dest] + else + emit_xor_m32_imm(dst, MEMPARAMS, param->value); // xor [dest],param + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_xor_m32_r32(dst, MEMPARAMS, reg); // xor [dest],reg + } +} + + +/*------------------------------------------------- + emit_shl_r32_p32 - shl operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_shl_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_shl_r32_imm(dst, reg, param->value); // shl reg,param + } + else + { + emit_mov_r32_p32(drcbe, 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 +-------------------------------------------------*/ + +static void emit_shl_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_shl_m32_imm(dst, MEMPARAMS, param->value); // shl [dest],param + } + else + { + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_shl_m32_cl(dst, MEMPARAMS); // shl [dest],cl + } +} + + +/*------------------------------------------------- + emit_shr_r32_p32 - shr operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_shr_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_shr_r32_imm(dst, reg, param->value); // shr reg,param + } + else + { + emit_mov_r32_p32(drcbe, 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 +-------------------------------------------------*/ + +static void emit_shr_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_shr_m32_imm(dst, MEMPARAMS, param->value); // shr [dest],param + } + else + { + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_shr_m32_cl(dst, MEMPARAMS); // shr [dest],cl + } +} + + +/*------------------------------------------------- + emit_sar_r32_p32 - sar operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_sar_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_sar_r32_imm(dst, reg, param->value); // sar reg,param + } + else + { + emit_mov_r32_p32(drcbe, 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 +-------------------------------------------------*/ + +static void emit_sar_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_sar_m32_imm(dst, MEMPARAMS, param->value); // sar [dest],param + } + else + { + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_sar_m32_cl(dst, MEMPARAMS); // sar [dest],cl + } +} + + +/*------------------------------------------------- + emit_rol_r32_p32 - rol operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_rol_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_rol_r32_imm(dst, reg, param->value); // rol reg,param + } + else + { + emit_mov_r32_p32(drcbe, 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 +-------------------------------------------------*/ + +static void emit_rol_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_rol_m32_imm(dst, MEMPARAMS, param->value); // rol [dest],param + } + else + { + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_rol_m32_cl(dst, MEMPARAMS); // rol [dest],cl + } +} + + +/*------------------------------------------------- + emit_ror_r32_p32 - ror operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_ror_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_ror_r32_imm(dst, reg, param->value); // ror reg,param + } + else + { + emit_mov_r32_p32(drcbe, 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 +-------------------------------------------------*/ + +static void emit_ror_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_ror_m32_imm(dst, MEMPARAMS, param->value); // ror [dest],param + } + else + { + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_ror_m32_cl(dst, MEMPARAMS); // ror [dest],cl + } +} + + +/*------------------------------------------------- + emit_rcl_r32_p32 - rcl operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_rcl_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_rcl_r32_imm(dst, reg, param->value); // rcl reg,param + } + else + { + emit_mov_r32_p32(drcbe, 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 +-------------------------------------------------*/ + +static void emit_rcl_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_rcl_m32_imm(dst, MEMPARAMS, param->value); // rcl [dest],param + } + else + { + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_rcl_m32_cl(dst, MEMPARAMS); // rcl [dest],cl + } +} + + +/*------------------------------------------------- + emit_rcr_r32_p32 - rcr operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_rcr_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_rcr_r32_imm(dst, reg, param->value); // rcr reg,param + } + else + { + emit_mov_r32_p32(drcbe, 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 +-------------------------------------------------*/ + +static void emit_rcr_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_rcr_m32_imm(dst, MEMPARAMS, param->value); // rcr [dest],param + } + else + { + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_rcr_m32_cl(dst, MEMPARAMS); // rcr [dest],cl + } +} + + + +/*************************************************************************** + EMITTERS FOR 64-BIT OPERATIONS WITH PARAMETERS +***************************************************************************/ + +/*------------------------------------------------- + emit_mov_r64_p64 - move a 64-bit parameter + into a register +-------------------------------------------------*/ + +static void emit_mov_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (param->value == 0) + emit_xor_r32_r32(dst, reg, reg); // xor reg,reg + else + emit_mov_r64_imm(dst, reg, param->value); // mov reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_mov_r64_m64(dst, reg, MABS(drcbe, param->value)); // mov reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + if (reg != param->value) + emit_mov_r64_r64(dst, reg, param->value); // mov reg,param + } +} + + +/*------------------------------------------------- + emit_mov_m64_p64 - move a 64-bit parameter + into a memory location +-------------------------------------------------*/ + +static void emit_mov_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (short_immediate(param->value)) + emit_mov_m64_imm(dst, MEMPARAMS, param->value); // mov [mem],param + else + { + emit_mov_r64_imm(dst, REG_RAX, param->value); // mov rax,[param] + emit_mov_m64_r64(dst, MEMPARAMS, REG_RAX); // mov [mem],rax + } + } + else if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_mov_r64_m64(dst, REG_RAX, MABS(drcbe, param->value)); // mov rax,[param] + emit_mov_m64_r64(dst, MEMPARAMS, REG_EAX); // mov [mem],rax + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m64_r64(dst, MEMPARAMS, param->value); // mov [mem],param +} + + +/*------------------------------------------------- + emit_mov_p64_r64 - move a registers into a + 64-bit parameter +-------------------------------------------------*/ + +static void emit_mov_p64_r64(drcbe_state *drcbe, x86code **dst, const drcuml_parameter *param, UINT8 reg) +{ + assert(param->type != DRCUML_PTYPE_IMMEDIATE); + if (param->type == DRCUML_PTYPE_MEMORY) + emit_mov_m64_r64(dst, MABS(drcbe, param->value), reg); // mov [param],reg + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + if (reg != param->value) + emit_mov_r64_r64(dst, param->value, reg); // mov param,reg + } +} + + +/*------------------------------------------------- + emit_add_r64_p64 - add operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_add_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + { + if (short_immediate(param->value)) + emit_add_r64_imm(dst, reg, param->value); // add reg,param + else + { + emit_mov_r64_imm(dst, REG_R11, param->value); // mov r11,param + emit_add_r64_r64(dst, reg, REG_R11); // add reg,r11 + } + } + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_add_r64_m64(dst, reg, MABS(drcbe, param->value)); // add reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_add_r64_r64(dst, reg, param->value); // add reg,param +} + + +/*------------------------------------------------- + emit_add_m64_p64 - add operation to a 64-bit + memory location from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_add_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + { + if (short_immediate(param->value)) + emit_add_m64_imm(dst, MEMPARAMS, param->value); // add [mem],param + else + { + emit_mov_r64_imm(dst, REG_R11, param->value); // mov r11,param + emit_add_m64_r64(dst, MEMPARAMS, REG_R11); // add [mem],r11 + } + } + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r64_p64(drcbe, dst, reg, param); // mov reg,param + emit_add_m64_r64(dst, MEMPARAMS, reg); // add [dest],reg + } +} + + +/*------------------------------------------------- + emit_adc_r64_p64 - adc operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_adc_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (short_immediate(param->value)) + emit_adc_r64_imm(dst, reg, param->value); // adc reg,param + else + { + emit_mov_r64_imm(dst, REG_R11, param->value); // mov r11,param + emit_adc_r64_r64(dst, reg, REG_R11); // adc reg,r11 + } + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_adc_r64_m64(dst, reg, MABS(drcbe, param->value)); // adc reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_adc_r64_r64(dst, reg, param->value); // adc reg,param +} + + +/*------------------------------------------------- + emit_adc_m64_p64 - adc operation to a 64-bit + memory locaiton from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_adc_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE && short_immediate(param->value)) + emit_adc_m64_imm(dst, MEMPARAMS, param->value); // adc [mem],param + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r64_p64(drcbe, dst, reg, param); // mov reg,param + emit_adc_m64_r64(dst, MEMPARAMS, reg); // adc [dest],reg + } +} + + +/*------------------------------------------------- + emit_sub_r64_p64 - sub operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_sub_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + { + if (short_immediate(param->value)) + emit_sub_r64_imm(dst, reg, param->value); // sub reg,param + else + { + emit_mov_r64_imm(dst, REG_R11, param->value); // mov r11,param + emit_sub_r64_r64(dst, reg, REG_R11); // sub reg,r11 + } + } + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_sub_r64_m64(dst, reg, MABS(drcbe, param->value)); // sub reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_sub_r64_r64(dst, reg, param->value); // sub reg,param +} + + +/*------------------------------------------------- + emit_sub_m64_p64 - sub operation to a 64-bit + memory location from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_sub_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + { + if (short_immediate(param->value)) + emit_sub_m64_imm(dst, MEMPARAMS, param->value); // sub [mem],param + else + { + emit_mov_r64_imm(dst, REG_R11, param->value); // mov r11,param + emit_sub_m64_r64(dst, MEMPARAMS, REG_R11); // sub [mem],r11 + } + } + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r64_p64(drcbe, dst, reg, param); // mov reg,param + emit_sub_m64_r64(dst, MEMPARAMS, reg); // sub [dest],reg + } +} + + +/*------------------------------------------------- + emit_sbb_r64_p64 - sbb operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_sbb_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (short_immediate(param->value)) + emit_sbb_r64_imm(dst, reg, param->value); // sbb reg,param + else + { + emit_mov_r64_imm(dst, REG_R11, param->value); // mov r11,param + emit_sbb_r64_r64(dst, reg, REG_R11); // sbb reg,r11 + } + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_sbb_r64_m64(dst, reg, MABS(drcbe, param->value)); // sbb reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_sbb_r64_r64(dst, reg, param->value); // sbb reg,param +} + + +/*------------------------------------------------- + emit_sbb_m64_p64 - sbb operation to a 64-bit + memory location from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_sbb_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE && short_immediate(param->value)) + emit_sbb_m64_imm(dst, MEMPARAMS, param->value); // sbb [mem],param + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r64_p64(drcbe, dst, reg, param); // mov reg,param + emit_sbb_m64_r64(dst, MEMPARAMS, reg); // sbb [dest],reg + } +} + + +/*------------------------------------------------- + emit_cmp_r64_p64 - cmp operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_cmp_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (short_immediate(param->value)) + emit_cmp_r64_imm(dst, reg, param->value); // cmp reg,param + else + { + emit_mov_r64_imm(dst, REG_R11, param->value); // mov r11,param + emit_cmp_r64_r64(dst, reg, REG_R11); // cmp reg,r11 + } + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_cmp_r64_m64(dst, reg, MABS(drcbe, param->value)); // cmp reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_cmp_r64_r64(dst, reg, param->value); // cmp reg,param +} + + +/*------------------------------------------------- + emit_cmp_m64_p64 - cmp operation to a 64-bit + memory location from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_cmp_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE && short_immediate(param->value)) + emit_cmp_m64_imm(dst, MEMPARAMS, param->value); // cmp [dest],param + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r64_p64(drcbe, dst, reg, param); // mov reg,param + emit_cmp_m64_r64(dst, MEMPARAMS, reg); // cmp [dest],reg + } +} + + +/*------------------------------------------------- + emit_and_r64_p64 - and operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_and_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != U64(0xffffffffffffffff)) + { + if (short_immediate(param->value)) + emit_and_r64_imm(dst, reg, param->value); // and reg,param + else + { + emit_mov_r64_imm(dst, REG_R11, param->value); // mov r11,param + emit_and_r64_r64(dst, reg, REG_R11); // and reg,r11 + } + } + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_and_r64_m64(dst, reg, MABS(drcbe, param->value)); // and reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_and_r64_r64(dst, reg, param->value); // and reg,param +} + + +/*------------------------------------------------- + emit_and_m64_p64 - and operation to a 64-bit + memory location from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_and_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != U64(0xffffffffffffffff)) + { + if (short_immediate(param->value)) + emit_and_m64_imm(dst, MEMPARAMS, param->value); // and [mem],param + else + { + emit_mov_r64_imm(dst, REG_R11, param->value); // mov r11,param + emit_and_m64_r64(dst, MEMPARAMS, REG_R11); // and [mem],r11 + } + } + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r64_p64(drcbe, dst, reg, param); // mov reg,param + emit_and_m64_r64(dst, MEMPARAMS, reg); // and [dest],reg + } +} + + +/*------------------------------------------------- + emit_test_r64_p64 - test operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_test_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (short_immediate(param->value)) + emit_test_r64_imm(dst, reg, param->value); // test reg,param + else + { + emit_mov_r64_imm(dst, REG_R11, param->value); // mov r11,param + emit_test_r64_r64(dst, reg, REG_R11); // test reg,r11 + } + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_test_m64_r64(dst, MABS(drcbe, param->value), reg); // test [param],reg + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_test_r64_r64(dst, reg, param->value); // test reg,param +} + + +/*------------------------------------------------- + emit_test_m64_p64 - test operation to a 64-bit + memory location from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_test_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE && short_immediate(param->value)) + emit_test_m64_imm(dst, MEMPARAMS, param->value); // test [dest],param + else if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_mov_r64_p64(drcbe, dst, REG_EAX, param); // mov reg,param + emit_test_m64_r64(dst, MEMPARAMS, REG_EAX); // test [dest],reg + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_test_m64_r64(dst, MEMPARAMS, param->value); // test [dest],param +} + + +/*------------------------------------------------- + emit_or_r64_p64 - or operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_or_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + { + if (short_immediate(param->value)) + emit_or_r64_imm(dst, reg, param->value); // or reg,param + else + { + emit_mov_r64_imm(dst, REG_R11, param->value); // mov r11,param + emit_or_r64_r64(dst, reg, REG_R11); // or reg,r11 + } + } + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_or_r64_m64(dst, reg, MABS(drcbe, param->value)); // or reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_or_r64_r64(dst, reg, param->value); // or reg,param +} + + +/*------------------------------------------------- + emit_or_m64_p64 - or operation to a 64-bit + memory location from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_or_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + { + if (short_immediate(param->value)) + emit_or_m64_imm(dst, MEMPARAMS, param->value); // or [mem],param + else + { + emit_mov_r64_imm(dst, REG_R11, param->value); // mov r11,param + emit_or_m64_r64(dst, MEMPARAMS, REG_R11); // or [mem],r11 + } + } + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r64_p64(drcbe, dst, reg, param); // mov reg,param + emit_or_m64_r64(dst, MEMPARAMS, reg); // or [dest],reg + } +} + + +/*------------------------------------------------- + emit_xor_r64_p64 - xor operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_xor_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + { + if (param->value == U64(0xffffffffffffffff)) + emit_not_r64(dst, reg); // not reg + else if (short_immediate(param->value)) + emit_xor_r64_imm(dst, reg, param->value); // xor reg,param + else + { + emit_mov_r64_imm(dst, REG_R11, param->value); // mov r11,param + emit_xor_r64_r64(dst, reg, REG_R11); // xor reg,r11 + } + } + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_xor_r64_m64(dst, reg, MABS(drcbe, param->value)); // xor reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_xor_r64_r64(dst, reg, param->value); // xor reg,param +} + + +/*------------------------------------------------- + emit_xor_m64_p64 - xor operation to a 64-bit + memory location from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_xor_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + { + if (param->value == U64(0xffffffffffffffff)) + emit_not_m64(dst, MEMPARAMS); // not [mem] + else if (short_immediate(param->value)) + emit_xor_m64_imm(dst, MEMPARAMS, param->value); // xor [mem],param + else + { + emit_mov_r64_imm(dst, REG_R11, param->value); // mov r11,param + emit_xor_m64_r64(dst, MEMPARAMS, REG_R11); // xor [mem],r11 + } + } + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r64_p64(drcbe, dst, reg, param); // mov reg,param + emit_xor_m64_r64(dst, MEMPARAMS, reg); // xor [dest],reg + } +} + + +/*------------------------------------------------- + emit_shl_r64_p64 - shl operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_shl_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_shl_r64_imm(dst, reg, param->value); // shl reg,param + } + else + { + emit_mov_r64_p64(drcbe, dst, REG_RCX, param); // mov rcx,param + emit_shl_r64_cl(dst, reg); // shl reg,cl + } +} + + +/*------------------------------------------------- + emit_shl_m64_p64 - shl operation to a 64-bit + memory location from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_shl_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT64)param->value == 0) + /* skip */; + else + emit_shl_m64_imm(dst, MEMPARAMS, param->value); // shl [dest],param + } + else + { + emit_mov_r64_p64(drcbe, dst, REG_RCX, param); // mov rcx,param + emit_shl_m64_cl(dst, MEMPARAMS); // shl [dest],cl + } +} + + +/*------------------------------------------------- + emit_shr_r64_p64 - shr operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_shr_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_shr_r64_imm(dst, reg, param->value); // shr reg,param + } + else + { + emit_mov_r64_p64(drcbe, dst, REG_RCX, param); // mov rcx,param + emit_shr_r64_cl(dst, reg); // shr reg,cl + } +} + + +/*------------------------------------------------- + emit_shr_m64_p64 - shr operation to a 64-bit + memory location from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_shr_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT64)param->value == 0) + /* skip */; + else + emit_shr_m64_imm(dst, MEMPARAMS, param->value); // shr [dest],param + } + else + { + emit_mov_r64_p64(drcbe, dst, REG_RCX, param); // mov rcx,param + emit_shr_m64_cl(dst, MEMPARAMS); // shr [dest],cl + } +} + + +/*------------------------------------------------- + emit_sar_r64_p64 - sar operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_sar_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_sar_r64_imm(dst, reg, param->value); // sar reg,param + } + else + { + emit_mov_r64_p64(drcbe, dst, REG_RCX, param); // mov rcx,param + emit_sar_r64_cl(dst, reg); // sar reg,cl + } +} + + +/*------------------------------------------------- + emit_sar_m64_p64 - sar operation to a 64-bit + memory location from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_sar_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT64)param->value == 0) + /* skip */; + else + emit_sar_m64_imm(dst, MEMPARAMS, param->value); // sar [dest],param + } + else + { + emit_mov_r64_p64(drcbe, dst, REG_RCX, param); // mov rcx,param + emit_sar_m64_cl(dst, MEMPARAMS); // sar [dest],cl + } +} + + +/*------------------------------------------------- + emit_rol_r64_p64 - rol operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_rol_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_rol_r64_imm(dst, reg, param->value); // rol reg,param + } + else + { + emit_mov_r64_p64(drcbe, dst, REG_RCX, param); // mov rcx,param + emit_rol_r64_cl(dst, reg); // rol reg,cl + } +} + + +/*------------------------------------------------- + emit_rol_m64_p64 - rol operation to a 64-bit + memory location from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_rol_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT64)param->value == 0) + /* skip */; + else + emit_rol_m64_imm(dst, MEMPARAMS, param->value); // rol [dest],param + } + else + { + emit_mov_r64_p64(drcbe, dst, REG_RCX, param); // mov rcx,param + emit_rol_m64_cl(dst, MEMPARAMS); // rol [dest],cl + } +} + + +/*------------------------------------------------- + emit_ror_r64_p64 - ror operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_ror_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_ror_r64_imm(dst, reg, param->value); // ror reg,param + } + else + { + emit_mov_r64_p64(drcbe, dst, REG_RCX, param); // mov rcx,param + emit_ror_r64_cl(dst, reg); // ror reg,cl + } +} + + +/*------------------------------------------------- + emit_ror_m64_p64 - ror operation to a 64-bit + memory location from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_ror_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT64)param->value == 0) + /* skip */; + else + emit_ror_m64_imm(dst, MEMPARAMS, param->value); // ror [dest],param + } + else + { + emit_mov_r64_p64(drcbe, dst, REG_RCX, param); // mov rcx,param + emit_ror_m64_cl(dst, MEMPARAMS); // ror [dest],cl + } +} + + +/*------------------------------------------------- + emit_rcl_r64_p64 - rcl operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_rcl_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_rcl_r64_imm(dst, reg, param->value); // rcl reg,param + } + else + { + emit_mov_r64_p64(drcbe, dst, REG_RCX, param); // mov rcx,param + emit_rcl_r64_cl(dst, reg); // rcl reg,cl + } +} + + +/*------------------------------------------------- + emit_rcl_m64_p64 - rcl operation to a 64-bit + memory location from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_rcl_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT64)param->value == 0) + /* skip */; + else + emit_rcl_m64_imm(dst, MEMPARAMS, param->value); // rcl [dest],param + } + else + { + emit_mov_r64_p64(drcbe, dst, REG_RCX, param); // mov rcx,param + emit_rcl_m64_cl(dst, MEMPARAMS); // rcl [dest],cl + } +} + + +/*------------------------------------------------- + emit_rcr_r64_p64 - rcr operation to a 64-bit + register from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_rcr_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_rcr_r64_imm(dst, reg, param->value); // rcr reg,param + } + else + { + emit_mov_r64_p64(drcbe, dst, REG_RCX, param); // mov rcx,param + emit_rcr_r64_cl(dst, reg); // rcr reg,cl + } +} + + +/*------------------------------------------------- + emit_rcr_m64_p64 - rcr operation to a 64-bit + memory location from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_rcr_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT64)param->value == 0) + /* skip */; + else + emit_rcr_m64_imm(dst, MEMPARAMS, param->value); // rcr [dest],param + } + else + { + emit_mov_r64_p64(drcbe, dst, REG_RCX, param); // mov rcx,param + emit_rcr_m64_cl(dst, MEMPARAMS); // rcr [dest],cl + } +} + + + +/*************************************************************************** + EMITTERS FOR FLOATING POINT OPERATIONS WITH PARAMETERS +***************************************************************************/ + +/*------------------------------------------------- + emit_movss_r128_p32 - move a 32-bit parameter + into a register +-------------------------------------------------*/ + +static void emit_movss_r128_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param) +{ + assert(param->type != DRCUML_PTYPE_IMMEDIATE); + if (param->type == DRCUML_PTYPE_MEMORY) + emit_movss_r128_m32(dst, reg, MABS(drcbe, param->value)); // movss reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + if (reg != param->value) + emit_movss_r128_r128(dst, reg, param->value); // movss reg,param + } +} + + +/*------------------------------------------------- + emit_movss_m32_p32 - move a 32-bit parameter + into a memory location +-------------------------------------------------*/ + +static void emit_movss_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param) +{ + assert(param->type != DRCUML_PTYPE_IMMEDIATE); + if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_movss_r128_m32(dst, REG_XMM0, MABS(drcbe, param->value)); // movss xmm0,[param] + emit_movss_m32_r128(dst, MEMPARAMS, REG_XMM0); // movss [mem],xmm0 + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_movss_m32_r128(dst, MEMPARAMS, param->value); // movss [mem],xmm0 +} + + +/*------------------------------------------------- + emit_movss_p32_r128 - move a register into a + 32-bit parameter +-------------------------------------------------*/ + +static void emit_movss_p32_r128(drcbe_state *drcbe, x86code **dst, const drcuml_parameter *param, UINT8 reg) +{ + assert(param->type != DRCUML_PTYPE_IMMEDIATE); + if (param->type == DRCUML_PTYPE_MEMORY) + emit_movss_m32_r128(dst, MABS(drcbe, param->value), reg); // movss [param],reg + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + if (reg != param->value) + emit_movss_r128_r128(dst, param->value, reg); // movss param,reg + } +} + + +/*------------------------------------------------- + emit_movsd_r128_p64 - move a 64-bit parameter + into a register +-------------------------------------------------*/ + +static void emit_movsd_r128_p64(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param) +{ + assert(param->type != DRCUML_PTYPE_IMMEDIATE); + if (param->type == DRCUML_PTYPE_MEMORY) + emit_movsd_r128_m64(dst, reg, MABS(drcbe, param->value)); // movsd reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + if (reg != param->value) + emit_movsd_r128_r128(dst, reg, param->value); // movsd reg,param + } +} + + +/*------------------------------------------------- + emit_movsd_m64_p64 - move a 64-bit parameter + into a memory location +-------------------------------------------------*/ + +static void emit_movsd_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param) +{ + assert(param->type != DRCUML_PTYPE_IMMEDIATE); + if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_movsd_r128_m64(dst, REG_XMM0, MABS(drcbe, param->value)); // movsd xmm0,[param] + emit_movsd_m64_r128(dst, MEMPARAMS, REG_XMM0); // movsd [mem],xmm0 + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_movsd_m64_r128(dst, MEMPARAMS, param->value); // movsd [mem],xmm0 +} + + +/*------------------------------------------------- + emit_movsd_p64_r128 - move a register into a + 64-bit parameter +-------------------------------------------------*/ + +static void emit_movsd_p64_r128(drcbe_state *drcbe, x86code **dst, const drcuml_parameter *param, UINT8 reg) +{ + assert(param->type != DRCUML_PTYPE_IMMEDIATE); + if (param->type == DRCUML_PTYPE_MEMORY) + emit_movsd_m64_r128(dst, MABS(drcbe, param->value), reg); // movsd [param],reg + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + if (reg != param->value) + emit_movsd_r128_r128(dst, param->value, reg); // movsd param,reg + } +} + + + +/*************************************************************************** + HELPERS TO CONVERT OTHER OPERATIONS TO MOVES +***************************************************************************/ + +/*------------------------------------------------- + convert_to_mov_imm - convert an instruction + into a mov immediate +-------------------------------------------------*/ + +static x86code *convert_to_mov_imm(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst, const drcuml_parameter *dstp, UINT64 imm) +{ + drcuml_instruction temp = *inst; + temp.numparams = 2; + temp.param[0] = *dstp; + temp.param[1].type = DRCUML_PTYPE_IMMEDIATE; + temp.param[1].value = imm; + return op_mov(drcbe, dst, &temp); +} + + +/*------------------------------------------------- + convert_to_mov_src1 - convert an instruction + into a mov src1 +-------------------------------------------------*/ + +static x86code *convert_to_mov_src1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst, const drcuml_parameter *dstp, const drcuml_parameter *srcp) +{ + drcuml_instruction temp = *inst; + temp.numparams = 2; + temp.param[0] = *dstp; + temp.param[1] = *srcp; + return op_mov(drcbe, dst, &temp); +} + + + +/*************************************************************************** + OUT-OF-BAND CODE FIXUP CALLBACKS +***************************************************************************/ + +/*------------------------------------------------- + fixup_label - callback to fixup forward- + referenced labels +-------------------------------------------------*/ + +static void fixup_label(void *parameter, drccodeptr labelcodeptr) +{ + drccodeptr src = 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 +-------------------------------------------------*/ + +static void fixup_exception(drccodeptr *codeptr, void *param1, void *param2, void *param3) +{ + drcuml_parameter handp, exp; + drcbe_state *drcbe = param1; + drccodeptr src = param2; + const drcuml_instruction *inst = param3; + drccodeptr dst = *codeptr; + drccodeptr *targetptr; + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &handp, PTYPE_M, &exp, PTYPE_MRI); + + /* look up the handle target */ + targetptr = drcuml_handle_codeptr_addr((drcuml_codehandle *)handp.value); + + /* first fixup the jump to get us here */ + ((UINT32 *)src)[-1] = dst - src; + + /* then store the exception parameter */ + emit_mov_m32_p32(drcbe, &dst, MABS(drcbe, &drcbe->state.exp), &exp); // mov [exp],exp + + /* push the original return address on the stack */ + emit_lea_r64_m64(&dst, REG_RAX, MABS(drcbe, src)); // lea rax,[return] + emit_push_r64(&dst, REG_RAX); // push rax + if (*targetptr != NULL) + emit_jmp(&dst, *targetptr); // jmp *targetptr + else + emit_jmp_m64(&dst, MABS(drcbe, targetptr)); // jmp [targetptr] + + *codeptr = dst; +} + + + +/*************************************************************************** + DEBUG HELPERS +***************************************************************************/ + +/*------------------------------------------------- + debug_log_hashjmp - callback to handle + logging of hashjmps +-------------------------------------------------*/ + +#if LOG_HASHJMPS +static void debug_log_hashjmp(int mode, offs_t pc) +{ + printf("mode=%d PC=%08X\n", mode, pc); +} +#endif + + + +/*************************************************************************** + COMPILE-TIME OPCODES +***************************************************************************/ + +/*------------------------------------------------- + op_handle - process a HANDLE opcode +-------------------------------------------------*/ + +static x86code *op_handle(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + assert(inst->numparams == 1); + assert(inst->param[0].type == DRCUML_PTYPE_MEMORY); + + /* register the current pointer for the handle */ + drcuml_handle_set_codeptr((drcuml_codehandle *)(FPTR)inst->param[0].value, dst); + + /* by default, the handle points to prolog code that moves the stack pointer */ + emit_sub_r64_imm(&dst, REG_RSP, 40); // sub rsp,40 + return dst; +} + + +/*------------------------------------------------- + op_hash - process a HASH opcode +-------------------------------------------------*/ + +static x86code *op_hash(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + assert(inst->numparams == 2); + assert(inst->param[0].type == DRCUML_PTYPE_IMMEDIATE); + assert(inst->param[1].type == DRCUML_PTYPE_IMMEDIATE); + + /* register the current pointer for the mode/PC */ + drchash_set_codeptr(drcbe->hash, inst->param[0].value, inst->param[1].value, dst); + return dst; +} + + +/*------------------------------------------------- + op_label - process a LABEL opcode +-------------------------------------------------*/ + +static x86code *op_label(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + assert(inst->numparams == 1); + assert(inst->param[0].type == DRCUML_PTYPE_IMMEDIATE); + + /* register the current pointer for the label */ + drclabel_set_codeptr(drcbe->labels, inst->param[0].value, dst); + return dst; +} + + +/*------------------------------------------------- + op_comment - process a COMMENT opcode +-------------------------------------------------*/ + +static x86code *op_comment(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + assert(inst->numparams == 1); + assert(inst->param[0].type == DRCUML_PTYPE_MEMORY); + + /* do nothing */ + return dst; +} + + +/*------------------------------------------------- + op_mapvar - process a MAPVAR opcode +-------------------------------------------------*/ + +static x86code *op_mapvar(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + assert(inst->numparams == 2); + assert(inst->param[0].type == DRCUML_PTYPE_MAPVAR); + assert(inst->param[1].type == DRCUML_PTYPE_IMMEDIATE); + + /* set the value of the specified mapvar */ + drcmap_set_value(drcbe->map, dst, inst->param[0].value, inst->param[1].value); + return dst; +} + + + +/*************************************************************************** + CONTROL FLOW OPCODES +***************************************************************************/ + +/*------------------------------------------------- + op_debug - process a DEBUG opcode +-------------------------------------------------*/ + +static x86code *op_debug(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ +#ifdef ENABLE_DEBUGGER + if (Machine->debug_mode) + { + drcuml_parameter pcp; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_1(drcbe, inst, &pcp, PTYPE_MRI); + + /* push the parameter */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &pcp); // mov param1,pcp + emit_smart_call_m64(drcbe, &dst, &drcbe->mame_debug_hook); // call mame_debug_hook + } +#endif + + return dst; +} + + +/*------------------------------------------------- + op_exit - process an EXIT opcode +-------------------------------------------------*/ + +static x86code *op_exit(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter retp; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + + /* normalize parameters */ + param_normalize_1(drcbe, inst, &retp, PTYPE_MRI); + + /* load the parameter into EAX */ + emit_mov_r32_p32(drcbe, &dst, REG_EAX, &retp); // mov eax,retp + if (inst->condflags == DRCUML_COND_ALWAYS) + emit_jmp(&dst, drcbe->exit); // jmp exit + else + emit_jcc(&dst, X86_CONDITION(inst->condflags), drcbe->exit); // jcc exit + + return dst; +} + + +/*------------------------------------------------- + op_hashjmp - process a HASHJMP opcode +-------------------------------------------------*/ + +static x86code *op_hashjmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter modep, pcp, exp; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &modep, PTYPE_MRI, &pcp, PTYPE_MRI, &exp, PTYPE_M); + +#if LOG_HASHJMPS + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &pcp); + emit_mov_r32_p32(drcbe, &dst, REG_PARAM2, &modep); + emit_smart_call_m64(drcbe, &dst, &drcbe->debug_log_hashjmp); +#endif + + /* load the stack base one word early so we end up at the right spot after our call below */ + emit_mov_r64_m64(&dst, REG_RSP, MABS(drcbe, &drcbe->hashstacksave)); // mov rsp,[hashstacksave] + + /* fixed mode cases */ + if (modep.type == DRCUML_PTYPE_IMMEDIATE) + { + /* a straight immediate jump is direct, though we need the PC in EAX in case of failure */ + if (pcp.type == DRCUML_PTYPE_IMMEDIATE) + { + UINT32 l1val = (pcp.value >> drcbe->hash->l1shift) & drcbe->hash->l1mask; + UINT32 l2val = (pcp.value >> drcbe->hash->l2shift) & drcbe->hash->l2mask; + emit_call_m64(&dst, MABS(drcbe, &drcbe->hash->base[modep.value][l1val][l2val])); + // call hash[modep][l1val][l2val] + } + + /* a fixed mode but variable PC */ + else + { + emit_mov_r32_p32(drcbe, &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, drcbe->hash->l1shift); // shr edx,l1shift + emit_and_r32_imm(&dst, REG_EAX, drcbe->hash->l2mask << drcbe->hash->l2shift);// and eax,l2mask << l2shift + emit_mov_r64_m64(&dst, REG_RDX, MBISD(REG_RBP, REG_RDX, 8, offset_from_rbp(drcbe, (FPTR)&drcbe->hash->base[modep.value][0]))); + // mov rdx,hash[modep+edx*8] + emit_call_m64(&dst, MBISD(REG_RDX, REG_RAX, 8 >> drcbe->hash->l2shift, 0)); // call [rdx+rax*shift] + } + } + else + { + /* variable mode */ + int modereg = param_select_register(REG_ECX, &modep, NULL); + emit_mov_r32_p32(drcbe, &dst, modereg, &modep); // mov modereg,modep + emit_mov_r64_m64(&dst, REG_RCX, MBISD(REG_RBP, modereg, 8, offset_from_rbp(drcbe, (FPTR)&drcbe->hash->base[0]))); + // mov rcx,hash[modereg*8] + + /* fixed PC */ + if (pcp.type == DRCUML_PTYPE_IMMEDIATE) + { + UINT32 l1val = (pcp.value >> drcbe->hash->l1shift) & drcbe->hash->l1mask; + UINT32 l2val = (pcp.value >> drcbe->hash->l2shift) & drcbe->hash->l2mask; + emit_mov_r64_m64(&dst, REG_RDX, MBD(REG_RCX, l1val*8)); // mov rdx,[rcx+l1val*8] + emit_call_m64(&dst, MBD(REG_RDX, l2val*8)); // call [l2val*8] + } + + /* variable PC */ + else + { + emit_mov_r32_p32(drcbe, &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, drcbe->hash->l1shift); // shr edx,l1shift + emit_mov_r64_m64(&dst, REG_RDX, MBISD(REG_RCX, REG_RDX, 8, 0)); // mov rdx,[rcx+rdx*8] + emit_and_r32_imm(&dst, REG_EAX, drcbe->hash->l2mask << drcbe->hash->l2shift);// and eax,l2mask << l2shift + emit_call_m64(&dst, MBISD(REG_RDX, REG_RAX, 8 >> drcbe->hash->l2shift, 0)); // call [rdx+rax*shift] + } + } + + /* in all cases, if there is no code, we return here to generate the exception */ + emit_mov_m32_p32(drcbe, &dst, MABS(drcbe, &drcbe->state.exp.l), &pcp); // mov [exp],param + emit_sub_r64_imm(&dst, REG_RSP, 8); // sub rsp,8 + emit_call_m64(&dst, MABS(drcbe, exp.value)); // call [exp] + + return dst; +} + + +/*------------------------------------------------- + op_jmp - process a JMP opcode +-------------------------------------------------*/ + +static x86code *op_jmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter labelp; + x86code *jmptarget; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + + /* normalize parameters */ + param_normalize_1(drcbe, inst, &labelp, PTYPE_I); + + /* look up the jump target and jump there */ + jmptarget = (x86code *)drclabel_get_codeptr(drcbe->labels, labelp.value, fixup_label, dst); + if (jmptarget == NULL) + jmptarget = dst + 0x7ffffff0; + if (inst->condflags == DRCUML_COND_ALWAYS) + emit_jmp(&dst, jmptarget); // jmp target + else + emit_jcc(&dst, X86_CONDITION(inst->condflags), jmptarget); // jcc target + + return dst; +} + + +/*------------------------------------------------- + op_exh - process an EXH opcode +-------------------------------------------------*/ + +static x86code *op_exh(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter handp, exp; + drccodeptr *targetptr; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &handp, PTYPE_M, &exp, PTYPE_MRI); + + /* look up the handle target */ + targetptr = drcuml_handle_codeptr_addr((drcuml_codehandle *)handp.value); + + /* perform the exception processing inline if unconditional */ + if (inst->condflags == DRCUML_COND_ALWAYS) + { + emit_mov_m32_p32(drcbe, &dst, MABS(drcbe, &drcbe->state.exp.l), &exp); // mov [exp],exp + if (*targetptr != NULL) + emit_call(&dst, *targetptr); // call *targetptr + else + emit_call_m64(&dst, MABS(drcbe, targetptr)); // call [targetptr] + } + + /* otherwise, jump to an out-of-band handler */ + else + { + emit_jcc(&dst, X86_CONDITION(inst->condflags), dst + 0x7ffffff0); // jcc exception + drccache_request_oob_codegen(drcbe->cache, fixup_exception, drcbe, dst, (void *)inst); + } + return dst; +} + + +/*------------------------------------------------- + op_callh - process a CALLH opcode +-------------------------------------------------*/ + +static x86code *op_callh(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter handp; + drccodeptr *targetptr; + emit_link skip; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + + /* normalize parameters */ + param_normalize_1(drcbe, inst, &handp, PTYPE_M); + + /* look up the handle target */ + targetptr = drcuml_handle_codeptr_addr((drcuml_codehandle *)handp.value); + + /* skip if conditional */ + if (inst->condflags != DRCUML_COND_ALWAYS) + emit_jcc_short_link(&dst, X86_NOT_CONDITION(inst->condflags), &skip); // jcc skip + + /* jump through the handle; directly if a normal jump */ + if (*targetptr != NULL) + emit_call(&dst, *targetptr); // call *targetptr + else + emit_call_m64(&dst, MABS(drcbe, targetptr)); // call [targetptr] + + /* resolve the conditional link */ + if (inst->condflags != DRCUML_COND_ALWAYS) + resolve_link(&dst, &skip); // skip: + return dst; +} + + +/*------------------------------------------------- + op_ret - process a RET opcode +-------------------------------------------------*/ + +static x86code *op_ret(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + emit_link skip; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + assert(inst->numparams == 0); + + /* skip if conditional */ + if (inst->condflags != DRCUML_COND_ALWAYS) + emit_jcc_short_link(&dst, X86_NOT_CONDITION(inst->condflags), &skip); // jcc skip + + /* return */ + emit_add_r64_imm(&dst, REG_RSP, 40); // add rsp,40 + emit_ret(&dst); // ret + + /* resolve the conditional link */ + if (inst->condflags != DRCUML_COND_ALWAYS) + resolve_link(&dst, &skip); // skip: + return dst; +} + + +/*------------------------------------------------- + op_callc - process a CALLC opcode +-------------------------------------------------*/ + +static x86code *op_callc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter funcp, paramp; + emit_link skip; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &funcp, PTYPE_M, ¶mp, PTYPE_M); + + /* skip if conditional */ + if (inst->condflags != DRCUML_COND_ALWAYS) + emit_jcc_short_link(&dst, X86_NOT_CONDITION(inst->condflags), &skip); // jcc skip + + /* perform the call */ + emit_mov_r64_imm(&dst, REG_PARAM1, paramp.value); // mov param1,paramp + emit_smart_call_r64(drcbe, &dst, (x86code *)(FPTR)funcp.value, REG_RAX); // call funcp + + /* resolve the conditional link */ + if (inst->condflags != DRCUML_COND_ALWAYS) + resolve_link(&dst, &skip); // skip: + return dst; +} + + +/*------------------------------------------------- + op_recover - process a RECOVER opcode +-------------------------------------------------*/ + +static x86code *op_recover(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, mvparam; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &mvparam, PTYPE_I); + + /* call the recovery code */ + emit_mov_r64_m64(&dst, REG_RAX, MABS(drcbe, &drcbe->stacksave)); // mov rax,stacksave + emit_mov_r64_m64(&dst, REG_RAX, MBD(REG_RAX, -8)); // mov rax,[rax-4] + emit_mov_r64_imm(&dst, REG_PARAM1, (FPTR)drcbe->map); // mov param1,drcbe->map + emit_lea_r64_m64(&dst, REG_PARAM2, MBD(REG_RAX, -1)); // lea param2,[rax-1] + emit_mov_r64_imm(&dst, REG_PARAM3, inst->param[1].value); // mov param3,param[1].value + emit_smart_call_m64(drcbe, &dst, &drcbe->drcmap_get_value); // call drcmap_get_value + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + + return dst; +} + + + +/*************************************************************************** + INTERNAL REGISTER OPCODES +***************************************************************************/ + +/*------------------------------------------------- + op_setfmod - process a SETFMOD opcode +-------------------------------------------------*/ + +static x86code *op_setfmod(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter srcp; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_1(drcbe, inst, &srcp, PTYPE_MRI); + + /* immediate case */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + { + srcp.value &= 3; + emit_mov_m8_imm(&dst, MABS(drcbe, &drcbe->state.fmod), srcp.value); // mov [fmod],srcp + emit_ldmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssecontrol[srcp.value])); // ldmxcsr fp_control[srcp] + } + + /* register/memory case */ + else + { + emit_mov_r32_p32(drcbe, &dst, REG_EAX, &srcp); // mov eax,srcp + emit_and_r32_imm(&dst, REG_EAX, 3); // and eax,3 + emit_mov_m8_r8(&dst, MABS(drcbe, &drcbe->state.fmod), REG_AL); // mov [fmod],al + emit_ldmxcsr_m32(&dst, MBISD(REG_RBP, REG_RAX, 4, offset_from_rbp(drcbe, (FPTR)&drcbe->ssecontrol[0]))); + // ldmxcsr fp_control[eax] + } + + return dst; +} + + +/*------------------------------------------------- + op_getfmod - process a GETFMOD opcode +-------------------------------------------------*/ + +static x86code *op_getfmod(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_1(drcbe, inst, &dstp, PTYPE_MR); + + /* fetch the current mode and store to the destination */ + if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movzx_r32_m8(&dst, dstp.value, MABS(drcbe, &drcbe->state.fmod)); // movzx reg,[fmod] + else + { + emit_movzx_r32_m8(&dst, REG_EAX, MABS(drcbe, &drcbe->state.fmod)); // movzx eax,[fmod] + emit_mov_m32_r32(&dst, MABS(drcbe, dstp.value), REG_EAX); // mov [dstp],eax + } + + return dst; +} + + +/*------------------------------------------------- + op_getexp - process a GETEXP opcode +-------------------------------------------------*/ + +static x86code *op_getexp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_1(drcbe, inst, &dstp, PTYPE_MR); + + /* fetch the exception parameter and store to the destination */ + if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_r32_m32(&dst, dstp.value, MABS(drcbe, &drcbe->state.exp.l)); // mov reg,[exp] + else + { + emit_mov_r32_m32(&dst, REG_EAX, MABS(drcbe, &drcbe->state.exp.l)); // mov eax,[exp] + emit_mov_m32_r32(&dst, MABS(drcbe, dstp.value), REG_EAX); // mov [dstp],eax + } + + return dst; +} + + + +/*************************************************************************** + INTEGER OPERATIONS +***************************************************************************/ + +/*------------------------------------------------- + op_load1u - process a LOAD1U opcode +-------------------------------------------------*/ + +static x86code *op_load1u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + int basereg, dstreg; + INT32 baseoffs; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* determine the pointer base */ + basereg = get_base_register_and_offset(drcbe, &dst, basep.value, REG_RDX, &baseoffs); + + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_movzx_r32_m8(&dst, dstreg, MBD(basereg, baseoffs + 1*indp.value)); // movzx dstreg,[basep + 1*indp] + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_movzx_r32_m8(&dst, dstreg, MBISD(basereg, indreg, 1, baseoffs)); // movzx dstreg,[basep + 1*indp] + } + + /* store to appropriate target size */ + if (inst->size == 4) + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + else if (inst->size == 8) + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + return dst; +} + + +/*------------------------------------------------- + op_load1s - process a LOAD1S opcode +-------------------------------------------------*/ + +static x86code *op_load1s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + int basereg, dstreg; + INT32 baseoffs; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* determine the pointer base */ + basereg = get_base_register_and_offset(drcbe, &dst, basep.value, REG_RDX, &baseoffs); + + /* 32-bit form */ + if (inst->size == 4) + { + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_movsx_r32_m8(&dst, dstreg, MBD(basereg, baseoffs + 1*indp.value)); // movsx eax,[basep + 1*indp] + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_movsx_r32_m8(&dst, dstreg, MBISD(basereg, indreg, 1, baseoffs)); // movsx eax,[basep + 1*indp] + } + + /* general case */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* 64-bit form */ + else + { + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_movsx_r64_m8(&dst, dstreg, MBD(basereg, baseoffs + 1*indp.value)); // movsx rax,[basep + 1*indp] + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_movsx_r64_m8(&dst, dstreg, MBISD(basereg, indreg, 1, baseoffs)); // movsx rax,[basep + 1*indp] + } + + /* general case */ + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_load2u - process a LOAD2U opcode +-------------------------------------------------*/ + +static x86code *op_load2u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + int basereg, dstreg; + INT32 baseoffs; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* determine the pointer base */ + basereg = get_base_register_and_offset(drcbe, &dst, basep.value, REG_RDX, &baseoffs); + + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_movzx_r32_m16(&dst, dstreg, MBD(basereg, baseoffs + 2*indp.value)); // movzx dstreg,[basep + 2*indp] + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_movzx_r32_m16(&dst, dstreg, MBISD(basereg, indreg, 2, baseoffs)); // movzx dstreg,[basep + 2*indp] + } + + /* store to appropriate target size */ + if (inst->size == 4) + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + else if (inst->size == 8) + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + return dst; +} + + +/*------------------------------------------------- + op_load2s - process a LOAD2S opcode +-------------------------------------------------*/ + +static x86code *op_load2s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + int basereg, dstreg; + INT32 baseoffs; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* determine the pointer base */ + basereg = get_base_register_and_offset(drcbe, &dst, basep.value, REG_RDX, &baseoffs); + + /* 32-bit form */ + if (inst->size == 4) + { + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_movsx_r32_m16(&dst, dstreg, MBD(basereg, baseoffs + 2*indp.value)); // movsx eax,[basep + 2*indp] + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_movsx_r32_m16(&dst, dstreg, MBISD(basereg, indreg, 2, baseoffs)); // movsx eax,[basep + 2*indp] + } + + /* general case */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* 64-bit form */ + else + { + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_movsx_r64_m16(&dst, dstreg, MBD(basereg, baseoffs + 2*indp.value)); // movsx rax,[basep + 2*indp] + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_movsx_r64_m16(&dst, dstreg, MBISD(basereg, indreg, 2, baseoffs)); // movsx rax,[basep + 2*indp] + } + + /* general case */ + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_load4u - process a LOAD4U opcode +-------------------------------------------------*/ + +static x86code *op_load4u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + int basereg, dstreg; + INT32 baseoffs; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* determine the pointer base */ + basereg = get_base_register_and_offset(drcbe, &dst, basep.value, REG_RDX, &baseoffs); + + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_r32_m32(&dst, dstreg, MBD(basereg, baseoffs + 4*indp.value)); // mov dstreg,[basep + 4*indp] + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_mov_r32_m32(&dst, dstreg, MBISD(basereg, indreg, 4, baseoffs)); // mov dstreg,[basep + 4*indp] + } + + /* store to appropriate target size */ + if (inst->size == 4) + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + else if (inst->size == 8) + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + return dst; +} + + +/*------------------------------------------------- + op_load4s - process a LOAD4S opcode +-------------------------------------------------*/ + +static x86code *op_load4s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + int basereg, dstreg; + INT32 baseoffs; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* determine the pointer base */ + basereg = get_base_register_and_offset(drcbe, &dst, basep.value, REG_RDX, &baseoffs); + + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_movsxd_r64_m32(&dst, dstreg, MBD(basereg, baseoffs + 4*indp.value)); // mov dstreg,[basep + 4*indp] + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_movsxd_r64_m32(&dst, dstreg, MISD(indreg, 4, basep.value)); // movsx dstreg,[basep + 4*indp] + } + + /* general case */ + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + return dst; +} + + +/*------------------------------------------------- + op_load8u - process a LOAD8U opcode +-------------------------------------------------*/ + +static x86code *op_load8u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + int basereg, dstreg; + INT32 baseoffs; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* determine the pointer base */ + basereg = get_base_register_and_offset(drcbe, &dst, basep.value, REG_RDX, &baseoffs); + + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_r64_m64(&dst, dstreg, MBD(basereg, baseoffs + 8*indp.value)); // mov dstreg,[basep + 8*indp] + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_mov_r64_m64(&dst, dstreg, MBISD(basereg, indreg, 8, baseoffs)); // mov dstreg,[basep + 8*indp] + } + + /* general case */ + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,edx:dstreg + + return dst; +} + + +/*------------------------------------------------- + op_store1 - process a STORE1 opcode +-------------------------------------------------*/ + +static x86code *op_store1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter srcp, basep, indp; + int basereg, srcreg; + INT32 baseoffs; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &basep, PTYPE_M, &indp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* pick a source register for the general case */ + srcreg = param_select_register(REG_EAX, &srcp, NULL); + + /* determine the pointer base */ + basereg = get_base_register_and_offset(drcbe, &dst, basep.value, REG_RDX, &baseoffs); + + /* degenerate case: constant index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + { + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m8_imm(&dst, MBD(basereg, baseoffs + 1*indp.value), srcp.value); // mov [basep + 1*indp],srcp + else + { + emit_mov_r32_p32(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_mov_m8_r8(&dst, MBD(basereg, baseoffs + 1*indp.value), srcreg); // mov [basep + 1*indp],srcreg + } + } + + /* normal case: variable index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); // mov indreg,indp + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m8_imm(&dst, MBISD(basereg, indreg, 1, baseoffs), srcp.value); // mov [basep + 1*ecx],srcp + else + { + emit_mov_r32_p32(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_mov_m8_r8(&dst, MBISD(basereg, indreg, 1, baseoffs), srcreg); // mov [basep + 1*ecx],srcreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_store2 - process a STORE2 opcode +-------------------------------------------------*/ + +static x86code *op_store2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter srcp, basep, indp; + int basereg, srcreg; + INT32 baseoffs; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &basep, PTYPE_M, &indp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* pick a source register for the general case */ + srcreg = param_select_register(REG_EAX, &srcp, NULL); + + /* determine the pointer base */ + basereg = get_base_register_and_offset(drcbe, &dst, basep.value, REG_RDX, &baseoffs); + + /* degenerate case: constant index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + { + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m16_imm(&dst, MBD(basereg, baseoffs + 2*indp.value), srcp.value); // mov [basep + 2*indp],srcp + else + { + emit_mov_r32_p32(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_mov_m16_r16(&dst, MBD(basereg, baseoffs + 2*indp.value), srcreg); // mov [basep + 2*indp],srcreg + } + } + + /* normal case: variable index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); // mov indreg,indp + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m16_imm(&dst, MBISD(basereg, indreg, 2, baseoffs), srcp.value); // mov [basep + 2*ecx],srcp + else + { + emit_mov_r32_p32(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_mov_m16_r16(&dst, MBISD(basereg, indreg, 2, baseoffs), srcreg); // mov [basep + 2*ecx],srcreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_store4 - process a STORE4 opcode +-------------------------------------------------*/ + +static x86code *op_store4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter srcp, basep, indp; + int basereg, srcreg; + INT32 baseoffs; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &basep, PTYPE_M, &indp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* pick a source register for the general case */ + srcreg = param_select_register(REG_EAX, &srcp, NULL); + + /* determine the pointer base */ + basereg = get_base_register_and_offset(drcbe, &dst, basep.value, REG_RDX, &baseoffs); + + /* degenerate case: constant index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + { + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m32_imm(&dst, MBD(basereg, baseoffs + 4*indp.value), srcp.value); // mov [basep + 4*indp],srcp + else + { + emit_mov_r32_p32(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_mov_m32_r32(&dst, MBD(basereg, baseoffs + 4*indp.value), srcreg); // mov [basep + 4*indp],srcreg + } + } + + /* normal case: variable index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); // mov indreg,indp + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m32_imm(&dst, MBISD(basereg, indreg, 4, baseoffs), srcp.value); // mov [basep + 4*ecx],srcp + else + { + emit_mov_r32_p32(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_mov_m32_r32(&dst, MBISD(basereg, indreg, 4, baseoffs), srcreg); // mov [basep + 4*ecx],srcreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_store8 - process a STORE8 opcode +-------------------------------------------------*/ + +static x86code *op_store8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter srcp, basep, indp; + int basereg, srcreg; + INT32 baseoffs; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &basep, PTYPE_M, &indp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* pick a source register for the general case */ + srcreg = param_select_register(REG_EAX, &srcp, NULL); + + /* determine the pointer base */ + basereg = get_base_register_and_offset(drcbe, &dst, basep.value, REG_RDX, &baseoffs); + + /* degenerate case: constant index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + { + if (srcp.type == DRCUML_PTYPE_IMMEDIATE && short_immediate(srcp.value)) + emit_mov_m64_imm(&dst, MBD(basereg, baseoffs + 8*indp.value), srcp.value); // mov [basep + 8*indp],srcp + else + { + emit_mov_r64_p64(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_mov_m64_r64(&dst, MBD(basereg, baseoffs + 8*indp.value), srcreg); // mov [basep + 8*indp],srcreg + } + } + + /* normal case: variable index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); // mov indreg,indp + if (srcp.type == DRCUML_PTYPE_IMMEDIATE && short_immediate(srcp.value)) + emit_mov_m64_imm(&dst, MBISD(basereg, indreg, 8, baseoffs), srcp.value); // mov [basep + 8*ecx],srcp + else + { + emit_mov_r64_p64(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_mov_m64_r64(&dst, MBISD(basereg, indreg, 8, baseoffs), srcreg); // mov [basep + 8*ecx],srcreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_read1u - process a READ1U opcode +-------------------------------------------------*/ + +static x86code *op_read1u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* set up a call to the read byte handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].read_byte);// call read_byte + emit_movzx_r32_r8(&dst, dstreg, REG_AL); // movzx dstreg,al + + /* store to appropriate target size */ + if (inst->size == 4) + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + else if (inst->size == 8) + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + return dst; +} + + +/*------------------------------------------------- + op_read1s - process a READ1S opcode +-------------------------------------------------*/ + +static x86code *op_read1s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* set up a call to the read byte handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].read_byte);// call read_byte + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + emit_movsx_r32_r8(&dst, dstreg, REG_AL); // movsx dstreg,al + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* 64-bit form */ + else + { + /* general case */ + emit_movsx_r64_r8(&dst, dstreg, REG_AL); // movsx dstreg,al + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_read2u - process a READ2U opcode +-------------------------------------------------*/ + +static x86code *op_read2u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* set up a call to the read word handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].read_word);// call read_word + emit_movzx_r32_r16(&dst, dstreg, REG_AX); // movzx dstreg,ax + + /* store to appropriate target size */ + if (inst->size == 4) + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + else if (inst->size == 8) + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + return dst; +} + + +/*------------------------------------------------- + op_read2s - process a READ2S opcode +-------------------------------------------------*/ + +static x86code *op_read2s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* set up a call to the read word handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].read_word);// call read_word + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + emit_movsx_r32_r16(&dst, dstreg, REG_AX); // movsx dstreg,ax + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* 64-bit form */ + else + { + /* general case */ + emit_movsx_r64_r16(&dst, dstreg, REG_AX); // movsx dstreg,ax + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_read2m - process a READ2M opcode +-------------------------------------------------*/ + +static x86code *op_read2m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp, maskp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &maskp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* set up a call to the read dword masked handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_mov_r32_p32(drcbe, &dst, REG_PARAM2, &maskp); // mov param1,maskp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].read_word_masked);// call read_word_masked + emit_movzx_r32_r16(&dst, dstreg, REG_AX); // movzx dstreg,ax + + /* store to appropriate target size */ + if (inst->size == 4) + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + else if (inst->size == 8) + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + return dst; +} + + +/*------------------------------------------------- + op_read4u - process a READ4U opcode +-------------------------------------------------*/ + +static x86code *op_read4u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* set up a call to the read dword handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].read_dword);// call read_dword + + /* store to appropriate target size */ + if (inst->size == 4) + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + else if (inst->size == 8) + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_RAX); // mov dstp,rax + + return dst; +} + + +/*------------------------------------------------- + op_read4s - process a READ4S opcode +-------------------------------------------------*/ + +static x86code *op_read4s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* set up a call to the read dword handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].read_dword);// call read_dword + + /* 64-bit form */ + emit_movsxd_r64_r32(&dst, dstreg, REG_EAX); // movsxd dstreg,eax + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + return dst; +} + + +/*------------------------------------------------- + op_read4m - process a READ4M opcode +-------------------------------------------------*/ + +static x86code *op_read4m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp, maskp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &maskp, PTYPE_MRI); + + /* set up a call to the read dword masked handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_mov_r32_p32(drcbe, &dst, REG_PARAM2, &maskp); // mov param2,maskp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].read_dword_masked);// call read_dword_masked + + /* store to appropriate target size */ + if (inst->size == 4) + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + else if (inst->size == 8) + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_RAX); // mov dstp,rax + + return dst; +} + + +/*------------------------------------------------- + op_read8u - process a READ8U opcode +-------------------------------------------------*/ + +static x86code *op_read8u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* set up a call to the read qword handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].read_qword);// call read_qword + + /* 64-bit form */ + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_RAX); // mov dstp,rax + + return dst; +} + + +/*------------------------------------------------- + op_read8m - process a READ8M opcode +-------------------------------------------------*/ + +static x86code *op_read8m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp, maskp; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &maskp, PTYPE_MRI); + + /* set up a call to the read qword masked handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_mov_r64_p64(drcbe, &dst, REG_PARAM2, &maskp); // mov param2,maskp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].read_qword_masked);// call read_qword_masked + + /* 64-bit form */ + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_RAX); // mov dstp,rax + + return dst; +} + + +/*------------------------------------------------- + op_write1 - process a WRITE1 opcode +-------------------------------------------------*/ + +static x86code *op_write1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* set up a call to the write byte handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_mov_r32_p32(drcbe, &dst, REG_PARAM2, &srcp); // mov param2,srcp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].write_byte);// call write_byte + + return dst; +} + + +/*------------------------------------------------- + op_write2 - process a WRITE2 opcode +-------------------------------------------------*/ + +static x86code *op_write2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* set up a call to the write word handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_mov_r32_p32(drcbe, &dst, REG_PARAM2, &srcp); // mov param2,srcp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].write_word);// call write_word + + return dst; +} + + +/*------------------------------------------------- + op_writ2m - process a WRIT2M opcode +-------------------------------------------------*/ + +static x86code *op_writ2m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, maskp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &maskp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* set up a call to the write word handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_mov_r32_p32(drcbe, &dst, REG_PARAM2, &srcp); // mov param2,srcp + emit_mov_r32_p32(drcbe, &dst, REG_PARAM3, &maskp); // mov param3,maskp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].write_word_masked);// call write_word_masked + return dst; +} + + +/*------------------------------------------------- + op_write4 - process a WRITE4 opcode +-------------------------------------------------*/ + +static x86code *op_write4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* set up a call to the write dword handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_mov_r32_p32(drcbe, &dst, REG_PARAM2, &srcp); // mov param2,srcp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].write_dword);// call write_dword + + return dst; +} + + +/*------------------------------------------------- + op_writ4m - process a WRIT4M opcode +-------------------------------------------------*/ + +static x86code *op_writ4m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, maskp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &maskp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* set up a call to the write word handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_mov_r32_p32(drcbe, &dst, REG_PARAM2, &srcp); // mov param2,srcp + emit_mov_r32_p32(drcbe, &dst, REG_PARAM3, &maskp); // mov param3,maskp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].write_dword_masked);// call write_dword_masked + return dst; +} + + +/*------------------------------------------------- + op_write8 - process a WRITE8 opcode +-------------------------------------------------*/ + +static x86code *op_write8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, srcp; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* set up a call to the write qword handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_mov_r64_p64(drcbe, &dst, REG_PARAM2, &srcp); // mov param2,srcp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].write_qword);// call write_qword + + return dst; +} + + +/*------------------------------------------------- + op_writ8m - process a WRIT8M opcode +-------------------------------------------------*/ + +static x86code *op_writ8m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, maskp, srcp; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &maskp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* set up a call to the write word handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + emit_mov_r64_p64(drcbe, &dst, REG_PARAM2, &srcp); // mov param2,srcp + emit_mov_r64_p64(drcbe, &dst, REG_PARAM3, &maskp); // mov param3,maskp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].write_qword_masked);// call write_qword_masked + return dst; +} + + +/*------------------------------------------------- + op_flags - process a FLAGS opcode +-------------------------------------------------*/ + +static x86code *op_flags(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, maskp, tablep; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &maskp, PTYPE_I, &tablep, PTYPE_M); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_ECX, &dstp, NULL); + + /* translate live flags into UML flags */ + emit_pushf(&dst); // pushf + emit_pop_r64(&dst, REG_RAX); // pop rax + emit_and_r32_imm(&dst, REG_EAX, 0x8c5); // and eax,0x8c5 + emit_movzx_r32_m8(&dst, REG_EAX, MBISD(REG_RBP, REG_RAX, 1, offset_from_rbp(drcbe, (FPTR)&drcbe->flagsmap[0]))); + // movzx eax,[flags_map] + + /* 32-bit form */ + if (inst->size == 4) + { + /* no masking */ + if (maskp.value == 0xffffffff) + { + emit_mov_r32_m32(&dst, dstreg, MBISD(REG_RBP, REG_EAX, 4, offset_from_rbp(drcbe, tablep.value))); + // mov dstreg,[eax*4+table] + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &dstp); // mov dstreg,dstp + emit_mov_r32_m32(&dst, REG_EDX, MBISD(REG_RBP, REG_EAX, 4, offset_from_rbp(drcbe, tablep.value))); + // mov edx,[eax*4+table] + emit_and_r32_imm(&dst, dstreg, ~maskp.value); // and dstreg,~mask + emit_and_r32_imm(&dst, REG_EDX, maskp.value); // and edx,mask + emit_or_r32_r32(&dst, dstreg, REG_EDX); // or dstreg,edx + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else + { + /* no masking */ + if (maskp.value == U64(0xffffffffffffffff)) + { + emit_mov_r64_m64(&dst, dstreg, MBISD(REG_RBP, REG_EAX, 8, offset_from_rbp(drcbe, tablep.value))); + // mov dstreg,[eax*8+table] + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &dstp); // mov dstreg,dstp + emit_mov_r64_m64(&dst, REG_EDX, MBISD(REG_RBP, REG_EAX, 8, offset_from_rbp(drcbe, tablep.value))); + // mov edx,[eax*84+table] + if (short_immediate(~maskp.value)) + emit_and_r64_imm(&dst, dstreg, ~maskp.value); // and dstreg,~mask + else + { + emit_mov_r64_imm(&dst, REG_R11, ~maskp.value); // mov r11,~mask + emit_and_r64_r64(&dst, dstreg, REG_R11); // and dstreg,r11 + } + if (short_immediate(maskp.value)) + emit_and_r64_imm(&dst, REG_EDX, maskp.value); // and edx,mask + else + { + emit_mov_r64_imm(&dst, REG_R11, maskp.value); // mov r11,mask + emit_and_r64_r64(&dst, REG_EDX, REG_R11); // and edx,r11 + } + emit_or_r64_r64(&dst, dstreg, REG_EDX); // or dstreg,edx + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_mov - process a MOV opcode +-------------------------------------------------*/ + +static x86code *op_mov(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + x86code *savedst = dst; + emit_link skip = { 0 }; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + + /* normalize parameters, but only if we got here directly */ + /* other opcodes call through here with pre-normalized parameters */ + if (inst->opcode == DRCUML_OP_MOV) + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MRI); + else + { + dstp = inst->param[0]; + srcp = inst->param[1]; + } + + /* degenerate case: dest and source are equal */ + if (dstp.type == srcp.type && dstp.value == srcp.value) + return dst; + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* always start with a jmp */ + if (inst->condflags != DRCUML_COND_ALWAYS) + emit_jcc_short_link(&dst, X86_NOT_CONDITION(inst->condflags), &skip); // jcc skip + + /* 32-bit form */ + if (inst->size == 4) + { + /* register to memory */ + if (dstp.type == DRCUML_PTYPE_MEMORY && srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_r32(&dst, MABS(drcbe, dstp.value), srcp.value); // mov [dstp],srcp + + /* immediate to memory */ + else if (dstp.type == DRCUML_PTYPE_MEMORY && srcp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m32_imm(&dst, MABS(drcbe, dstp.value), srcp.value); // mov [dstp],srcp + + /* conditional memory to register */ + else if (inst->condflags != 0 && dstp.type == DRCUML_PTYPE_INT_REGISTER && srcp.type == DRCUML_PTYPE_MEMORY) + { + dst = savedst; + skip.target = NULL; + emit_cmovcc_r32_m32(&dst, X86_CONDITION(inst->condflags), dstp.value, MABS(drcbe, srcp.value)); + // cmovcc dstp,[srcp] + } + + /* conditional register to register */ + else if (inst->condflags != 0 && dstp.type == DRCUML_PTYPE_INT_REGISTER && srcp.type == DRCUML_PTYPE_INT_REGISTER) + { + dst = savedst; + skip.target = NULL; + emit_cmovcc_r32_r32(&dst, X86_CONDITION(inst->condflags), dstp.value, srcp.value); + // cmovcc dstp,srcp + } + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &srcp); // mov dstreg,srcp + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* register to memory */ + if (dstp.type == DRCUML_PTYPE_MEMORY && srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m64_r64(&dst, MABS(drcbe, dstp.value), srcp.value); // mov [dstp],srcp + + /* immediate to memory */ + else if (dstp.type == DRCUML_PTYPE_MEMORY && srcp.type == DRCUML_PTYPE_IMMEDIATE && short_immediate(srcp.value)) + emit_mov_m64_imm(&dst, MABS(drcbe, dstp.value), srcp.value); // mov [dstp],srcp + + /* conditional memory to register */ + else if (inst->condflags != 0 && dstp.type == DRCUML_PTYPE_INT_REGISTER && srcp.type == DRCUML_PTYPE_MEMORY) + { + dst = savedst; + skip.target = NULL; + emit_cmovcc_r64_m64(&dst, X86_CONDITION(inst->condflags), dstp.value, MABS(drcbe, srcp.value)); + // cmovcc dstp,[srcp] + } + + /* conditional register to register */ + else if (inst->condflags != 0 && dstp.type == DRCUML_PTYPE_INT_REGISTER && srcp.type == DRCUML_PTYPE_INT_REGISTER) + { + dst = savedst; + skip.target = NULL; + emit_cmovcc_r64_r64(&dst, X86_CONDITION(inst->condflags), dstp.value, srcp.value); + // cmovcc dstp,srcp + } + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &srcp); // mov dstreg,srcp + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* resolve the jump */ + if (skip.target != NULL) + resolve_link(&dst, &skip); + return dst; +} + + +/*------------------------------------------------- + op_zext1 - process a ZEXT1 opcode +-------------------------------------------------*/ + +static x86code *op_zext1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT8)srcp.value); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_movzx_r32_m8(&dst, dstreg, MABS(drcbe, srcp.value)); // movzx dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movzx_r32_r8(&dst, dstreg, srcp.value); // movzx dstreg,srcp + + /* 32-bit form */ + if (inst->size == 4) + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + /* 64-bit form */ + else if (inst->size == 8) + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + return dst; +} + + +/*------------------------------------------------- + op_zext2 - process a ZEXT2 opcode +-------------------------------------------------*/ + +static x86code *op_zext2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT16)srcp.value); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_movzx_r32_m16(&dst, dstreg, MABS(drcbe, srcp.value)); // movzx dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movzx_r32_r16(&dst, dstreg, srcp.value); // movzx dstreg,srcp + + /* 32-bit form */ + if (inst->size == 4) + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + /* 64-bit form */ + else if (inst->size == 8) + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + return dst; +} + + +/*------------------------------------------------- + op_zext4 - process a ZEXT4 opcode +-------------------------------------------------*/ + +static x86code *op_zext4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT32)srcp.value); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_mov_r32_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // mov dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_r32_r32(&dst, dstreg, srcp.value); // mov dstreg,srcp + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + return dst; +} + + +/*------------------------------------------------- + op_sext1 - process a SEXT1 opcode +-------------------------------------------------*/ + +static x86code *op_sext1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT8)srcp.value); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* convert 8-bit source registers */ + if (srcp.type == DRCUML_PTYPE_INT_REGISTER && (srcp.value & 4)) + { + emit_mov_r32_r32(&dst, REG_EAX, srcp.value); // mov eax,srcp + srcp.value = REG_EAX; + } + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_movsx_r32_m8(&dst, dstreg, MABS(drcbe, srcp.value)); // movsx dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movsx_r32_r8(&dst, dstreg, srcp.value); // movsx dstreg,srcp + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_movsx_r64_m8(&dst, dstreg, MABS(drcbe, srcp.value)); // movsx dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movsx_r64_r8(&dst, dstreg, srcp.value); // movsx dstreg,srcp + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_sext2 - process a SEXT2 opcode +-------------------------------------------------*/ + +static x86code *op_sext2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT16)srcp.value); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_movsx_r32_m16(&dst, dstreg, MABS(drcbe, srcp.value)); // movsx dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movsx_r32_r16(&dst, dstreg, srcp.value); // movsx dstreg,srcp + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_movsx_r64_m16(&dst, dstreg, MABS(drcbe, srcp.value)); // movsx dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movsx_r64_r16(&dst, dstreg, srcp.value); // movsx dstreg,srcp + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_sext4 - process a SEXT4 opcode +-------------------------------------------------*/ + +static x86code *op_sext4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT32)srcp.value); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* 64-bit form */ + if (inst->size == 8) + { + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_movsxd_r64_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // movsxd dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movsxd_r64_r32(&dst, dstreg, srcp.value); // movsxd dstreg,srcp + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_add - process a ADD opcode +-------------------------------------------------*/ + +static x86code *op_add(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3_commutative(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, src1p.value + src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (dstp.type == DRCUML_PTYPE_MEMORY && src1p.type == DRCUML_PTYPE_MEMORY && src1p.value == dstp.value) + emit_add_m32_p32(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // add [dstp],src2p + + /* reg = reg + imm */ + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER && src1p.type == DRCUML_PTYPE_INT_REGISTER && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + emit_lea_r32_m32(&dst, dstp.value, MBD(src1p.value, src2p.value)); // lea dstp,[src1p+src2p] + + /* reg = reg + reg */ + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER && src1p.type == DRCUML_PTYPE_INT_REGISTER && src2p.type == DRCUML_PTYPE_INT_REGISTER && inst->condflags == 0) + emit_lea_r32_m32(&dst, dstp.value, MBISD(src1p.value, src2p.value, 1, 0)); // lea dstp,[src1p+src2p] + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_add_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // add dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (dstp.type == DRCUML_PTYPE_MEMORY && src1p.type == DRCUML_PTYPE_MEMORY && src1p.value == dstp.value) + emit_add_m64_p64(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // add [dstp],src2p + + /* reg = reg + imm */ + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER && src1p.type == DRCUML_PTYPE_INT_REGISTER && src2p.type == DRCUML_PTYPE_IMMEDIATE && short_immediate(src2p.value) && inst->condflags == 0) + emit_lea_r64_m64(&dst, dstp.value, MBD(src1p.value, src2p.value)); // lea dstp,[src1p+src2p] + + /* reg = reg + reg */ + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER && src1p.type == DRCUML_PTYPE_INT_REGISTER && src2p.type == DRCUML_PTYPE_INT_REGISTER && inst->condflags == 0) + emit_lea_r64_m64(&dst, dstp.value, MBISD(src1p.value, src2p.value, 1, 0)); // lea dstp,[src1p+src2p] + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_add_r64_p64(drcbe, &dst, dstreg, &src2p, inst); // add dstreg,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_addc - process a ADDC opcode +-------------------------------------------------*/ + +static x86code *op_addc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3_commutative(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_adc_m32_p32(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // adc [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_adc_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // adc dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_adc_m64_p64(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // adc [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_adc_r64_p64(drcbe, &dst, dstreg, &src2p, inst); // adc dstreg,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_sub - process a SUB opcode +-------------------------------------------------*/ + +static x86code *op_sub(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, src1p.value - src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (dstp.type == DRCUML_PTYPE_MEMORY && src1p.type == DRCUML_PTYPE_MEMORY && src1p.value == dstp.value) + emit_sub_m32_p32(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // sub [dstp],src2p + + /* reg = reg - imm */ + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER && src1p.type == DRCUML_PTYPE_INT_REGISTER && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + emit_lea_r32_m32(&dst, dstp.value, MBD(src1p.value, -src2p.value)); // lea dstp,[src1p-src2p] + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_sub_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // sub dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (dstp.type == DRCUML_PTYPE_MEMORY && src1p.type == DRCUML_PTYPE_MEMORY && src1p.value == dstp.value) + emit_sub_m64_p64(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // sub [dstp],src2p + + /* reg = reg - imm */ + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER && src1p.type == DRCUML_PTYPE_INT_REGISTER && src2p.type == DRCUML_PTYPE_IMMEDIATE && short_immediate(src2p.type) && inst->condflags == 0) + emit_lea_r64_m64(&dst, dstp.value, MBD(src1p.value, -src2p.value)); // lea dstp,[src1p-src2p] + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_sub_r64_p64(drcbe, &dst, dstreg, &src2p, inst); // sub dstreg,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_subc - process a SUBC opcode +-------------------------------------------------*/ + +static x86code *op_subc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_sbb_m32_p32(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // sbb [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_sbb_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // sbb dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_sbb_m64_p64(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // sbb [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_sbb_r64_p64(drcbe, &dst, dstreg, &src2p, inst); // sbb dstreg,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_cmp - process a CMP opcode +-------------------------------------------------*/ + +static x86code *op_cmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter src1p, src2p; + int src1reg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && src1p.value == src2p.value) + { + emit_cmp_r32_r32(&dst, REG_EAX, REG_EAX); // cmp eax,eax + return dst; + } + + /* pick a target register for the general case */ + src1reg = param_select_register(REG_EAX, &src1p, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + /* memory versus anything */ + if (src1p.type == DRCUML_PTYPE_MEMORY) + emit_cmp_m32_p32(drcbe, &dst, MABS(drcbe, src1p.value), &src2p, inst); // cmp [dstp],src2p + + /* general case */ + else + { + if (src1p.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_r32_imm(&dst, src1reg, src1p.value); // mov src1reg,imm + emit_cmp_r32_p32(drcbe, &dst, src1reg, &src2p, inst); // cmp src1reg,src2p + } + } + + /* 64-bit form */ + else + { + /* memory versus anything */ + if (src1p.type == DRCUML_PTYPE_MEMORY) + emit_cmp_m64_p64(drcbe, &dst, MABS(drcbe, src1p.value), &src2p, inst); // cmp [dstp],src2p + + /* general case */ + else + { + if (src1p.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_r64_imm(&dst, src1reg, src1p.value); // mov src1reg,imm + emit_cmp_r64_p64(drcbe, &dst, src1reg, &src2p, inst); // cmp src1reg,src2p + } + } + return dst; +} + + +/*------------------------------------------------- + op_mulu - process a MULU opcode +-------------------------------------------------*/ + +static x86code *op_mulu(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, edstp, src1p, src2p; + int compute_hi; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_4_commutative(drcbe, inst, &dstp, PTYPE_MR, &edstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + compute_hi = (dstp.type != edstp.type || dstp.value != edstp.value); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + { + if (inst->size == 4) + { + if (!compute_hi) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT32)src1p.value * (UINT32)src2p.value); + else + { + UINT64 result = (UINT64)(UINT32)src1p.value * (UINT64)(UINT32)src2p.value; + dst = convert_to_mov_imm(drcbe, dst, inst, &dstp, result & 0xffffffff); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, result >> 32); + } + } + } + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + emit_mov_r32_p32(drcbe, &dst, REG_EAX, &src1p); // mov eax,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_mul_m32(&dst, MABS(drcbe, src2p.value)); // mul [src2p] + else if (src2p.type == DRCUML_PTYPE_INT_REGISTER) + emit_mul_r32(&dst, src2p.value); // mul src2p + else if (src2p.type == DRCUML_PTYPE_IMMEDIATE) + { + emit_mov_r32_imm(&dst, REG_EDX, src2p.value); // mov edx,src2p + emit_mul_r32(&dst, REG_EDX); // mul edx + } + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + if (compute_hi) + emit_mov_p32_r32(drcbe, &dst, &edstp, REG_EDX); // mov edstp,edx + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + emit_mov_r64_p64(drcbe, &dst, REG_RAX, &src1p); // mov rax,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_mul_m64(&dst, MABS(drcbe, src2p.value)); // mul [src2p] + else if (src2p.type == DRCUML_PTYPE_INT_REGISTER) + emit_mul_r64(&dst, src2p.value); // mul src2p + else if (src2p.type == DRCUML_PTYPE_IMMEDIATE) + { + emit_mov_r64_imm(&dst, REG_RDX, src2p.value); // mov rdx,src2p + emit_mul_r64(&dst, REG_RDX); // mul rdx + } + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_RAX); // mov dstp,rax + if (compute_hi) + emit_mov_p64_r64(drcbe, &dst, &edstp, REG_RDX); // mov edstp,rdx + } + return dst; +} + + +/*------------------------------------------------- + op_muls - process a MULS opcode +-------------------------------------------------*/ + +static x86code *op_muls(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, edstp, src1p, src2p; + int compute_hi; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_4_commutative(drcbe, inst, &dstp, PTYPE_MR, &edstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + compute_hi = (dstp.type != edstp.type || dstp.value != edstp.value); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + { + if (inst->size == 4) + { + if (!compute_hi) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT32)src1p.value * (INT32)src2p.value); + else + { + UINT64 result = (INT64)(INT32)src1p.value * (INT64)(INT32)src2p.value; + dst = convert_to_mov_imm(drcbe, dst, inst, &dstp, result & 0xffffffff); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, result >> 32); + } + } + } + + /* 32-bit form */ + if (inst->size == 4) + { + /* 32-bit destination with memory/immediate or register/immediate */ + if (!compute_hi && src1p.type != DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE) + { + if (src1p.type == DRCUML_PTYPE_MEMORY) + emit_imul_r32_m32_imm(&dst, REG_EAX, MABS(drcbe, src1p.value), src2p.value); // imul eax,[src1p],src2p + else if (src1p.type == DRCUML_PTYPE_INT_REGISTER) + emit_imul_r32_r32_imm(&dst, REG_EAX, src1p.value, src2p.value); // imul eax,src1p,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + } + + /* 32-bit destination, general case */ + else if (!compute_hi) + { + emit_mov_r32_p32(drcbe, &dst, REG_EAX, &src1p); // mov eax,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_imul_r32_m32(&dst, REG_EAX, MABS(drcbe, src2p.value)); // imul eax,[src2p] + else if (src2p.type == DRCUML_PTYPE_INT_REGISTER) + emit_imul_r32_r32(&dst, REG_EAX, src2p.value); // imul eax,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + } + + /* 64-bit destination, general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, REG_EAX, &src1p); // mov eax,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_imul_m32(&dst, MABS(drcbe, src2p.value)); // imul [src2p] + else if (src2p.type == DRCUML_PTYPE_INT_REGISTER) + emit_imul_r32(&dst, src2p.value); // imul src2p + else if (src2p.type == DRCUML_PTYPE_IMMEDIATE) + { + emit_mov_r32_imm(&dst, REG_EDX, src2p.value); // mov edx,src2p + emit_imul_r32(&dst, REG_EDX); // imul edx + } + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + emit_mov_p32_r32(drcbe, &dst, &edstp, REG_EDX); // mov edstp,edx + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* 64-bit destination with memory/immediate or register/immediate */ + if (!compute_hi && src1p.type != DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && short_immediate(src2p.type)) + { + if (src1p.type == DRCUML_PTYPE_MEMORY) + emit_imul_r64_m64_imm(&dst, REG_RAX, MABS(drcbe, src1p.value), src2p.value);// imul rax,[src1p],src2p + else if (src1p.type == DRCUML_PTYPE_INT_REGISTER) + emit_imul_r64_r64_imm(&dst, REG_RAX, src1p.value, src2p.value); // imul rax,src1p,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_RAX); // mov dstp,rax + } + + /* 64-bit destination, general case */ + else if (!compute_hi) + { + emit_mov_r64_p64(drcbe, &dst, REG_RAX, &src1p); // mov rax,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_imul_r64_m64(&dst, REG_RAX, MABS(drcbe, src2p.value)); // imul rax,[src2p] + else if (src2p.type == DRCUML_PTYPE_INT_REGISTER) + emit_imul_r64_r64(&dst, REG_RAX, src2p.value); // imul rax,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_RAX); // mov dstp,rax + } + + /* 128-bit destination, general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, REG_RAX, &src1p); // mov rax,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_imul_m64(&dst, MABS(drcbe, src2p.value)); // imul [src2p] + else if (src2p.type == DRCUML_PTYPE_INT_REGISTER) + emit_imul_r64(&dst, src2p.value); // imul src2p + else if (src2p.type == DRCUML_PTYPE_IMMEDIATE) + { + emit_mov_r64_imm(&dst, REG_RDX, src2p.value); // mov rdx,src2p + emit_imul_r64(&dst, REG_RDX); // imul rdx + } + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_RAX); // mov dstp,rax + emit_mov_p64_r64(drcbe, &dst, &edstp, REG_RDX); // mov edstp,rdx + } + } + return dst; +} + + +/*------------------------------------------------- + op_divu - process a DIVU opcode +-------------------------------------------------*/ + +static x86code *op_divu(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, edstp, src1p, src2p; + int compute_rem; + emit_link skip; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &dstp, PTYPE_MR, &edstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + compute_rem = (dstp.type != edstp.type || dstp.value != edstp.value); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + { + if (inst->size == 4) + { + if ((UINT32)src2p.value == 0) + return dst; + if (!compute_rem) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT32)src1p.value / (UINT32)src2p.value); + else + { + dst = convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT32)src1p.value / (UINT32)src2p.value); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT32)src1p.value % (UINT32)src2p.value); + } + } + else if (inst->size == 8) + { + UINT64 reslo, reshi; + if (src2p.value == 0) + return dst; + if (!compute_rem) + { + reslo = (UINT64)src1p.value / (UINT64)src2p.value; + return convert_to_mov_imm(drcbe, dst, inst, &dstp, reslo); + } + else + { + reslo = (UINT64)src1p.value / (UINT64)src2p.value; + reshi = (UINT64)src1p.value % (UINT64)src2p.value; + dst = convert_to_mov_imm(drcbe, dst, inst, &dstp, reslo); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, reshi); + } + } + } + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + emit_mov_r32_p32(drcbe, &dst, REG_ECX, &src2p); // mov ecx,src2p + emit_jecxz_link(&dst, &skip); // jecxz skip + emit_mov_r32_p32(drcbe, &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(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + if (compute_rem) + emit_mov_p32_r32(drcbe, &dst, &edstp, REG_EDX); // mov edstp,edx + resolve_link(&dst, &skip); // skip: + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + emit_mov_r64_p64(drcbe, &dst, REG_RCX, &src2p); // mov rcx,src2p + emit_jrcxz_link(&dst, &skip); // jrcxz skip + emit_mov_r64_p64(drcbe, &dst, REG_RAX, &src1p); // mov rax,src1p + emit_xor_r32_r32(&dst, REG_EDX, REG_EDX); // xor edx,edx + emit_div_r64(&dst, REG_RCX); // div rcx + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_RAX); // mov dstp,rax + if (compute_rem) + emit_mov_p64_r64(drcbe, &dst, &edstp, REG_RDX); // mov edstp,rdx + resolve_link(&dst, &skip); // skip: + } + return dst; +} + + +/*------------------------------------------------- + op_divs - process a DIVS opcode +-------------------------------------------------*/ + +static x86code *op_divs(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, edstp, src1p, src2p; + int compute_rem; + emit_link skip; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &dstp, PTYPE_MR, &edstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + compute_rem = (dstp.type != edstp.type || dstp.value != edstp.value); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + { + if (inst->size == 4) + { + if ((INT32)src2p.value == 0) + return dst; + if (!compute_rem) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT32)src1p.value / (INT32)src2p.value); + else + { + dst = convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT32)src1p.value / (INT32)src2p.value); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT32)src1p.value % (INT32)src2p.value); + } + } + else if (inst->size == 8) + { + UINT64 reslo, reshi; + if (src2p.value == 0) + return dst; + if (!compute_rem) + { + reslo = (INT64)src1p.value / (INT64)src2p.value; + return convert_to_mov_imm(drcbe, dst, inst, &dstp, reslo); + } + else + { + reslo = (INT64)src1p.value / (INT64)src2p.value; + reshi = (INT64)src1p.value % (INT64)src2p.value; + dst = convert_to_mov_imm(drcbe, dst, inst, &dstp, reslo); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, reshi); + } + } + } + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + emit_mov_r32_p32(drcbe, &dst, REG_ECX, &src2p); // mov ecx,src2p + emit_jecxz_link(&dst, &skip); // jecxz skip + emit_mov_r32_p32(drcbe, &dst, REG_EAX, &src1p); // mov eax,src1p + emit_cdq(&dst); // cdq + emit_idiv_r32(&dst, REG_ECX); // idiv ecx + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + if (compute_rem) + emit_mov_p32_r32(drcbe, &dst, &edstp, REG_EDX); // mov edstp,edx + resolve_link(&dst, &skip); // skip: + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + emit_mov_r64_p64(drcbe, &dst, REG_RCX, &src2p); // mov rcx,src2p + emit_jrcxz_link(&dst, &skip); // jrcxz skip + emit_mov_r64_p64(drcbe, &dst, REG_RAX, &src1p); // mov rax,src1p + emit_cqo(&dst); // cqo + emit_idiv_r64(&dst, REG_RCX); // idiv rcx + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_RAX); // mov dstp,rax + if (compute_rem) + emit_mov_p64_r64(drcbe, &dst, &edstp, REG_RDX); // mov edstp,rdx + resolve_link(&dst, &skip); // skip: + } + return dst; +} + + +/*------------------------------------------------- + op_and - process a AND opcode +-------------------------------------------------*/ + +static x86code *op_and(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3_commutative(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, src1p.value & src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && (src2p.value & size_to_mask[inst->size]) == size_to_mask[inst->size] && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_and_m32_p32(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // and [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_and_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // and dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_and_m64_p64(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // and [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_and_r64_p64(drcbe, &dst, dstreg, &src2p, inst); // and dstreg,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_test - process a TEST opcode +-------------------------------------------------*/ + +static x86code *op_test(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter src1p, src2p; + int src1reg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_2_commutative(drcbe, inst, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* pick a target register for the general case */ + src1reg = param_select_register(REG_EAX, &src1p, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + /* src1p in memory */ + if (src1p.type == DRCUML_PTYPE_MEMORY) + emit_test_m32_p32(drcbe, &dst, MABS(drcbe, src1p.value), &src2p, inst); // test [src1p],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, src1reg, &src1p); // mov src1reg,src1p + emit_test_r32_p32(drcbe, &dst, src1reg, &src2p, inst); // test src1reg,src2p + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* src1p in memory */ + if (src1p.type == DRCUML_PTYPE_MEMORY) + emit_test_m64_p64(drcbe, &dst, MABS(drcbe, src1p.value), &src2p, inst); // test [src1p],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, src1reg, &src1p); // mov src1reg,src1p + emit_test_r64_p64(drcbe, &dst, src1reg, &src2p, inst); // test src1reg,src2p + } + } + return dst; +} + + +/*------------------------------------------------- + op_or - process a OR opcode +-------------------------------------------------*/ + +static x86code *op_or(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3_commutative(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, src1p.value | src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_or_m32_p32(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // or [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_or_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // or dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_or_m64_p64(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // or [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_or_r64_p64(drcbe, &dst, dstreg, &src2p, inst); // or dstreg,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_xor - process a XOR opcode +-------------------------------------------------*/ + +static x86code *op_xor(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3_commutative(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, src1p.value & src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_xor_m32_p32(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // xor [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_xor_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // xor dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_xor_m64_p64(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // xor [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_xor_r64_p64(drcbe, &dst, dstreg, &src2p, inst); // xor dstreg,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_shl - process a SHL opcode +-------------------------------------------------*/ + +static x86code *op_shl(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, src1p.value << src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_shl_m32_p32(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // shl [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_shl_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // shl dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_shl_m64_p64(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // shl [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_shl_r64_p64(drcbe, &dst, dstreg, &src2p, inst); // shl dstreg,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_shr - process a SHR opcode +-------------------------------------------------*/ + +static x86code *op_shr(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT64)src1p.value >> src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_shr_m32_p32(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // shr [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_shr_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // shr dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_shr_m64_p64(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // shr [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_shr_r64_p64(drcbe, &dst, dstreg, &src2p, inst); // shr dstreg,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_sar - process a SAR opcode +-------------------------------------------------*/ + +static x86code *op_sar(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + { + if (inst->size == 4) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT32)src1p.value >> src2p.value); + else if (inst->size == 8) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT64)src1p.value >> src2p.value); + } + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_sar_m32_p32(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // sar [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_sar_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // sar dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_sar_m64_p64(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // sar [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_sar_r64_p64(drcbe, &dst, dstreg, &src2p, inst); // sar dstreg,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_rol - process a rol opcode +-------------------------------------------------*/ + +static x86code *op_rol(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT64)src1p.value >> src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_rol_m32_p32(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // rol [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_rol_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // rol dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_rol_m64_p64(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // rol [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_rol_r64_p64(drcbe, &dst, dstreg, &src2p, inst); // rol dstreg,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_ror - process a ROR opcode +-------------------------------------------------*/ + +static x86code *op_ror(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT64)src1p.value >> src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_ror_m32_p32(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // ror [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_ror_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // ror dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_ror_m64_p64(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // ror [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_ror_r64_p64(drcbe, &dst, dstreg, &src2p, inst); // ror dstreg,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_rolc - process a ROLC opcode +-------------------------------------------------*/ + +static x86code *op_rolc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT64)src1p.value >> src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_rcl_m32_p32(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // rcl [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_rcl_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // rcl dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_rcl_m64_p64(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // rcl [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_rcl_r64_p64(drcbe, &dst, dstreg, &src2p, inst); // rcl dstreg,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_rorc - process a RORC opcode +-------------------------------------------------*/ + +static x86code *op_rorc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT64)src1p.value >> src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_rcr_m32_p32(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // rcr [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_rcr_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // rcr dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_rcr_m64_p64(drcbe, &dst, MABS(drcbe, dstp.value), &src2p, inst); // rcr [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_rcr_r64_p64(drcbe, &dst, dstreg, &src2p, inst); // rcr dstreg,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + return dst; +} + + + +/*************************************************************************** + FLOATING POINT OPERATIONS +***************************************************************************/ + +/*------------------------------------------------- + op_fload - process a FLOAD opcode +-------------------------------------------------*/ + +static x86code *op_fload(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + int basereg, dstreg; + INT32 baseoffs; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MF, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_XMM0, &dstp, NULL); + + /* determine the pointer base */ + basereg = get_base_register_and_offset(drcbe, &dst, basep.value, REG_RDX, &baseoffs); + + /* 32-bit form */ + if (inst->size == 4) + { + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_movss_r128_m32(&dst, dstreg, MBD(basereg, baseoffs + 4*indp.value)); // movss dstreg,[basep + 4*indp] + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); // mov indreg,indp + emit_movss_r128_m32(&dst, dstreg, MBISD(basereg, indreg, 4, baseoffs)); // movss dstreg,[basep + 4*indp] + } + emit_movss_p32_r128(drcbe, &dst, &dstp, dstreg); // movss dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_movsd_r128_m64(&dst, dstreg, MBD(basereg, baseoffs + 8*indp.value)); // movsd dstreg,[basep + 8*indp] + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); // mov indreg,indp + emit_movsd_r128_m64(&dst, dstreg, MBISD(basereg, indreg, 8, baseoffs)); // movsd dstreg,[basep + 8*indp] + } + emit_movsd_p64_r128(drcbe, &dst, &dstp, dstreg); // movsd dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_fstore - process a FSTORE opcode +-------------------------------------------------*/ + +static x86code *op_fstore(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter srcp, basep, indp; + int basereg, srcreg; + INT32 baseoffs; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &basep, PTYPE_M, &indp, PTYPE_MRI, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + srcreg = param_select_register(REG_XMM0, &srcp, NULL); + + /* determine the pointer base */ + basereg = get_base_register_and_offset(drcbe, &dst, basep.value, REG_RDX, &baseoffs); + + /* 32-bit form */ + if (inst->size == 4) + { + emit_movss_r128_p32(drcbe, &dst, srcreg, &srcp); // movss srcreg,srcp + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_movss_m32_r128(&dst, MBD(basereg, baseoffs + 4*indp.value), srcreg); // movss [basep + 4*indp],srcreg + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); // mov indreg,indp + emit_movss_m32_r128(&dst, MBISD(basereg, indreg, 4, baseoffs), srcreg); // movss [basep + 4*indp],srcreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + emit_movsd_r128_p64(drcbe, &dst, srcreg, &srcp); // movsd srcreg,srcp + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_movsd_m64_r128(&dst, MBD(basereg, baseoffs + 8*indp.value), srcreg); // movsd [basep + 8*indp],srcreg + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); // mov indreg,indp + emit_movsd_m64_r128(&dst, MBISD(basereg, indreg, 8, baseoffs), srcreg); // movsd [basep + 8*indp],srcreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_fread - process a FREAD opcode +-------------------------------------------------*/ + +static x86code *op_fread(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MF, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* set up a call to the read dword/qword handler */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + if (inst->size == 4) + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].read_dword);// call read_dword + else if (inst->size == 8) + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].read_qword);// call read_qword + + /* store result */ + if (inst->size == 4) + { + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_mov_m32_r32(&dst, MABS(drcbe, dstp.value), REG_EAX); // mov [dstp],eax + else if (dstp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_movd_r128_r32(&dst, dstp.value, REG_EAX); // movd dstp,eax + } + else if (inst->size == 8) + { + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_mov_m64_r64(&dst, MABS(drcbe, dstp.value), REG_RAX); // mov [dstp],rax + else if (dstp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_movq_r128_r64(&dst, dstp.value, REG_RAX); // movq dstp,rax + } + return dst; +} + + +/*------------------------------------------------- + op_fwrite - process a FWRITE opcode +-------------------------------------------------*/ + +static x86code *op_fwrite(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &srcp, PTYPE_MF); + + /* general case */ + emit_mov_r32_p32(drcbe, &dst, REG_PARAM1, &addrp); // mov param1,addrp + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_mov_r32_m32(&dst, REG_PARAM2, MABS(drcbe, srcp.value)); // mov param2,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_movd_r32_r128(&dst, REG_PARAM2, srcp.value); // movd param2,srcp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].write_dword);// call write_dword + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_mov_r64_m64(&dst, REG_PARAM2, MABS(drcbe, srcp.value)); // mov param2,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_movq_r64_r128(&dst, REG_PARAM2, srcp.value); // movq param2,srcp + emit_smart_call_m64(drcbe, &dst, (x86code **)&drcbe->accessors[spacep.value].write_qword);// call write_qword + } + + return dst; +} + + +/*------------------------------------------------- + op_fmov - process a FMOV opcode +-------------------------------------------------*/ + +static x86code *op_fmov(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + emit_link skip = { 0 }; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_XMM0, &dstp, NULL); + + /* always start with a jmp */ + if (inst->condflags != DRCUML_COND_ALWAYS) + emit_jcc_short_link(&dst, X86_NOT_CONDITION(inst->condflags), &skip); // jcc skip + + /* 32-bit form */ + if (inst->size == 4) + { + emit_movss_r128_p32(drcbe, &dst, dstreg, &srcp); // movss dstreg,srcp + emit_movss_p32_r128(drcbe, &dst, &dstp, dstreg); // movss dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + emit_movsd_r128_p64(drcbe, &dst, dstreg, &srcp); // movsd dstreg,srcp + emit_movsd_p64_r128(drcbe, &dst, &dstp, dstreg); // movsd dstp,dstreg + } + + /* resolve the jump */ + if (inst->condflags != DRCUML_COND_ALWAYS) + resolve_link(&dst, &skip); // skip: + return dst; +} + + +/*------------------------------------------------- + op_ftoi4 - process a FTOI4 opcode +-------------------------------------------------*/ + +static x86code *op_ftoi4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtss2si_r32_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtss2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtss2si_r32_r128(&dst, dstreg, srcp.value); // cvtss2si dstreg,srcp + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtsd2si_r32_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtsd2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtsd2si_r32_r128(&dst, dstreg, srcp.value); // cvtsd2si dstreg,srcp + } + + /* general case */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + return dst; +} + + +/*------------------------------------------------- + op_ftoi4t - process a FTOI4T opcode +-------------------------------------------------*/ + +static x86code *op_ftoi4t(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvttss2si_r32_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // cvttss2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvttss2si_r32_r128(&dst, dstreg, srcp.value); // cvttss2si dstreg,srcp + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvttsd2si_r32_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // cvttsd2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvttsd2si_r32_r128(&dst, dstreg, srcp.value); // cvttsd2si dstreg,srcp + } + + /* general case */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + return dst; +} + + +/*------------------------------------------------- + op_ftoi4r - process a FTOI4R opcode +-------------------------------------------------*/ + +static x86code *op_ftoi4r(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* save and set the control word */ + emit_stmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssemodesave)); // stmxcsr [ssemodesave] + emit_ldmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssecontrol[DRCUML_FMOD_ROUND])); // ldmxcsr fpcontrol[DRCUML_FMOD_ROUND] + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtss2si_r32_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtss2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtss2si_r32_r128(&dst, dstreg, srcp.value); // cvtss2si dstreg,srcp + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtsd2si_r32_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtsd2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtsd2si_r32_r128(&dst, dstreg, srcp.value); // cvtsd2si dstreg,srcp + } + + /* restore control word and proceed */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + emit_ldmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssemodesave)); // ldmxcsr [ssemodesave] + return dst; +} + + +/*------------------------------------------------- + op_ftoi4f - process a FTOI4F opcode +-------------------------------------------------*/ + +static x86code *op_ftoi4f(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* save and set the control word */ + emit_stmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssemodesave)); // stmxcsr [ssemodesave] + emit_ldmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssecontrol[DRCUML_FMOD_FLOOR])); // ldmxcsr fpcontrol[DRCUML_FMOD_FLOOR] + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtss2si_r32_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtss2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtss2si_r32_r128(&dst, dstreg, srcp.value); // cvtss2si dstreg,srcp + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtsd2si_r32_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtsd2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtsd2si_r32_r128(&dst, dstreg, srcp.value); // cvtsd2si dstreg,srcp + } + + /* restore control word and proceed */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + emit_ldmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssemodesave)); // ldmxcsr [ssemodesave] + return dst; +} + + +/*------------------------------------------------- + op_ftoi4c - process a FTOI4C opcode +-------------------------------------------------*/ + +static x86code *op_ftoi4c(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* save and set the control word */ + emit_stmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssemodesave)); // stmxcsr [ssemodesave] + emit_ldmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssecontrol[DRCUML_FMOD_CEIL])); // ldmxcsr fpcontrol[DRCUML_FMOD_CEIL] + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtss2si_r32_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtss2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtss2si_r32_r128(&dst, dstreg, srcp.value); // cvtss2si dstreg,srcp + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtsd2si_r32_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtsd2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtsd2si_r32_r128(&dst, dstreg, srcp.value); // cvtsd2si dstreg,srcp + } + + /* restore control word and proceed */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + emit_ldmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssemodesave)); // ldmxcsr [ssemodesave] + return dst; +} + + +/*------------------------------------------------- + op_ftoi8 - process a FTOI8 opcode +-------------------------------------------------*/ + +static x86code *op_ftoi8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtss2si_r64_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtss2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtss2si_r64_r128(&dst, dstreg, srcp.value); // cvtss2si dstreg,srcp + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtsd2si_r64_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtsd2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtsd2si_r64_r128(&dst, dstreg, srcp.value); // cvtsd2si dstreg,srcp + } + + /* general case */ + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + return dst; +} + + +/*------------------------------------------------- + op_ftoi8t - process a FTOI8T opcode +-------------------------------------------------*/ + +static x86code *op_ftoi8t(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvttss2si_r64_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // cvttss2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvttss2si_r64_r128(&dst, dstreg, srcp.value); // cvttss2si dstreg,srcp + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvttsd2si_r64_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // cvttsd2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvttsd2si_r64_r128(&dst, dstreg, srcp.value); // cvttsd2si dstreg,srcp + } + + /* general case */ + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + return dst; +} + + +/*------------------------------------------------- + op_ftoi8r - process a FTOI8R opcode +-------------------------------------------------*/ + +static x86code *op_ftoi8r(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* save and set the control word */ + emit_stmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssemodesave)); // stmxcsr [ssemodesave] + emit_ldmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssecontrol[DRCUML_FMOD_ROUND])); // ldmxcsr fpcontrol[DRCUML_FMOD_ROUND] + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtss2si_r64_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtss2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtss2si_r64_r128(&dst, dstreg, srcp.value); // cvtss2si dstreg,srcp + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtsd2si_r64_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtsd2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtsd2si_r64_r128(&dst, dstreg, srcp.value); // cvtsd2si dstreg,srcp + } + + /* restore control word and proceed */ + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + emit_ldmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssemodesave)); // ldmxcsr [ssemodesave] + return dst; +} + + +/*------------------------------------------------- + op_ftoi8f - process a FTOI8F opcode +-------------------------------------------------*/ + +static x86code *op_ftoi8f(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* save and set the control word */ + emit_stmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssemodesave)); // stmxcsr [ssemodesave] + emit_ldmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssecontrol[DRCUML_FMOD_FLOOR])); // ldmxcsr fpcontrol[DRCUML_FMOD_FLOOR] + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtss2si_r64_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtss2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtss2si_r64_r128(&dst, dstreg, srcp.value); // cvtss2si dstreg,srcp + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtsd2si_r64_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtsd2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtsd2si_r64_r128(&dst, dstreg, srcp.value); // cvtsd2si dstreg,srcp + } + + /* restore control word and proceed */ + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + emit_ldmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssemodesave)); // ldmxcsr [ssemodesave] + return dst; +} + + +/*------------------------------------------------- + op_ftoi8c - process a FTOI8C opcode +-------------------------------------------------*/ + +static x86code *op_ftoi8c(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* save and set the control word */ + emit_stmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssemodesave)); // stmxcsr [ssemodesave] + emit_ldmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssecontrol[DRCUML_FMOD_CEIL])); // ldmxcsr fpcontrol[DRCUML_FMOD_CEIL] + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtss2si_r64_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtss2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtss2si_r64_r128(&dst, dstreg, srcp.value); // cvtss2si dstreg,srcp + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtsd2si_r64_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtsd2si dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtsd2si_r64_r128(&dst, dstreg, srcp.value); // cvtsd2si dstreg,srcp + } + + /* restore control word and proceed */ + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + emit_ldmxcsr_m32(&dst, MABS(drcbe, &drcbe->ssemodesave)); // ldmxcsr [ssemodesave] + return dst; +} + + +/*------------------------------------------------- + op_ffrfs - process a FFRFS opcode +-------------------------------------------------*/ + +static x86code *op_ffrfs(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_XMM0, &dstp, NULL); + + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtss2sd_r128_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtss2sd dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtss2sd_r128_r128(&dst, dstreg, srcp.value); // cvtss2sd dstreg,srcp + emit_movsd_p64_r128(drcbe, &dst, &dstp, dstreg); // movsd dstp,dstreg + + return dst; +} + + +/*------------------------------------------------- + op_ffrfd - process a FFRFD opcode +-------------------------------------------------*/ + +static x86code *op_ffrfd(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_XMM0, &dstp, NULL); + + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtsd2ss_r128_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtsd2ss dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtsd2ss_r128_r128(&dst, dstreg, srcp.value); // cvtsd2ss dstreg,srcp + emit_movss_p32_r128(drcbe, &dst, &dstp, dstreg); // movss dstp,dstreg + + return dst; +} + + +/*------------------------------------------------- + op_ffri4 - process a FFRI4 opcode +-------------------------------------------------*/ + +static x86code *op_ffri4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_XMM0, &dstp, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtsi2ss_r128_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtsi2ss dstreg,[srcp] + else + { + int srcreg = param_select_register(REG_EAX, &srcp, NULL); + emit_mov_r32_p32(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_cvtsi2ss_r128_r32(&dst, dstreg, srcreg); // cvtsi2ss dstreg,srcreg + } + emit_movss_p32_r128(drcbe, &dst, &dstp, dstreg); // movss dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtsi2sd_r128_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtsi2sd dstreg,[srcp] + else + { + int srcreg = param_select_register(REG_EAX, &srcp, NULL); + emit_mov_r32_p32(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_cvtsi2sd_r128_r32(&dst, dstreg, srcreg); // cvtsi2sd dstreg,srcreg + } + emit_movsd_p64_r128(drcbe, &dst, &dstp, dstreg); // movsd dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_ffri8 - process a FFRI8 opcode +-------------------------------------------------*/ + +static x86code *op_ffri8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_XMM0, &dstp, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtsi2ss_r128_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtsi2ss dstreg,[srcp] + else + { + int srcreg = param_select_register(REG_EAX, &srcp, NULL); + emit_mov_r64_p64(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_cvtsi2ss_r128_r64(&dst, dstreg, srcreg); // cvtsi2ss dstreg,srcreg + } + emit_movss_p32_r128(drcbe, &dst, &dstp, dstreg); // movss dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtsi2sd_r128_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtsi2sd dstreg,[srcp] + else + { + int srcreg = param_select_register(REG_EAX, &srcp, NULL); + emit_mov_r64_p64(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_cvtsi2sd_r128_r64(&dst, dstreg, srcreg); // cvtsi2sd dstreg,srcreg + } + emit_movsd_p64_r128(drcbe, &dst, &dstp, dstreg); // movsd dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_fadd - process a FADD opcode +-------------------------------------------------*/ + +static x86code *op_fadd(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3_commutative(drcbe, inst, &dstp, PTYPE_MF, &src1p, PTYPE_MF, &src2p, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_XMM0, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + emit_movss_r128_p32(drcbe, &dst, dstreg, &src1p); // movss dstreg,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_addss_r128_m32(&dst, dstreg, MABS(drcbe, src2p.value)); // addss dstreg,[src2p] + else if (src2p.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_addss_r128_r128(&dst, dstreg, src2p.value); // addss dstreg,src2p + emit_movss_p32_r128(drcbe, &dst, &dstp, dstreg); // movss dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + emit_movsd_r128_p64(drcbe, &dst, dstreg, &src1p); // movsd dstreg,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_addsd_r128_m64(&dst, dstreg, MABS(drcbe, src2p.value)); // addsd dstreg,[src2p] + else if (src2p.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_addsd_r128_r128(&dst, dstreg, src2p.value); // addsd dstreg,src2p + emit_movsd_p64_r128(drcbe, &dst, &dstp, dstreg); // movsd dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_fsub - process a FSUB opcode +-------------------------------------------------*/ + +static x86code *op_fsub(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MF, &src1p, PTYPE_MF, &src2p, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_XMM0, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + emit_movss_r128_p32(drcbe, &dst, dstreg, &src1p); // movss dstreg,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_subss_r128_m32(&dst, dstreg, MABS(drcbe, src2p.value)); // subss dstreg,[src2p] + else if (src2p.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_subss_r128_r128(&dst, dstreg, src2p.value); // subss dstreg,src2p + emit_movss_p32_r128(drcbe, &dst, &dstp, dstreg); // movss dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + emit_movsd_r128_p64(drcbe, &dst, dstreg, &src1p); // movsd dstreg,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_subsd_r128_m64(&dst, dstreg, MABS(drcbe, src2p.value)); // subsd dstreg,[src2p] + else if (src2p.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_subsd_r128_r128(&dst, dstreg, src2p.value); // subsd dstreg,src2p + emit_movsd_p64_r128(drcbe, &dst, &dstp, dstreg); // movsd dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_fcmp - process a FCMP opcode +-------------------------------------------------*/ + +static x86code *op_fcmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter src1p, src2p; + int src1reg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &src1p, PTYPE_MF, &src2p, PTYPE_MF); + + /* pick a target register for the general case */ + src1reg = param_select_register(REG_XMM0, &src1p, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + emit_movss_r128_p32(drcbe, &dst, src1reg, &src1p); // movss src1reg,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_comiss_r128_m32(&dst, src1reg, MABS(drcbe, src2p.value)); // comiss src1reg,[src2p] + else if (src2p.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_comiss_r128_r128(&dst, src1reg, src2p.value); // comiss src1reg,src2p + } + + /* 64-bit form */ + else if (inst->size == 8) + { + emit_movsd_r128_p64(drcbe, &dst, src1reg, &src1p); // movsd src1reg,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_comisd_r128_m64(&dst, src1reg, MABS(drcbe, src2p.value)); // comisd src1reg,[src2p] + else if (src2p.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_comisd_r128_r128(&dst, src1reg, src2p.value); // comisd src1reg,src2p + } + return dst; +} + + +/*------------------------------------------------- + op_fmul - process a FMUL opcode +-------------------------------------------------*/ + +static x86code *op_fmul(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3_commutative(drcbe, inst, &dstp, PTYPE_MF, &src1p, PTYPE_MF, &src2p, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_XMM0, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + emit_movss_r128_p32(drcbe, &dst, dstreg, &src1p); // movss dstreg,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_mulss_r128_m32(&dst, dstreg, MABS(drcbe, src2p.value)); // mulss dstreg,[src2p] + else if (src2p.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_mulss_r128_r128(&dst, dstreg, src2p.value); // mulss dstreg,src2p + emit_movss_p32_r128(drcbe, &dst, &dstp, dstreg); // movss dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + emit_movsd_r128_p64(drcbe, &dst, dstreg, &src1p); // movsd dstreg,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_mulsd_r128_m64(&dst, dstreg, MABS(drcbe, src2p.value)); // mulsd dstreg,[src2p] + else if (src2p.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_mulsd_r128_r128(&dst, dstreg, src2p.value); // mulsd dstreg,src2p + emit_movsd_p64_r128(drcbe, &dst, &dstp, dstreg); // movsd dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_fdiv - process a FDIV opcode +-------------------------------------------------*/ + +static x86code *op_fdiv(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MF, &src1p, PTYPE_MF, &src2p, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_XMM0, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + emit_movss_r128_p32(drcbe, &dst, dstreg, &src1p); // movss dstreg,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_divss_r128_m32(&dst, dstreg, MABS(drcbe, src2p.value)); // divss dstreg,[src2p] + else if (src2p.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_divss_r128_r128(&dst, dstreg, src2p.value); // divss dstreg,src2p + emit_movss_p32_r128(drcbe, &dst, &dstp, dstreg); // movss dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + emit_movsd_r128_p64(drcbe, &dst, dstreg, &src1p); // movsd dstreg,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_divsd_r128_m64(&dst, dstreg, MABS(drcbe, src2p.value)); // divsd dstreg,[src2p] + else if (src2p.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_divsd_r128_r128(&dst, dstreg, src2p.value); // divsd dstreg,src2p + emit_movsd_p64_r128(drcbe, &dst, &dstp, dstreg); // movsd dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_fneg - process a FNEG opcode +-------------------------------------------------*/ + +static x86code *op_fneg(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_XMM0, &dstp, &srcp); + + /* 32-bit form */ + if (inst->size == 4) + { + emit_xorps_r128_r128(&dst, dstreg, dstreg); // xorps dstreg,dstreg + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_subss_r128_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // subss dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_subss_r128_r128(&dst, dstreg, srcp.value); // subss dstreg,srcp + emit_movss_p32_r128(drcbe, &dst, &dstp, dstreg); // movss dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + emit_xorpd_r128_r128(&dst, dstreg, dstreg); // xorpd dstreg,dstreg + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_subsd_r128_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // subsd dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_subsd_r128_r128(&dst, dstreg, srcp.value); // subsd dstreg,srcp + emit_movsd_p64_r128(drcbe, &dst, &dstp, dstreg); // movsd dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_fabs - process a FABS opcode +-------------------------------------------------*/ + +static x86code *op_fabs(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_XMM0, &dstp, &srcp); + + /* 32-bit form */ + if (inst->size == 4) + { + emit_movss_r128_p32(drcbe, &dst, dstreg, &srcp); // movss dstreg,srcp + emit_andps_r128_m128(&dst, dstreg, MABS(drcbe, drcbe->absmask32)); // andps dstreg,[absmask32] + emit_movss_p32_r128(drcbe, &dst, &dstp, dstreg); // movss dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + emit_movsd_r128_p64(drcbe, &dst, dstreg, &srcp); // movsd dstreg,srcp + emit_andpd_r128_m128(&dst, dstreg, MABS(drcbe, drcbe->absmask64)); // andpd dstreg,[absmask64] + emit_movsd_p64_r128(drcbe, &dst, &srcp, dstreg); // movsd dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_fsqrt - process a FSQRT opcode +-------------------------------------------------*/ + +static x86code *op_fsqrt(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_XMM0, &dstp, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_sqrtss_r128_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // sqrtss dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_sqrtss_r128_r128(&dst, dstreg, srcp.value); // sqrtss dstreg,srcp + emit_movss_p32_r128(drcbe, &dst, &dstp, dstreg); // movss dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_sqrtsd_r128_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // sqrtsd dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_sqrtsd_r128_r128(&dst, dstreg, srcp.value); // sqrtsd dstreg,srcp + emit_movsd_p64_r128(drcbe, &dst, &dstp, dstreg); // movsd dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_frecip - process a FRECIP opcode +-------------------------------------------------*/ + +static x86code *op_frecip(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_XMM0, &dstp, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_rcpss_r128_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // rcpss dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_rcpss_r128_r128(&dst, dstreg, srcp.value); // rcpss dstreg,srcp + emit_movss_p32_r128(drcbe, &dst, &dstp, dstreg); // movss dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtsd2ss_r128_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtsd2ss dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtsd2ss_r128_r128(&dst, dstreg, srcp.value); // cvtsd2ss dstreg,srcp + emit_rcpss_r128_r128(&dst, dstreg, dstreg); // rcpss dstreg,dstreg + emit_cvtss2sd_r128_r128(&dst, dstreg, dstreg); // cvtss2sd dstreg,dstreg + emit_movsd_p64_r128(drcbe, &dst, &dstp, dstreg); // movsd dstp,dstreg + } + return dst; +} + + +/*------------------------------------------------- + op_frsqrt - process a FRSQRT opcode +-------------------------------------------------*/ + +static x86code *op_frsqrt(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, PTYPE_MF); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_XMM0, &dstp, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_rsqrtss_r128_m32(&dst, dstreg, MABS(drcbe, srcp.value)); // rsqrtss dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_rsqrtss_r128_r128(&dst, dstreg, srcp.value); // rsqrtss dstreg,srcp + emit_movss_p32_r128(drcbe, &dst, &dstp, dstreg); // movss dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_cvtsd2ss_r128_m64(&dst, dstreg, MABS(drcbe, srcp.value)); // cvtsd2ss dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_FLOAT_REGISTER) + emit_cvtsd2ss_r128_r128(&dst, dstreg, srcp.value); // cvtsd2ss dstreg,srcp + emit_rsqrtss_r128_r128(&dst, dstreg, dstreg); // rsqrtss dstreg,dstreg + emit_cvtss2sd_r128_r128(&dst, dstreg, dstreg); // cvtss2sd dstreg,dstreg + emit_movsd_p64_r128(drcbe, &dst, &dstp, dstreg); // movsd dstp,dstreg + } + return dst; +} diff --git a/src/emu/cpu/drcbex86.c b/src/emu/cpu/drcbex86.c new file mode 100644 index 00000000000..ebff66c8a40 --- /dev/null +++ b/src/emu/cpu/drcbex86.c @@ -0,0 +1,7418 @@ +/*************************************************************************** + + drcbex86.c + + 32-bit x86 back-end for the universal machine language. + + Copyright Aaron Giles + Released for general non-commercial use under the MAME license + Visit http://mamedev.org for licensing and usage restrictions. + +**************************************************************************** + + 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 + +**************************************************************************** + + --------------- + 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] - temporary space for hash jump + [esp+4] - saved ebp + [esp+8] - saved edi + [esp+12] - saved esi + [esp+16] - saved ebx + [esp+20] - ret + [esp+24] - input parameter (entry handle) + +***************************************************************************/ + +#include "drcuml.h" +#include "drcumld.h" +#include "drcbeut.h" +#include "debugger.h" +#include "x86emit.h" +#undef REG_SP +#include "x86log.h" +#include <math.h> + + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define LOG_HASHJMPS (0) + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define PTYPE_M (1 << DRCUML_PTYPE_MEMORY) +#define PTYPE_I (1 << DRCUML_PTYPE_IMMEDIATE) +#define PTYPE_R (1 << DRCUML_PTYPE_INT_REGISTER) +#define PTYPE_F (1 << DRCUML_PTYPE_FLOAT_REGISTER) +#define PTYPE_MI (PTYPE_M | PTYPE_I) +#define PTYPE_RI (PTYPE_R | PTYPE_I) +#define PTYPE_MR (PTYPE_M | PTYPE_R) +#define PTYPE_MRI (PTYPE_M | PTYPE_R | PTYPE_I) +#define PTYPE_MF (PTYPE_M | PTYPE_F) + + + +/*************************************************************************** + MACROS +***************************************************************************/ + +#define X86_CONDITION(condflags) (condition_map[condflags - DRCUML_COND_Z]) +#define X86_NOT_CONDITION(condflags) (condition_map[condflags - DRCUML_COND_Z] ^ 1) + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* entry point */ +typedef UINT32 (*x86_entry_point_func)(x86code *entry); + +/* opcode handler */ +typedef x86code *(*opcode_generate_func)(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); + + +/* opcode table entry */ +typedef struct _opcode_table_entry opcode_table_entry; +struct _opcode_table_entry +{ + drcuml_opcode opcode; /* opcode in question */ + opcode_generate_func func; /* function pointer to the work */ +}; + + +/* internal backend-specific state */ +struct _drcbe_state +{ + drcuml_state * drcuml; /* pointer back to our owner */ + drccache * cache; /* pointer to the cache */ + drcuml_machine_state state; /* state of the machine */ + drchash_state * hash; /* hash table state */ + drcmap_state * map; /* code map */ + drclabel_list * labels; /* label list */ + + x86_entry_point_func entry; /* entry point */ + x86code * exit; /* exit point */ + x86code * nocode; /* nocode handler */ + + UINT32 * reglo[REG_MAX]; /* pointer to low part of data for each register */ + UINT32 * reghi[REG_MAX]; /* pointer to high part of data for each register */ + double fptemp; /* temporary storage for floating point */ + + UINT16 fpumode; /* saved FPU mode */ + UINT16 fmodesave; /* temporary location for saving */ + + void * stacksave; /* saved stack pointer */ + void * hashstacksave; /* saved stack pointer for hashjmp */ + UINT64 reslo; /* extended low result */ + UINT64 reshi; /* extended high result */ + + x86log_context * log; /* logging */ +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* primary back-end callbacks */ +static drcbe_state *drcbex86_alloc(drcuml_state *drcuml, drccache *cache, UINT32 flags, int modes, int addrbits, int ignorebits); +static void drcbex86_free(drcbe_state *drcbe); +static void drcbex86_reset(drcbe_state *drcbe); +static int drcbex86_execute(drcbe_state *drcbe, drcuml_codehandle *entry); +static void drcbex86_generate(drcbe_state *drcbe, drcuml_block *block, const drcuml_instruction *instlist, UINT32 numinst); +static int drcbex86_hash_exists(drcbe_state *drcbe, UINT32 mode, UINT32 pc); + +/* private helper functions */ +static void fixup_label(void *parameter, drccodeptr labelcodeptr); + +/* miscellaneous functions */ +static void dmulu(UINT64 *dstlo, UINT64 *dsthi, UINT64 src1, UINT64 src2); +static void dmuls(UINT64 *dstlo, UINT64 *dsthi, INT64 src1, INT64 src2); +static void ddivu(UINT64 *dstlo, UINT64 *dsthi, UINT64 src1, UINT64 src2); +static void ddivs(UINT64 *dstlo, UINT64 *dsthi, INT64 src1, INT64 src2); + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +/* globally-accessible interface to the backend */ +const drcbe_interface drcbe_x86_be_interface = +{ + drcbex86_alloc, + drcbex86_free, + drcbex86_reset, + drcbex86_execute, + drcbex86_generate, + drcbex86_hash_exists +}; + +/* opcode table */ +static opcode_generate_func opcode_table[DRCUML_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 UINT8 int_register_map[DRCUML_REG_I_END - DRCUML_REG_I0] = +{ + REG_EBX, REG_ESI, REG_EDI, REG_EBP +}; + +/* flags mapping tables */ +static UINT8 flags_map[0x1000]; + +/* condition mapping table */ +static UINT8 condition_map[DRCUML_COND_MAX - DRCUML_COND_Z] = +{ + COND_Z, /* DRCUML_COND_Z = 0x80, requires Z */ + COND_NZ, /* DRCUML_COND_NZ, requires Z */ + COND_S, /* DRCUML_COND_S, requires S */ + COND_NS, /* DRCUML_COND_NS, requires S */ + COND_C, /* DRCUML_COND_C, requires C */ + COND_NC, /* DRCUML_COND_NC, requires C */ + COND_O, /* DRCUML_COND_V, requires V */ + COND_NO, /* DRCUML_COND_NV, requires V */ + COND_P, /* DRCUML_COND_U, requires U */ + COND_NP, /* DRCUML_COND_NU, requires U */ + COND_A, /* DRCUML_COND_A, requires CZ */ + COND_BE, /* DRCUML_COND_BE, requires CZ */ + COND_G, /* DRCUML_COND_G, requires SVZ */ + COND_LE, /* DRCUML_COND_LE, requires SVZ */ + COND_L, /* DRCUML_COND_L, requires SV */ + COND_GE, /* DRCUML_COND_GE, requires SV */ +}; + +/* FPU control register mapping */ +static const UINT16 fp_control[4] = +{ + 0x0e3f, /* DRCUML_FMOD_TRUNC */ + 0x023f, /* DRCUML_FMOD_ROUND */ + 0x0a3f, /* DRCUML_FMOD_CEIL */ + 0x063f /* DRCUML_FMOD_FLOOR */ +}; + + + +/*************************************************************************** + TABLES +***************************************************************************/ + +static x86code *op_handle(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_hash(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_label(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_comment(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_mapvar(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); + +static x86code *op_debug(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_exit(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_hashjmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_jmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_exh(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_callh(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ret(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_callc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_recover(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); + +static x86code *op_setfmod(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_getfmod(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_getexp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); + +static x86code *op_load1u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_load1s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_load2u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_load2s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_load4u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_load4s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_load8u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_store1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_store2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_store4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_store8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read1u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read1s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read2u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read2s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read2m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read4u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read4s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read4m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read8u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_read8m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_write1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_write2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_writ2m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_write4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_writ4m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_write8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_writ8m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_flags(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_mov(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_zext1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_zext2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_zext4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_sext1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_sext2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_sext4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_add(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_addc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_sub(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_subc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_cmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_mulu(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_muls(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_divu(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_divs(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_and(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_test(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_or(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_xor(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_shl(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_shr(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_sar(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ror(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_rol(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_rorc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_rolc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); + +static x86code *op_fload(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fstore(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fread(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fwrite(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fmov(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi4t(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi4r(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi4f(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi4c(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi8t(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi8r(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi8f(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ftoi8c(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ffrfs(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ffrfd(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ffri4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_ffri8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fadd(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fsub(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fcmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fmul(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fdiv(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fneg(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fabs(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_fsqrt(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_frecip(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); +static x86code *op_frsqrt(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst); + +static const opcode_table_entry opcode_table_source[] = +{ + /* Compile-time opcodes */ + { DRCUML_OP_HANDLE, op_handle }, /* HANDLE handle */ + { DRCUML_OP_HASH, op_hash }, /* HASH mode,pc */ + { DRCUML_OP_LABEL, op_label }, /* LABEL imm */ + { DRCUML_OP_COMMENT, op_comment }, /* COMMENT string */ + { DRCUML_OP_MAPVAR, op_mapvar }, /* MAPVAR mapvar,value */ + + /* Control Flow Operations */ + { DRCUML_OP_DEBUG, op_debug }, /* DEBUG pc */ + { DRCUML_OP_EXIT, op_exit }, /* EXIT src1[,c] */ + { DRCUML_OP_HASHJMP, op_hashjmp }, /* HASHJMP mode,pc,handle */ + { DRCUML_OP_JMP, op_jmp }, /* JMP imm[,c] */ + { DRCUML_OP_EXH, op_exh }, /* EXH handle,param[,c] */ + { DRCUML_OP_CALLH, op_callh }, /* CALLH handle[,c] */ + { DRCUML_OP_RET, op_ret }, /* RET [c] */ + { DRCUML_OP_CALLC, op_callc }, /* CALLC func,ptr[,c] */ + { DRCUML_OP_RECOVER, op_recover }, /* RECOVER dst,mapvar */ + + /* Internal Register Operations */ + { DRCUML_OP_SETFMOD, op_setfmod }, /* SETFMOD src */ + { DRCUML_OP_GETFMOD, op_getfmod }, /* GETFMOD dst */ + { DRCUML_OP_GETEXP, op_getexp }, /* GETEXP dst,index */ + + /* Integer Operations */ + { DRCUML_OP_LOAD1U, op_load1u }, /* LOAD1U dst,base,index */ + { DRCUML_OP_LOAD1S, op_load1s }, /* LOAD1S dst,base,index */ + { DRCUML_OP_LOAD2U, op_load2u }, /* LOAD2U dst,base,index */ + { DRCUML_OP_LOAD2S, op_load2s }, /* LOAD2S dst,base,index */ + { DRCUML_OP_LOAD4U, op_load4u }, /* LOAD4U dst,base,index */ + { DRCUML_OP_LOAD4S, op_load4s }, /* LOAD4S dst,base,index */ + { DRCUML_OP_LOAD8U, op_load8u }, /* LOAD8U dst,base,index */ + { DRCUML_OP_STORE1, op_store1 }, /* STORE1 base,index,src */ + { DRCUML_OP_STORE2, op_store2 }, /* STORE2 base,index,src */ + { DRCUML_OP_STORE4, op_store4 }, /* STORE4 base,index,src */ + { DRCUML_OP_STORE8, op_store8 }, /* STORE8 base,index,src */ + { DRCUML_OP_READ1U, op_read1u }, /* READ1U dst,space,src1 */ + { DRCUML_OP_READ1S, op_read1s }, /* READ1S dst,space,src1 */ + { DRCUML_OP_READ2U, op_read2u }, /* READ2U dst,space,src1 */ + { DRCUML_OP_READ2S, op_read2s }, /* READ2S dst,space,src1 */ + { DRCUML_OP_READ2M, op_read2m }, /* READ2M dst,space,src1,mask */ + { DRCUML_OP_READ4U, op_read4u }, /* READ4U dst,space,src1 */ + { DRCUML_OP_READ4S, op_read4s }, /* READ4S dst,space,src1 */ + { DRCUML_OP_READ4M, op_read4m }, /* READ4M dst,space,src1,mask */ + { DRCUML_OP_READ8U, op_read8u }, /* READ8U dst,space,src1 */ + { DRCUML_OP_READ8M, op_read8m }, /* READ8M dst,space,src1,mask */ + { DRCUML_OP_WRITE1, op_write1 }, /* WRITE1 space,dst,src1 */ + { DRCUML_OP_WRITE2, op_write2 }, /* WRITE2 space,dst,src1 */ + { DRCUML_OP_WRIT2M, op_writ2m }, /* WRIT2M space,dst,src1 */ + { DRCUML_OP_WRITE4, op_write4 }, /* WRITE4 space,dst,src1 */ + { DRCUML_OP_WRIT4M, op_writ4m }, /* WRIT4M space,dst,mask,src1 */ + { DRCUML_OP_WRITE8, op_write8 }, /* WRITE8 space,dst,src1 */ + { DRCUML_OP_WRIT8M, op_writ8m }, /* WRIT8M space,dst,mask,src1 */ + { DRCUML_OP_FLAGS, op_flags }, /* FLAGS dst,mask,table */ + { DRCUML_OP_MOV, op_mov }, /* MOV dst,src[,c] */ + { DRCUML_OP_ZEXT1, op_zext1 }, /* ZEXT1 dst,src */ + { DRCUML_OP_ZEXT2, op_zext2 }, /* ZEXT2 dst,src */ + { DRCUML_OP_ZEXT4, op_zext4 }, /* ZEXT4 dst,src */ + { DRCUML_OP_SEXT1, op_sext1 }, /* SEXT1 dst,src */ + { DRCUML_OP_SEXT2, op_sext2 }, /* SEXT2 dst,src */ + { DRCUML_OP_SEXT4, op_sext4 }, /* SEXT4 dst,src */ + { DRCUML_OP_ADD, op_add }, /* ADD dst,src1,src2[,f] */ + { DRCUML_OP_ADDC, op_addc }, /* ADDC dst,src1,src2[,f] */ + { DRCUML_OP_SUB, op_sub }, /* SUB dst,src1,src2[,f] */ + { DRCUML_OP_SUBB, op_subc }, /* SUBB dst,src1,src2[,f] */ + { DRCUML_OP_CMP, op_cmp }, /* CMP src1,src2[,f] */ + { DRCUML_OP_MULU, op_mulu }, /* MULU dst,edst,src1,src2[,f] */ + { DRCUML_OP_MULS, op_muls }, /* MULS dst,edst,src1,src2[,f] */ + { DRCUML_OP_DIVU, op_divu }, /* DIVU dst,edst,src1,src2[,f] */ + { DRCUML_OP_DIVS, op_divs }, /* DIVS dst,edst,src1,src2[,f] */ + { DRCUML_OP_AND, op_and }, /* AND dst,src1,src2[,f] */ + { DRCUML_OP_TEST, op_test }, /* TEST src1,src2[,f] */ + { DRCUML_OP_OR, op_or }, /* OR dst,src1,src2[,f] */ + { DRCUML_OP_XOR, op_xor }, /* XOR dst,src1,src2[,f] */ + { DRCUML_OP_SHL, op_shl }, /* SHL dst,src,count[,f] */ + { DRCUML_OP_SHR, op_shr }, /* SHR dst,src,count[,f] */ + { DRCUML_OP_SAR, op_sar }, /* SAR dst,src,count[,f] */ + { DRCUML_OP_ROL, op_rol }, /* ROL dst,src,count[,f] */ + { DRCUML_OP_ROLC, op_rolc }, /* ROLC dst,src,count[,f] */ + { DRCUML_OP_ROR, op_ror }, /* ROR dst,src,count[,f] */ + { DRCUML_OP_RORC, op_rorc }, /* RORC dst,src,count[,f] */ + + /* Floating Point Operations */ + { DRCUML_OP_FLOAD, op_fload }, /* FLOAD dst,base,index */ + { DRCUML_OP_FSTORE, op_fstore }, /* FSTORE base,index,src */ + { DRCUML_OP_FREAD, op_fread }, /* FREAD dst,space,src1 */ + { DRCUML_OP_FWRITE, op_fwrite }, /* FWRITE space,dst,src1 */ + { DRCUML_OP_FMOV, op_fmov }, /* FMOV dst,src1[,c] */ + { DRCUML_OP_FTOI4, op_ftoi4 }, /* FTOI4 dst,src1 */ + { DRCUML_OP_FTOI4T, op_ftoi4t }, /* FTOI4T dst,src1 */ + { DRCUML_OP_FTOI4R, op_ftoi4r }, /* FTOI4R dst,src1 */ + { DRCUML_OP_FTOI4F, op_ftoi4f }, /* FTOI4F dst,src1 */ + { DRCUML_OP_FTOI4C, op_ftoi4c }, /* FTOI4C dst,src1 */ + { DRCUML_OP_FTOI8, op_ftoi8 }, /* FTOI8 dst,src1 */ + { DRCUML_OP_FTOI8T, op_ftoi8t }, /* FTOI8T dst,src1 */ + { DRCUML_OP_FTOI8R, op_ftoi8r }, /* FTOI8R dst,src1 */ + { DRCUML_OP_FTOI8F, op_ftoi8f }, /* FTOI8F dst,src1 */ + { DRCUML_OP_FTOI8C, op_ftoi8c }, /* FTOI8C dst,src1 */ + { DRCUML_OP_FFRFS, op_ffrfs }, /* FFRFS dst,src1 */ + { DRCUML_OP_FFRFD, op_ffrfd }, /* FFRFD dst,src1 */ + { DRCUML_OP_FFRI4, op_ffri4 }, /* FFRI4 dst,src1 */ + { DRCUML_OP_FFRI8, op_ffri8 }, /* FFRI8 dst,src1 */ + { DRCUML_OP_FADD, op_fadd }, /* FADD dst,src1,src2 */ + { DRCUML_OP_FSUB, op_fsub }, /* FSUB dst,src1,src2 */ + { DRCUML_OP_FCMP, op_fcmp }, /* FCMP src1,src2 */ + { DRCUML_OP_FMUL, op_fmul }, /* FMUL dst,src1,src2 */ + { DRCUML_OP_FDIV, op_fdiv }, /* FDIV dst,src1,src2 */ + { DRCUML_OP_FNEG, op_fneg }, /* FNEG dst,src1 */ + { DRCUML_OP_FABS, op_fabs }, /* FABS dst,src1 */ + { DRCUML_OP_FSQRT, op_fsqrt }, /* FSQRT dst,src1 */ + { DRCUML_OP_FRECIP, op_frecip }, /* FRECIP dst,src1 */ + { DRCUML_OP_FRSQRT, op_frsqrt } /* FRSQRT dst,src1 */ +}; + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + param_select_register - select a register + to use, avoiding conflicts with the optional + checkparam +-------------------------------------------------*/ + +INLINE int param_select_register(int defreg, const drcuml_parameter *param, const drcuml_parameter *checkparam) +{ + if (param->type == DRCUML_PTYPE_INT_REGISTER && (checkparam == NULL || param->type != checkparam->type || param->value != checkparam->value)) + return param->value; + return defreg; +} + + +/*------------------------------------------------- + emit_combine_z_flags - combine the Z flag from + two 32-bit operations +-------------------------------------------------*/ + +INLINE void 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 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 +} + + + +/*************************************************************************** + BACKEND CALLBACKS +***************************************************************************/ + +/*------------------------------------------------- + drcbex86_alloc - allocate back-end-specific + state +-------------------------------------------------*/ + +static drcbe_state *drcbex86_alloc(drcuml_state *drcuml, drccache *cache, UINT32 flags, int modes, int addrbits, int ignorebits) +{ + int opnum, regnum, entry; + drcbe_state *drcbe; + + /* allocate space in the cache for our state */ + drcbe = drccache_memory_alloc(cache, sizeof(*drcbe)); + if (drcbe == NULL) + return NULL; + memset(drcbe, 0, sizeof(*drcbe)); + + /* remember our pointers */ + drcbe->drcuml = drcuml; + drcbe->cache = cache; + + /* allocate hash tables */ + drcbe->hash = drchash_alloc(cache, modes, addrbits, ignorebits); + if (drcbe->hash == NULL) + return NULL; + + /* allocate code map */ + drcbe->map = drcmap_alloc(cache, 0); + if (drcbe->map == NULL) + return NULL; + + /* allocate a label tracker */ + drcbe->labels = drclabel_list_alloc(cache); + if (drcbe->labels == NULL) + return NULL; + + /* build the opcode table (static but it doesn't hurt to regenerate it) */ + for (opnum = 0; opnum < ARRAY_LENGTH(opcode_table_source); opnum++) + opcode_table[opcode_table_source[opnum].opcode] = opcode_table_source[opnum].func; + + /* build the flags map (static but it doesn't hurt to regenerate it) */ + for (entry = 0; entry < ARRAY_LENGTH(flags_map); entry++) + { + UINT8 flags = 0; + if (entry & 0x001) flags |= DRCUML_FLAG_C; + if (entry & 0x004) flags |= DRCUML_FLAG_U; + if (entry & 0x040) flags |= DRCUML_FLAG_Z; + if (entry & 0x080) flags |= DRCUML_FLAG_S; + if (entry & 0x800) flags |= DRCUML_FLAG_V; + flags_map[entry] = flags; + } + + /* compute hi pointers for each register */ + for (regnum = 0; regnum < ARRAY_LENGTH(int_register_map); regnum++) + if (int_register_map[regnum] != 0) + { + drcbe->reglo[int_register_map[regnum]] = &drcbe->state.r[regnum].l; + drcbe->reghi[int_register_map[regnum]] = &drcbe->state.r[regnum].h; + } + + /* create the log */ + if (flags & DRCUML_OPTION_LOG_NATIVE) + drcbe->log = x86log_create_context("drcbex86.asm"); + + return drcbe; +} + + +/*------------------------------------------------- + drcbex86_free - free back-end specific state +-------------------------------------------------*/ + +static void drcbex86_free(drcbe_state *drcbe) +{ + /* free the log context */ + if (drcbe->log != NULL) + x86log_free_context(drcbe->log); +} + + +/*------------------------------------------------- + drcbex86_reset - reset back-end specific state +-------------------------------------------------*/ + +static void drcbex86_reset(drcbe_state *drcbe) +{ + x86code **dst; + + /* output a note to the log */ + if (drcbe->log != NULL) + x86log_printf(drcbe->log, "\n\n===========\nCACHE RESET\n===========\n\n"); + + /* generate a little bit of glue code to set up the environment */ + dst = (x86code **)drccache_begin_codegen(drcbe->cache, 500); + if (dst == NULL) + fatalerror("Out of cache space after a reset!"); + + /* generate an entry point */ + drcbe->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_mov_m32_r32(dst, MABS(&drcbe->hashstacksave), REG_ESP); // mov [hashstacksave],esp + emit_sub_r32_imm(dst, REG_ESP, 4); // sub esp,4 + emit_mov_m32_r32(dst, MABS(&drcbe->stacksave), REG_ESP); // mov [stacksave],esp + emit_fstcw_m16(dst, MABS(&drcbe->fpumode)); // fstcw [fpumode] + emit_jmp_r32(dst, REG_EAX); // jmp eax + if (drcbe->log != NULL) + x86log_disasm_code_range(drcbe->log, "entry_point", (x86code *)drcbe->entry, *dst); + + /* generate an exit point */ + drcbe->exit = *dst; + emit_fldcw_m16(dst, MABS(&drcbe->fpumode)); // fldcw [fpumode] + emit_mov_r32_m32(dst, REG_ESP, MABS(&drcbe->hashstacksave)); // mov esp,[hashstacksave] + 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 (drcbe->log != NULL) + x86log_disasm_code_range(drcbe->log, "exit_point", drcbe->exit, *dst); + + /* generate a no code point */ + drcbe->nocode = *dst; + emit_ret(dst); // ret + if (drcbe->log != NULL) + x86log_disasm_code_range(drcbe->log, "nocode", drcbe->nocode, *dst); + + /* finish up codegen */ + drccache_end_codegen(drcbe->cache); + + /* reset our hash tables */ + drchash_reset(drcbe->hash); + drchash_set_default_codeptr(drcbe->hash, drcbe->nocode); +} + + +/*------------------------------------------------- + drcbex86_generate - generate code +-------------------------------------------------*/ + +static void drcbex86_generate(drcbe_state *drcbe, drcuml_block *block, const drcuml_instruction *instlist, UINT32 numinst) +{ + const char *blockname = NULL; + char blockbuffer[100]; + drccodeptr *cachetop; + x86code *base; + x86code *dst; + int inum; + + /* tell all of our utility objects that a block is beginning */ + drchash_block_begin(drcbe->hash, block, instlist, numinst); + drclabel_block_begin(drcbe->labels, block); + drcmap_block_begin(drcbe->map, block); + + /* begin codegen; fail if we can't */ + cachetop = drccache_begin_codegen(drcbe->cache, numinst * 8 * 4); + if (cachetop == NULL) + drcuml_block_abort(block); + + /* compute the base by aligning the cache top to a cache line (assumed to be 64 bytes) */ + base = (x86code *)(((FPTR)*cachetop + 63) & ~63); + dst = base; + + /* generate code */ + for (inum = 0; inum < numinst; inum++) + { + const drcuml_instruction *inst = &instlist[inum]; + assert(inst->opcode < ARRAY_LENGTH(opcode_table)); + + /* add a comment */ + if (drcbe->log != NULL) + { + char dasm[100]; + drcuml_disasm(inst, dasm); + x86log_add_comment(drcbe->log, dst, "%s", dasm); + } + + /* extract a blockname */ + if (blockname == NULL) + { + if (inst->opcode == DRCUML_OP_HANDLE) + blockname = drcuml_handle_name((drcuml_codehandle *)(FPTR)inst->param[0].value); + else if (inst->opcode == DRCUML_OP_HASH) + { + sprintf(blockbuffer, "Code: mode=%d PC=%08X", (UINT32)inst->param[0].value, (offs_t)inst->param[1].value); + blockname = blockbuffer; + } + } + + /* generate code */ + dst = (*opcode_table[inst->opcode])(drcbe, dst, inst); + } + + /* complete codegen */ + *cachetop = (drccodeptr)dst; + drccache_end_codegen(drcbe->cache); + + /* log it */ + if (drcbe->log != NULL) + x86log_disasm_code_range(drcbe->log, (blockname == NULL) ? "Unknown block" : blockname, base, drccache_top(drcbe->cache)); + + /* tell all of our utility objects that the block is finished */ + drchash_block_end(drcbe->hash, block); + drclabel_block_end(drcbe->labels, block); + drcmap_block_end(drcbe->map, block); +} + + +/*------------------------------------------------- + drcbex86_hash_exists - return true if the + given mode/pc exists in the hash table +-------------------------------------------------*/ + +static int drcbex86_hash_exists(drcbe_state *drcbe, UINT32 mode, UINT32 pc) +{ + return drchash_code_exists(drcbe->hash, mode, pc); +} + + +/*------------------------------------------------- + drcbec_execute - execute a block of code + registered at the given mode/pc +-------------------------------------------------*/ + +static int drcbex86_execute(drcbe_state *drcbe, drcuml_codehandle *entry) +{ + /* call our entry point which will jump to the destination */ + return (*drcbe->entry)((x86code *)drcuml_handle_codeptr(entry)); +} + + + +/*************************************************************************** + COMPILE HELPERS +***************************************************************************/ + +/*------------------------------------------------- + param_normalize - convert a full parameter + into a reduced set +-------------------------------------------------*/ + +static void param_normalize(drcbe_state *drcbe, const drcuml_parameter *src, drcuml_parameter *dest, UINT32 allowed) +{ + int regnum; + + switch (src->type) + { + /* immediates pass through */ + case DRCUML_PTYPE_IMMEDIATE: + assert(allowed & PTYPE_I); + dest->type = DRCUML_PTYPE_IMMEDIATE; + dest->value = src->value; + break; + + /* mapvars are converted to immediates with their current value */ + case DRCUML_PTYPE_MAPVAR: + assert(allowed & PTYPE_I); + dest->type = DRCUML_PTYPE_IMMEDIATE; + dest->value = drcmap_get_last_value(drcbe->map, src->value); + break; + + /* memory passes through */ + case DRCUML_PTYPE_MEMORY: + assert(allowed & PTYPE_M); + dest->type = DRCUML_PTYPE_MEMORY; + dest->value = src->value; + break; + + /* if a register maps to a register, keep it as a register; otherwise map it to memory */ + case DRCUML_PTYPE_INT_REGISTER: + assert(allowed & PTYPE_R); + assert(allowed & PTYPE_M); + regnum = int_register_map[src->value - DRCUML_REG_I0]; + if (regnum != 0) + { + dest->type = DRCUML_PTYPE_INT_REGISTER; + dest->value = regnum; + } + else + { + dest->type = DRCUML_PTYPE_MEMORY; + dest->value = (FPTR)&drcbe->state.r[src->value - DRCUML_REG_I0]; + } + break; + + /* all floating point registers map to memory */ + case DRCUML_PTYPE_FLOAT_REGISTER: + assert(allowed & PTYPE_F); + assert(allowed & PTYPE_M); + dest->type = DRCUML_PTYPE_MEMORY; + dest->value = (FPTR)&drcbe->state.f[src->value - DRCUML_REG_F0]; + break; + + /* everything else is unexpected */ + default: + fatalerror("Unexpected parameter type"); + break; + } +} + + +/*------------------------------------------------- + param_normalize_1 - normalize a single + parameter instruction +-------------------------------------------------*/ + +static void param_normalize_1(drcbe_state *drcbe, const drcuml_instruction *inst, drcuml_parameter *dest0, UINT32 allowed0) +{ + assert(inst->numparams == 1); + param_normalize(drcbe, &inst->param[0], dest0, allowed0); +} + + +/*------------------------------------------------- + param_normalize_2 - normalize a 2 + parameter instruction +-------------------------------------------------*/ + +static void param_normalize_2(drcbe_state *drcbe, const drcuml_instruction *inst, drcuml_parameter *dest0, UINT32 allowed0, drcuml_parameter *dest1, UINT32 allowed1) +{ + assert(inst->numparams == 2); + param_normalize(drcbe, &inst->param[0], dest0, allowed0); + param_normalize(drcbe, &inst->param[1], dest1, allowed1); +} + + +/*------------------------------------------------- + param_normalize_2_commutative - normalize a 2 + parameter instruction, shuffling the + parameters on the assumption that the two + parameters can be swapped +-------------------------------------------------*/ + +static void param_normalize_2_commutative(drcbe_state *drcbe, const drcuml_instruction *inst, drcuml_parameter *dest0, UINT32 allowed0, drcuml_parameter *dest1, UINT32 allowed1) +{ + param_normalize_2(drcbe, inst, dest0, allowed0, dest1, allowed1); + + /* if the inner parameter is a memory operand, push it to the outer */ + if (dest0->type == DRCUML_PTYPE_MEMORY) + { + drcuml_parameter temp = *dest0; + *dest0 = *dest1; + *dest1 = temp; + } + + /* if the inner parameter is an immediate, push it to the outer */ + if (dest0->type == DRCUML_PTYPE_IMMEDIATE) + { + drcuml_parameter temp = *dest0; + *dest0 = *dest1; + *dest1 = temp; + } +} + + +/*------------------------------------------------- + param_normalize_3 - normalize a 3 + parameter instruction +-------------------------------------------------*/ + +static void param_normalize_3(drcbe_state *drcbe, const drcuml_instruction *inst, drcuml_parameter *dest0, UINT32 allowed0, drcuml_parameter *dest1, UINT32 allowed1, drcuml_parameter *dest2, UINT32 allowed2) +{ + assert(inst->numparams == 3); + param_normalize(drcbe, &inst->param[0], dest0, allowed0); + param_normalize(drcbe, &inst->param[1], dest1, allowed1); + param_normalize(drcbe, &inst->param[2], dest2, allowed2); +} + + +/*------------------------------------------------- + param_normalize_3_commutative - normalize a 3 + parameter instruction, shuffling the + parameters on the assumption that the last + 2 can be swapped +-------------------------------------------------*/ + +static void param_normalize_3_commutative(drcbe_state *drcbe, const drcuml_instruction *inst, drcuml_parameter *dest0, UINT32 allowed0, drcuml_parameter *dest1, UINT32 allowed1, drcuml_parameter *dest2, UINT32 allowed2) +{ + param_normalize_3(drcbe, inst, dest0, allowed0, dest1, allowed1, dest2, allowed2); + + /* if the inner parameter is a memory operand, push it to the outer */ + if (dest1->type == DRCUML_PTYPE_MEMORY) + { + drcuml_parameter temp = *dest1; + *dest1 = *dest2; + *dest2 = temp; + } + + /* if the inner parameter is an immediate, push it to the outer */ + if (dest1->type == DRCUML_PTYPE_IMMEDIATE) + { + drcuml_parameter temp = *dest1; + *dest1 = *dest2; + *dest2 = temp; + } + + /* if the destination and outer parameters are equal, move the outer to the inner */ + if (dest0->type == dest2->type && dest0->value == dest2->value && dest0->type != DRCUML_PTYPE_IMMEDIATE) + { + drcuml_parameter temp = *dest1; + *dest1 = *dest2; + *dest2 = temp; + } +} + + +/*------------------------------------------------- + param_normalize_4 - normalize a 4 + parameter instruction +-------------------------------------------------*/ + +static void param_normalize_4(drcbe_state *drcbe, const drcuml_instruction *inst, drcuml_parameter *dest0, UINT32 allowed0, drcuml_parameter *dest1, UINT32 allowed1, drcuml_parameter *dest2, UINT32 allowed2, drcuml_parameter *dest3, UINT32 allowed3) +{ + assert(inst->numparams == 4); + param_normalize(drcbe, &inst->param[0], dest0, allowed0); + param_normalize(drcbe, &inst->param[1], dest1, allowed1); + param_normalize(drcbe, &inst->param[2], dest2, allowed2); + param_normalize(drcbe, &inst->param[3], dest3, allowed3); +} + + +/*------------------------------------------------- + param_normalize_4_commutative - normalize a 4 + parameter instruction, shuffling the + parameters on the assumption that the last + 2 can be swapped +-------------------------------------------------*/ + +static void param_normalize_4_commutative(drcbe_state *drcbe, const drcuml_instruction *inst, drcuml_parameter *dest0, UINT32 allowed0, drcuml_parameter *dest1, UINT32 allowed1, drcuml_parameter *dest2, UINT32 allowed2, drcuml_parameter *dest3, UINT32 allowed3) +{ + param_normalize_4(drcbe, inst, dest0, allowed0, dest1, allowed1, dest2, allowed2, dest3, allowed3); + + /* if the inner parameter is a memory operand, push it to the outer */ + if (dest2->type == DRCUML_PTYPE_MEMORY) + { + drcuml_parameter temp = *dest2; + *dest2 = *dest3; + *dest3 = temp; + } + + /* if the inner parameter is an immediate, push it to the outer */ + if (dest2->type == DRCUML_PTYPE_IMMEDIATE) + { + drcuml_parameter temp = *dest2; + *dest2 = *dest3; + *dest3 = temp; + } + + /* if the destination and outer parameters are equal, move the outer to the inner */ + if (dest0->type == dest3->type && dest0->value == dest3->value && dest0->type != DRCUML_PTYPE_IMMEDIATE) + { + drcuml_parameter temp = *dest2; + *dest2 = *dest3; + *dest3 = temp; + } +} + + + +/*************************************************************************** + EMITTERS FOR 32-BIT OPERATIONS WITH PARAMETERS +***************************************************************************/ + +/*------------------------------------------------- + emit_mov_r32_p32 - move a 32-bit parameter + into a register +-------------------------------------------------*/ + +static void emit_mov_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (param->value == 0) + emit_xor_r32_r32(dst, reg, reg); // xor reg,reg + else + emit_mov_r32_imm(dst, reg, param->value); // mov reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_mov_r32_m32(dst, reg, MABS(param->value)); // mov reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + if (reg != param->value) + emit_mov_r32_r32(dst, reg, param->value); // mov reg,param + } +} + + +/*------------------------------------------------- + emit_mov_m32_p32 - move a 32-bit parameter + into a memory location +-------------------------------------------------*/ + +static void emit_mov_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m32_imm(dst, MEMPARAMS, param->value); // mov [mem],param + else if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_mov_r32_m32(dst, REG_EAX, MABS(param->value)); // mov eax,[param] + emit_mov_m32_r32(dst, MEMPARAMS, REG_EAX); // mov [mem],eax + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_r32(dst, MEMPARAMS, param->value); // mov [mem],param +} + + +/*------------------------------------------------- + emit_mov_p32_r32 - move a register into a + 32-bit parameter +-------------------------------------------------*/ + +static void emit_mov_p32_r32(drcbe_state *drcbe, x86code **dst, const drcuml_parameter *param, UINT8 reg) +{ + assert(param->type != DRCUML_PTYPE_IMMEDIATE); + if (param->type == DRCUML_PTYPE_MEMORY) + emit_mov_m32_r32(dst, MABS(param->value), reg); // mov [param],reg + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + if (reg != param->value) + emit_mov_r32_r32(dst, param->value, reg); // mov param,reg + } +} + + +/*------------------------------------------------- + emit_push_p32 - push a 32-bit parameter onto + the stack +-------------------------------------------------*/ + +static void emit_push_p32(drcbe_state *drcbe, x86code **dst, const drcuml_parameter *src) +{ + if (src->type == DRCUML_PTYPE_IMMEDIATE) + emit_push_imm(dst, src->value); // push src + else if (src->type == DRCUML_PTYPE_MEMORY) + emit_push_m32(dst, MABS(src->value)); // push [src] + else if (src->type == DRCUML_PTYPE_INT_REGISTER) + emit_push_r32(dst, src->value); // push src +} + + +/*------------------------------------------------- + emit_add_r32_p32 - add operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_add_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_add_r32_imm(dst, reg, param->value); // add reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_add_r32_m32(dst, reg, MABS(param->value)); // add reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_add_r32_r32(dst, reg, param->value); // add reg,param +} + + +/*------------------------------------------------- + emit_add_m32_p32 - add operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_add_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_add_m32_imm(dst, MEMPARAMS, param->value); // add [dest],param + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_add_m32_r32(dst, MEMPARAMS, reg); // add [dest],reg + } +} + + +/*------------------------------------------------- + emit_adc_r32_p32 - adc operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_adc_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_adc_r32_imm(dst, reg, param->value); // adc reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_adc_r32_m32(dst, reg, MABS(param->value)); // adc reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_adc_r32_r32(dst, reg, param->value); // adc reg,param +} + + +/*------------------------------------------------- + emit_adc_m32_p32 - adc operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_adc_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_adc_m32_imm(dst, MEMPARAMS, param->value); // adc [dest],param + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_adc_m32_r32(dst, MEMPARAMS, reg); // adc [dest],reg + } +} + + +/*------------------------------------------------- + emit_sub_r32_p32 - sub operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_sub_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_sub_r32_imm(dst, reg, param->value); // sub reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_sub_r32_m32(dst, reg, MABS(param->value)); // sub reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_sub_r32_r32(dst, reg, param->value); // sub reg,param +} + + +/*------------------------------------------------- + emit_sub_m32_p32 - sub operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_sub_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_sub_m32_imm(dst, MEMPARAMS, param->value); // sub [dest],param + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_sub_m32_r32(dst, MEMPARAMS, reg); // sub [dest],reg + } +} + + +/*------------------------------------------------- + emit_sbb_r32_p32 - sbb operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_sbb_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_sbb_r32_imm(dst, reg, param->value); // sbb reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_sbb_r32_m32(dst, reg, MABS(param->value)); // sbb reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_sbb_r32_r32(dst, reg, param->value); // sbb reg,param +} + + +/*------------------------------------------------- + emit_sbb_m32_p32 - sbb operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_sbb_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 || param->value != 0) + emit_sbb_m32_imm(dst, MEMPARAMS, param->value); // sbb [dest],param + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_sbb_m32_r32(dst, MEMPARAMS, reg); // sbb [dest],reg + } +} + + +/*------------------------------------------------- + emit_cmp_r32_p32 - cmp operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_cmp_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + emit_cmp_r32_imm(dst, reg, param->value); // cmp reg,param + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_cmp_r32_m32(dst, reg, MABS(param->value)); // cmp reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_cmp_r32_r32(dst, reg, param->value); // cmp reg,param +} + + +/*------------------------------------------------- + emit_cmp_r32_p32hi - cmp operation to a 32-bit + register from the upper 32 bits of a 64-bit + parameter +-------------------------------------------------*/ + +static void emit_cmp_r32_p32hi(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + emit_cmp_r32_imm(dst, reg, param->value >> 32); // cmp reg,param >> 32 + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_cmp_r32_m32(dst, reg, MABS(param->value + 4)); // cmp reg,[param+4] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_cmp_r32_m32(dst, reg, MABS(drcbe->reghi[param->value])); // cmp reg,reghi[param] +} + + +/*------------------------------------------------- + emit_cmp_m32_p32 - cmp operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_cmp_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + emit_cmp_m32_imm(dst, MEMPARAMS, param->value); // cmp [dest],param + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_cmp_m32_r32(dst, MEMPARAMS, reg); // cmp [dest],reg + } +} + + +/*------------------------------------------------- + emit_cmp_m32_p32hi - cmp operation to a 32-bit + memory location from the upper half of a + 64-bit parameter +-------------------------------------------------*/ + +static void emit_cmp_m32_p32hi(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + emit_cmp_m32_imm(dst, MEMPARAMS, param->value >> 32); // cmp [dest],param >> 32 + else + { + if (param->type == DRCUML_PTYPE_MEMORY) + emit_mov_r32_m32(dst, REG_EAX, MABS(param->value + 4)); // mov eax,[param+4] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_r32_m32(dst, REG_EAX, MABS(drcbe->reghi[param->value])); // mov eax,reghi[param] + emit_cmp_m32_r32(dst, MEMPARAMS, REG_EAX); // cmp [dest],eax + } +} + + +/*------------------------------------------------- + emit_and_r32_p32 - and operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_and_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0xffffffff) + /* skip */; + else if (inst->condflags != 0 && (UINT32)param->value == 0) + emit_xor_r32_r32(dst, reg, reg); // xor reg,reg + else + emit_and_r32_imm(dst, reg, param->value); // and reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_and_r32_m32(dst, reg, MABS(param->value)); // and reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_and_r32_r32(dst, reg, param->value); // and reg,param +} + + +/*------------------------------------------------- + emit_and_m32_p32 - and operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_and_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0xffffffff) + /* skip */; + else if (inst->condflags != 0 && (UINT32)param->value == 0) + emit_mov_m32_imm(dst, MEMPARAMS, 0); // mov [dest],0 + else + emit_and_m32_imm(dst, MEMPARAMS, param->value); // and [dest],param + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_and_m32_r32(dst, MEMPARAMS, reg); // and [dest],reg + } +} + + +/*------------------------------------------------- + emit_test_r32_p32 - test operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_test_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + emit_test_r32_imm(dst, reg, param->value); // test reg,param + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_test_m32_r32(dst, MABS(param->value), reg); // test [param],reg + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_test_r32_r32(dst, reg, param->value); // test reg,param +} + + +/*------------------------------------------------- + emit_test_m32_p32 - test operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_test_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + emit_test_m32_imm(dst, MEMPARAMS, param->value); // test [dest],param + else if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_mov_r32_p32(drcbe, dst, REG_EAX, param); // mov reg,param + emit_test_m32_r32(dst, MEMPARAMS, REG_EAX); // test [dest],reg + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_test_m32_r32(dst, MEMPARAMS, param->value); // test [dest],param +} + + +/*------------------------------------------------- + emit_or_r32_p32 - or operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_or_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else if (inst->condflags != 0 && (UINT32)param->value == 0xffffffff) + emit_mov_r32_imm(dst, reg, -1); // mov reg,-1 + else + emit_or_r32_imm(dst, reg, param->value); // or reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_or_r32_m32(dst, reg, MABS(param->value)); // or reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_or_r32_r32(dst, reg, param->value); // or reg,param +} + + +/*------------------------------------------------- + emit_or_m32_p32 - or operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_or_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else if (inst->condflags != 0 && (UINT32)param->value == 0xffffffff) + emit_mov_m32_imm(dst, MEMPARAMS, -1); // mov [dest],-1 + else + emit_or_m32_imm(dst, MEMPARAMS, param->value); // or [dest],param + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_or_m32_r32(dst, MEMPARAMS, reg); // or [dest],reg + } +} + + +/*------------------------------------------------- + emit_xor_r32_p32 - xor operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_xor_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else if (inst->condflags != 0 && (UINT32)param->value == 0xffffffff) + emit_not_r32(dst, reg); // not reg + else + emit_xor_r32_imm(dst, reg, param->value); // xor reg,param + } + else if (param->type == DRCUML_PTYPE_MEMORY) + emit_xor_r32_m32(dst, reg, MABS(param->value)); // xor reg,[param] + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + emit_xor_r32_r32(dst, reg, param->value); // xor reg,param +} + + +/*------------------------------------------------- + emit_xor_m32_p32 - xor operation to a 32-bit + memory location from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_xor_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else if (inst->condflags != 0 && (UINT32)param->value == 0xffffffff) + emit_not_m32(dst, MEMPARAMS); // not [dest] + else + emit_xor_m32_imm(dst, MEMPARAMS, param->value); // xor [dest],param + } + else + { + int reg = param_select_register(REG_EAX, param, NULL); + emit_mov_r32_p32(drcbe, dst, reg, param); // mov reg,param + emit_xor_m32_r32(dst, MEMPARAMS, reg); // xor [dest],reg + } +} + + +/*------------------------------------------------- + emit_shl_r32_p32 - shl operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_shl_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_shl_r32_imm(dst, reg, param->value); // shl reg,param + } + else + { + emit_mov_r32_p32(drcbe, 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 +-------------------------------------------------*/ + +static void emit_shl_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_shl_m32_imm(dst, MEMPARAMS, param->value); // shl [dest],param + } + else + { + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_shl_m32_cl(dst, MEMPARAMS); // shl [dest],cl + } +} + + +/*------------------------------------------------- + emit_shr_r32_p32 - shr operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_shr_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_shr_r32_imm(dst, reg, param->value); // shr reg,param + } + else + { + emit_mov_r32_p32(drcbe, 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 +-------------------------------------------------*/ + +static void emit_shr_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_shr_m32_imm(dst, MEMPARAMS, param->value); // shr [dest],param + } + else + { + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_shr_m32_cl(dst, MEMPARAMS); // shr [dest],cl + } +} + + +/*------------------------------------------------- + emit_sar_r32_p32 - sar operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_sar_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_sar_r32_imm(dst, reg, param->value); // sar reg,param + } + else + { + emit_mov_r32_p32(drcbe, 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 +-------------------------------------------------*/ + +static void emit_sar_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_sar_m32_imm(dst, MEMPARAMS, param->value); // sar [dest],param + } + else + { + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_sar_m32_cl(dst, MEMPARAMS); // sar [dest],cl + } +} + + +/*------------------------------------------------- + emit_rol_r32_p32 - rol operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_rol_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_rol_r32_imm(dst, reg, param->value); // rol reg,param + } + else + { + emit_mov_r32_p32(drcbe, 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 +-------------------------------------------------*/ + +static void emit_rol_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_rol_m32_imm(dst, MEMPARAMS, param->value); // rol [dest],param + } + else + { + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_rol_m32_cl(dst, MEMPARAMS); // rol [dest],cl + } +} + + +/*------------------------------------------------- + emit_ror_r32_p32 - ror operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_ror_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_ror_r32_imm(dst, reg, param->value); // ror reg,param + } + else + { + emit_mov_r32_p32(drcbe, 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 +-------------------------------------------------*/ + +static void emit_ror_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_ror_m32_imm(dst, MEMPARAMS, param->value); // ror [dest],param + } + else + { + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_ror_m32_cl(dst, MEMPARAMS); // ror [dest],cl + } +} + + +/*------------------------------------------------- + emit_rcl_r32_p32 - rcl operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_rcl_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_rcl_r32_imm(dst, reg, param->value); // rcl reg,param + } + else + { + emit_mov_r32_p32(drcbe, 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 +-------------------------------------------------*/ + +static void emit_rcl_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_rcl_m32_imm(dst, MEMPARAMS, param->value); // rcl [dest],param + } + else + { + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_rcl_m32_cl(dst, MEMPARAMS); // rcl [dest],cl + } +} + + +/*------------------------------------------------- + emit_rcr_r32_p32 - rcr operation to a 32-bit + register from a 32-bit parameter +-------------------------------------------------*/ + +static void emit_rcr_r32_p32(drcbe_state *drcbe, x86code **dst, UINT8 reg, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_rcr_r32_imm(dst, reg, param->value); // rcr reg,param + } + else + { + emit_mov_r32_p32(drcbe, 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 +-------------------------------------------------*/ + +static void emit_rcr_m32_p32(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags != 0 && (UINT32)param->value == 0) + /* skip */; + else + emit_rcr_m32_imm(dst, MEMPARAMS, param->value); // rcr [dest],param + } + else + { + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_rcr_m32_cl(dst, MEMPARAMS); // rcr [dest],cl + } +} + + + +/*************************************************************************** + EMITTERS FOR 64-BIT OPERATIONS WITH PARAMETERS +***************************************************************************/ + +/*------------------------------------------------- + emit_mov_r64_p64 - move a 64-bit parameter + into a pair of registers +-------------------------------------------------*/ + +static void emit_mov_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if ((UINT32)param->value == 0) + emit_xor_r32_r32(dst, reglo, reglo); // xor reglo,reglo + else + emit_mov_r32_imm(dst, reglo, param->value); // mov reglo,param + if ((UINT32)(param->value >> 32) == 0) + emit_xor_r32_r32(dst, reghi, reghi); // xor reghi,reghi + else + emit_mov_r32_imm(dst, reghi, param->value >> 32); // mov reghi,param >> 32 + } + else if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_mov_r32_m32(dst, reglo, MABS(param->value)); // mov reglo,[param] + emit_mov_r32_m32(dst, reghi, MABS(param->value + 4)); // mov reghi,[param+4] + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + if (reglo != param->value) + emit_mov_r32_r32(dst, reglo, param->value); // mov reglo,param + emit_mov_r32_m32(dst, reghi, MABS(drcbe->reghi[param->value])); // mov reghi,reghi[param] + } +} + + +/*------------------------------------------------- + emit_mov_p64_r64 - move a pair of registers + into a 64-bit parameter +-------------------------------------------------*/ + +static void emit_mov_p64_r64(drcbe_state *drcbe, x86code **dst, const drcuml_parameter *param, UINT8 reglo, UINT8 reghi) +{ + assert(param->type != DRCUML_PTYPE_IMMEDIATE); + if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_mov_m32_r32(dst, MABS(param->value), reglo); // mov [param],reglo + emit_mov_m32_r32(dst, MABS(param->value + 4), reghi); // mov [param+4],reghi + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + if (reglo != param->value) + emit_mov_r32_r32(dst, param->value, reglo); // mov param,reglo + emit_mov_m32_r32(dst, MABS(drcbe->reghi[param->value]), reghi); // mov reghi[param],reghi + } +} + + +/*------------------------------------------------- + emit_push_p64 - push a 64-bit parameter onto + the stack +-------------------------------------------------*/ + +static void emit_push_p64(drcbe_state *drcbe, x86code **dst, const drcuml_parameter *src) +{ + if (src->type == DRCUML_PTYPE_IMMEDIATE) + { + emit_push_imm(dst, src->value >> 32); // push src >> 32 + emit_push_imm(dst, src->value); // push src + } + else if (src->type == DRCUML_PTYPE_MEMORY) + { + emit_push_m32(dst, MABS(src->value + 4)); // push [src+4] + emit_push_m32(dst, MABS(src->value)); // push [src] + } + else if (src->type == DRCUML_PTYPE_INT_REGISTER) + { + emit_push_m32(dst, MABS(drcbe->reghi[src->value])); // push reghi[src] + emit_push_r32(dst, src->value); // push src + } +} + + +/*------------------------------------------------- + emit_add_r64_p64 - add operation to a 64-bit + pair of registers from a 64-bit parameter +-------------------------------------------------*/ + +static void emit_add_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_add_r32_m32(dst, reglo, MABS(param->value)); // add reglo,[param] + if (saveflags) emit_pushf(dst); // pushf + emit_adc_r32_m32(dst, reghi, MABS(param->value + 4)); // adc reghi,[param] + } + else if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + emit_add_r32_imm(dst, reglo, param->value); // add reglo,param + if (saveflags) emit_pushf(dst); // pushf + emit_adc_r32_imm(dst, reghi, param->value >> 32); // adc reghi,param >> 32 + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + emit_add_r32_r32(dst, reglo, param->value); // add reglo,param + if (saveflags) emit_pushf(dst); // pushf + emit_adc_r32_m32(dst, reghi, MABS(drcbe->reghi[param->value])); // 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 +-------------------------------------------------*/ + +static void emit_add_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + emit_add_m32_imm(dst, MEMPARAMS, param->value); // add [dest],param + if (saveflags) emit_pushf(dst); // pushf + emit_adc_m32_imm(dst, MEMPARAMS + 4, param->value >> 32); // adc [dest+4],param >> 32 + } + else + { + int reglo = (param->type == DRCUML_PTYPE_INT_REGISTER) ? param->value : REG_EAX; + emit_mov_r64_p64(drcbe, dst, reglo, REG_EDX, param); // mov edx:reglo,param + emit_add_m32_r32(dst, MEMPARAMS, reglo); // add [dest],reglo + if (saveflags) emit_pushf(dst); // pushf + emit_adc_m32_r32(dst, MEMPARAMS + 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 +-------------------------------------------------*/ + +static void emit_adc_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_adc_r32_m32(dst, reglo, MABS(param->value)); // adc reglo,[param] + if (saveflags) emit_pushf(dst); // pushf + emit_adc_r32_m32(dst, reghi, MABS(param->value + 4)); // adc reghi,[param] + } + else if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + emit_adc_r32_imm(dst, reglo, param->value); // adc reglo,param + if (saveflags) emit_pushf(dst); // pushf + emit_adc_r32_imm(dst, reghi, param->value >> 32); // adc reghi,param >> 32 + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + emit_adc_r32_r32(dst, reglo, param->value); // adc reglo,param + if (saveflags) emit_pushf(dst); // pushf + emit_adc_r32_m32(dst, reghi, MABS(drcbe->reghi[param->value])); // 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 +-------------------------------------------------*/ + +static void emit_adc_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + emit_adc_m32_imm(dst, MEMPARAMS, param->value); // adc [dest],param + if (saveflags) emit_pushf(dst); // pushf + emit_adc_m32_imm(dst, MEMPARAMS + 4, param->value >> 32); // adc [dest+4],param >> 32 + } + else + { + int reglo = (param->type == DRCUML_PTYPE_INT_REGISTER) ? param->value : REG_EAX; + emit_mov_r64_p64(drcbe, dst, reglo, REG_EDX, param); // mov edx:reglo,param + emit_adc_m32_r32(dst, MEMPARAMS, reglo); // adc [dest],reglo + if (saveflags) emit_pushf(dst); // pushf + emit_adc_m32_r32(dst, MEMPARAMS + 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 +-------------------------------------------------*/ + +static void emit_sub_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_sub_r32_m32(dst, reglo, MABS(param->value)); // sub reglo,[param] + if (saveflags) emit_pushf(dst); // pushf + emit_sbb_r32_m32(dst, reghi, MABS(param->value + 4)); // sbb reghi,[param] + } + else if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + emit_sub_r32_imm(dst, reglo, param->value); // sub reglo,param + if (saveflags) emit_pushf(dst); // pushf + emit_sbb_r32_imm(dst, reghi, param->value >> 32); // sbb reghi,param >> 32 + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + emit_sub_r32_r32(dst, reglo, param->value); // sub reglo,param + if (saveflags) emit_pushf(dst); // pushf + emit_sbb_r32_m32(dst, reghi, MABS(drcbe->reghi[param->value])); // 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 +-------------------------------------------------*/ + +static void emit_sub_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + emit_sub_m32_imm(dst, MEMPARAMS, param->value); // sub [dest],param + if (saveflags) emit_pushf(dst); // pushf + emit_sbb_m32_imm(dst, MEMPARAMS + 4, param->value >> 32); // sbb [dest+4],param >> 32 + } + else + { + int reglo = (param->type == DRCUML_PTYPE_INT_REGISTER) ? param->value : REG_EAX; + emit_mov_r64_p64(drcbe, dst, reglo, REG_EDX, param); // mov edx:reglo,param + emit_sub_m32_r32(dst, MEMPARAMS, reglo); // sub [dest],reglo + if (saveflags) emit_pushf(dst); // pushf + emit_sbb_m32_r32(dst, MEMPARAMS + 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 +-------------------------------------------------*/ + +static void emit_sbb_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_sbb_r32_m32(dst, reglo, MABS(param->value)); // sbb reglo,[param] + if (saveflags) emit_pushf(dst); // pushf + emit_sbb_r32_m32(dst, reghi, MABS(param->value + 4)); // sbb reghi,[param] + } + else if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + emit_sbb_r32_imm(dst, reglo, param->value); // sbb reglo,param + if (saveflags) emit_pushf(dst); // pushf + emit_sbb_r32_imm(dst, reghi, param->value >> 32); // sbb reghi,param >> 32 + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + emit_sbb_r32_r32(dst, reglo, param->value); // sbb reglo,param + if (saveflags) emit_pushf(dst); // pushf + emit_sbb_r32_m32(dst, reghi, MABS(drcbe->reghi[param->value])); // 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 +-------------------------------------------------*/ + +static void emit_sbb_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + emit_sbb_m32_imm(dst, MEMPARAMS, param->value); // sbb [dest],param + if (saveflags) emit_pushf(dst); // pushf + emit_sbb_m32_imm(dst, MEMPARAMS + 4, param->value >> 32); // sbb [dest+4],param >> 32 + } + else + { + int reglo = (param->type == DRCUML_PTYPE_INT_REGISTER) ? param->value : REG_EAX; + emit_mov_r64_p64(drcbe, dst, reglo, REG_EDX, param); // mov edx:reglo,param + emit_sbb_m32_r32(dst, MEMPARAMS, reglo); // sbb [dest],reglo + if (saveflags) emit_pushf(dst); // pushf + emit_sbb_m32_r32(dst, MEMPARAMS + 4, REG_EDX); // sbb [dest+4],edx + } + 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 +-------------------------------------------------*/ + +static void emit_and_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_and_r32_m32(dst, reglo, MABS(param->value)); // and reglo,[param] + if (saveflags) emit_pushf(dst); // pushf + emit_and_r32_m32(dst, reghi, MABS(param->value + 4)); // and reghi,[param] + } + else if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags == 0 && (UINT32)param->value == 0xffffffff) + /* skip */; + else if (inst->condflags == 0 && (UINT32)param->value == 0) + emit_xor_r32_r32(dst, reglo, reglo); // xor reglo,reglo + else + emit_and_r32_imm(dst, reglo, param->value); // and reglo,param + if (saveflags) emit_pushf(dst); // pushf + if (inst->condflags == 0 && (UINT32)(param->value >> 32) == 0xffffffff) + /* skip */; + else if (inst->condflags == 0 && (UINT32)(param->value >> 32) == 0) + emit_xor_r32_r32(dst, reghi, reghi); // xor reghi,reghi + else + emit_and_r32_imm(dst, reghi, param->value >> 32); // and reghi,param >> 32 + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + emit_and_r32_r32(dst, reglo, param->value); // and reglo,param + if (saveflags) emit_pushf(dst); // pushf + emit_and_r32_m32(dst, reghi, MABS(drcbe->reghi[param->value])); // 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 +-------------------------------------------------*/ + +static void emit_and_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags == 0 && (UINT32)param->value == 0xffffffff) + /* skip */; + else if (inst->condflags == 0 && (UINT32)param->value == 0) + emit_mov_m32_imm(dst, MEMPARAMS, 0); // mov [dest],0 + else + emit_and_m32_imm(dst, MEMPARAMS, param->value); // and [dest],param + if (saveflags) emit_pushf(dst); // pushf + if (inst->condflags == 0 && (UINT32)(param->value >> 32) == 0xffffffff) + /* skip */; + else if (inst->condflags == 0 && (UINT32)(param->value >> 32) == 0) + emit_mov_m32_imm(dst, MEMPARAMS + 4, 0); // mov [dest+4],0 + else + emit_and_m32_imm(dst, MEMPARAMS + 4, param->value >> 32); // and [dest+4],param >> 32 + } + else + { + int reglo = (param->type == DRCUML_PTYPE_INT_REGISTER) ? param->value : REG_EAX; + emit_mov_r64_p64(drcbe, dst, reglo, REG_EDX, param); // mov edx:reglo,param + emit_and_m32_r32(dst, MEMPARAMS, reglo); // and [dest],reglo + if (saveflags) emit_pushf(dst); // pushf + emit_and_m32_r32(dst, MEMPARAMS + 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 +-------------------------------------------------*/ + +static void emit_test_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_test_m32_r32(dst, MABS(param->value), reglo); // test [param],reglo + if (saveflags) emit_pushf(dst); // pushf + emit_test_m32_r32(dst, MABS(param->value + 4), reghi); // test [param],reghi + } + else if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + emit_test_r32_imm(dst, reglo, param->value); // test reglo,param + if (saveflags) emit_pushf(dst); // pushf + emit_test_r32_imm(dst, reghi, param->value >> 32); // test reghi,param >> 32 + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + emit_test_r32_r32(dst, reglo, param->value); // test reglo,param + if (saveflags) emit_pushf(dst); // pushf + emit_test_m32_r32(dst, MABS(drcbe->reghi[param->value]), 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 +-------------------------------------------------*/ + +static void emit_test_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + emit_test_m32_imm(dst, MEMPARAMS, param->value); // test [dest],param + if (saveflags) emit_pushf(dst); // pushf + emit_test_m32_imm(dst, MEMPARAMS + 4, param->value >> 32); // test [dest+4],param >> 32 + } + else + { + int reglo = (param->type == DRCUML_PTYPE_INT_REGISTER) ? param->value : REG_EAX; + emit_mov_r64_p64(drcbe, dst, reglo, REG_EDX, param); // mov edx:reglo,param + emit_test_m32_r32(dst, MEMPARAMS, reglo); // test [dest],reglo + if (saveflags) emit_pushf(dst); // pushf + emit_test_m32_r32(dst, MEMPARAMS + 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 +-------------------------------------------------*/ + +static void emit_or_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_or_r32_m32(dst, reglo, MABS(param->value)); // or reglo,[param] + if (saveflags) emit_pushf(dst); // pushf + emit_or_r32_m32(dst, reghi, MABS(param->value + 4)); // or reghi,[param] + } + else if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags == 0 && (UINT32)param->value == 0) + /* skip */; + else if (inst->condflags == 0 && (UINT32)param->value == 0xffffffff) + emit_mov_r32_imm(dst, reglo, -1); // mov reglo,-1 + else + emit_or_r32_imm(dst, reglo, param->value); // or reglo,param + if (saveflags) emit_pushf(dst); // pushf + if (inst->condflags == 0 && (UINT32)(param->value >> 32) == 0) + /* skip */; + else if (inst->condflags == 0 && (UINT32)(param->value >> 32) == 0xffffffff) + emit_mov_r32_imm(dst, reghi, -1); // mov reghi,-1 + else + emit_or_r32_imm(dst, reghi, param->value >> 32); // or reghi,param >> 32 + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + emit_or_r32_r32(dst, reglo, param->value); // or reglo,param + if (saveflags) emit_pushf(dst); // pushf + emit_or_r32_m32(dst, reghi, MABS(drcbe->reghi[param->value])); // 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 +-------------------------------------------------*/ + +static void emit_or_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags == 0 && (UINT32)param->value == 0) + /* skip */; + else if (inst->condflags == 0 && (UINT32)param->value == 0xffffffff) + emit_mov_m32_imm(dst, MEMPARAMS, -1); // mov [dest],-1 + else + emit_or_m32_imm(dst, MEMPARAMS, param->value); // or [dest],param + if (saveflags) emit_pushf(dst); // pushf + if (inst->condflags == 0 && (UINT32)(param->value >> 32) == 0) + /* skip */; + else if (inst->condflags == 0 && (UINT32)(param->value >> 32) == 0xffffffff) + emit_mov_m32_imm(dst, MEMPARAMS + 4, -1); // mov [dest+4],-1 + else + emit_or_m32_imm(dst, MEMPARAMS + 4, param->value >> 32); // or [dest+4],param >> 32 + } + else + { + int reglo = (param->type == DRCUML_PTYPE_INT_REGISTER) ? param->value : REG_EAX; + emit_mov_r64_p64(drcbe, dst, reglo, REG_EDX, param); // mov edx:reglo,param + emit_or_m32_r32(dst, MEMPARAMS, reglo); // or [dest],reglo + if (saveflags) emit_pushf(dst); // pushf + emit_or_m32_r32(dst, MEMPARAMS + 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 +-------------------------------------------------*/ + +static void emit_xor_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_MEMORY) + { + emit_xor_r32_m32(dst, reglo, MABS(param->value)); // xor reglo,[param] + if (saveflags) emit_pushf(dst); // pushf + emit_xor_r32_m32(dst, reghi, MABS(param->value + 4)); // xor reghi,[param] + } + else if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags == 0 && (UINT32)param->value == 0) + /* skip */; + else if (inst->condflags == 0 && (UINT32)param->value == 0xffffffff) + emit_not_r32(dst, reglo); // not reglo + else + emit_xor_r32_imm(dst, reglo, param->value); // xor reglo,param + if (saveflags) emit_pushf(dst); // pushf + if (inst->condflags == 0 && (UINT32)(param->value >> 32) == 0) + /* skip */; + else if (inst->condflags == 0 && (UINT32)(param->value >> 32) == 0xffffffff) + emit_not_r32(dst, reghi); // not reghi + else + emit_xor_r32_imm(dst, reghi, param->value >> 32); // xor reghi,param >> 32 + } + else if (param->type == DRCUML_PTYPE_INT_REGISTER) + { + emit_xor_r32_r32(dst, reglo, param->value); // xor reglo,param + if (saveflags) emit_pushf(dst); // pushf + emit_xor_r32_m32(dst, reghi, MABS(drcbe->reghi[param->value])); // 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 +-------------------------------------------------*/ + +static void emit_xor_m64_p64(drcbe_state *drcbe, x86code **dst, DECLARE_MEMPARAMS, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->condflags == 0 && (UINT32)param->value == 0) + /* skip */; + else if (inst->condflags == 0 && (UINT32)param->value == 0xffffffff) + emit_not_m32(dst, MEMPARAMS); // not [dest] + else + emit_xor_m32_imm(dst, MEMPARAMS, param->value); // xor [dest],param + if (saveflags) emit_pushf(dst); // pushf + if (inst->condflags == 0 && (UINT32)(param->value >> 32) == 0) + /* skip */; + else if (inst->condflags == 0 && (UINT32)(param->value >> 32) == 0xffffffff) + emit_not_m32(dst, MEMPARAMS + 4); // not [dest+4] + else + emit_xor_m32_imm(dst, MEMPARAMS + 4, param->value >> 32); // xor [dest+4],param >> 32 + } + else + { + int reglo = (param->type == DRCUML_PTYPE_INT_REGISTER) ? param->value : REG_EAX; + emit_mov_r64_p64(drcbe, dst, reglo, REG_EDX, param); // mov edx:reglo,param + emit_xor_m32_r32(dst, MEMPARAMS, reglo); // xor [dest],reglo + if (saveflags) emit_pushf(dst); // pushf + emit_xor_m32_r32(dst, MEMPARAMS + 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 +-------------------------------------------------*/ + +static void emit_shl_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = (inst->condflags != 0); + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + int count = param->value & 63; + if (inst->condflags != 0 && count == 0) + /* skip */; + else + { + while (count >= 32) + { + if (inst->condflags != 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->condflags != 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(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 + emit_jcc_short_link(dst, COND_Z, &skip1); // jz skip1 + if (inst->condflags != 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, 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 + 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 + } + 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 +-------------------------------------------------*/ + +static void emit_shr_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + int count = param->value & 63; + if (inst->condflags != 0 && count == 0) + /* skip */; + else + { + while (count >= 32) + { + if (inst->condflags != 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->condflags != 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(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 + emit_jcc_short_link(dst, COND_Z, &skip1); // jz skip1 + if (inst->condflags != 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, 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 + 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 + } + 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 +-------------------------------------------------*/ + +static void emit_sar_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + int count = param->value & 63; + if (inst->condflags != 0 && count == 0) + /* skip */; + else + { + while (count >= 32) + { + if (inst->condflags != 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->condflags != 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(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 + emit_jcc_short_link(dst, COND_Z, &skip1); // jz skip1 + if (inst->condflags != 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, 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 + 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 + } + 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 +-------------------------------------------------*/ + +static void emit_rol_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + int count = param->value & 63; + if (inst->condflags != 0 && count == 0) + /* skip */; + else + { + while (count >= 32) + { + if (inst->condflags != 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->condflags != 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; + emit_mov_m32_r32(dst, MBD(REG_ESP, -8), REG_EBX); // mov [esp-8],ebx + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 + emit_jcc_short_link(dst, COND_Z, &skip1); // jz skip1 + if (inst->condflags != 0) + { + emit_sub_r32_imm(dst, REG_ECX, 31); // sub ecx,31 + emit_mov_r32_r32(dst, REG_EBX, reglo); // mov ebx,reglo + emit_shld_r32_r32_imm(dst, reglo, reghi, 31); // shld reglo,reghi,31 + emit_shld_r32_r32_imm(dst, reghi, REG_EBX, 31); // shld reghi,ebx,31 + emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 + emit_jcc_short_link(dst, COND_Z, &skip2); // jz skip2 + emit_sub_r32_imm(dst, REG_ECX, 31); // sub ecx,31 + emit_mov_r32_r32(dst, REG_EBX, reglo); // mov ebx,reglo + emit_shld_r32_r32_imm(dst, reglo, reghi, 31); // shld reglo,reghi,31 + emit_shld_r32_r32_imm(dst, reghi, REG_EBX, 31); // shld reghi,ebx,31 + resolve_link(dst, &skip2); // skip2: + } + else + emit_xchg_r32_r32(dst, reghi, reglo); // xchg reghi,reglo + resolve_link(dst, &skip1); // skip1: + emit_mov_r32_r32(dst, REG_EBX, 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, REG_EBX); // shld reghi,ebx,cl + emit_mov_r32_m32(dst, MBD(REG_ESP, saveflags ? -4 : -8), REG_EBX); // 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 +-------------------------------------------------*/ + +static void emit_ror_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + if (param->type == DRCUML_PTYPE_IMMEDIATE) + { + int count = param->value & 63; + if (inst->condflags != 0 && count == 0) + /* skip */; + else + { + while (count >= 32) + { + if (inst->condflags != 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->condflags != 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; + emit_mov_m32_r32(dst, MBD(REG_ESP, -8), REG_EBX); // mov [esp-8],ebx + emit_mov_r32_p32(drcbe, dst, REG_ECX, param); // mov ecx,param + emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 + emit_jcc_short_link(dst, COND_Z, &skip1); // jz skip1 + if (inst->condflags != 0) + { + emit_sub_r32_imm(dst, REG_ECX, 31); // sub ecx,31 + emit_mov_r32_r32(dst, REG_EBX, reglo); // mov ebx,reglo + emit_shrd_r32_r32_imm(dst, reglo, reghi, 31); // shrd reglo,reghi,31 + emit_shrd_r32_r32_imm(dst, reghi, REG_EBX, 31); // shrd reghi,ebx,31 + emit_test_r32_imm(dst, REG_ECX, 0x20); // test ecx,0x20 + emit_jcc_short_link(dst, COND_Z, &skip2); // jz skip2 + emit_sub_r32_imm(dst, REG_ECX, 31); // sub ecx,31 + emit_mov_r32_r32(dst, REG_EBX, reglo); // mov ebx,reglo + emit_shrd_r32_r32_imm(dst, reglo, reghi, 31); // shrd reglo,reghi,31 + emit_shrd_r32_r32_imm(dst, reghi, REG_EBX, 31); // shrd reghi,ebx,31 + resolve_link(dst, &skip2); // skip2: + } + else + emit_xchg_r32_r32(dst, reghi, reglo); // xchg reghi,reglo + resolve_link(dst, &skip1); // skip1: + emit_mov_r32_r32(dst, REG_EBX, 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, REG_EBX); // shrd reghi,ebx,cl + emit_mov_r32_m32(dst, MBD(REG_ESP, saveflags ? -4 : -8), REG_EBX); // 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 +-------------------------------------------------*/ + +static void emit_rcl_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = ((inst->condflags & DRCUML_FLAG_Z) != 0); + emit_link skipall, skiploop; + x86code *loop; + + emit_mov_r32_p32(drcbe, 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 + 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 + 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 + 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 +-------------------------------------------------*/ + +static void emit_rcr_r64_p64(drcbe_state *drcbe, x86code **dst, UINT8 reglo, UINT8 reghi, const drcuml_parameter *param, const drcuml_instruction *inst) +{ + int saveflags = (inst->condflags != 0); + emit_link skipall, skiploop; + x86code *loop; + + emit_mov_r32_p32(drcbe, 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 + 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 + 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 + 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 +-------------------------------------------------*/ + +static void emit_fld_p(x86code **dst, int size, const drcuml_parameter *param) +{ + assert(param->type == DRCUML_PTYPE_MEMORY); + assert(size == 4 || size == 8); + if (size == 4) + emit_fld_m32(dst, MABS(param->value)); + else if (size == 8) + emit_fld_m64(dst, MABS(param->value)); +} + + +/*------------------------------------------------- + emit_fstp_p - store a floating point parameter + from the stack and pop it +-------------------------------------------------*/ + +static void emit_fstp_p(x86code **dst, int size, const drcuml_parameter *param) +{ + assert(param->type == DRCUML_PTYPE_MEMORY); + assert(size == 4 || size == 8); + if (size == 4) + emit_fstp_m32(dst, MABS(param->value)); + else if (size == 8) + emit_fstp_m64(dst, MABS(param->value)); +} + + + +/*************************************************************************** + HELPERS TO CONVERT OTHER OPERATIONS TO MOVES +***************************************************************************/ + +/*------------------------------------------------- + convert_to_mov_imm - convert an instruction + into a mov immediate +-------------------------------------------------*/ + +static x86code *convert_to_mov_imm(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst, const drcuml_parameter *dstp, UINT64 imm) +{ + drcuml_instruction temp = *inst; + temp.numparams = 2; + temp.param[0] = *dstp; + temp.param[1].type = DRCUML_PTYPE_IMMEDIATE; + temp.param[1].value = imm; + return op_mov(drcbe, dst, &temp); +} + + +/*------------------------------------------------- + convert_to_mov_src1 - convert an instruction + into a mov src1 +-------------------------------------------------*/ + +static x86code *convert_to_mov_src1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst, const drcuml_parameter *dstp, const drcuml_parameter *srcp) +{ + drcuml_instruction temp = *inst; + temp.numparams = 2; + temp.param[0] = *dstp; + temp.param[1] = *srcp; + return op_mov(drcbe, dst, &temp); +} + + + +/*************************************************************************** + OUT-OF-BAND CODE FIXUP CALLBACKS +***************************************************************************/ + +/*------------------------------------------------- + fixup_label - callback to fixup forward- + referenced labels +-------------------------------------------------*/ + +static void fixup_label(void *parameter, drccodeptr labelcodeptr) +{ + drccodeptr src = 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 +-------------------------------------------------*/ + +static void fixup_exception(drccodeptr *codeptr, void *param1, void *param2, void *param3) +{ + drcuml_parameter handp, exp; + drcbe_state *drcbe = param1; + drccodeptr src = param2; + const drcuml_instruction *inst = param3; + drccodeptr dst = *codeptr; + drccodeptr *targetptr; + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &handp, PTYPE_M, &exp, PTYPE_MRI); + + /* look up the handle target */ + targetptr = drcuml_handle_codeptr_addr((drcuml_codehandle *)(FPTR)handp.value); + + /* first fixup the jump to get us here */ + ((UINT32 *)src)[-1] = dst - src; + + /* then store the exception parameter */ + emit_mov_m32_p32(drcbe, &dst, MABS(&drcbe->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 +-------------------------------------------------*/ + +#if LOG_HASHJMPS +static void debug_log_hashjmp(int mode, offs_t pc) +{ + printf("mode=%d PC=%08X\n", mode, pc); +} +#endif + + + +/*************************************************************************** + COMPILE-TIME OPCODES +***************************************************************************/ + +/*------------------------------------------------- + op_handle - process a HANDLE opcode +-------------------------------------------------*/ + +static x86code *op_handle(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + assert(inst->numparams == 1); + assert(inst->param[0].type == DRCUML_PTYPE_MEMORY); + + /* register the current pointer for the handle */ + drcuml_handle_set_codeptr((drcuml_codehandle *)(FPTR)inst->param[0].value, dst); + return dst; +} + + +/*------------------------------------------------- + op_hash - process a HASH opcode +-------------------------------------------------*/ + +static x86code *op_hash(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + assert(inst->numparams == 2); + assert(inst->param[0].type == DRCUML_PTYPE_IMMEDIATE); + assert(inst->param[1].type == DRCUML_PTYPE_IMMEDIATE); + + /* register the current pointer for the mode/PC */ + drchash_set_codeptr(drcbe->hash, inst->param[0].value, inst->param[1].value, dst); + return dst; +} + + +/*------------------------------------------------- + op_label - process a LABEL opcode +-------------------------------------------------*/ + +static x86code *op_label(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + assert(inst->numparams == 1); + assert(inst->param[0].type == DRCUML_PTYPE_IMMEDIATE); + + /* register the current pointer for the label */ + drclabel_set_codeptr(drcbe->labels, inst->param[0].value, dst); + return dst; +} + + +/*------------------------------------------------- + op_comment - process a COMMENT opcode +-------------------------------------------------*/ + +static x86code *op_comment(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + assert(inst->numparams == 1); + assert(inst->param[0].type == DRCUML_PTYPE_MEMORY); + + /* do nothing */ + return dst; +} + + +/*------------------------------------------------- + op_mapvar - process a MAPVAR opcode +-------------------------------------------------*/ + +static x86code *op_mapvar(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + assert(inst->numparams == 2); + assert(inst->param[0].type == DRCUML_PTYPE_MAPVAR); + assert(inst->param[1].type == DRCUML_PTYPE_IMMEDIATE); + + /* set the value of the specified mapvar */ + drcmap_set_value(drcbe->map, dst, inst->param[0].value, inst->param[1].value); + return dst; +} + + + +/*************************************************************************** + CONTROL FLOW OPCODES +***************************************************************************/ + +/*------------------------------------------------- + op_debug - process a DEBUG opcode +-------------------------------------------------*/ + +static x86code *op_debug(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ +#ifdef ENABLE_DEBUGGER + if (Machine->debug_mode) + { + drcuml_parameter pcp; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_1(drcbe, inst, &pcp, PTYPE_MRI); + + /* push the parameter */ + emit_push_p32(drcbe, &dst, &pcp); // push pcp + emit_call(&dst, (x86code *)mame_debug_hook); // call mame_debug_hook + emit_add_r32_imm(&dst, REG_ESP, 4); // add esp,4 + } +#endif + + return dst; +} + + +/*------------------------------------------------- + op_exit - process an EXIT opcode +-------------------------------------------------*/ + +static x86code *op_exit(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter retp; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + + /* normalize parameters */ + param_normalize_1(drcbe, inst, &retp, PTYPE_MRI); + + /* load the parameter into EAX */ + emit_mov_r32_p32(drcbe, &dst, REG_EAX, &retp); // mov eax,retp + if (inst->condflags == DRCUML_COND_ALWAYS) + emit_jmp(&dst, drcbe->exit); // jmp exit + else + emit_jcc(&dst, X86_CONDITION(inst->condflags), drcbe->exit); // jcc exit + + return dst; +} + + +/*------------------------------------------------- + op_hashjmp - process a HASHJMP opcode +-------------------------------------------------*/ + +static x86code *op_hashjmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter modep, pcp, exp; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &modep, PTYPE_MRI, &pcp, PTYPE_MRI, &exp, PTYPE_M); + +#if LOG_HASHJMPS + emit_push_p32(drcbe, &dst, &pcp); + emit_push_p32(drcbe, &dst, &modep); + emit_call(&dst, (x86code *)debug_log_hashjmp); + emit_add_r32_imm(&dst, REG_ESP, 8); +#endif + + /* 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(&drcbe->hashstacksave)); // mov esp,[hashstacksave] + + /* fixed mode cases */ + if (modep.type == DRCUML_PTYPE_IMMEDIATE) + { + /* a straight immediate jump is direct, though we need the PC in EAX in case of failure */ + if (pcp.type == DRCUML_PTYPE_IMMEDIATE) + { + UINT32 l1val = (pcp.value >> drcbe->hash->l1shift) & drcbe->hash->l1mask; + UINT32 l2val = (pcp.value >> drcbe->hash->l2shift) & drcbe->hash->l2mask; + emit_call_m32(&dst, MABS(&drcbe->hash->base[modep.value][l1val][l2val])); // call hash[modep][l1val][l2val] + } + + /* a fixed mode but variable PC */ + else + { + emit_mov_r32_p32(drcbe, &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, drcbe->hash->l1shift); // shr edx,l1shift + emit_and_r32_imm(&dst, REG_EAX, drcbe->hash->l2mask << drcbe->hash->l2shift);// and eax,l2mask << l2shift + emit_mov_r32_m32(&dst, REG_EDX, MISD(REG_EDX, 4, &drcbe->hash->base[modep.value][0])); + // mov edx,hash[modep+edx*4] + emit_call_m32(&dst, MBISD(REG_EDX, REG_EAX, 4 >> drcbe->hash->l2shift, 0)); // call [edx+eax*shift] + } + } + else + { + /* variable mode */ + int modereg = param_select_register(REG_ECX, &modep, NULL); + emit_mov_r32_p32(drcbe, &dst, modereg, &modep); // mov modereg,modep + emit_mov_r32_m32(&dst, REG_ECX, MISD(modereg, 4, &drcbe->hash->base[0])); // mov ecx,hash[modereg*4] + + /* fixed PC */ + if (pcp.type == DRCUML_PTYPE_IMMEDIATE) + { + UINT32 l1val = (pcp.value >> drcbe->hash->l1shift) & drcbe->hash->l1mask; + UINT32 l2val = (pcp.value >> drcbe->hash->l2shift) & drcbe->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(drcbe, &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, drcbe->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, drcbe->hash->l2mask << drcbe->hash->l2shift);// and eax,l2mask << l2shift + emit_call_m32(&dst, MBISD(REG_EDX, REG_EAX, 4 >> drcbe->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(drcbe, &dst, MABS(&drcbe->state.exp.l), &pcp); // mov [exp],param + emit_sub_r32_imm(&dst, REG_ESP, 4); // sub esp,4 + emit_call_m32(&dst, MABS(exp.value)); // call [exp] + + return dst; +} + + +/*------------------------------------------------- + op_jmp - process a JMP opcode +-------------------------------------------------*/ + +static x86code *op_jmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter labelp; + x86code *jmptarget; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + + /* normalize parameters */ + param_normalize_1(drcbe, inst, &labelp, PTYPE_I); + + /* look up the jump target and jump there */ + jmptarget = (x86code *)drclabel_get_codeptr(drcbe->labels, labelp.value, fixup_label, dst); + if (inst->condflags == DRCUML_COND_ALWAYS) + emit_jmp(&dst, jmptarget); // jmp target + else + emit_jcc(&dst, X86_CONDITION(inst->condflags), jmptarget); // jcc target + + return dst; +} + + +/*------------------------------------------------- + op_exh - process an EXH opcode +-------------------------------------------------*/ + +static x86code *op_exh(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter handp, exp; + drccodeptr *targetptr; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &handp, PTYPE_M, &exp, PTYPE_MRI); + + /* look up the handle target */ + targetptr = drcuml_handle_codeptr_addr((drcuml_codehandle *)(FPTR)handp.value); + + /* perform the exception processing inline if unconditional */ + if (inst->condflags == DRCUML_COND_ALWAYS) + { + emit_mov_m32_p32(drcbe, &dst, MABS(&drcbe->state.exp.l), &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->condflags), 0); // jcc exception + drccache_request_oob_codegen(drcbe->cache, fixup_exception, drcbe, dst, (void *)inst); + } + return dst; +} + + +/*------------------------------------------------- + op_callh - process a CALLH opcode +-------------------------------------------------*/ + +static x86code *op_callh(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter handp; + drccodeptr *targetptr; + emit_link skip = { 0 }; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + + /* normalize parameters */ + param_normalize_1(drcbe, inst, &handp, PTYPE_M); + + /* look up the handle target */ + targetptr = drcuml_handle_codeptr_addr((drcuml_codehandle *)(FPTR)handp.value); + + /* skip if conditional */ + if (inst->condflags != DRCUML_COND_ALWAYS) + emit_jcc_short_link(&dst, X86_NOT_CONDITION(inst->condflags), &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->condflags != DRCUML_COND_ALWAYS) + resolve_link(&dst, &skip); // skip: + return dst; +} + + +/*------------------------------------------------- + op_ret - process a RET opcode +-------------------------------------------------*/ + +static x86code *op_ret(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + emit_link skip = { 0 }; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + assert(inst->numparams == 0); + + /* skip if conditional */ + if (inst->condflags != DRCUML_COND_ALWAYS) + emit_jcc_short_link(&dst, X86_NOT_CONDITION(inst->condflags), &skip); // jcc skip + + /* return */ + emit_ret(&dst); // ret + + /* resolve the conditional link */ + if (inst->condflags != DRCUML_COND_ALWAYS) + resolve_link(&dst, &skip); // skip: + return dst; +} + + +/*------------------------------------------------- + op_callc - process a CALLC opcode +-------------------------------------------------*/ + +static x86code *op_callc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter funcp, paramp; + emit_link skip = { 0 }; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &funcp, PTYPE_M, ¶mp, PTYPE_M); + + /* skip if conditional */ + if (inst->condflags != DRCUML_COND_ALWAYS) + emit_jcc_short_link(&dst, X86_NOT_CONDITION(inst->condflags), &skip); // jcc skip + + /* perform the call */ + emit_push_imm(&dst, paramp.value); // push paramp + emit_call(&dst, (x86code *)(FPTR)funcp.value); // call funcp + emit_add_r32_imm(&dst, REG_ESP, 4); // add esp,4 + + /* resolve the conditional link */ + if (inst->condflags != DRCUML_COND_ALWAYS) + resolve_link(&dst, &skip); // skip: + return dst; +} + + +/*------------------------------------------------- + op_recover - process a RECOVER opcode +-------------------------------------------------*/ + +static x86code *op_recover(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, mvparam; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &mvparam, PTYPE_I); + + /* call the recovery code */ + emit_mov_r32_m32(&dst, REG_EAX, MABS(&drcbe->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_push_imm(&dst, inst->param[1].value); // push param1 + emit_push_r32(&dst, REG_EAX); // push eax + emit_push_imm(&dst, (FPTR)drcbe->map); // push drcbe->map + emit_call(&dst, (x86code *)drcmap_get_value); // call drcmap_get_value + emit_add_r32_imm(&dst, REG_ESP, 12); // add esp,12 + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + + return dst; +} + + + +/*************************************************************************** + INTERNAL REGISTER OPCODES +***************************************************************************/ + +/*------------------------------------------------- + op_setfmod - process a SETFMOD opcode +-------------------------------------------------*/ + +static x86code *op_setfmod(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter srcp; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_1(drcbe, inst, &srcp, PTYPE_MRI); + + /* immediate case */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + { + srcp.value &= 3; + emit_mov_m8_imm(&dst, MABS(&drcbe->state.fmod), srcp.value); // mov [fmod],srcp + emit_fldcw_m16(&dst, MABS(&fp_control[srcp.value])); // fldcw fp_control[srcp] + } + + /* register/memory case */ + else + { + emit_mov_r32_p32(drcbe, &dst, REG_EAX, &srcp); // mov eax,srcp + emit_and_r32_imm(&dst, REG_EAX, 3); // and eax,3 + emit_mov_m8_r8(&dst, MABS(&drcbe->state.fmod), REG_AL); // mov [fmod],al + emit_fldcw_m16(&dst, MISD(REG_EAX, 2, &fp_control[0])); // fldcw fp_control[eax] + } + + return dst; +} + + +/*------------------------------------------------- + op_getfmod - process a GETFMOD opcode +-------------------------------------------------*/ + +static x86code *op_getfmod(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_1(drcbe, inst, &dstp, PTYPE_MR); + + /* fetch the current mode and store to the destination */ + if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movzx_r32_m8(&dst, dstp.value, MABS(&drcbe->state.fmod)); // movzx reg,[fmod] + else + { + emit_movzx_r32_m8(&dst, REG_EAX, MABS(&drcbe->state.fmod)); // movzx eax,[fmod] + emit_mov_m32_r32(&dst, MABS(dstp.value), REG_EAX); // mov [dstp],eax + } + + return dst; +} + + +/*------------------------------------------------- + op_getexp - process a GETEXP opcode +-------------------------------------------------*/ + +static x86code *op_getexp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_1(drcbe, inst, &dstp, PTYPE_MR); + + /* fetch the exception parameter and store to the destination */ + if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_r32_m32(&dst, dstp.value, MABS(&drcbe->state.exp.l)); // mov reg,[exp] + else + { + emit_mov_r32_m32(&dst, REG_EAX, MABS(&drcbe->state.exp.l)); // mov eax,[exp] + emit_mov_m32_r32(&dst, MABS(dstp.value), REG_EAX); // mov [dstp],eax + } + + return dst; +} + + + +/*************************************************************************** + INTEGER OPERATIONS +***************************************************************************/ + +/*------------------------------------------------- + op_load1u - process a LOAD1U opcode +-------------------------------------------------*/ + +static x86code *op_load1u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_movzx_r32_m8(&dst, dstreg, MABS(basep.value + 1*indp.value)); // movzx dstreg,[basep + 1*indp] + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_movzx_r32_m8(&dst, dstreg, MISD(indreg, 1, basep.value)); // movzx dstreg,[basep + 1*indp] + } + + /* store low 32 bits */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + /* 64-bit form stores upper 32 bits */ + if (inst->size == 8) + { + /* general case */ + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_mov_m32_imm(&dst, MABS(dstp.value + 4), 0); // mov [dstp+4],0 + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_imm(&dst, MABS(drcbe->reghi[dstp.value]), 0); // mov [reghi],0 + } + return dst; +} + + +/*------------------------------------------------- + op_load1s - process a LOAD1S opcode +-------------------------------------------------*/ + +static x86code *op_load1s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = (inst->size == 8) ? REG_EAX : param_select_register(REG_EAX, &dstp, NULL); + + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_movsx_r32_m8(&dst, dstreg, MABS(basep.value + 1*indp.value)); // movsx eax,[basep + 1*indp] + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_movsx_r32_m8(&dst, dstreg, MISD(indreg, 1, basep.value)); // movsx eax,[basep + 1*indp] + } + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* 64-bit form */ + else + { + /* general case */ + emit_cdq(&dst); // cdq + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax + } + return dst; +} + + +/*------------------------------------------------- + op_load2u - process a LOAD2U opcode +-------------------------------------------------*/ + +static x86code *op_load2u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_movzx_r32_m16(&dst, dstreg, MABS(basep.value + 2*indp.value)); // movzx dstreg,[basep + 2*indp] + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_movzx_r32_m16(&dst, dstreg, MISD(indreg, 2, basep.value)); // movzx dstreg,[basep + 2*indp] + } + + /* store low 32 bits */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + /* 64-bit form stores upper 32 bits */ + if (inst->size == 8) + { + /* general case */ + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_mov_m32_imm(&dst, MABS(dstp.value + 4), 0); // mov [dstp+4],0 + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_imm(&dst, MABS(drcbe->reghi[dstp.value]), 0); // mov [reghi],0 + } + return dst; +} + + +/*------------------------------------------------- + op_load2s - process a LOAD2S opcode +-------------------------------------------------*/ + +static x86code *op_load2s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = (inst->size == 8) ? REG_EAX : param_select_register(REG_EAX, &dstp, NULL); + + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_movsx_r32_m16(&dst, dstreg, MABS(basep.value + 2*indp.value)); // movsx eax,[basep + 2*indp] + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_movsx_r32_m16(&dst, dstreg, MISD(indreg, 2, basep.value)); // movsx eax,[basep + 2*indp] + } + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* 64-bit form */ + else + { + /* general case */ + emit_cdq(&dst); // cdq + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax + } + return dst; +} + + +/*------------------------------------------------- + op_load4u - process a LOAD4U opcode +-------------------------------------------------*/ + +static x86code *op_load4u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_r32_m32(&dst, dstreg, MABS(basep.value + 4*indp.value)); // mov dstreg,[basep + 4*indp] + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_mov_r32_m32(&dst, dstreg, MISD(indreg, 4, basep.value)); // mov dstreg,[basep + 4*indp] + } + + /* store low 32 bits */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + /* 64-bit form stores upper 32 bits */ + if (inst->size == 8) + { + /* general case */ + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_mov_m32_imm(&dst, MABS(dstp.value + 4), 0); // mov [dstp+4],0 + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_imm(&dst, MABS(drcbe->reghi[dstp.value]), 0); // mov [reghi],0 + } + return dst; +} + + +/*------------------------------------------------- + op_load4s - process a LOAD4S opcode +-------------------------------------------------*/ + +static x86code *op_load4s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_r32_m32(&dst, REG_EAX, MABS(basep.value + 4*indp.value)); // mov eax,[basep + 4*indp] + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_mov_r32_m32(&dst, REG_EAX, MISD(indreg, 4, basep.value)); // movsx eax,[basep + 4*indp] + } + + /* general case */ + emit_cdq(&dst); // cdq + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax + + return dst; +} + + +/*------------------------------------------------- + op_load8u - process a LOAD8U opcode +-------------------------------------------------*/ + +static x86code *op_load8u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + { + emit_mov_r32_m32(&dst, REG_EDX, MABS(basep.value + 8*indp.value + 4)); // mov edx,[basep + 8*indp + 4] + emit_mov_r32_m32(&dst, dstreg, MABS(basep.value + 8*indp.value)); // mov dstreg,[basep + 8*indp] + } + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + emit_mov_r32_m32(&dst, REG_EDX, MISD(indreg, 8, basep.value + 4)); // mov edx,[basep + 8*indp + 4] + emit_mov_r32_m32(&dst, dstreg, MISD(indreg, 8, basep.value)); // mov dstreg,[basep + 8*indp] + } + + /* general case */ + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,edx:dstreg + + return dst; +} + + +/*------------------------------------------------- + op_store1 - process a STORE1 opcode +-------------------------------------------------*/ + +static x86code *op_store1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter srcp, basep, indp; + int srcreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &basep, PTYPE_M, &indp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* pick a source register for the general case */ + srcreg = param_select_register(REG_EAX, &srcp, NULL); + if (srcreg & 4) + srcreg = REG_EAX; + + /* degenerate case: constant index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + { + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m8_imm(&dst, MABS(basep.value + 1*indp.value), srcp.value); // mov [basep + 1*indp],srcp + else + { + emit_mov_r32_p32(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_mov_m8_r8(&dst, MABS(basep.value + 1*indp.value), srcreg); // mov [basep + 1*indp],srcreg + } + } + + /* normal case: variable index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); // mov indreg,indp + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m8_imm(&dst, MISD(indreg, 1, basep.value), srcp.value); // mov [basep + 1*ecx],srcp + else + { + emit_mov_r32_p32(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_mov_m8_r8(&dst, MISD(indreg, 1, basep.value), srcreg); // mov [basep + 1*ecx],srcreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_store2 - process a STORE2 opcode +-------------------------------------------------*/ + +static x86code *op_store2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter srcp, basep, indp; + int srcreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &basep, PTYPE_M, &indp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* pick a source register for the general case */ + srcreg = param_select_register(REG_EAX, &srcp, NULL); + + /* degenerate case: constant index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + { + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m16_imm(&dst, MABS(basep.value + 2*indp.value), srcp.value); // mov [basep + 2*indp],srcp + else + { + emit_mov_r32_p32(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_mov_m16_r16(&dst, MABS(basep.value + 2*indp.value), srcreg); // mov [basep + 2*indp],srcreg + } + } + + /* normal case: variable index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); // mov indreg,indp + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m16_imm(&dst, MISD(indreg, 2, basep.value), srcp.value); // mov [basep + 2*ecx],srcp + else + { + emit_mov_r32_p32(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_mov_m16_r16(&dst, MISD(indreg, 2, basep.value), srcreg); // mov [basep + 2*ecx],srcreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_store4 - process a STORE4 opcode +-------------------------------------------------*/ + +static x86code *op_store4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter srcp, basep, indp; + int srcreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &basep, PTYPE_M, &indp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* pick a source register for the general case */ + srcreg = param_select_register(REG_EAX, &srcp, NULL); + + /* degenerate case: constant index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + { + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m32_imm(&dst, MABS(basep.value + 4*indp.value), srcp.value); // mov [basep + 4*indp],srcp + else + { + emit_mov_r32_p32(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_mov_m32_r32(&dst, MABS(basep.value + indp.value), srcreg); // mov [basep + 4*indp],srcreg + } + } + + /* normal case: variable index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); // mov indreg,indp + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m32_imm(&dst, MISD(indreg, 4, basep.value), srcp.value); // mov [basep + 4*ecx],srcp + else + { + emit_mov_r32_p32(drcbe, &dst, srcreg, &srcp); // mov srcreg,srcp + emit_mov_m32_r32(&dst, MISD(indreg, 4, basep.value), srcreg); // mov [basep + 4*ecx],srcreg + } + } + return dst; +} + + +/*------------------------------------------------- + op_store8 - process a STORE8 opcode +-------------------------------------------------*/ + +static x86code *op_store8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter srcp, basep, indp; + int srcreg; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &basep, PTYPE_M, &indp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* pick a source register for the general case */ + srcreg = param_select_register(REG_EAX, &srcp, NULL); + + /* degenerate case: constant index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + { + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + { + emit_mov_m32_imm(&dst, MABS(basep.value + 8*indp.value), srcp.value); // mov [basep + 8*indp],srcp + emit_mov_m32_imm(&dst, MABS(basep.value + 4 + 8*indp.value), srcp.value >> 32); + // mov [basep + 4 + 8*indp],srcp >> 32 + } + else + { + emit_mov_r64_p64(drcbe, &dst, srcreg, REG_EDX, &srcp); // mov edx:srcreg,srcp + emit_mov_m32_r32(&dst, MABS(basep.value + 8*indp.value), srcreg); // mov [basep + 8*indp],srcreg + emit_mov_m32_r32(&dst, MABS(basep.value + 4 + 8*indp.value), REG_EDX); // mov [basep + 8*indp + 4],edx + } + } + + /* normal case: variable index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); // mov indreg,indp + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + { + emit_mov_m32_imm(&dst, MISD(indreg, 8, basep.value), srcp.value); // mov [basep + 8*ecx],srcp + emit_mov_m32_imm(&dst, MISD(indreg, 8, basep.value + 4), srcp.value >> 32); // mov [basep + 8*ecx + 4],srcp >> 32 + } + else + { + emit_mov_r64_p64(drcbe, &dst, srcreg, REG_EDX, &srcp); // mov edx:srcreg,srcp + emit_mov_m32_r32(&dst, MISD(indreg, 8, basep.value), srcreg); // mov [basep + 8*ecx],srcreg + emit_mov_m32_r32(&dst, MISD(indreg, 8, basep.value + 4), REG_EDX); // mov [basep + 4 + 8*ecx],edx + } + } + return dst; +} + + +/*------------------------------------------------- + op_read1u - process a READ1U opcode +-------------------------------------------------*/ + +static x86code *op_read1u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* set up a call to the read byte handler */ + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->read_byte);// call read_byte + emit_add_r32_imm(&dst, REG_ESP, 4); // add esp,4 + emit_movzx_r32_r8(&dst, dstreg, REG_AL); // movzx dstreg,al + + /* store low 32 bits */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + /* 64-bit form stores upper 32 bits */ + if (inst->size == 8) + { + /* general case */ + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_mov_m32_imm(&dst, MABS(dstp.value + 4), 0); // mov [dstp+4],0 + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_imm(&dst, MABS(drcbe->reghi[dstp.value]), 0); // mov [reghi],0 + } + return dst; +} + + +/*------------------------------------------------- + op_read1s - process a READ1S opcode +-------------------------------------------------*/ + +static x86code *op_read1s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* set up a call to the read byte handler */ + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->read_byte);// call read_byte + emit_add_r32_imm(&dst, REG_ESP, 4); // add esp,4 + emit_movsx_r32_r8(&dst, dstreg, REG_AL); // movsx dstreg,al + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* 64-bit form */ + else + { + /* general case */ + emit_cdq(&dst); // cdq + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax + } + return dst; +} + + +/*------------------------------------------------- + op_read2u - process a READ2U opcode +-------------------------------------------------*/ + +static x86code *op_read2u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* set up a call to the read word handler */ + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->read_word);// call read_word + emit_add_r32_imm(&dst, REG_ESP, 4); // add esp,4 + emit_movzx_r32_r16(&dst, dstreg, REG_AX); // movzx dstreg,ax + + /* store low 32 bits */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + /* 64-bit form stores upper 32 bits */ + if (inst->size == 8) + { + /* general case */ + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_mov_m32_imm(&dst, MABS(dstp.value + 4), 0); // mov [dstp+4],0 + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_imm(&dst, MABS(drcbe->reghi[dstp.value]), 0); // mov [reghi],0 + } + return dst; +} + + +/*------------------------------------------------- + op_read2s - process a READ2S opcode +-------------------------------------------------*/ + +static x86code *op_read2s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* set up a call to the read word handler */ + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->read_word);// call read_word + emit_add_r32_imm(&dst, REG_ESP, 4); // add esp,4 + emit_movsx_r32_r16(&dst, dstreg, REG_AX); // movsx dstreg,ax + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* 64-bit form */ + else + { + /* general case */ + emit_cdq(&dst); // cdq + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax + } + return dst; +} + + +/*------------------------------------------------- + op_read2m - process a READ2M opcode +-------------------------------------------------*/ + +static x86code *op_read2m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp, maskp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &maskp, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* set up a call to the read dword masked handler */ + emit_push_p32(drcbe, &dst, &maskp); // push maskp + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->read_word_masked); + // call read_word_masked + emit_add_r32_imm(&dst, REG_ESP, 8); // add esp,8 + emit_movzx_r32_r16(&dst, dstreg, REG_AX); // movzx dstreg,ax + + /* store low 32 bits */ + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + + /* 64-bit form stores upper 32 bits */ + if (inst->size == 8) + { + /* general case */ + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_mov_m32_imm(&dst, MABS(dstp.value + 4), 0); // mov [dstp+4],0 + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_imm(&dst, MABS(drcbe->reghi[dstp.value]), 0); // mov [reghi],0 + } + return dst; +} + + +/*------------------------------------------------- + op_read4u - process a READ4U opcode +-------------------------------------------------*/ + +static x86code *op_read4u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* set up a call to the read dword handler */ + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->read_dword);// call read_dword + emit_add_r32_imm(&dst, REG_ESP, 4); // add esp,4 + + /* store low 32 bits */ + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + + /* 64-bit form stores upper 32 bits */ + if (inst->size == 8) + { + /* general case */ + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_mov_m32_imm(&dst, MABS(dstp.value + 4), 0); // mov [dstp+4],0 + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_imm(&dst, MABS(drcbe->reghi[dstp.value]), 0); // mov [reghi],0 + } + return dst; +} + + +/*------------------------------------------------- + op_read4s - process a READ4S opcode +-------------------------------------------------*/ + +static x86code *op_read4s(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* set up a call to the read dword handler */ + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->read_dword);// call read_dword + emit_add_r32_imm(&dst, REG_ESP, 4); // add esp,4 + + /* 64-bit form */ + emit_cdq(&dst); // cdq + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dst,edx:eax + + return dst; +} + + +/*------------------------------------------------- + op_read4m - process a READ4M opcode +-------------------------------------------------*/ + +static x86code *op_read4m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp, maskp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &maskp, PTYPE_MRI); + + /* set up a call to the read dword masked handler */ + emit_push_p32(drcbe, &dst, &maskp); // push maskp + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->read_dword_masked); + // call read_dword_masked + emit_add_r32_imm(&dst, REG_ESP, 8); // add esp,8 + + /* store low 32 bits */ + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + + /* 64-bit form stores upper 32 bits */ + if (inst->size == 8) + { + /* general case */ + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_mov_m32_imm(&dst, MABS(dstp.value + 4), 0); // mov [dstp+4],0 + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_imm(&dst, MABS(drcbe->reghi[dstp.value]), 0); // mov [reghi],0 + } + return dst; +} + + +/*------------------------------------------------- + op_read8u - process a READ8U opcode +-------------------------------------------------*/ + +static x86code *op_read8u(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* set up a call to the read qword handler */ + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->read_qword);// call read_qword + emit_add_r32_imm(&dst, REG_ESP, 4); // add esp,4 + + /* 64-bit form */ + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dst,edx:eax + + return dst; +} + + +/*------------------------------------------------- + op_read8m - process a READ8M opcode +-------------------------------------------------*/ + +static x86code *op_read8m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp, maskp; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &dstp, PTYPE_MR, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &maskp, PTYPE_MRI); + + /* set up a call to the read qword masked handler */ + emit_push_p64(drcbe, &dst, &maskp); // push maskp + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->read_qword_masked); + // call read_qword_masked + emit_add_r32_imm(&dst, REG_ESP, 12); // add esp,12 + + /* 64-bit form */ + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dst,edx:eax + + return dst; +} + + +/*------------------------------------------------- + op_write1 - process a WRITE1 opcode +-------------------------------------------------*/ + +static x86code *op_write1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* set up a call to the write byte handler */ + emit_push_p32(drcbe, &dst, &srcp); // push srcp + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->write_byte);// call write_byte + emit_add_r32_imm(&dst, REG_ESP, 8); // add esp,8 + + return dst; +} + + +/*------------------------------------------------- + op_write2 - process a WRITE2 opcode +-------------------------------------------------*/ + +static x86code *op_write2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* set up a call to the write word handler */ + emit_push_p32(drcbe, &dst, &srcp); // push srcp + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->write_word);// call write_word + emit_add_r32_imm(&dst, REG_ESP, 8); // add esp,8 + + return dst; +} + + +/*------------------------------------------------- + op_writ2m - process a WRIT2M opcode +-------------------------------------------------*/ + +static x86code *op_writ2m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, maskp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &maskp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* set up a call to the write word handler */ + emit_push_p32(drcbe, &dst, &maskp); // push maskp + emit_push_p32(drcbe, &dst, &srcp); // push srcp + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->write_word_masked); + // call write_word_masked + emit_add_r32_imm(&dst, REG_ESP, 12); // add esp,12 + return dst; +} + + +/*------------------------------------------------- + op_write4 - process a WRITE4 opcode +-------------------------------------------------*/ + +static x86code *op_write4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* set up a call to the write dword handler */ + emit_push_p32(drcbe, &dst, &srcp); // push srcp + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->write_dword);// call write_dword + emit_add_r32_imm(&dst, REG_ESP, 8); // add esp,8 + + return dst; +} + + +/*------------------------------------------------- + op_writ4m - process a WRIT4M opcode +-------------------------------------------------*/ + +static x86code *op_writ4m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, maskp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &maskp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* set up a call to the write word handler */ + emit_push_p32(drcbe, &dst, &maskp); // push maskp + emit_push_p32(drcbe, &dst, &srcp); // push srcp + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->write_dword_masked); + // call write_dword_masked + emit_add_r32_imm(&dst, REG_ESP, 12); // add esp,12 + return dst; +} + + +/*------------------------------------------------- + op_write8 - process a WRITE8 opcode +-------------------------------------------------*/ + +static x86code *op_write8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, srcp; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* set up a call to the write qword handler */ + emit_push_p64(drcbe, &dst, &srcp); // push srcp + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->write_qword);// call write_qword + + emit_add_r32_imm(&dst, REG_ESP, 12); // add esp,12 + return dst; +} + + +/*------------------------------------------------- + op_writ8m - process a WRIT8M opcode +-------------------------------------------------*/ + +static x86code *op_writ8m(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, maskp, srcp; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &maskp, PTYPE_MRI, &srcp, PTYPE_MRI); + + /* set up a call to the write word handler */ + emit_push_p64(drcbe, &dst, &maskp); // push maskp + emit_push_p64(drcbe, &dst, &srcp); // push srcp + emit_push_p32(drcbe, &dst, &addrp); // push addrp + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->write_qword_masked); + // call write_qword_masked + emit_add_r32_imm(&dst, REG_ESP, 20); // add esp,20 + return dst; +} + + +/*------------------------------------------------- + op_flags - process a FLAGS opcode +-------------------------------------------------*/ + +static x86code *op_flags(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, maskp, tablep; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &maskp, PTYPE_I, &tablep, PTYPE_M); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_ECX, &dstp, NULL); + + /* translate live flags into UML flags */ + emit_pushf(&dst); // pushf + emit_pop_r32(&dst, REG_EAX); // pop eax + emit_and_r32_imm(&dst, REG_EAX, 0x8c5); // and eax,0x8c5 + emit_movzx_r32_m8(&dst, REG_EAX, MBD(REG_EAX, flags_map)); // movzx eax,[flags_map] + + /* 32-bit form */ + if (inst->size == 4) + { + /* no masking */ + if (maskp.value == 0xffffffff) + { + emit_mov_r32_m32(&dst, dstreg, MISD(REG_EAX, 4, tablep.value)); // mov dstreg,[eax*4+table] + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &dstp); // mov dstreg,dstp + emit_mov_r32_m32(&dst, REG_EDX, MISD(REG_EAX, 4, tablep.value)); // mov edx,[eax*4+table] + emit_and_r32_imm(&dst, dstreg, ~maskp.value); // and dstreg,~mask + emit_and_r32_imm(&dst, REG_EDX, maskp.value); // and edx,mask + emit_or_r32_r32(&dst, dstreg, REG_EDX); // or dstreg,edx + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else + { + /* no masking */ + if (maskp.value == U64(0xffffffffffffffff)) + { + emit_mov_r32_m32(&dst, dstreg, MISD(REG_EAX, 8, tablep.value)); // mov dstreg,[eax*4+table] + emit_mov_r32_m32(&dst, REG_EDX, MISD(REG_EAX, 8, tablep.value + 4)); // mov edx,[eax*4+table+4] + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dst,edx:dstreg + } + + /* general case */ + else + { + if ((UINT32)maskp.value != 0) + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &dstp); // mov dstreg,dstp + emit_mov_r32_m32(&dst, REG_EDX, MISD(REG_EAX, 8, tablep.value)); // mov edx,[eax*8+table] + emit_and_r32_imm(&dst, dstreg, ~maskp.value); // and dstreg,~mask + emit_and_r32_imm(&dst, REG_EDX, maskp.value); // and edx,mask + emit_or_r32_r32(&dst, dstreg, REG_EDX); // or dstreg,edx + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg.lo + } + if ((UINT32)(maskp.value >> 32) != 0) + { + if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_r32_m32(&dst, REG_ECX, MABS(drcbe->reghi[dstp.value])); // mov ecx,dstp.hi + else + emit_mov_r32_m32(&dst, REG_ECX, MABS(dstp.value + 4)); // mov ecx,dstp.hi + emit_mov_r32_m32(&dst, REG_EDX, MISD(REG_EDX, 8, tablep.value+4)); // mov edx,[eax*8+table+4] + emit_and_r32_imm(&dst, REG_ECX, ~maskp.value); // and ecx,~mask + emit_and_r32_imm(&dst, REG_EDX, maskp.value); // and edx,mask + emit_or_r32_r32(&dst, REG_ECX, REG_EDX); // or ecx,edx + if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_r32(&dst, MABS(drcbe->reghi[dstp.value]), REG_ECX); // mov dstp.hi,ecx + else + emit_mov_m32_r32(&dst, MABS(dstp.value + 4), REG_ECX); // mov dstp.hi,ecx + } + } + } + return dst; +} + + +/*------------------------------------------------- + op_mov - process a MOV opcode +-------------------------------------------------*/ + +static x86code *op_mov(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + x86code *savedst = dst; + emit_link skip = { 0 }; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + + /* normalize parameters, but only if we got here directly */ + /* other opcodes call through here with pre-normalized parameters */ + if (inst->opcode == DRCUML_OP_MOV) + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MRI); + else + { + dstp = inst->param[0]; + srcp = inst->param[1]; + } + + /* degenerate case: dest and source are equal */ + if (dstp.type == srcp.type && dstp.value == srcp.value) + return dst; + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* always start with a jmp */ + if (inst->condflags != DRCUML_COND_ALWAYS) + emit_jcc_short_link(&dst, X86_NOT_CONDITION(inst->condflags), &skip); // jcc skip + + /* 32-bit form */ + if (inst->size == 4) + { + /* register to memory */ + if (dstp.type == DRCUML_PTYPE_MEMORY && srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_r32(&dst, MABS(dstp.value), srcp.value); // mov [dstp],srcp + + /* immediate to memory */ + else if (dstp.type == DRCUML_PTYPE_MEMORY && srcp.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_m32_imm(&dst, MABS(dstp.value), srcp.value); // mov [dstp],srcp + + /* conditional memory to register */ + else if (inst->condflags != 0 && dstp.type == DRCUML_PTYPE_INT_REGISTER && srcp.type == DRCUML_PTYPE_MEMORY) + { + dst = savedst; + skip.target = NULL; + emit_cmovcc_r32_m32(&dst, X86_CONDITION(inst->condflags), dstp.value, MABS(srcp.value)); + // cmovcc dstp,[srcp] + } + + /* conditional register to register */ + else if (inst->condflags != 0 && dstp.type == DRCUML_PTYPE_INT_REGISTER && srcp.type == DRCUML_PTYPE_INT_REGISTER) + { + dst = savedst; + skip.target = NULL; + emit_cmovcc_r32_r32(&dst, X86_CONDITION(inst->condflags), dstp.value, srcp.value); + // cmovcc dstp,srcp + } + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &srcp); // mov dstreg,srcp + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* register to memory */ + if (dstp.type == DRCUML_PTYPE_MEMORY && srcp.type == DRCUML_PTYPE_INT_REGISTER) + { + emit_mov_r32_m32(&dst, REG_EAX, MABS(drcbe->reghi[srcp.value])); // mov eax,reghi[srcp] + emit_mov_m32_r32(&dst, MABS(dstp.value), srcp.value); // mov [dstp],srcp + emit_mov_m32_r32(&dst, MABS(dstp.value + 4), REG_EAX); // mov [dstp+4],eax + } + + /* immediate to memory */ + else if (dstp.type == DRCUML_PTYPE_MEMORY && srcp.type == DRCUML_PTYPE_INT_REGISTER) + { + emit_mov_m32_imm(&dst, MABS(dstp.value), srcp.value); // mov [dstp],srcp + emit_mov_m32_imm(&dst, MABS(dstp.value + 4), srcp.value >> 32); // mov [dstp+4],srcp >> 32 + } + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, REG_EDX, &srcp); // mov edx:dstreg,srcp + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,edx:dstreg + } + } + + /* resolve the jump */ + if (skip.target != NULL) + resolve_link(&dst, &skip); + return dst; +} + + +/*------------------------------------------------- + op_zext1 - process a ZEXT1 opcode +-------------------------------------------------*/ + +static x86code *op_zext1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT8)srcp.value); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* convert 8-bit source registers */ + if (srcp.type == DRCUML_PTYPE_INT_REGISTER && (srcp.value & 4)) + { + emit_mov_r32_r32(&dst, REG_EAX, srcp.value); // mov eax,srcp + srcp.value = REG_EAX; + } + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_movzx_r32_m8(&dst, dstreg, MABS(srcp.value)); // movzx dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movzx_r32_r8(&dst, dstreg, srcp.value); // movzx dstreg,srcp + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_movzx_r32_m8(&dst, dstreg, MABS(srcp.value)); // movzx dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movzx_r32_r8(&dst, dstreg, srcp.value); // movzx dstreg,srcp + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_mov_m32_imm(&dst, MABS(dstp.value + 4), 0); // mov [dstp+4],0 + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_imm(&dst, MABS(drcbe->reghi[dstp.value]), 0); // mov reghi[dstp],0 + } + return dst; +} + + +/*------------------------------------------------- + op_zext2 - process a ZEXT2 opcode +-------------------------------------------------*/ + +static x86code *op_zext2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT16)srcp.value); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_movzx_r32_m16(&dst, dstreg, MABS(srcp.value)); // movzx dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movzx_r32_r16(&dst, dstreg, srcp.value); // movzx dstreg,srcp + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_movzx_r32_m16(&dst, dstreg, MABS(srcp.value)); // movzx dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movzx_r32_r16(&dst, dstreg, srcp.value); // movzx dstreg,srcp + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_mov_m32_imm(&dst, MABS(dstp.value + 4), 0); // mov [dstp+4],0 + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_imm(&dst, MABS(drcbe->reghi[dstp.value]), 0); // mov reghi[dstp],0 + } + return dst; +} + + +/*------------------------------------------------- + op_zext4 - process a ZEXT4 opcode +-------------------------------------------------*/ + +static x86code *op_zext4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT32)srcp.value); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* 64-bit form */ + if (inst->size == 8) + { + /* general case */ + emit_mov_r32_p32(drcbe, &dst, dstreg, &srcp); // mov dstreg,srcp + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_mov_m32_imm(&dst, MABS(dstp.value + 4), 0); // mov [dstp+4],0 + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_m32_imm(&dst, MABS(drcbe->reghi[dstp.value]), 0); // mov reghi[dstp],0 + } + return dst; +} + + +/*------------------------------------------------- + op_sext1 - process a SEXT1 opcode +-------------------------------------------------*/ + +static x86code *op_sext1(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT8)srcp.value); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* convert 8-bit source registers */ + if (srcp.type == DRCUML_PTYPE_INT_REGISTER && (srcp.value & 4)) + { + emit_mov_r32_r32(&dst, REG_EAX, srcp.value); // mov eax,srcp + srcp.value = REG_EAX; + } + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_movsx_r32_m8(&dst, dstreg, MABS(srcp.value)); // movsx dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movsx_r32_r8(&dst, dstreg, srcp.value); // movsx dstreg,srcp + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_movsx_r32_m8(&dst, REG_EAX, MABS(srcp.value)); // movsx eax,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movsx_r32_r8(&dst, REG_EAX, srcp.value); // movsx eax,srcp + emit_cdq(&dst); // cdq + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax + } + return dst; +} + + +/*------------------------------------------------- + op_sext2 - process a SEXT2 opcode +-------------------------------------------------*/ + +static x86code *op_sext2(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT16)srcp.value); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_movsx_r32_m16(&dst, dstreg, MABS(srcp.value)); // movsx dstreg,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movsx_r32_r16(&dst, dstreg, srcp.value); // movsx dstreg,srcp + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_movsx_r32_m16(&dst, REG_EAX, MABS(srcp.value)); // movsx eax,[srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + emit_movsx_r32_r16(&dst, REG_EAX, srcp.value); // movsx eax,srcp + emit_cdq(&dst); // cdq + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax + } + return dst; +} + + +/*------------------------------------------------- + op_sext4 - process a SEXT4 opcode +-------------------------------------------------*/ + +static x86code *op_sext4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT32)srcp.value); + + /* 64-bit form */ + if (inst->size == 8) + { + /* general case */ + emit_mov_r32_p32(drcbe, &dst, REG_EAX, &srcp); // mov eax,srcp + emit_cdq(&dst); // cdq + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax + } + return dst; +} + + +/*------------------------------------------------- + op_add - process a ADD opcode +-------------------------------------------------*/ + +static x86code *op_add(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3_commutative(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, src1p.value + src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (dstp.type == DRCUML_PTYPE_MEMORY && src1p.type == DRCUML_PTYPE_MEMORY && src1p.value == dstp.value) + emit_add_m32_p32(drcbe, &dst, MABS(dstp.value), &src2p, inst); // add [dstp],src2p + + /* reg = reg + imm */ + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER && src1p.type == DRCUML_PTYPE_INT_REGISTER && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + emit_lea_r32_m32(&dst, dstp.value, MBD(src1p.value, src2p.value)); // lea dstp,[src1p+src2p] + + /* reg = reg + reg */ + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER && src1p.type == DRCUML_PTYPE_INT_REGISTER && src2p.type == DRCUML_PTYPE_INT_REGISTER && inst->condflags == 0) + emit_lea_r32_m32(&dst, dstp.value, MBISD(src1p.value, src2p.value, 1, 0)); // lea dstp,[src1p+src2p] + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_add_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // add dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_add_m64_p64(drcbe, &dst, MABS(dstp.value), &src2p, inst); // add [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src1p); // mov dstreg:dstp,[src1p] + emit_add_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src2p, inst); // add dstreg:dstp,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax + } + } + return dst; +} + + +/*------------------------------------------------- + op_addc - process a ADDC opcode +-------------------------------------------------*/ + +static x86code *op_addc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3_commutative(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_adc_m32_p32(drcbe, &dst, MABS(dstp.value), &src2p, inst); // adc [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_adc_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // adc dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_adc_m64_p64(drcbe, &dst, MABS(dstp.value), &src2p, inst); // adc [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src1p); // mov dstreg:dstp,[src1p] + emit_adc_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src2p, inst); // adc dstreg:dstp,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax + } + } + return dst; +} + + +/*------------------------------------------------- + op_sub - process a SUB opcode +-------------------------------------------------*/ + +static x86code *op_sub(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, src1p.value - src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (dstp.type == DRCUML_PTYPE_MEMORY && src1p.type == DRCUML_PTYPE_MEMORY && src1p.value == dstp.value) + emit_sub_m32_p32(drcbe, &dst, MABS(dstp.value), &src2p, inst); // sub [dstp],src2p + + /* reg = reg - imm */ + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER && src1p.type == DRCUML_PTYPE_INT_REGISTER && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + emit_lea_r32_m32(&dst, dstp.value, MBD(src1p.value, -src2p.value)); // lea dstp,[src1p-src2p] + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_sub_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // sub dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_sub_m64_p64(drcbe, &dst, MABS(dstp.value), &src2p, inst); // sub [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src1p); // mov dstreg:dstp,[src1p] + emit_sub_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src2p, inst); // sub dstreg:dstp,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax + } + } + return dst; +} + + +/*------------------------------------------------- + op_subc - process a SUBC opcode +-------------------------------------------------*/ + +static x86code *op_subc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_sbb_m32_p32(drcbe, &dst, MABS(dstp.value), &src2p, inst); // sbb [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_sbb_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // sbb dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_sbb_m64_p64(drcbe, &dst, MABS(dstp.value), &src2p, inst); // sbb [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src1p); // mov dstreg:dstp,[src1p] + emit_sbb_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src2p, inst); // sbb dstreg:dstp,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax + } + } + return dst; +} + + +/*------------------------------------------------- + op_cmp - process a CMP opcode +-------------------------------------------------*/ + +static x86code *op_cmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter src1p, src2p; + emit_link skip; + int src1reg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && src1p.value == src2p.value) + { + emit_cmp_r32_r32(&dst, REG_EAX, REG_EAX); // cmp eax,eax + return dst; + } + + /* pick a target register for the general case */ + src1reg = param_select_register(REG_EAX, &src1p, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + /* memory versus anything */ + if (src1p.type == DRCUML_PTYPE_MEMORY) + emit_cmp_m32_p32(drcbe, &dst, MABS(src1p.value), &src2p, inst); // cmp [dstp],src2p + + /* general case */ + else + { + if (src1p.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_r32_imm(&dst, src1reg, src1p.value); // mov src1reg,imm + emit_cmp_r32_p32(drcbe, &dst, src1reg, &src2p, inst); // cmp src1reg,src2p + } + } + + /* 64-bit form */ + else + { + /* memory versus anything */ + if (src1p.type == DRCUML_PTYPE_MEMORY) + { + emit_cmp_m32_p32hi(drcbe, &dst, MABS(src1p.value + 4), &src2p, inst); // cmp [dstp],src2p.hi + emit_jcc_short_link(&dst, COND_NE, &skip); // jne skip + emit_cmp_m32_p32(drcbe, &dst, MABS(src1p.value), &src2p, inst); // cmp [dstp],src2p + resolve_link(&dst, &skip); // skip: + } + + /* general case */ + else + { + if (src1p.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_r32_imm(&dst, REG_EAX, src1p.value >> 32); // mov eax,imm >> 32 + else if (src1p.type == DRCUML_PTYPE_INT_REGISTER) + emit_mov_r32_m32(&dst, REG_EAX, MABS(drcbe->reghi[src1p.value])); // mov eax,reghi[src1p] + emit_cmp_r32_p32hi(drcbe, &dst, REG_EAX, &src2p, inst); // cmp eax,src2p.hi + emit_jcc_short_link(&dst, COND_NE, &skip); // jne skip + if (src1p.type == DRCUML_PTYPE_IMMEDIATE) + emit_mov_r32_imm(&dst, src1reg, src1p.value); // mov src1reg,imm + emit_cmp_r32_p32(drcbe, &dst, src1reg, &src2p, inst); // cmp src1reg,src2p + resolve_link(&dst, &skip); // skip: + } + } + return dst; +} + + +/*------------------------------------------------- + op_mulu - process a MULU opcode +-------------------------------------------------*/ + +static x86code *op_mulu(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, edstp, src1p, src2p; + int compute_hi; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_4_commutative(drcbe, inst, &dstp, PTYPE_MR, &edstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + compute_hi = (dstp.type != edstp.type || dstp.value != edstp.value); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + { + if (inst->size == 4) + { + if (!compute_hi) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT32)src1p.value * (UINT32)src2p.value); + else + { + UINT64 result = (UINT64)(UINT32)src1p.value * (UINT64)(UINT32)src2p.value; + dst = convert_to_mov_imm(drcbe, dst, inst, &dstp, result & 0xffffffff); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, result >> 32); + } + } + else if (inst->size == 8) + { + UINT64 reslo, reshi; + if (!compute_hi) + { + dmulu(&reslo, &reslo, src1p.value, src2p.value); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, reslo); + } + else + { + dmulu(&reslo, &reshi, src1p.value, src2p.value); + dst = convert_to_mov_imm(drcbe, dst, inst, &dstp, reslo); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, reshi); + } + } + } + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + emit_mov_r32_p32(drcbe, &dst, REG_EAX, &src1p); // mov eax,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_mul_m32(&dst, MABS(src2p.value)); // mul [src2p] + else if (src2p.type == DRCUML_PTYPE_INT_REGISTER) + emit_mul_r32(&dst, src2p.value); // mul src2p + else if (src2p.type == DRCUML_PTYPE_IMMEDIATE) + { + emit_mov_r32_imm(&dst, REG_EDX, src2p.value); // mov edx,src2p + emit_mul_r32(&dst, REG_EDX); // mul edx + } + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + if (compute_hi) + emit_mov_p32_r32(drcbe, &dst, &edstp, REG_EDX); // mov edstp,edx + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + emit_push_p64(drcbe, &dst, &src2p); // push src2p + emit_push_p64(drcbe, &dst, &src1p); // push src1p + if (!compute_hi) + emit_push_imm(&dst, (FPTR)&drcbe->reslo); // push &reslo + else + emit_push_imm(&dst, (FPTR)&drcbe->reshi); // push &reshi + emit_push_imm(&dst, (FPTR)&drcbe->reslo); // push &reslo + emit_call(&dst, (x86code *)dmulu); // call dmulu + emit_add_r32_imm(&dst, REG_ESP, 24); // add esp,24 + emit_mov_r32_m32(&dst, REG_EAX, MABS((UINT32 *)&drcbe->reslo + 0)); // mov eax,reslo.lo + emit_mov_r32_m32(&dst, REG_EDX, MABS((UINT32 *)&drcbe->reslo + 1)); // mov edx,reslo.hi + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax + if (inst->condflags != 0) + emit_or_r32_r32(&dst, REG_EAX, REG_EDX); // or eax,edx + if (compute_hi) + { + emit_mov_r32_m32(&dst, REG_EAX, MABS((UINT32 *)&drcbe->reshi + 0)); // mov eax,reshi.lo + emit_mov_r32_m32(&dst, REG_EDX, MABS((UINT32 *)&drcbe->reshi + 1)); // mov edx,reshi.hi + emit_mov_p64_r64(drcbe, &dst, &edstp, REG_EAX, REG_EDX); // mov edstp,edx:eax + } + } + return dst; +} + + +/*------------------------------------------------- + op_muls - process a MULS opcode +-------------------------------------------------*/ + +static x86code *op_muls(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, edstp, src1p, src2p; + int compute_hi; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_4_commutative(drcbe, inst, &dstp, PTYPE_MR, &edstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + compute_hi = (dstp.type != edstp.type || dstp.value != edstp.value); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + { + if (inst->size == 4) + { + if (!compute_hi) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT32)src1p.value * (INT32)src2p.value); + else + { + UINT64 result = (INT64)(INT32)src1p.value * (INT64)(INT32)src2p.value; + dst = convert_to_mov_imm(drcbe, dst, inst, &dstp, result & 0xffffffff); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, result >> 32); + } + } + else if (inst->size == 8) + { + UINT64 reslo, reshi; + if (!compute_hi) + { + dmuls(&reslo, &reslo, src1p.value, src2p.value); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, reslo); + } + else + { + dmuls(&reslo, &reshi, src1p.value, src2p.value); + dst = convert_to_mov_imm(drcbe, dst, inst, &dstp, reslo); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, reshi); + } + } + } + + /* 32-bit form */ + if (inst->size == 4) + { + /* 32-bit destination with memory/immediate or register/immediate */ + if (!compute_hi && src1p.type != DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE) + { + if (src1p.type == DRCUML_PTYPE_MEMORY) + emit_imul_r32_m32_imm(&dst, REG_EAX, MABS(src1p.value), src2p.value); // imul eax,[src1p],src2p + else if (src1p.type == DRCUML_PTYPE_INT_REGISTER) + emit_imul_r32_r32_imm(&dst, REG_EAX, src1p.value, src2p.value); // imul eax,src1p,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + } + + /* 32-bit destination, general case */ + else if (!compute_hi) + { + emit_mov_r32_p32(drcbe, &dst, REG_EAX, &src1p); // mov eax,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_imul_r32_m32(&dst, REG_EAX, MABS(src2p.value)); // imul eax,[src2p] + else if (src2p.type == DRCUML_PTYPE_INT_REGISTER) + emit_imul_r32_r32(&dst, REG_EAX, src2p.value); // imul eax,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + } + + /* 64-bit destination, general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, REG_EAX, &src1p); // mov eax,src1p + if (src2p.type == DRCUML_PTYPE_MEMORY) + emit_imul_m32(&dst, MABS(src2p.value)); // imul [src2p] + else if (src2p.type == DRCUML_PTYPE_INT_REGISTER) + emit_imul_r32(&dst, src2p.value); // imul src2p + else if (src2p.type == DRCUML_PTYPE_IMMEDIATE) + { + emit_mov_r32_imm(&dst, REG_EDX, src2p.value); // mov edx,src2p + emit_imul_r32(&dst, REG_EDX); // imul edx + } + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + emit_mov_p32_r32(drcbe, &dst, &edstp, REG_EDX); // mov edstp,edx + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + emit_push_p64(drcbe, &dst, &src2p); // push src2p + emit_push_p64(drcbe, &dst, &src1p); // push src1p + if (!compute_hi) + emit_push_imm(&dst, (FPTR)&drcbe->reslo); // push &reslo + else + emit_push_imm(&dst, (FPTR)&drcbe->reshi); // push &reshi + emit_push_imm(&dst, (FPTR)&drcbe->reslo); // push &reslo + emit_call(&dst, (x86code *)dmuls); // call dmuls + emit_add_r32_imm(&dst, REG_ESP, 24); // add esp,24 + emit_mov_r32_m32(&dst, REG_EAX, MABS((UINT32 *)&drcbe->reslo + 0)); // mov eax,reslo.lo + emit_mov_r32_m32(&dst, REG_EDX, MABS((UINT32 *)&drcbe->reslo + 1)); // mov edx,reslo.hi + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax + if (inst->condflags != 0) + emit_or_r32_r32(&dst, REG_EAX, REG_EDX); // or eax,edx + if (compute_hi) + { + emit_mov_r32_m32(&dst, REG_EAX, MABS((UINT32 *)&drcbe->reshi + 0)); // mov eax,reshi.lo + emit_mov_r32_m32(&dst, REG_EDX, MABS((UINT32 *)&drcbe->reshi + 1)); // mov edx,reshi.hi + emit_mov_p64_r64(drcbe, &dst, &edstp, REG_EAX, REG_EDX); // mov edstp,edx:eax + } + } + return dst; +} + + +/*------------------------------------------------- + op_divu - process a DIVU opcode +-------------------------------------------------*/ + +static x86code *op_divu(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, edstp, src1p, src2p; + int compute_rem; + emit_link skip; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &dstp, PTYPE_MR, &edstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + compute_rem = (dstp.type != edstp.type || dstp.value != edstp.value); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + { + if (inst->size == 4) + { + if ((UINT32)src2p.value == 0) + return dst; + if (!compute_rem) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT32)src1p.value / (UINT32)src2p.value); + else + { + dst = convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT32)src1p.value / (UINT32)src2p.value); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT32)src1p.value % (UINT32)src2p.value); + } + } + else if (inst->size == 8) + { + UINT64 reslo, reshi; + if (src2p.value == 0) + return dst; + if (!compute_rem) + { + ddivu(&reslo, &reslo, src1p.value, src2p.value); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, reslo); + } + else + { + ddivu(&reslo, &reshi, src1p.value, src2p.value); + dst = convert_to_mov_imm(drcbe, dst, inst, &dstp, reslo); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, reshi); + } + } + } + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + emit_mov_r32_p32(drcbe, &dst, REG_ECX, &src2p); // mov ecx,src2p + emit_jecxz_link(&dst, &skip); // jecxz skip + emit_mov_r32_p32(drcbe, &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(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + if (compute_rem) + emit_mov_p32_r32(drcbe, &dst, &edstp, REG_EDX); // mov edstp,edx + resolve_link(&dst, &skip); // skip: + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + emit_push_p64(drcbe, &dst, &src2p); // push src2p + emit_push_p64(drcbe, &dst, &src1p); // push src1p + if (!compute_rem) + emit_push_imm(&dst, (FPTR)&drcbe->reslo); // push &reslo + else + emit_push_imm(&dst, (FPTR)&drcbe->reshi); // push &reshi + emit_push_imm(&dst, (FPTR)&drcbe->reslo); // push &reslo + emit_call(&dst, (x86code *)ddivu); // call ddivu + emit_add_r32_imm(&dst, REG_ESP, 24); // add esp,24 + emit_mov_r32_m32(&dst, REG_EAX, MABS((UINT32 *)&drcbe->reslo + 0)); // mov eax,reslo.lo + emit_mov_r32_m32(&dst, REG_EDX, MABS((UINT32 *)&drcbe->reslo + 1)); // mov edx,reslo.hi + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax + if (inst->condflags != 0) + emit_or_r32_r32(&dst, REG_EAX, REG_EDX); // or eax,edx + if (compute_rem) + { + emit_mov_r32_m32(&dst, REG_EAX, MABS((UINT32 *)&drcbe->reshi + 0)); // mov eax,reshi.lo + emit_mov_r32_m32(&dst, REG_EDX, MABS((UINT32 *)&drcbe->reshi + 1)); // mov edx,reshi.hi + emit_mov_p64_r64(drcbe, &dst, &edstp, REG_EAX, REG_EDX); // mov edstp,edx:eax + } + } + return dst; +} + + +/*------------------------------------------------- + op_divs - process a DIVS opcode +-------------------------------------------------*/ + +static x86code *op_divs(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, edstp, src1p, src2p; + int compute_rem; + emit_link skip; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_4(drcbe, inst, &dstp, PTYPE_MR, &edstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + compute_rem = (dstp.type != edstp.type || dstp.value != edstp.value); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + { + if (inst->size == 4) + { + if ((INT32)src2p.value == 0) + return dst; + if (!compute_rem) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT32)src1p.value / (INT32)src2p.value); + else + { + dst = convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT32)src1p.value / (INT32)src2p.value); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT32)src1p.value % (INT32)src2p.value); + } + } + else if (inst->size == 8) + { + UINT64 reslo, reshi; + if (src2p.value == 0) + return dst; + if (!compute_rem) + { + ddivs(&reslo, &reslo, src1p.value, src2p.value); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, reslo); + } + else + { + ddivs(&reslo, &reshi, src1p.value, src2p.value); + dst = convert_to_mov_imm(drcbe, dst, inst, &dstp, reslo); + return convert_to_mov_imm(drcbe, dst, inst, &dstp, reshi); + } + } + } + + /* 32-bit form */ + if (inst->size == 4) + { + /* general case */ + emit_mov_r32_p32(drcbe, &dst, REG_ECX, &src2p); // mov ecx,src2p + emit_jecxz_link(&dst, &skip); // jecxz skip + emit_mov_r32_p32(drcbe, &dst, REG_EAX, &src1p); // mov eax,src1p + emit_cdq(&dst); // cdq + emit_idiv_r32(&dst, REG_ECX); // idiv ecx + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + if (compute_rem) + emit_mov_p32_r32(drcbe, &dst, &edstp, REG_EDX); // mov edstp,edx + resolve_link(&dst, &skip); // skip: + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + emit_push_p64(drcbe, &dst, &src2p); // push src2p + emit_push_p64(drcbe, &dst, &src1p); // push src1p + if (!compute_rem) + emit_push_imm(&dst, (FPTR)&drcbe->reslo); // push &reslo + else + emit_push_imm(&dst, (FPTR)&drcbe->reshi); // push &reshi + emit_push_imm(&dst, (FPTR)&drcbe->reslo); // push &reslo + emit_call(&dst, (x86code *)ddivs); // call ddivs + emit_add_r32_imm(&dst, REG_ESP, 24); // add esp,24 + emit_mov_r32_m32(&dst, REG_EAX, MABS((UINT32 *)&drcbe->reslo + 0)); // mov eax,reslo.lo + emit_mov_r32_m32(&dst, REG_EDX, MABS((UINT32 *)&drcbe->reslo + 1)); // mov edx,reslo.hi + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax + if (inst->condflags != 0) + emit_or_r32_r32(&dst, REG_EAX, REG_EDX); // or eax,edx + if (compute_rem) + { + emit_mov_r32_m32(&dst, REG_EAX, MABS((UINT32 *)&drcbe->reshi + 0)); // mov eax,reshi.lo + emit_mov_r32_m32(&dst, REG_EDX, MABS((UINT32 *)&drcbe->reshi + 1)); // mov edx,reshi.hi + emit_mov_p64_r64(drcbe, &dst, &edstp, REG_EAX, REG_EDX); // mov edstp,edx:eax + } + } + return dst; +} + + +/*------------------------------------------------- + op_and - process a AND opcode +-------------------------------------------------*/ + +static x86code *op_and(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3_commutative(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, src1p.value & src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && (src2p.value & size_to_mask[inst->size]) == size_to_mask[inst->size] && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_and_m32_p32(drcbe, &dst, MABS(dstp.value), &src2p, inst); // and [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_and_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // and dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_and_m64_p64(drcbe, &dst, MABS(dstp.value), &src2p, inst); // and [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src1p); // mov dstreg:dstp,[src1p] + emit_and_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src2p, inst); // and dstreg:dstp,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax + } + } + return dst; +} + + +/*------------------------------------------------- + op_test - process a TEST opcode +-------------------------------------------------*/ + +static x86code *op_test(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter src1p, src2p; + int src1reg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_2_commutative(drcbe, inst, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* pick a target register for the general case */ + src1reg = param_select_register(REG_EAX, &src1p, NULL); + + /* 32-bit form */ + if (inst->size == 4) + { + /* src1p in memory */ + if (src1p.type == DRCUML_PTYPE_MEMORY) + emit_test_m32_p32(drcbe, &dst, MABS(src1p.value), &src2p, inst); // test [src1p],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, src1reg, &src1p); // mov src1reg,src1p + emit_test_r32_p32(drcbe, &dst, src1reg, &src2p, inst); // test src1reg,src2p + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* src1p in memory */ + if (src1p.type == DRCUML_PTYPE_MEMORY) + emit_test_m64_p64(drcbe, &dst, MABS(src1p.value), &src2p, inst); // test [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, src1reg, REG_EDX, &src1p); // mov src1reg:dstp,[src1p] + emit_test_r64_p64(drcbe, &dst, src1reg, REG_EDX, &src2p, inst); // test src1reg:dstp,src2p + } + } + return dst; +} + + +/*------------------------------------------------- + op_or - process a OR opcode +-------------------------------------------------*/ + +static x86code *op_or(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3_commutative(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, src1p.value | src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_or_m32_p32(drcbe, &dst, MABS(dstp.value), &src2p, inst); // or [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_or_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // or dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_or_m64_p64(drcbe, &dst, MABS(dstp.value), &src2p, inst); // or [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src1p); // mov dstreg:dstp,[src1p] + emit_or_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src2p, inst); // or dstreg:dstp,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax + } + } + return dst; +} + + +/*------------------------------------------------- + op_xor - process a XOR opcode +-------------------------------------------------*/ + +static x86code *op_xor(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3_commutative(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, src1p.value & src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_xor_m32_p32(drcbe, &dst, MABS(dstp.value), &src2p, inst); // xor [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_xor_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // xor dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_xor_m64_p64(drcbe, &dst, MABS(dstp.value), &src2p, inst); // xor [dstp],src2p + + /* general case */ + else + { + emit_mov_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src1p); // mov dstreg:dstp,[src1p] + emit_xor_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src2p, inst); // xor dstreg:dstp,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax + } + } + return dst; +} + + +/*------------------------------------------------- + op_shl - process a SHL opcode +-------------------------------------------------*/ + +static x86code *op_shl(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, src1p.value << src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_shl_m32_p32(drcbe, &dst, MABS(dstp.value), &src2p, inst); // shl [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_shl_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // shl dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + emit_mov_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src1p); // mov dstreg:dstp,[src1p] + emit_shl_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src2p, inst); // shl dstreg:dstp,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax + } + return dst; +} + + +/*------------------------------------------------- + op_shr - process a SHR opcode +-------------------------------------------------*/ + +static x86code *op_shr(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT64)src1p.value >> src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_shr_m32_p32(drcbe, &dst, MABS(dstp.value), &src2p, inst); // shr [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_shr_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // shr dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + emit_mov_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src1p); // mov dstreg:dstp,[src1p] + emit_shr_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src2p, inst); // shr dstreg:dstp,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax + } + return dst; +} + + +/*------------------------------------------------- + op_sar - process a SAR opcode +-------------------------------------------------*/ + +static x86code *op_sar(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + { + if (inst->size == 4) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT32)src1p.value >> src2p.value); + else if (inst->size == 8) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (INT64)src1p.value >> src2p.value); + } + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_sar_m32_p32(drcbe, &dst, MABS(dstp.value), &src2p, inst); // sar [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_sar_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // sar dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + emit_mov_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src1p); // mov dstreg:dstp,[src1p] + emit_sar_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src2p, inst); // sar dstreg:dstp,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax + } + return dst; +} + + +/*------------------------------------------------- + op_rol - process a rol opcode +-------------------------------------------------*/ + +static x86code *op_rol(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT64)src1p.value >> src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_rol_m32_p32(drcbe, &dst, MABS(dstp.value), &src2p, inst); // rol [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_rol_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // rol dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + emit_mov_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src1p); // mov dstreg:dstp,[src1p] + emit_rol_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src2p, inst); // rol dstreg:dstp,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax + } + return dst; +} + + +/*------------------------------------------------- + op_ror - process a ROR opcode +-------------------------------------------------*/ + +static x86code *op_ror(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT64)src1p.value >> src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_ror_m32_p32(drcbe, &dst, MABS(dstp.value), &src2p, inst); // ror [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_ror_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // ror dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + emit_mov_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src1p); // mov dstreg:dstp,[src1p] + emit_ror_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src2p, inst); // ror dstreg:dstp,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax + } + return dst; +} + + +/*------------------------------------------------- + op_rolc - process a ROLC opcode +-------------------------------------------------*/ + +static x86code *op_rolc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT64)src1p.value >> src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_rcl_m32_p32(drcbe, &dst, MABS(dstp.value), &src2p, inst); // rcl [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_rcl_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // rcl dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + emit_mov_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src1p); // mov dstreg:dstp,[src1p] + emit_rcl_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src2p, inst); // rcl dstreg:dstp,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax + } + return dst; +} + + +/*------------------------------------------------- + op_rorc - process a RORC opcode +-------------------------------------------------*/ + +static x86code *op_rorc(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + int dstreg; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MR, &src1p, PTYPE_MRI, &src2p, PTYPE_MRI); + + /* degenerate cases -- convert to a move */ + if (src1p.type == DRCUML_PTYPE_IMMEDIATE && src2p.type == DRCUML_PTYPE_IMMEDIATE && inst->condflags == 0) + return convert_to_mov_imm(drcbe, dst, inst, &dstp, (UINT64)src1p.value >> src2p.value); + if (src2p.type == DRCUML_PTYPE_IMMEDIATE && src2p.value == 0 && inst->condflags == 0) + return convert_to_mov_src1(drcbe, dst, inst, &dstp, &src1p); + + /* pick a target register for the general case */ + dstreg = param_select_register(REG_EAX, &dstp, &src2p); + + /* 32-bit form */ + if (inst->size == 4) + { + /* dstp == src1p in memory */ + if (src1p.type == dstp.type && src1p.value == dstp.value && dstp.type == DRCUML_PTYPE_MEMORY) + emit_rcr_m32_p32(drcbe, &dst, MABS(dstp.value), &src2p, inst); // rcr [dstp],src2p + + /* general case */ + else + { + emit_mov_r32_p32(drcbe, &dst, dstreg, &src1p); // mov dstreg,src1p + emit_rcr_r32_p32(drcbe, &dst, dstreg, &src2p, inst); // rcr dstreg,src2p + emit_mov_p32_r32(drcbe, &dst, &dstp, dstreg); // mov dstp,dstreg + } + } + + /* 64-bit form */ + else if (inst->size == 8) + { + /* general case */ + emit_mov_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src1p); // mov dstreg:dstp,[src1p] + emit_rcr_r64_p64(drcbe, &dst, dstreg, REG_EDX, &src2p, inst); // rcr dstreg:dstp,src2p + emit_mov_p64_r64(drcbe, &dst, &dstp, dstreg, REG_EDX); // mov dstp,dstreg:eax + } + return dst; +} + + + +/*************************************************************************** + FLOATING POINT OPERATIONS +***************************************************************************/ + +/*------------------------------------------------- + op_fload - process a FLOAD opcode +-------------------------------------------------*/ + +static x86code *op_fload(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, basep, indp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MF, &basep, PTYPE_M, &indp, PTYPE_MRI); + + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->size == 4) + emit_fld_m32(&dst, MABS(basep.value + 4*indp.value)); // fld [basep + 4*indp] + else if (inst->size == 8) + emit_fld_m64(&dst, MABS(basep.value + 8*indp.value)); // fld [basep + 8*indp] + } + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + if (inst->size == 4) + emit_fld_m32(&dst, MISD(indreg, 4, basep.value)); // fld [basep + 4*indp] + else if (inst->size == 8) + emit_fld_m64(&dst, MISD(indreg, 8, basep.value)); // fld [basep + 8*indp] + } + + /* general case */ + emit_fstp_p(&dst, inst->size, &dstp); // fstp dstp + + return dst; +} + + +/*------------------------------------------------- + op_fstore - process a FSTORE opcode +-------------------------------------------------*/ + +static x86code *op_fstore(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter srcp, basep, indp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &basep, PTYPE_M, &indp, PTYPE_MRI, &srcp, PTYPE_MF); + + /* general case */ + emit_fld_p(&dst, inst->size, &srcp); + + /* immediate index */ + if (indp.type == DRCUML_PTYPE_IMMEDIATE) + { + if (inst->size == 4) + emit_fstp_m32(&dst, MABS(basep.value + 4*indp.value)); // fstp [basep + 4*indp] + else if (inst->size == 8) + emit_fstp_m64(&dst, MABS(basep.value + 8*indp.value)); // fstp [basep + 8*indp] + } + + /* other index */ + else + { + int indreg = param_select_register(REG_ECX, &indp, NULL); + emit_mov_r32_p32(drcbe, &dst, indreg, &indp); + if (inst->size == 4) + emit_fstp_m32(&dst, MISD(indreg, 4, basep.value)); // fstp [basep + 4*indp] + else if (inst->size == 8) + emit_fstp_m64(&dst, MISD(indreg, 8, basep.value)); // fstp [basep + 8*indp] + } + return dst; +} + + +/*------------------------------------------------- + op_fread - process a FREAD opcode +-------------------------------------------------*/ + +static x86code *op_fread(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, spacep, addrp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MF, &spacep, PTYPE_I, &addrp, PTYPE_MRI); + + /* set up a call to the read dword/qword handler */ + emit_push_p32(drcbe, &dst, &addrp); // push addrp + if (inst->size == 4) + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->read_dword);// call read_dword + else if (inst->size == 8) + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->read_qword);// call read_qword + emit_add_r32_imm(&dst, REG_ESP, 4); // add esp,4 + + /* store result */ + if (inst->size == 4) + emit_mov_p32_r32(drcbe, &dst, &dstp, REG_EAX); // mov dstp,eax + else if (inst->size == 8) + emit_mov_p64_r64(drcbe, &dst, &dstp, REG_EAX, REG_EDX); // mov dstp,edx:eax + + return dst; +} + + +/*------------------------------------------------- + op_fwrite - process a FWRITE opcode +-------------------------------------------------*/ + +static x86code *op_fwrite(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter spacep, addrp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &spacep, PTYPE_I, &addrp, PTYPE_MRI, &srcp, PTYPE_MF); + + /* set up a call to the write dword/qword handler */ + if (inst->size == 4) + emit_push_p32(drcbe, &dst, &srcp); // push srcp + else if (inst->size == 8) + emit_push_p64(drcbe, &dst, &srcp); // push srcp + emit_push_p32(drcbe, &dst, &addrp); // push addrp + if (inst->size == 4) + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->write_dword);// call write_dword + else if (inst->size == 8) + emit_call(&dst, (x86code *)active_address_space[spacep.value].accessors->write_qword);// call write_qword + emit_add_r32_imm(&dst, REG_ESP, 4 + inst->size); // add esp,8 + + return dst; +} + + +/*------------------------------------------------- + op_fmov - process a FMOV opcode +-------------------------------------------------*/ + +static x86code *op_fmov(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + emit_link skip = { 0 }; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS || (inst->condflags >= COND_Z && inst->condflags < DRCUML_COND_MAX)); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, PTYPE_MF); + + /* always start with a jmp */ + if (inst->condflags != DRCUML_COND_ALWAYS) + emit_jcc_short_link(&dst, X86_NOT_CONDITION(inst->condflags), &skip); // jcc skip + + /* general case */ + emit_fld_p(&dst, inst->size, &srcp); // fld srcp + emit_fstp_p(&dst, inst->size, &dstp); // fstp dstp + + /* resolve the jump */ + if (skip.target != NULL) + resolve_link(&dst, &skip); // skip: + return dst; +} + + +/*------------------------------------------------- + op_ftoi4 - process a FTOI4 opcode +-------------------------------------------------*/ + +static x86code *op_ftoi4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* general case */ + emit_fld_p(&dst, inst->size, &srcp); // fld srcp + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_fistp_m32(&dst, MABS(dstp.value)); // fistp [dstp] + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + { + emit_fistp_m32(&dst, MABS(drcbe->reglo[dstp.value])); // fistp reglo[dstp] + emit_mov_r32_m32(&dst, dstp.value, MABS(drcbe->reglo[dstp.value])); // mov dstp,reglo[dstp] + } + return dst; +} + + +/*------------------------------------------------- + op_ftoi4t - process a FTOI4T opcode +-------------------------------------------------*/ + +static x86code *op_ftoi4t(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* general case */ + emit_fld_p(&dst, inst->size, &srcp); // fld srcp + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_fisttp_m32(&dst, MABS(dstp.value)); // fisttp [dstp] + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + { + emit_fisttp_m32(&dst, MABS(drcbe->reglo[dstp.value])); // fisttp reglo[dstp] + emit_mov_r32_m32(&dst, dstp.value, MABS(drcbe->reglo[dstp.value])); // mov dstp,reglo[dstp] + } + return dst; +} + + +/*------------------------------------------------- + op_ftoi4r - process a FTOI4R opcode +-------------------------------------------------*/ + +static x86code *op_ftoi4r(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* save and set the control word */ + emit_fstcw_m16(&dst, MABS(&drcbe->fmodesave)); // fstcw [fmodesave] + emit_fldcw_m16(&dst, MABS(&fp_control[DRCUML_FMOD_ROUND])); // fldcw fpcontrol[DRCUML_FMOD_ROUND] + + /* general case */ + emit_fld_p(&dst, inst->size, &srcp); // fld srcp + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_fistp_m32(&dst, MABS(dstp.value)); // fistp [dstp] + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + { + emit_fistp_m32(&dst, MABS(drcbe->reglo[dstp.value])); // fistp reglo[dstp] + emit_mov_r32_m32(&dst, dstp.value, MABS(drcbe->reglo[dstp.value])); // mov dstp,reglo[dstp] + } + + /* restore control word and proceed */ + emit_fldcw_m16(&dst, MABS(&drcbe->fmodesave)); // fldcw [fmodesave] + return dst; +} + + +/*------------------------------------------------- + op_ftoi4f - process a FTOI4F opcode +-------------------------------------------------*/ + +static x86code *op_ftoi4f(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* save and set the control word */ + emit_fstcw_m16(&dst, MABS(&drcbe->fmodesave)); // fstcw [fmodesave] + emit_fldcw_m16(&dst, MABS(&fp_control[DRCUML_FMOD_FLOOR])); // fldcw fpcontrol[DRCUML_FMOD_FLOOR] + + /* general case */ + emit_fld_p(&dst, inst->size, &srcp); // fld srcp + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_fistp_m32(&dst, MABS(dstp.value)); // fistp [dstp] + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + { + emit_fistp_m32(&dst, MABS(drcbe->reglo[dstp.value])); // fistp reglo[dstp] + emit_mov_r32_m32(&dst, dstp.value, MABS(drcbe->reglo[dstp.value])); // mov dstp,reglo[dstp] + } + + /* restore control word and proceed */ + emit_fldcw_m16(&dst, MABS(&drcbe->fmodesave)); // fldcw [fmodesave] + return dst; +} + + +/*------------------------------------------------- + op_ftoi4c - process a FTOI4C opcode +-------------------------------------------------*/ + +static x86code *op_ftoi4c(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* save and set the control word */ + emit_fstcw_m16(&dst, MABS(&drcbe->fmodesave)); // fstcw [fmodesave] + emit_fldcw_m16(&dst, MABS(&fp_control[DRCUML_FMOD_CEIL])); // fldcw fpcontrol[DRCUML_FMOD_CEIL] + + /* general case */ + emit_fld_p(&dst, inst->size, &srcp); // fld srcp + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_fistp_m32(&dst, MABS(dstp.value)); // fistp [dstp] + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + { + emit_fistp_m32(&dst, MABS(drcbe->reglo[dstp.value])); // fistp reglo[dstp] + emit_mov_r32_m32(&dst, dstp.value, MABS(drcbe->reglo[dstp.value])); // mov dstp,reglo[dstp] + } + + /* restore control word and proceed */ + emit_fldcw_m16(&dst, MABS(&drcbe->fmodesave)); // fldcw [fmodesave] + return dst; +} + + +/*------------------------------------------------- + op_ftoi8 - process a FTOI8 opcode +-------------------------------------------------*/ + +static x86code *op_ftoi8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* general case */ + emit_fld_p(&dst, inst->size, &srcp); // fld srcp + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_fistp_m64(&dst, MABS(dstp.value)); // fistp [dstp] + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + { + emit_fistp_m64(&dst, MABS(drcbe->reglo[dstp.value])); // fistp reglo[dstp] + emit_mov_r32_m32(&dst, dstp.value, MABS(drcbe->reglo[dstp.value])); // mov dstp,reglo[dstp] + } + return dst; +} + + +/*------------------------------------------------- + op_ftoi8t - process a FTOI8T opcode +-------------------------------------------------*/ + +static x86code *op_ftoi8t(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* general case */ + emit_fld_p(&dst, inst->size, &srcp); // fld srcp + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_fisttp_m64(&dst, MABS(dstp.value)); // fisttp [dstp] + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + { + emit_fisttp_m64(&dst, MABS(drcbe->reglo[dstp.value])); // fisttp reglo[dstp] + emit_mov_r32_m32(&dst, dstp.value, MABS(drcbe->reglo[dstp.value])); // mov dstp,reglo[dstp] + } + return dst; +} + + +/*------------------------------------------------- + op_ftoi8r - process a FTOI8R opcode +-------------------------------------------------*/ + +static x86code *op_ftoi8r(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* save and set the control word */ + emit_fstcw_m16(&dst, MABS(&drcbe->fmodesave)); // fstcw [fmodesave] + emit_fldcw_m16(&dst, MABS(&fp_control[DRCUML_FMOD_ROUND])); // fldcw fpcontrol[DRCUML_FMOD_ROUND] + + /* general case */ + emit_fld_p(&dst, inst->size, &srcp); // fld srcp + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_fistp_m64(&dst, MABS(dstp.value)); // fistp [dstp] + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + { + emit_fistp_m64(&dst, MABS(drcbe->reglo[dstp.value])); // fistp reglo[dstp] + emit_mov_r32_m32(&dst, dstp.value, MABS(drcbe->reglo[dstp.value])); // mov dstp,reglo[dstp] + } + + /* restore control word and proceed */ + emit_fldcw_m16(&dst, MABS(&drcbe->fmodesave)); // fldcw [fmodesave] + return dst; +} + + +/*------------------------------------------------- + op_ftoi8f - process a FTOI8F opcode +-------------------------------------------------*/ + +static x86code *op_ftoi8f(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* save and set the control word */ + emit_fstcw_m16(&dst, MABS(&drcbe->fmodesave)); // fstcw [fmodesave] + emit_fldcw_m16(&dst, MABS(&fp_control[DRCUML_FMOD_FLOOR])); // fldcw fpcontrol[DRCUML_FMOD_FLOOR] + + /* general case */ + emit_fld_p(&dst, inst->size, &srcp); // fld srcp + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_fistp_m64(&dst, MABS(dstp.value)); // fistp [dstp] + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + { + emit_fistp_m64(&dst, MABS(drcbe->reglo[dstp.value])); // fistp reglo[dstp] + emit_mov_r32_m32(&dst, dstp.value, MABS(drcbe->reglo[dstp.value])); // mov dstp,reglo[dstp] + } + + /* restore control word and proceed */ + emit_fldcw_m16(&dst, MABS(&drcbe->fmodesave)); // fldcw [fmodesave] + return dst; +} + + +/*------------------------------------------------- + op_ftoi8c - process a FTOI8C opcode +-------------------------------------------------*/ + +static x86code *op_ftoi8c(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MR, &srcp, PTYPE_MF); + + /* save and set the control word */ + emit_fstcw_m16(&dst, MABS(&drcbe->fmodesave)); // fstcw [fmodesave] + emit_fldcw_m16(&dst, MABS(&fp_control[DRCUML_FMOD_CEIL])); // fldcw fpcontrol[DRCUML_FMOD_CEIL] + + /* general case */ + emit_fld_p(&dst, inst->size, &srcp); // fld srcp + if (dstp.type == DRCUML_PTYPE_MEMORY) + emit_fistp_m64(&dst, MABS(dstp.value)); // fistp [dstp] + else if (dstp.type == DRCUML_PTYPE_INT_REGISTER) + { + emit_fistp_m64(&dst, MABS(drcbe->reglo[dstp.value])); // fistp reglo[dstp] + emit_mov_r32_m32(&dst, dstp.value, MABS(drcbe->reglo[dstp.value])); // mov dstp,reglo[dstp] + } + + /* restore control word and proceed */ + emit_fldcw_m16(&dst, MABS(&drcbe->fmodesave)); // fldcw [fmodesave] + return dst; +} + + +/*------------------------------------------------- + op_ffrfs - process a FFRFS opcode +-------------------------------------------------*/ + +static x86code *op_ffrfs(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, PTYPE_MF); + + /* general case */ + emit_fld_m32(&dst, MABS(srcp.value)); // fld [srcp] + emit_fstp_p(&dst, inst->size, &dstp); // fstp dstp + + return dst; +} + + +/*------------------------------------------------- + op_ffrfd - process a FFRFD opcode +-------------------------------------------------*/ + +static x86code *op_ffrfd(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, PTYPE_MF); + + /* general case */ + emit_fld_m64(&dst, MABS(srcp.value)); // fld [srcp] + emit_fstp_p(&dst, inst->size, &dstp); // fstp dstp + + return dst; +} + + +/*------------------------------------------------- + op_ffri4 - process a FFRI4 opcode +-------------------------------------------------*/ + +static x86code *op_ffri4(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, PTYPE_MRI); + + /* general case */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + { + emit_mov_m32_imm(&dst, MABS(&drcbe->fptemp), srcp.value); // mov [fptemp],srcp + emit_fild_m32(&dst, MABS(&drcbe->fptemp)); // fild [fptemp] + } + else if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_fild_m32(&dst, MABS(srcp.value)); // fild [srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + { + emit_mov_m32_r32(&dst, MABS(drcbe->reglo[srcp.value]), srcp.value); // mov reglo[srcp],srcp + emit_fild_m32(&dst, MABS(drcbe->reglo[srcp.value])); // fild reglo[srcp] + } + emit_fstp_p(&dst, inst->size, &dstp); // fstp [dstp] + return dst; +} + + +/*------------------------------------------------- + op_ffri8 - process a FFRI8 opcode +-------------------------------------------------*/ + +static x86code *op_ffri8(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, PTYPE_MRI); + + /* general case */ + if (srcp.type == DRCUML_PTYPE_IMMEDIATE) + { + emit_mov_m32_imm(&dst, MABS(&drcbe->fptemp), srcp.value); // mov [fptemp],srcp + emit_mov_m32_imm(&dst, MABS((UINT8 *)&drcbe->fptemp + 4), srcp.value); // mov [fptemp+4],srcp + emit_fild_m64(&dst, MABS(&drcbe->fptemp)); // fild [fptemp] + } + else if (srcp.type == DRCUML_PTYPE_MEMORY) + emit_fild_m64(&dst, MABS(srcp.value)); // fild [srcp] + else if (srcp.type == DRCUML_PTYPE_INT_REGISTER) + { + emit_mov_m32_r32(&dst, MABS(drcbe->reglo[srcp.value]), srcp.value); // mov reglo[srcp],srcp + emit_fild_m64(&dst, MABS(drcbe->reglo[srcp.value])); // fild reglo[srcp] + } + emit_fstp_p(&dst, inst->size, &dstp); // fstp [dstp] + return dst; +} + + +/*------------------------------------------------- + op_fadd - process a FADD opcode +-------------------------------------------------*/ + +static x86code *op_fadd(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MF, &src1p, PTYPE_MF, &src2p, PTYPE_MF); + + /* 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 + + return dst; +} + + +/*------------------------------------------------- + op_fsub - process a FSUB opcode +-------------------------------------------------*/ + +static x86code *op_fsub(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MF, &src1p, PTYPE_MF, &src2p, 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 + + return dst; +} + + +/*------------------------------------------------- + op_fcmp - process a FCMP opcode +-------------------------------------------------*/ + +static x86code *op_fcmp(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter src1p, src2p; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert((inst->condflags & ~(DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &src1p, PTYPE_MF, &src2p, 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 + + return dst; +} + + +/*------------------------------------------------- + op_fmul - process a FMUL opcode +-------------------------------------------------*/ + +static x86code *op_fmul(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MF, &src1p, PTYPE_MF, &src2p, PTYPE_MF); + + /* 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 + + return dst; +} + + +/*------------------------------------------------- + op_fdiv - process a FDIV opcode +-------------------------------------------------*/ + +static x86code *op_fdiv(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, src1p, src2p; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_3(drcbe, inst, &dstp, PTYPE_MF, &src1p, PTYPE_MF, &src2p, 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 + + return dst; +} + + +/*------------------------------------------------- + op_fneg - process a FNEG opcode +-------------------------------------------------*/ + +static x86code *op_fneg(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, 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 + + return dst; +} + + +/*------------------------------------------------- + op_fabs - process a FABS opcode +-------------------------------------------------*/ + +static x86code *op_fabs(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, 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 + + return dst; +} + + +/*------------------------------------------------- + op_fsqrt - process a FSQRT opcode +-------------------------------------------------*/ + +static x86code *op_fsqrt(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, 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 + + return dst; +} + + +/*------------------------------------------------- + op_frecip - process a FRECIP opcode +-------------------------------------------------*/ + +static x86code *op_frecip(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, 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 + + return dst; +} + + +/*------------------------------------------------- + op_frsqrt - process a FRSQRT opcode +-------------------------------------------------*/ + +static x86code *op_frsqrt(drcbe_state *drcbe, x86code *dst, const drcuml_instruction *inst) +{ + drcuml_parameter dstp, srcp; + + /* validate instruction */ + assert(inst->size == 4 || inst->size == 8); + assert(inst->condflags == DRCUML_COND_ALWAYS); + + /* normalize parameters */ + param_normalize_2(drcbe, inst, &dstp, PTYPE_MF, &srcp, 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 + + return dst; +} + + + +/*************************************************************************** + MISCELLAENOUS FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + dmulu - perform a double-wide unsigned multiply +-------------------------------------------------*/ + +static void dmulu(UINT64 *dstlo, UINT64 *dsthi, UINT64 src1, UINT64 src2) +{ + UINT64 lo, hi, prevlo; + UINT64 a, b, temp; + + /* shortcut if we don't care about the high bits */ + if (dstlo == dsthi) + { + *dstlo = src1 * src2; + return; + } + + /* fetch source values */ + a = src1; + b = src2; + if (a == 0 || b == 0) + { + *dsthi = *dstlo = 0; + return; + } + + /* 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); + + /* store the results */ + *dsthi = hi; + *dstlo = lo; +} + + +/*------------------------------------------------- + dmuls - perform a double-wide signed multiply +-------------------------------------------------*/ + +static void dmuls(UINT64 *dstlo, UINT64 *dsthi, INT64 src1, INT64 src2) +{ + UINT64 lo, hi, prevlo; + UINT64 a, b, temp; + + /* shortcut if we don't care about the high bits */ + if (dstlo == dsthi) + { + *dstlo = src1 * src2; + return; + } + + /* 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; + } + + /* 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; +} + + +/*------------------------------------------------- + ddivu - perform a double-wide unsigned divide +-------------------------------------------------*/ + +static void ddivu(UINT64 *dstlo, UINT64 *dsthi, UINT64 src1, UINT64 src2) +{ + /* do nothing if src2 == 0 */ + if (src2 == 0) + return; + + /* shortcut if no remainder */ + *dstlo = src1 / src2; + if (dstlo != dsthi) + *dsthi = src1 % src2; +} + + +/*------------------------------------------------- + ddivs - perform a double-wide signed divide +-------------------------------------------------*/ + +static void ddivs(UINT64 *dstlo, UINT64 *dsthi, INT64 src1, INT64 src2) +{ + /* do nothing if src2 == 0 */ + if (src2 == 0) + return; + + /* shortcut if no remainder */ + *dstlo = src1 / src2; + if (dstlo != dsthi) + *dsthi = src1 % src2; +} diff --git a/src/emu/cpu/drccache.c b/src/emu/cpu/drccache.c new file mode 100644 index 00000000000..83b00da4907 --- /dev/null +++ b/src/emu/cpu/drccache.c @@ -0,0 +1,418 @@ +/*************************************************************************** + + drccache.h + + Universal dynamic recompiler cache management. + + Copyright Aaron Giles + Released for general non-commercial use under the MAME license + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include <stddef.h> +#include "cpuintrf.h" +#include "drccache.h" + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* largest block of code that can be generated at once */ +#define CODEGEN_MAX_BYTES 65536 + +/* minimum alignment, in bytes (must be power of 2) */ +#define CACHE_ALIGNMENT 8 + +/* largest permanent allocation we allow */ +#define MAX_PERMANENT_ALLOC 1024 + +/* size of "near" area at the base of the cache */ +#define NEAR_CACHE_SIZE 65536 + + + +/*************************************************************************** + MACROS +***************************************************************************/ + +/* ensure that all memory allocated is aligned to an 8-byte boundary */ +#define ALIGN_PTR_UP(p) ((void *)(((FPTR)(p) + (CACHE_ALIGNMENT - 1)) & ~(CACHE_ALIGNMENT - 1))) +#define ALIGN_PTR_DOWN(p) ((void *)((FPTR)(p) & ~(CACHE_ALIGNMENT - 1))) + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* out-of-bounds codegen handlers */ +typedef struct _oob_handler oob_handler; +struct _oob_handler +{ + oob_handler * next; /* next handler */ + drccache_oob_func callback; /* callback function */ + void * param1; /* 1st pointer parameter */ + void * param2; /* 2nd pointer parameter */ + void * param3; /* 3rd pointer parameter */ +}; + + +/* a linked list of free items */ +typedef struct _free_link free_link; +struct _free_link +{ + free_link * next; /* pointer to the next guy */ +}; + + +/* cache state */ +struct _drccache +{ + /* core parameters */ + drccodeptr near; /* pointer to the near part of the cache */ + drccodeptr neartop; /* top of the near part of the cache */ + drccodeptr base; /* base pointer to the compiler cache */ + drccodeptr top; /* current top of cache */ + drccodeptr end; /* end of cache memory */ + drccodeptr codegen; /* start of generated code */ + size_t size; /* size of the cache in bytes */ + + /* oob management */ + oob_handler * ooblist; /* list of oob handlers */ + oob_handler ** oobtail; /* pointer to tail oob pointer */ + + /* free lists */ + free_link * free[MAX_PERMANENT_ALLOC / CACHE_ALIGNMENT]; + free_link * nearfree[MAX_PERMANENT_ALLOC / CACHE_ALIGNMENT]; +}; + + + +/*************************************************************************** + INITIALIZATION/TEARDOWN +***************************************************************************/ + +/*------------------------------------------------- + drccache_alloc - allocate the cache itself +-------------------------------------------------*/ + +drccache *drccache_alloc(size_t bytes) +{ + drccache cache, *cacheptr; + + assert(bytes >= sizeof(cache) + NEAR_CACHE_SIZE); + + /* build a local structure first */ + memset(&cache, 0, sizeof(cache)); + cache.near = osd_alloc_executable(bytes); + cache.neartop = cache.near; + cache.base = cache.near + NEAR_CACHE_SIZE; + cache.top = cache.base; + cache.end = cache.near + bytes; + cache.size = bytes; + + /* now allocate the cache structure itself from that */ + cacheptr = drccache_memory_alloc(&cache, sizeof(cache)); + *cacheptr = cache; + + /* return the allocated result */ + return cacheptr; +} + + +/*------------------------------------------------- + drccache_free - free the cache +-------------------------------------------------*/ + +void drccache_free(drccache *cache) +{ + /* release the memory; this includes the cache object itself */ + osd_free_executable(cache->near, cache->size); +} + + + +/*************************************************************************** + CACHE INFORMATION +***************************************************************************/ + +/*------------------------------------------------- + drccache_contains_pointer - return true if a + pointer is within the cache +-------------------------------------------------*/ + +int drccache_contains_pointer(drccache *cache, const void *ptr) +{ + return ((const drccodeptr)ptr >= cache->near && (const drccodeptr)ptr < cache->near + cache->size); +} + + +/*------------------------------------------------- + drccache_contains_near_pointer - return true + if a pointer is within the cache +-------------------------------------------------*/ + +int drccache_contains_near_pointer(drccache *cache, const void *ptr) +{ + return ((const drccodeptr)ptr >= cache->near && (const drccodeptr)ptr < cache->neartop); +} + + +/*------------------------------------------------- + drccache_near - return a pointer to the near + part of the cache +-------------------------------------------------*/ + +drccodeptr drccache_near(drccache *cache) +{ + return cache->near; +} + + +/*------------------------------------------------- + drccache_base - return a pointer to the base + of the cache +-------------------------------------------------*/ + +drccodeptr drccache_base(drccache *cache) +{ + return cache->base; +} + + +/*------------------------------------------------- + drccache_top - return the current top of the + cache +-------------------------------------------------*/ + +drccodeptr drccache_top(drccache *cache) +{ + return cache->top; +} + + + +/*************************************************************************** + MEMORY MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + drccache_flush - flush the cache contents +-------------------------------------------------*/ + +void drccache_flush(drccache *cache) +{ + /* can't flush in the middle of codegen */ + assert(cache->codegen == NULL); + + /* just reset the top back to the base and re-seed */ + cache->top = cache->base; +} + + +/*------------------------------------------------- + drccache_memory_alloc - allocate permanent + memory from the cache +-------------------------------------------------*/ + +void *drccache_memory_alloc(drccache *cache, size_t bytes) +{ + drccodeptr ptr; + + assert(bytes > 0); + + /* pick first from the free list */ + if (bytes < MAX_PERMANENT_ALLOC) + { + free_link **linkptr = &cache->free[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT]; + free_link *link = *linkptr; + if (link != NULL) + { + *linkptr = link->next; + return link; + } + } + + /* if no space, we just fail */ + ptr = ALIGN_PTR_DOWN(cache->end - bytes); + if (cache->top > ptr) + return NULL; + + /* otherwise update the end of the cache */ + cache->end = ptr; + return ptr; +} + + +/*------------------------------------------------- + drccache_memory_alloc_near - allocate + permanent memory from the near part of the + cache +-------------------------------------------------*/ + +void *drccache_memory_alloc_near(drccache *cache, size_t bytes) +{ + drccodeptr ptr; + + assert(bytes > 0); + + /* pick first from the free list */ + if (bytes < MAX_PERMANENT_ALLOC) + { + free_link **linkptr = &cache->nearfree[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT]; + free_link *link = *linkptr; + if (link != NULL) + { + *linkptr = link->next; + return link; + } + } + + /* if no space, we just fail */ + ptr = ALIGN_PTR_UP(cache->neartop); + if (ptr + bytes > cache->base) + return NULL; + + /* otherwise update the top of the near part of the cache */ + cache->neartop = ptr + bytes; + return ptr; +} + + +/*------------------------------------------------- + drccache_memory_free - release permanent + memory allocated from the cache +-------------------------------------------------*/ + +void drccache_memory_free(drccache *cache, void *memory, size_t bytes) +{ + free_link **linkptr; + free_link *link = memory; + + assert(bytes < MAX_PERMANENT_ALLOC); + assert(((drccodeptr)memory >= cache->near && (drccodeptr)memory < cache->base) || ((drccodeptr)memory >= cache->end && (drccodeptr)memory < cache->near + cache->size)); + + /* determine which free list to add to */ + if ((drccodeptr)memory < cache->base) + linkptr = &cache->nearfree[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT]; + else + linkptr = &cache->free[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT]; + + /* link is into the free list for our size */ + link->next = *linkptr; + *linkptr = link; +} + + +/*------------------------------------------------- + drccache_memory_alloc_temporary - allocate + temporary memory from the cache +-------------------------------------------------*/ + +void *drccache_memory_alloc_temporary(drccache *cache, size_t bytes) +{ + drccodeptr ptr = cache->top; + + /* can't allocate in the middle of codegen */ + assert(cache->codegen == NULL); + + /* if no space, we just fail */ + if (ptr + bytes >= cache->end) + return NULL; + + /* otherwise, update the cache top */ + cache->top = ALIGN_PTR_UP(ptr + bytes); + return ptr; +} + + + +/*************************************************************************** + CODE GENERATION +***************************************************************************/ + +/*------------------------------------------------- + drccache_begin_codegen - begin code + generation +-------------------------------------------------*/ + +drccodeptr *drccache_begin_codegen(drccache *cache, UINT32 reserve_bytes) +{ + drccodeptr ptr = cache->top; + + /* can't restart in the middle of codegen */ + assert(cache->codegen == NULL); + assert(cache->ooblist == NULL); + + /* if still no space, we just fail */ + if (ptr + reserve_bytes >= cache->end) + return NULL; + + /* otherwise, return a pointer to the cache top */ + cache->codegen = cache->top; + cache->oobtail = &cache->ooblist; + return &cache->top; +} + + +/*------------------------------------------------- + drccache_end_codegen - complete code + generation +-------------------------------------------------*/ + +drccodeptr drccache_end_codegen(drccache *cache) +{ + drccodeptr result = cache->codegen; + + /* run the OOB handlers */ + while (cache->ooblist != NULL) + { + /* remove us from the list */ + oob_handler *oob = cache->ooblist; + cache->ooblist = oob->next; + + /* call the callback */ + (*oob->callback)(&cache->top, oob->param1, oob->param2, oob->param3); + assert(cache->top - cache->codegen < CODEGEN_MAX_BYTES); + + /* release our memory */ + drccache_memory_free(cache, oob, sizeof(*oob)); + } + + /* update the cache top */ + cache->top = ALIGN_PTR_UP(cache->top); + cache->codegen = NULL; + + return result; +} + + +/*------------------------------------------------- + drccache_request_oob_codegen - request + callback for out-of-band codegen +-------------------------------------------------*/ + +void drccache_request_oob_codegen(drccache *cache, drccache_oob_func callback, void *param1, void *param2, void *param3) +{ + oob_handler *oob; + + assert(cache->codegen != NULL); + + /* pull an item from the free list */ + oob = drccache_memory_alloc(cache, sizeof(*oob)); + assert(oob != NULL); + + /* fill it in */ + oob->next = NULL; + oob->callback = callback; + oob->param1 = param1; + oob->param2 = param2; + oob->param3 = param3; + + /* add to the tail */ + *cache->oobtail = oob; + cache->oobtail = &oob->next; +} diff --git a/src/emu/cpu/drccache.h b/src/emu/cpu/drccache.h new file mode 100644 index 00000000000..8fa7c82da1e --- /dev/null +++ b/src/emu/cpu/drccache.h @@ -0,0 +1,107 @@ +/*************************************************************************** + + drccache.h + + Universal dynamic recompiler cache management. + + Copyright Aaron Giles + Released for general non-commercial use under the MAME license + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#ifndef __DRCCACHE_H__ +#define __DRCCACHE_H__ + + + +/*************************************************************************** + MACROS +***************************************************************************/ + +/* ensure that a given pointer is within the cache boundaries */ +#define assert_in_cache(c,p) assert(drccache_contains_pointer(c, p)) +#define assert_in_near_cache(c,p) assert(drccache_contains_near_pointer(c, p)) + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* generic code pointer */ +typedef UINT8 *drccodeptr; + +/* opaque cache state */ +typedef struct _drccache drccache; + +/* out of band codegen callback */ +typedef void (*drccache_oob_func)(drccodeptr *codeptr, void *param1, void *param2, void *param3); + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* ----- initialization/teardown ----- */ + +/* allocate the cache itself */ +drccache *drccache_alloc(size_t bytes); + +/* free the cache */ +void drccache_free(drccache *cache); + + + +/* ----- cache information ----- */ + +/* return true if a pointer is within the cache */ +int drccache_contains_pointer(drccache *cache, const void *ptr); + +/* return true if a pointer is within the near area of the cache */ +int drccache_contains_near_pointer(drccache *cache, const void *ptr); + +/* return a pointer to the near part of the cache */ +drccodeptr drccache_near(drccache *cache); + +/* return a pointer to the base of the cache, which is where code generation starts */ +drccodeptr drccache_base(drccache *cache); + +/* return the current top of the cache, which is where the next code will be generated */ +drccodeptr drccache_top(drccache *cache); + + + +/* ----- memory management ----- */ + +/* flush the cache contents */ +void drccache_flush(drccache *cache); + +/* allocate permanent memory from the cache */ +void *drccache_memory_alloc(drccache *cache, size_t bytes); + +/* allocate permanent memory from the near portion of the cache */ +void *drccache_memory_alloc_near(drccache *cache, size_t bytes); + +/* release permanent memory allocated from the cache */ +void drccache_memory_free(drccache *cache, void *memory, size_t bytes); + +/* allocate temporary memory from the cache (released on a reset) */ +void *drccache_memory_alloc_temporary(drccache *cache, size_t bytes); + + + +/* ----- code generation ----- */ + +/* begin code generation */ +drccodeptr *drccache_begin_codegen(drccache *cache, UINT32 reserve_bytes); + +/* complete code generation */ +drccodeptr drccache_end_codegen(drccache *cache); + +/* request callback for out-of-band codegen */ +void drccache_request_oob_codegen(drccache *cache, drccache_oob_func callback, void *param1, void *param2, void *param3); + + +#endif diff --git a/src/emu/cpu/drcfe.c b/src/emu/cpu/drcfe.c index 05dbe43b5c4..d528a5d13f3 100644 --- a/src/emu/cpu/drcfe.c +++ b/src/emu/cpu/drcfe.c @@ -5,7 +5,7 @@ Generic dynamic recompiler frontend structures and utilities. Copyright Aaron Giles - Released for general use under the MAME license + Released for general non-commercial use under the MAME license Visit http://mamedev.org for licensing and usage restrictions. ***************************************************************************/ @@ -44,7 +44,7 @@ struct _drcfe_state UINT32 window_end; /* code window end offset = startpc + window_end */ UINT32 max_sequence; /* maximum instructions to include in a sequence */ - drcfe_describe_func describe; /* callback to describe a single instruction */ + drcfe_describe_func describe; /* callback to describe a single instruction */ void * param; /* parameter for the callback */ /* CPU parameters */ diff --git a/src/emu/cpu/drcfe.h b/src/emu/cpu/drcfe.h index a2fefe04834..0041bfb2cc9 100644 --- a/src/emu/cpu/drcfe.h +++ b/src/emu/cpu/drcfe.h @@ -5,7 +5,7 @@ Generic dynamic recompiler frontend structures and utilities. Copyright Aaron Giles - Released for general use under the MAME license + Released for general non-commercial use under the MAME license Visit http://mamedev.org for licensing and usage restrictions. **************************************************************************** @@ -34,13 +34,6 @@ #ifndef __DRCFE_H__ #define __DRCFE_H__ -#ifdef PTR64 -#include "x64drc.h" -#else -#include "x86drc.h" -#endif - - /*************************************************************************** CONSTANTS @@ -159,16 +152,6 @@ struct _drcfe_config drcfe_describe_func describe; /* callback to describe a single instruction */ }; -/* - for each register: - callback to load - callback to store - pointer to memory - if (pointer != NULL) - - can return reference to memory for operations - else - - must always load/store from register -*/ /*************************************************************************** diff --git a/src/emu/cpu/drcuml.c b/src/emu/cpu/drcuml.c new file mode 100644 index 00000000000..ff89fc87aa8 --- /dev/null +++ b/src/emu/cpu/drcuml.c @@ -0,0 +1,858 @@ +/*************************************************************************** + + drcuml.c + + Universal machine language for dynamic recompiling CPU cores. + + Copyright Aaron Giles + Released for general non-commercial use under the MAME license + Visit http://mamedev.org for licensing and usage restrictions. + +**************************************************************************** + + Future improvements/changes: + + * 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 + + * New instructions? + - EXTRACT dst,src,shift,mask + dst = (src >> shift) & mask + + - INSERT dst,src,shift,mask + dst = (dst & ~mask) | ((src << shift) & mask) + + - 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 + + * Add interface for getting hints from the backend: + - number of direct-mapped integer registers + - number of direct-mapped floating point registers + +***************************************************************************/ + +#include "drcuml.h" +#include "drcumlsh.h" +#include "drcumld.h" +#include "mame.h" +#include "deprecat.h" +#include <stdarg.h> +#include <setjmp.h> + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* opcode validation condition/flag valid bitmasks */ +#define OV_CONDFLAG_NONE 0x00 +#define OV_CONDFLAG_COND 0x80 +#define OV_CONDFLAG_FLAGS 0x1f + +/* opcode validation parameter valid bitmasks */ +#define OV_PARAM_ALLOWED_NONE (1 << DRCUML_PTYPE_NONE) +#define OV_PARAM_ALLOWED_IMM (1 << DRCUML_PTYPE_IMMEDIATE) +#define OV_PARAM_ALLOWED_IREG (1 << DRCUML_PTYPE_INT_REGISTER) +#define OV_PARAM_ALLOWED_FREG (1 << DRCUML_PTYPE_FLOAT_REGISTER) +#define OV_PARAM_ALLOWED_MVAR (1 << DRCUML_PTYPE_MAPVAR) +#define OV_PARAM_ALLOWED_MEM (1 << DRCUML_PTYPE_MEMORY) +#define OV_PARAM_ALLOWED_NCMEM (OV_PARAM_ALLOWED_MEM | 0x80) +#define OV_PARAM_ALLOWED_IMV (OV_PARAM_ALLOWED_IMM | OV_PARAM_ALLOWED_MVAR) +#define OV_PARAM_ALLOWED_IRM (OV_PARAM_ALLOWED_IREG | OV_PARAM_ALLOWED_MEM) +#define OV_PARAM_ALLOWED_FRM (OV_PARAM_ALLOWED_FREG | OV_PARAM_ALLOWED_MEM) +#define OV_PARAM_ALLOWED_IANY (OV_PARAM_ALLOWED_IRM | OV_PARAM_ALLOWED_IMV) +#define OV_PARAM_ALLOWED_FANY (OV_PARAM_ALLOWED_FRM) + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* structure describing UML generation state */ +struct _drcuml_state +{ + drccache * cache; /* pointer to the codegen cache */ + drcuml_block * blocklist; /* list of active blocks */ + + const drcbe_interface * beintf; /* backend interface pointer */ + drcbe_state * bestate; /* pointer to the back-end state */ + + drcuml_codehandle * handlelist; /* head of linked list of handles */ + + FILE * umllog; /* handle to the UML logfile */ +}; + + +/* structure describing UML codegen block */ +struct _drcuml_block +{ + drcuml_state * drcuml; /* pointer back to the owning UML */ + drcuml_block * next; /* pointer to next block */ + drcuml_instruction * inst; /* pointer to the instruction list */ + UINT8 inuse; /* this block is in use */ + UINT32 maxinst; /* maximum number of instructions */ + UINT32 nextinst; /* next instruction to fill in the cache */ + jmp_buf * errorbuf; /* setjmp buffer for deep error handling */ +}; + + +/* structure describing a global handle */ +struct _drcuml_codehandle +{ + drccodeptr code; /* pointer to the associated code */ + char * string; /* pointer to string attached to handle */ + drcuml_codehandle * next; /* link to next handle in the list */ + drcuml_state * drcuml; /* pointer to owning object */ +}; + + +/* structure describing an opcode for validation */ +typedef struct _drcuml_opcode_valid drcuml_opcode_valid; +struct _drcuml_opcode_valid +{ + drcuml_opcode opcode; /* the opcode itself */ + UINT8 sizes; /* allowed sizes */ + UINT8 condflags; /* allowed conditions/flags */ + UINT8 p0types; /* allowed types for parameter 0 */ + UINT8 p1types; /* allowed types for parameter 1 */ + UINT8 p2types; /* allowed types for parameter 2 */ + UINT8 p3types; /* allowed types for parameter 3 */ +}; + + + +/*************************************************************************** + TABLES +***************************************************************************/ + +/* macro to simplify the table */ +#define OPVALID_ENTRY_0(op,sizes,condflag) { DRCUML_OP_##op, sizes, OV_CONDFLAG_##condflag, OV_PARAM_ALLOWED_NONE, OV_PARAM_ALLOWED_NONE, OV_PARAM_ALLOWED_NONE, OV_PARAM_ALLOWED_NONE }, +#define OPVALID_ENTRY_1(op,sizes,condflag,p0) { DRCUML_OP_##op, sizes, OV_CONDFLAG_##condflag, OV_PARAM_ALLOWED_##p0, OV_PARAM_ALLOWED_NONE, OV_PARAM_ALLOWED_NONE, OV_PARAM_ALLOWED_NONE }, +#define OPVALID_ENTRY_2(op,sizes,condflag,p0,p1) { DRCUML_OP_##op, sizes, OV_CONDFLAG_##condflag, OV_PARAM_ALLOWED_##p0, OV_PARAM_ALLOWED_##p1, OV_PARAM_ALLOWED_NONE, OV_PARAM_ALLOWED_NONE }, +#define OPVALID_ENTRY_3(op,sizes,condflag,p0,p1,p2) { DRCUML_OP_##op, sizes, OV_CONDFLAG_##condflag, OV_PARAM_ALLOWED_##p0, OV_PARAM_ALLOWED_##p1, OV_PARAM_ALLOWED_##p2, OV_PARAM_ALLOWED_NONE }, +#define OPVALID_ENTRY_4(op,sizes,condflag,p0,p1,p2,p3) { DRCUML_OP_##op, sizes, OV_CONDFLAG_##condflag, OV_PARAM_ALLOWED_##p0, OV_PARAM_ALLOWED_##p1, OV_PARAM_ALLOWED_##p2, OV_PARAM_ALLOWED_##p3 }, + +/* opcode validation table */ +static const drcuml_opcode_valid opcode_valid_list[] = +{ + /* Compile-time opcodes */ + OPVALID_ENTRY_1(HANDLE, 4, NONE, MEM) + OPVALID_ENTRY_2(HASH, 4, NONE, IMV, IMV) + OPVALID_ENTRY_1(LABEL, 4, NONE, IMV) + OPVALID_ENTRY_1(COMMENT, 4, NONE, NCMEM) + OPVALID_ENTRY_2(MAPVAR, 4, NONE, MVAR, IMV) + + /* Control Flow Operations */ + OPVALID_ENTRY_1(DEBUG, 4, NONE, IANY) + OPVALID_ENTRY_1(EXIT, 4, COND, IANY) + OPVALID_ENTRY_3(HASHJMP, 4, NONE, IANY, IANY, MEM) + OPVALID_ENTRY_1(LABEL, 4, NONE, IMV) + OPVALID_ENTRY_1(JMP, 4, COND, IMV) + OPVALID_ENTRY_2(EXH, 4, COND, MEM, IANY) + OPVALID_ENTRY_1(CALLH, 4, COND, MEM) + OPVALID_ENTRY_0(RET, 4, COND) + OPVALID_ENTRY_2(CALLC, 4, COND, NCMEM,NCMEM) + OPVALID_ENTRY_2(RECOVER, 4, NONE, IRM, MVAR) + + /* Internal Register Operations */ + OPVALID_ENTRY_1(SETFMOD, 4, NONE, IANY) + OPVALID_ENTRY_1(GETFMOD, 4, NONE, IRM) + OPVALID_ENTRY_1(GETEXP, 4, NONE, IRM) + + /* Integer Operations */ + OPVALID_ENTRY_3(LOAD1U, 4|8, NONE, IRM, NCMEM,IANY) + OPVALID_ENTRY_3(LOAD1S, 4|8, NONE, IRM, NCMEM,IANY) + OPVALID_ENTRY_3(LOAD2U, 4|8, NONE, IRM, NCMEM,IANY) + OPVALID_ENTRY_3(LOAD2S, 4|8, NONE, IRM, NCMEM,IANY) + OPVALID_ENTRY_3(LOAD4U, 4|8, NONE, IRM, NCMEM,IANY) + OPVALID_ENTRY_3(LOAD4S, 8, NONE, IRM, NCMEM,IANY) + OPVALID_ENTRY_3(LOAD8U, 8, NONE, IRM, NCMEM,IANY) + OPVALID_ENTRY_3(STORE1, 4|8, NONE, NCMEM,IANY, IANY) + OPVALID_ENTRY_3(STORE2, 4|8, NONE, NCMEM,IANY, IANY) + OPVALID_ENTRY_3(STORE4, 4|8, NONE, NCMEM,IANY, IANY) + OPVALID_ENTRY_3(STORE8, 8, NONE, NCMEM,IANY, IANY) + OPVALID_ENTRY_3(READ1U, 4|8, NONE, IRM, IMV, IANY) + OPVALID_ENTRY_3(READ1S, 4|8, NONE, IRM, IMV, IANY) + OPVALID_ENTRY_3(READ2U, 4|8, NONE, IRM, IMV, IANY) + OPVALID_ENTRY_3(READ2S, 4|8, NONE, IRM, IMV, IANY) + OPVALID_ENTRY_4(READ2M, 4|8, NONE, IRM, IMV, IANY, IANY) + OPVALID_ENTRY_3(READ4U, 4|8, NONE, IRM, IMV, IANY) + OPVALID_ENTRY_3(READ4S, 8, NONE, IRM, IMV, IANY) + OPVALID_ENTRY_4(READ4M, 4|8, NONE, IRM, IMV, IANY, IANY) + OPVALID_ENTRY_3(READ8U, 8, NONE, IRM, IMV, IANY) + OPVALID_ENTRY_4(READ8M, 8, NONE, IRM, IMV, IANY, IANY) + OPVALID_ENTRY_3(WRITE1, 4|8, NONE, IMV, IANY, IANY) + OPVALID_ENTRY_3(WRITE2, 4|8, NONE, IMV, IANY, IANY) + OPVALID_ENTRY_4(WRIT2M, 4|8, NONE, IMV, IANY, IANY, IANY) + OPVALID_ENTRY_3(WRITE4, 4|8, NONE, IMV, IANY, IANY) + OPVALID_ENTRY_4(WRIT4M, 4|8, NONE, IMV, IANY, IANY, IANY) + OPVALID_ENTRY_3(WRITE8, 8, NONE, IMV, IANY, IANY) + OPVALID_ENTRY_4(WRIT8M, 8, NONE, IMV, IANY, IANY, IANY) + OPVALID_ENTRY_3(FLAGS, 4|8, NONE, IRM, IMV, MEM) + OPVALID_ENTRY_2(MOV, 4|8, COND, IRM, IANY) + OPVALID_ENTRY_2(ZEXT1, 4|8, NONE, IRM, IANY) + OPVALID_ENTRY_2(ZEXT2, 4|8, NONE, IRM, IANY) + OPVALID_ENTRY_2(ZEXT4, 8, NONE, IRM, IANY) + OPVALID_ENTRY_2(SEXT1, 4|8, NONE, IRM, IANY) + OPVALID_ENTRY_2(SEXT2, 4|8, NONE, IRM, IANY) + OPVALID_ENTRY_2(SEXT4, 8, NONE, IRM, IANY) + OPVALID_ENTRY_3(ADD, 4|8, FLAGS, IRM, IANY, IANY) + OPVALID_ENTRY_3(ADDC, 4|8, FLAGS, IRM, IANY, IANY) + OPVALID_ENTRY_3(SUB, 4|8, FLAGS, IRM, IANY, IANY) + OPVALID_ENTRY_3(SUBB, 4|8, FLAGS, IRM, IANY, IANY) + OPVALID_ENTRY_2(CMP, 4|8, FLAGS, IANY, IANY) + OPVALID_ENTRY_4(MULU, 4|8, FLAGS, IRM, IRM, IANY, IANY) + OPVALID_ENTRY_4(MULS, 4|8, FLAGS, IRM, IRM, IANY, IANY) + OPVALID_ENTRY_4(DIVU, 4|8, FLAGS, IRM, IRM, IANY, IANY) + OPVALID_ENTRY_4(DIVS, 4|8, FLAGS, IRM, IRM, IANY, IANY) + OPVALID_ENTRY_3(AND, 4|8, FLAGS, IRM, IANY, IANY) + OPVALID_ENTRY_2(TEST, 4|8, FLAGS, IANY, IANY) + OPVALID_ENTRY_3(OR, 4|8, FLAGS, IRM, IANY, IANY) + OPVALID_ENTRY_3(XOR, 4|8, FLAGS, IRM, IANY, IANY) + OPVALID_ENTRY_3(SHL, 4|8, FLAGS, IRM, IANY, IANY) + OPVALID_ENTRY_3(SHR, 4|8, FLAGS, IRM, IANY, IANY) + OPVALID_ENTRY_3(SAR, 4|8, FLAGS, IRM, IANY, IANY) + OPVALID_ENTRY_3(ROL, 4|8, FLAGS, IRM, IANY, IANY) + OPVALID_ENTRY_3(ROLC, 4|8, FLAGS, IRM, IANY, IANY) + OPVALID_ENTRY_3(ROR, 4|8, FLAGS, IRM, IANY, IANY) + OPVALID_ENTRY_3(RORC, 4|8, FLAGS, IRM, IANY, IANY) + + /* Floating Point Operations */ + OPVALID_ENTRY_3(FLOAD, 4|8, NONE, FRM, NCMEM,IANY) + OPVALID_ENTRY_3(FSTORE, 4|8, NONE, NCMEM,IANY, FANY) + OPVALID_ENTRY_3(FREAD, 4|8, NONE, FRM, IMV, IANY) + OPVALID_ENTRY_3(FWRITE, 4|8, NONE, IMV, IANY, FANY) + OPVALID_ENTRY_2(FMOV, 4|8, COND, FRM, FANY) + OPVALID_ENTRY_2(FTOI4, 4|8, NONE, IRM, FANY) + OPVALID_ENTRY_2(FTOI4T, 4|8, NONE, IRM, FANY) + OPVALID_ENTRY_2(FTOI4R, 4|8, NONE, IRM, FANY) + OPVALID_ENTRY_2(FTOI4F, 4|8, NONE, IRM, FANY) + OPVALID_ENTRY_2(FTOI4C, 4|8, NONE, IRM, FANY) + OPVALID_ENTRY_2(FTOI8, 4|8, NONE, IRM, FANY) + OPVALID_ENTRY_2(FTOI8T, 4|8, NONE, IRM, FANY) + OPVALID_ENTRY_2(FTOI8R, 4|8, NONE, IRM, FANY) + OPVALID_ENTRY_2(FTOI8F, 4|8, NONE, IRM, FANY) + OPVALID_ENTRY_2(FTOI8C, 4|8, NONE, IRM, FANY) + OPVALID_ENTRY_2(FFRFS, 8, NONE, FRM, FANY) + OPVALID_ENTRY_2(FFRFD, 4 , NONE, FRM, FANY) + OPVALID_ENTRY_2(FFRI4, 4|8, NONE, FRM, IANY) + OPVALID_ENTRY_2(FFRI8, 4|8, NONE, FRM, IANY) + OPVALID_ENTRY_3(FADD, 4|8, FLAGS, FRM, FANY, FANY) + OPVALID_ENTRY_3(FSUB, 4|8, FLAGS, FRM, FANY, FANY) + OPVALID_ENTRY_2(FCMP, 4|8, FLAGS, FANY, FANY) + OPVALID_ENTRY_3(FMUL, 4|8, FLAGS, FRM, FANY, FANY) + OPVALID_ENTRY_3(FDIV, 4|8, FLAGS, FRM, FANY, FANY) + OPVALID_ENTRY_2(FNEG, 4|8, FLAGS, FRM, FANY) + OPVALID_ENTRY_2(FABS, 4|8, FLAGS, FRM, FANY) + OPVALID_ENTRY_2(FSQRT, 4|8, FLAGS, FRM, FANY) + OPVALID_ENTRY_2(FRECIP, 4|8, NONE, FRM, FANY) + OPVALID_ENTRY_2(FRSQRT, 4|8, NONE, FRM, FANY) +}; + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +/* we presume the existence of a C back-end interface */ +extern const drcbe_interface drcbe_c_be_interface; + +/* extern a reference to the native DRC interface if it exists */ +#ifndef NATIVE_DRC +#define NATIVE_DRC drcbe_c_be_interface +#else +extern const drcbe_interface NATIVE_DRC; +#endif + +/* table that is built up on first alloc which looks up via opcode */ +static drcuml_opcode_valid opcode_valid_table[DRCUML_OP_MAX]; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +#ifdef MAME_DEBUG +static void validate_instruction(drcuml_block *block, const drcuml_instruction *inst); +#else +#define validate_instruction(block, inst) do { } while (0) +#endif + + + +/*************************************************************************** + INITIALIZATION/TEARDOWN +***************************************************************************/ + +/*------------------------------------------------- + drcuml_alloc - allocate state for the code + generator and initialize the back-end +-------------------------------------------------*/ + +drcuml_state *drcuml_alloc(drccache *cache, UINT32 flags, int modes, int addrbits, int ignorebits) +{ + drcuml_state *drcuml; + int opnum; + + /* allocate state */ + drcuml = drccache_memory_alloc(cache, sizeof(*drcuml)); + if (drcuml == NULL) + return NULL; + memset(drcuml, 0, sizeof(*drcuml)); + + /* initialize the state */ + drcuml->cache = cache; + drcuml->beintf = (flags & DRCUML_OPTION_USE_C) ? &drcbe_c_be_interface : &NATIVE_DRC; + + /* if we're to log, create the logfile */ + if (flags & DRCUML_OPTION_LOG_UML) + drcuml->umllog = fopen("drcuml.asm", "w"); + + /* allocate the back-end */ + drcuml->bestate = (*drcuml->beintf->be_alloc)(drcuml, cache, flags, modes, addrbits, ignorebits); + if (drcuml->bestate == NULL) + { + drcuml_free(drcuml); + return NULL; + } + + /* update the valid opcode table */ + for (opnum = 0; opnum < ARRAY_LENGTH(opcode_valid_list); opnum++) + opcode_valid_table[opcode_valid_list[opnum].opcode] = opcode_valid_list[opnum]; + + return drcuml; +} + + +/*------------------------------------------------- + drcuml_reset - reset the state completely, + flushing the cache and all information +-------------------------------------------------*/ + +void drcuml_reset(drcuml_state *drcuml) +{ + drcuml_codehandle *handle; + jmp_buf errorbuf; + + /* flush the cache */ + drccache_flush(drcuml->cache); + + /* if we error here, we are screwed */ + if (setjmp(errorbuf) != 0) + fatalerror("Out of cache space in drcuml_reset"); + + /* reset all handle code pointers */ + for (handle = drcuml->handlelist; handle != NULL; handle = handle->next) + handle->code = NULL; + + /* call the backend to reset */ + (*drcuml->beintf->be_reset)(drcuml->bestate); +} + + +/*------------------------------------------------- + drcuml_free - free state for the code generator + and the back-end +-------------------------------------------------*/ + +void drcuml_free(drcuml_state *drcuml) +{ + /* free the back-end */ + if (drcuml->bestate != NULL) + (*drcuml->beintf->be_free)(drcuml->bestate); + + /* free all the blocks */ + while (drcuml->blocklist != NULL) + { + drcuml_block *block = drcuml->blocklist; + + /* remove the item from the list */ + drcuml->blocklist = block->next; + + /* free memory */ + if (block->inst != NULL) + free(block->inst); + free(block); + } + + /* close any files */ + if (drcuml->umllog != NULL) + fclose(drcuml->umllog); +} + + + +/*************************************************************************** + CODE BLOCK GENERATION +***************************************************************************/ + +/*------------------------------------------------- + drcuml_block_begin - begin a new code block +-------------------------------------------------*/ + +drcuml_block *drcuml_block_begin(drcuml_state *drcuml, UINT32 maxinst, jmp_buf *errorbuf) +{ + drcuml_block *bestblock = NULL; + drcuml_block *block; + + /* find an inactive block that matches our qualifications */ + for (block = drcuml->blocklist; block != NULL; block = block->next) + if (!block->inuse && block->maxinst >= maxinst && (bestblock == NULL || block->maxinst < bestblock->maxinst)) + bestblock = block; + + /* if we failed to find one, allocate a new one */ + if (bestblock == NULL) + { + /* allocate the block structure itself */ + bestblock = malloc(sizeof(*bestblock)); + if (bestblock == NULL) + fatalerror("Out of memory allocating block in drcuml_block_begin"); + memset(bestblock, 0, sizeof(*bestblock)); + + /* fill in the structure */ + bestblock->drcuml = drcuml; + bestblock->next = drcuml->blocklist; + bestblock->maxinst = maxinst * 3 / 2; + bestblock->inst = malloc(sizeof(drcuml_instruction) * bestblock->maxinst); + if (bestblock->inst == NULL) + fatalerror("Out of memory allocating instruction array in drcuml_block_begin"); + + /* hook us into the list */ + drcuml->blocklist = bestblock; + } + + /* set up the block information and return it */ + bestblock->inuse = TRUE; + bestblock->nextinst = 0; + bestblock->errorbuf = errorbuf; + + return bestblock; +} + + +/*------------------------------------------------- + drcuml_block_append_0 - append an opcode with + 0 parameters to the block +-------------------------------------------------*/ + +void drcuml_block_append_0(drcuml_block *block, drcuml_opcode op, UINT8 size, UINT8 condflags) +{ + drcuml_instruction *inst = &block->inst[block->nextinst++]; + + assert(block->inuse); + + /* get a pointer to the next instruction */ + if (block->nextinst > block->maxinst) + fatalerror("Overran maxinst in drcuml_block_append"); + + /* fill in the instruction */ + inst->opcode = (UINT8)op; + inst->size = size; + inst->condflags = condflags; + inst->numparams = 0; + + /* validation */ + validate_instruction(block, inst); +} + + +/*------------------------------------------------- + drcuml_block_append_1 - append an opcode with + 1 parameter to the block +-------------------------------------------------*/ + +void drcuml_block_append_1(drcuml_block *block, drcuml_opcode op, UINT8 size, UINT8 condflags, drcuml_ptype p0type, drcuml_pvalue p0value) +{ + drcuml_instruction *inst = &block->inst[block->nextinst++]; + + assert(block->inuse); + + /* get a pointer to the next instruction */ + if (block->nextinst > block->maxinst) + fatalerror("Overran maxinst in drcuml_block_append"); + + /* fill in the instruction */ + inst->opcode = (UINT8)op; + inst->size = size; + inst->condflags = condflags; + inst->numparams = 1; + inst->param[0].type = p0type; + inst->param[0].value = p0value; + + /* validation */ + validate_instruction(block, inst); +} + + +/*------------------------------------------------- + drcuml_block_append_2 - append an opcode with + 2 parameters to the block +-------------------------------------------------*/ + +void drcuml_block_append_2(drcuml_block *block, drcuml_opcode op, UINT8 size, UINT8 condflags, drcuml_ptype p0type, drcuml_pvalue p0value, drcuml_ptype p1type, drcuml_pvalue p1value) +{ + drcuml_instruction *inst = &block->inst[block->nextinst++]; + + assert(block->inuse); + + /* get a pointer to the next instruction */ + if (block->nextinst > block->maxinst) + fatalerror("Overran maxinst in drcuml_block_append"); + + /* fill in the instruction */ + inst->opcode = (UINT8)op; + inst->size = size; + inst->condflags = condflags; + inst->numparams = 2; + inst->param[0].type = p0type; + inst->param[0].value = p0value; + inst->param[1].type = p1type; + inst->param[1].value = p1value; + + /* validation */ + validate_instruction(block, inst); +} + + +/*------------------------------------------------- + drcuml_block_append_3 - append an opcode with + 3 parameters to the block +-------------------------------------------------*/ + +void drcuml_block_append_3(drcuml_block *block, drcuml_opcode op, UINT8 size, UINT8 condflags, drcuml_ptype p0type, drcuml_pvalue p0value, drcuml_ptype p1type, drcuml_pvalue p1value, drcuml_ptype p2type, drcuml_pvalue p2value) +{ + drcuml_instruction *inst = &block->inst[block->nextinst++]; + + assert(block->inuse); + + /* get a pointer to the next instruction */ + if (block->nextinst > block->maxinst) + fatalerror("Overran maxinst in drcuml_block_append"); + + /* fill in the instruction */ + inst->opcode = (UINT8)op; + inst->size = size; + inst->condflags = condflags; + inst->numparams = 3; + inst->param[0].type = p0type; + inst->param[0].value = p0value; + inst->param[1].type = p1type; + inst->param[1].value = p1value; + inst->param[2].type = p2type; + inst->param[2].value = p2value; + + /* validation */ + validate_instruction(block, inst); +} + + +/*------------------------------------------------- + drcuml_block_append_4 - append an opcode with + 4 parameters to the block +-------------------------------------------------*/ + +void drcuml_block_append_4(drcuml_block *block, drcuml_opcode op, UINT8 size, UINT8 condflags, drcuml_ptype p0type, drcuml_pvalue p0value, drcuml_ptype p1type, drcuml_pvalue p1value, drcuml_ptype p2type, drcuml_pvalue p2value, drcuml_ptype p3type, drcuml_pvalue p3value) +{ + drcuml_instruction *inst = &block->inst[block->nextinst++]; + + assert(block->inuse); + + /* get a pointer to the next instruction */ + if (block->nextinst > block->maxinst) + fatalerror("Overran maxinst in drcuml_block_append"); + + /* fill in the instruction */ + inst->opcode = (UINT8)op; + inst->size = size; + inst->condflags = condflags; + inst->numparams = 4; + inst->param[0].type = p0type; + inst->param[0].value = p0value; + inst->param[1].type = p1type; + inst->param[1].value = p1value; + inst->param[2].type = p2type; + inst->param[2].value = p2value; + inst->param[3].type = p3type; + inst->param[3].value = p3value; + + /* validation */ + validate_instruction(block, inst); +} + + +/*------------------------------------------------- + drcuml_block_end - complete a code block and + commit it to the cache via the back-end +-------------------------------------------------*/ + +void drcuml_block_end(drcuml_block *block) +{ + drcuml_state *drcuml = block->drcuml; + + assert(block->inuse); + + /* if we have a logfile, generate a disassembly of the block */ + if (drcuml->umllog != NULL) + { + int instnum; + + /* iterate over instructions and output */ + for (instnum = 0; instnum < block->nextinst; instnum++) + { + char dasm[512]; + drcuml_disasm(&block->inst[instnum], dasm); + drcuml_log_printf(drcuml, "%4d: %s\n", instnum, dasm); + } + drcuml_log_printf(drcuml, "\n\n"); + fflush(drcuml->umllog); + } + + /* generate the code via the back-end */ + (*drcuml->beintf->be_generate)(drcuml->bestate, block, block->inst, block->nextinst); + + /* block is no longer in use */ + block->inuse = FALSE; +} + + +/*------------------------------------------------- + drcuml_block_abort - abort a code block in + progress +-------------------------------------------------*/ + +void drcuml_block_abort(drcuml_block *block) +{ + assert(block->inuse); + + /* block is no longer in use */ + block->inuse = FALSE; + + /* unwind */ + longjmp(*block->errorbuf, 1); +} + + +/*------------------------------------------------- + drcuml_hash_exists - return true if a hash + entry exists for the given mode/pc +-------------------------------------------------*/ + +int drcuml_hash_exists(drcuml_state *drcuml, UINT32 mode, UINT32 pc) +{ + return (*drcuml->beintf->be_hash_exists)(drcuml->bestate, mode, pc); +} + + + +/*************************************************************************** + CODE EXECUTION +***************************************************************************/ + +/*------------------------------------------------- + drcuml_execute - execute at the given PC/mode + for the specified cycles +-------------------------------------------------*/ + +int drcuml_execute(drcuml_state *drcuml, drcuml_codehandle *entry) +{ + return (*drcuml->beintf->be_execute)(drcuml->bestate, entry); +} + + + +/*************************************************************************** + CODE HANDLES +***************************************************************************/ + +/*------------------------------------------------- + drcuml_handle_alloc - allocate a new handle +-------------------------------------------------*/ + +drcuml_codehandle *drcuml_handle_alloc(drcuml_state *drcuml, const char *name) +{ + drcuml_codehandle *handle; + char *string; + + /* allocate space for a copy of the string */ + string = drccache_memory_alloc(drcuml->cache, strlen(name) + 1); + if (string == NULL) + return NULL; + strcpy(string, name); + + /* allocate a new handle info */ + handle = drccache_memory_alloc_near(drcuml->cache, sizeof(*handle)); + if (handle == NULL) + { + drccache_memory_free(drcuml->cache, string, strlen(name) + 1); + return NULL; + } + memset(handle, 0, sizeof(*handle)); + + /* fill in the rest of the info and add to the list of handles */ + handle->drcuml = drcuml; + handle->string = string; + handle->next = drcuml->handlelist; + drcuml->handlelist = handle; + + return handle; +} + + +/*------------------------------------------------- + drcuml_handle_set_codeptr - set a codeptr + associated with a handle +-------------------------------------------------*/ + +void drcuml_handle_set_codeptr(drcuml_codehandle *handle, drccodeptr code) +{ + assert(handle->code == NULL); + assert_in_cache(handle->drcuml->cache, code); + handle->code = code; +} + + +/*------------------------------------------------- + drcuml_handle_codeptr - get the pointer from + a handle +-------------------------------------------------*/ + +drccodeptr drcuml_handle_codeptr(const drcuml_codehandle *handle) +{ + return handle->code; +} + + +/*------------------------------------------------- + drcuml_handle_codeptr_addr - get the address + of the pointer from a handle +-------------------------------------------------*/ + +drccodeptr *drcuml_handle_codeptr_addr(drcuml_codehandle *handle) +{ + return &handle->code; +} + + +/*------------------------------------------------- + drcuml_handle_name - get the name of a handle +-------------------------------------------------*/ + +const char *drcuml_handle_name(const drcuml_codehandle *handle) +{ + return handle->string; +} + + + +/*************************************************************************** + CODE LOGGING +***************************************************************************/ + +/*------------------------------------------------- + drcuml_log_printf - directly printf to the UML + log if generated +-------------------------------------------------*/ + +void drcuml_log_printf(drcuml_state *drcuml, const char *format, ...) +{ + /* if we have a file, print to it */ + if (drcuml->umllog != NULL) + { + va_list va; + + /* do the printf */ + va_start(va, format); + vfprintf(drcuml->umllog, format, va); + va_end(va); + } +} + + +/*------------------------------------------------- + drcuml_add_comment - attach a comment to the + current output location in the specified block +-------------------------------------------------*/ + +void drcuml_add_comment(drcuml_block *block, const char *format, ...) +{ + char buffer[512]; + char *comment; + va_list va; + + assert(block->inuse); + + /* do the printf */ + va_start(va, format); + vsprintf(buffer, format, va); + va_end(va); + + /* allocate space in the cache to hold the comment */ + comment = drccache_memory_alloc_temporary(block->drcuml->cache, strlen(buffer) + 1); + if (comment == NULL) + return; + strcpy(comment, buffer); + + /* add an instruction with a pointer */ + drcuml_block_append_1(block, DRCUML_OP_COMMENT, 4, DRCUML_COND_ALWAYS, MEM(comment)); +} + + + +/*************************************************************************** + OPCODE VALIDATION +***************************************************************************/ + +/*------------------------------------------------- + validate_instruction - verify that the + instruction created meets all requirements +-------------------------------------------------*/ + +#ifdef MAME_DEBUG +static void validate_instruction(drcuml_block *block, const drcuml_instruction *inst) +{ + const drcuml_opcode_valid *opvalid = &opcode_valid_table[inst->opcode]; + + /* validate information */ + assert(inst->opcode != DRCUML_OP_INVALID && inst->opcode < DRCUML_OP_MAX); + assert(inst->size == 1 || inst->size == 2 || inst->size == 4 || inst->size == 8); + assert((opvalid->sizes & inst->size) != 0); + if (inst->numparams > 0) + { + assert(inst->param[0].type > DRCUML_PTYPE_NONE && inst->param[0].type < DRCUML_PTYPE_MAX); + assert(((opvalid->p0types >> inst->param[0].type) & 1) != 0); + if (inst->param[0].type == DRCUML_PTYPE_MEMORY && !(opvalid->p0types & 0x80)) + assert_in_near_cache(block->drcuml->cache, (void *)(FPTR)inst->param[0].value); + } + if (inst->numparams > 1) + { + assert(inst->param[1].type > DRCUML_PTYPE_NONE && inst->param[1].type < DRCUML_PTYPE_MAX); + assert(((opvalid->p1types >> inst->param[1].type) & 1) != 0); + if (inst->param[1].type == DRCUML_PTYPE_MEMORY && !(opvalid->p1types & 0x80)) + assert_in_near_cache(block->drcuml->cache, (void *)(FPTR)inst->param[1].value); + } + if (inst->numparams > 2) + { + assert(inst->param[2].type > DRCUML_PTYPE_NONE && inst->param[2].type < DRCUML_PTYPE_MAX); + assert(((opvalid->p2types >> inst->param[2].type) & 1) != 0); + if (inst->param[2].type == DRCUML_PTYPE_MEMORY && !(opvalid->p2types & 0x80)) + assert_in_near_cache(block->drcuml->cache, (void *)(FPTR)inst->param[2].value); + } + if (inst->numparams > 3) + { + assert(inst->param[3].type > DRCUML_PTYPE_NONE && inst->param[3].type < DRCUML_PTYPE_MAX); + assert(((opvalid->p3types >> inst->param[3].type) & 1) != 0); + if (inst->param[3].type == DRCUML_PTYPE_MEMORY && !(opvalid->p3types & 0x80)) + assert_in_near_cache(block->drcuml->cache, (void *)(FPTR)inst->param[3].value); + } +} +#endif diff --git a/src/emu/cpu/drcuml.h b/src/emu/cpu/drcuml.h new file mode 100644 index 00000000000..a4cfc570dab --- /dev/null +++ b/src/emu/cpu/drcuml.h @@ -0,0 +1,418 @@ +/*************************************************************************** + + drcuml.h + + Universal machine language for dynamic recompiling CPU cores. + + Copyright Aaron Giles + Released for general non-commercial use under the MAME license + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __DRCUML_H__ +#define __DRCUML_H__ + +#include "memory.h" +#include "drccache.h" +#include <setjmp.h> + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* these options are passed into drcuml_alloc() and control global behaviors */ +#define DRCUML_OPTION_USE_C 0x0001 /* always use the C back-end */ +#define DRCUML_OPTION_LOG_UML 0x0002 /* generate a UML disassembly of each block */ +#define DRCUML_OPTION_LOG_NATIVE 0x0004 /* tell the back-end to generate a native disassembly of each block */ + + +/* opcode parameter types */ +enum _drcuml_ptype +{ + DRCUML_PTYPE_NONE = 0, + DRCUML_PTYPE_IMMEDIATE, + DRCUML_PTYPE_INT_REGISTER, + DRCUML_PTYPE_FLOAT_REGISTER, + DRCUML_PTYPE_MAPVAR, + DRCUML_PTYPE_MEMORY, + DRCUML_PTYPE_MAX +}; +typedef enum _drcuml_ptype drcuml_ptype; + + +/* these define the registers for the UML */ +enum +{ + DRCUML_REG_INVALID = 0, /* 0 is invalid */ + + /* integer registers */ + DRCUML_REG_I0 = 0x400, + DRCUML_REG_I1, + DRCUML_REG_I2, + DRCUML_REG_I3, + DRCUML_REG_I4, + DRCUML_REG_I5, + DRCUML_REG_I6, + DRCUML_REG_I7, + DRCUML_REG_I8, + DRCUML_REG_I9, + DRCUML_REG_I_END, + + /* floating point registers */ + DRCUML_REG_F0 = 0x800, + DRCUML_REG_F1, + DRCUML_REG_F2, + DRCUML_REG_F3, + DRCUML_REG_F4, + DRCUML_REG_F5, + DRCUML_REG_F6, + DRCUML_REG_F7, + DRCUML_REG_F8, + DRCUML_REG_F9, + DRCUML_REG_F_END, + + /* map variables */ + DRCUML_MAPVAR_M0 = 0xc00, + DRCUML_MAPVAR_M1, + DRCUML_MAPVAR_M2, + DRCUML_MAPVAR_M3, + DRCUML_MAPVAR_M4, + DRCUML_MAPVAR_M5, + DRCUML_MAPVAR_M6, + DRCUML_MAPVAR_M7, + DRCUML_MAPVAR_M8, + DRCUML_MAPVAR_M9, + DRCUML_MAPVAR_END +}; + + +/* UML flag definitions */ +enum +{ + DRCUML_FLAG_C = 1, /* carry flag */ + DRCUML_FLAG_V = 2, /* overflow flag (defined for integer only) */ + DRCUML_FLAG_Z = 4, /* zero flag */ + DRCUML_FLAG_S = 8, /* sign flag (defined for integer only) */ + DRCUML_FLAG_U = 16 /* unordered flag (defined for FP only) */ +}; + + +/* these define the conditions for the UML */ +/* note that these are defined such that (condition ^ 1) is always the opposite */ +/* they are also defined so as not to conflict with the flag bits above */ +enum +{ + DRCUML_COND_ALWAYS = 0, + + DRCUML_COND_Z = 0x80, /* requires Z */ + DRCUML_COND_NZ, /* requires Z */ + DRCUML_COND_S, /* requires S */ + DRCUML_COND_NS, /* requires S */ + DRCUML_COND_C, /* requires C */ + DRCUML_COND_NC, /* requires C */ + DRCUML_COND_V, /* requires V */ + DRCUML_COND_NV, /* requires V */ + DRCUML_COND_U, /* requires U */ + DRCUML_COND_NU, /* requires U */ + DRCUML_COND_A, /* requires CZ */ + DRCUML_COND_BE, /* requires CZ */ + DRCUML_COND_G, /* requires SVZ */ + DRCUML_COND_LE, /* requires SVZ */ + DRCUML_COND_L, /* requires SV */ + DRCUML_COND_GE, /* requires SV */ + + DRCUML_COND_MAX +}; + + +/* basic condition code aliases */ +enum +{ + DRCUML_COND_E = DRCUML_COND_Z, + DRCUML_COND_NE = DRCUML_COND_NZ, + DRCUML_COND_B = DRCUML_COND_C, + DRCUML_COND_AE = DRCUML_COND_NC +}; + + +/* floating point modes */ +enum +{ + DRCUML_FMOD_TRUNC, /* truncate */ + DRCUML_FMOD_ROUND, /* round */ + DRCUML_FMOD_CEIL, /* round up */ + DRCUML_FMOD_FLOOR /* round down */ +}; + + +/* these define the opcodes for the UML */ +enum _drcuml_opcode +{ + DRCUML_OP_INVALID, + + /* Compile-time opcodes */ + DRCUML_OP_HANDLE, /* HANDLE handle */ + DRCUML_OP_HASH, /* HASH mode,pc */ + DRCUML_OP_LABEL, /* LABEL imm */ + DRCUML_OP_COMMENT, /* COMMENT string */ + DRCUML_OP_MAPVAR, /* MAPVAR mapvar,value */ + + /* Control Flow Operations */ + DRCUML_OP_DEBUG, /* DEBUG pc */ + DRCUML_OP_EXIT, /* EXIT src1[,c] */ + DRCUML_OP_HASHJMP, /* HASHJMP mode,pc,handle */ + DRCUML_OP_JMP, /* JMP imm[,c] */ + DRCUML_OP_EXH, /* EXH handle,param[,c] */ + DRCUML_OP_CALLH, /* CALLH handle[,c] */ + DRCUML_OP_RET, /* RET [c] */ + DRCUML_OP_CALLC, /* CALLC func,ptr[,c] */ + DRCUML_OP_RECOVER, /* RECOVER dst,mapvar */ + + /* Internal Register Operations */ + DRCUML_OP_SETFMOD, /* SETFMOD src */ + DRCUML_OP_GETFMOD, /* GETFMOD dst */ + DRCUML_OP_GETEXP, /* GETEXP dst,index */ + + /* Integer Operations */ + DRCUML_OP_LOAD1U, /* LOAD1U dst,base,index */ + DRCUML_OP_LOAD1S, /* LOAD1S dst,base,index */ + DRCUML_OP_LOAD2U, /* LOAD2U dst,base,index */ + DRCUML_OP_LOAD2S, /* LOAD2S dst,base,index */ + DRCUML_OP_LOAD4U, /* LOAD4U dst,base,index */ + DRCUML_OP_LOAD4S, /* LOAD4S dst,base,index */ + DRCUML_OP_LOAD8U, /* LOAD8U dst,base,index */ + DRCUML_OP_STORE1, /* STORE1 base,index,src */ + DRCUML_OP_STORE2, /* STORE2 base,index,src */ + DRCUML_OP_STORE4, /* STORE4 base,index,src */ + DRCUML_OP_STORE8, /* STORE8 base,index,src */ + DRCUML_OP_READ1U, /* READ1U dst,space,src1 */ + DRCUML_OP_READ1S, /* READ1S dst,space,src1 */ + DRCUML_OP_READ2U, /* READ2U dst,space,src1 */ + DRCUML_OP_READ2S, /* READ2S dst,space,src1 */ + DRCUML_OP_READ2M, /* READ2M dst,space,src1,mask */ + DRCUML_OP_READ4U, /* READ4U dst,space,src1 */ + DRCUML_OP_READ4S, /* READ4S dst,space,src1 */ + DRCUML_OP_READ4M, /* READ4M dst,space,src1,mask */ + DRCUML_OP_READ8U, /* READ8U dst,space,src1 */ + DRCUML_OP_READ8M, /* READ8M dst,space,src1,mask */ + DRCUML_OP_WRITE1, /* WRITE1 space,dst,src1 */ + DRCUML_OP_WRITE2, /* WRITE2 space,dst,src1 */ + DRCUML_OP_WRIT2M, /* WRIT2M space,dst,mask,src1 */ + DRCUML_OP_WRITE4, /* WRITE4 space,dst,src1 */ + DRCUML_OP_WRIT4M, /* WRIT4M space,dst,mask,src1 */ + DRCUML_OP_WRITE8, /* WRITE8 space,dst,src1 */ + DRCUML_OP_WRIT8M, /* WRIT8M space,dst,mask,src1 */ + DRCUML_OP_FLAGS, /* FLAGS dst,mask,table */ + DRCUML_OP_MOV, /* MOV dst,src[,c] */ + DRCUML_OP_ZEXT1, /* ZEXT1 dst,src */ + DRCUML_OP_ZEXT2, /* ZEXT2 dst,src */ + DRCUML_OP_ZEXT4, /* ZEXT4 dst,src */ + DRCUML_OP_SEXT1, /* SEXT1 dst,src */ + DRCUML_OP_SEXT2, /* SEXT2 dst,src */ + DRCUML_OP_SEXT4, /* SEXT4 dst,src */ + DRCUML_OP_ADD, /* ADD dst,src1,src2[,f] */ + DRCUML_OP_ADDC, /* ADDC dst,src1,src2[,f] */ + DRCUML_OP_SUB, /* SUB dst,src1,src2[,f] */ + DRCUML_OP_SUBB, /* SUBB dst,src1,src2[,f] */ + DRCUML_OP_CMP, /* CMP src1,src2[,f] */ + DRCUML_OP_MULU, /* MULU dst,edst,src1,src2[,f] */ + DRCUML_OP_MULS, /* MULS dst,edst,src1,src2[,f] */ + DRCUML_OP_DIVU, /* DIVU dst,edst,src1,src2[,f] */ + DRCUML_OP_DIVS, /* DIVS dst,edst,src1,src2[,f] */ + DRCUML_OP_AND, /* AND dst,src1,src2[,f] */ + DRCUML_OP_TEST, /* TEST src1,src2[,f] */ + DRCUML_OP_OR, /* OR dst,src1,src2[,f] */ + DRCUML_OP_XOR, /* XOR dst,src1,src2[,f] */ + DRCUML_OP_SHL, /* SHL dst,src,count[,f] */ + DRCUML_OP_SHR, /* SHR dst,src,count[,f] */ + DRCUML_OP_SAR, /* SAR dst,src,count[,f] */ + DRCUML_OP_ROL, /* ROL dst,src,count[,f] */ + DRCUML_OP_ROLC, /* ROLC dst,src,count[,f] */ + DRCUML_OP_ROR, /* ROL dst,src,count[,f] */ + DRCUML_OP_RORC, /* ROLC dst,src,count[,f] */ + + /* Floating Point Operations */ + DRCUML_OP_FLOAD, /* FLOAD dst,base,index */ + DRCUML_OP_FSTORE, /* FSTORE base,index,src */ + DRCUML_OP_FREAD, /* FREAD dst,space,src1 */ + DRCUML_OP_FWRITE, /* FWRITE space,dst,src1 */ + DRCUML_OP_FMOV, /* FSMOV dst,src1[,c] */ + DRCUML_OP_FTOI4, /* FTOI4 dst,src1 */ + DRCUML_OP_FTOI4T, /* FTOI4T dst,src1 */ + DRCUML_OP_FTOI4R, /* FTOI4R dst,src1 */ + DRCUML_OP_FTOI4F, /* FTOI4F dst,src1 */ + DRCUML_OP_FTOI4C, /* FTOI4C dst,src1 */ + DRCUML_OP_FTOI8, /* FTOI8 dst,src1 */ + DRCUML_OP_FTOI8T, /* FTOI8T dst,src1 */ + DRCUML_OP_FTOI8R, /* FTOI8R dst,src1 */ + DRCUML_OP_FTOI8F, /* FTOI8F dst,src1 */ + DRCUML_OP_FTOI8C, /* FTOI8C dst,src1 */ + DRCUML_OP_FFRFS, /* FFRFS dst,src1 */ + DRCUML_OP_FFRFD, /* FFRFD dst,src1 */ + DRCUML_OP_FFRI4, /* FFRI4 dst,src1 */ + DRCUML_OP_FFRI8, /* FFRI8 dst,src1 */ + DRCUML_OP_FADD, /* FADD dst,src1,src2 */ + DRCUML_OP_FSUB, /* FSUB dst,src1,src2 */ + DRCUML_OP_FCMP, /* FCMP src1,src2 */ + DRCUML_OP_FMUL, /* FMUL dst,src1,src2 */ + DRCUML_OP_FDIV, /* FDIV dst,src1,src2 */ + DRCUML_OP_FNEG, /* FNEG dst,src1 */ + DRCUML_OP_FABS, /* FABS dst,src1 */ + DRCUML_OP_FSQRT, /* FSQRT dst,src1 */ + DRCUML_OP_FRECIP, /* FRECIP dst,src1 */ + DRCUML_OP_FRSQRT, /* FRSQRT dst,src1 */ + + DRCUML_OP_MAX +}; +typedef enum _drcuml_opcode drcuml_opcode; + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* represents a reference to a local label in the code */ +typedef UINT32 drcuml_codelabel; + +/* represents the value of an opcode parameter */ +typedef UINT64 drcuml_pvalue; + + +/* opaque structure describing UML generation state */ +typedef struct _drcuml_state drcuml_state; + +/* opaque structure describing UML codegen block */ +typedef struct _drcuml_block drcuml_block; + +/* opaque structure describing back-end state */ +typedef struct _drcbe_state drcbe_state; + +/* opaque structure describing a code handle */ +typedef struct _drcuml_codehandle drcuml_codehandle; + + +/* a parameter for a UML instructon is encoded like this */ +typedef struct _drcuml_parameter drcuml_parameter; +struct _drcuml_parameter +{ + drcuml_ptype type; /* parameter type */ + drcuml_pvalue value; /* parameter value */ +}; + + +/* a single UML instructon is encoded like this */ +typedef struct _drcuml_instruction drcuml_instruction; +struct _drcuml_instruction +{ + drcuml_opcode opcode; /* opcode */ + UINT8 condflags; /* condition or flags */ + UINT8 size; /* operation size */ + UINT8 numparams; /* number of parameters */ + drcuml_parameter param[4]; /* up to 4 parameters */ +}; + + +/* typedefs for back-end callback functions */ +typedef drcbe_state *(*drcbe_alloc_func)(drcuml_state *drcuml, drccache *cache, UINT32 flags, int modes, int addrbits, int ignorebits); +typedef void (*drcbe_free_func)(drcbe_state *state); +typedef void (*drcbe_reset_func)(drcbe_state *state); +typedef int (*drcbe_execute_func)(drcbe_state *state, drcuml_codehandle *entry); +typedef void (*drcbe_generate_func)(drcbe_state *state, drcuml_block *block, const drcuml_instruction *instlist, UINT32 numinst); +typedef int (*drcbe_hash_exists)(drcbe_state *state, UINT32 mode, UINT32 pc); + + +/* interface structure for a back-end */ +typedef struct _drcbe_interface drcbe_interface; +struct _drcbe_interface +{ + drcbe_alloc_func be_alloc; + drcbe_free_func be_free; + drcbe_reset_func be_reset; + drcbe_execute_func be_execute; + drcbe_generate_func be_generate; + drcbe_hash_exists be_hash_exists; +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* ----- initialization/teardown ----- */ + +/* allocate state for the code generator and initialize the back-end */ +drcuml_state *drcuml_alloc(drccache *cache, UINT32 flags, int modes, int addrbits, int ignorebits); + +/* reset the state completely, flushing the cache and all information */ +void drcuml_reset(drcuml_state *drcuml); + +/* free state for the code generator and the back-end */ +void drcuml_free(drcuml_state *drcuml); + + + +/* ----- code block generation ----- */ + +/* begin a new code block */ +drcuml_block *drcuml_block_begin(drcuml_state *drcuml, UINT32 maxinst, jmp_buf *errorbuf); + +/* append an opcode to the block, with 0-4 parameters */ +void drcuml_block_append_0(drcuml_block *block, drcuml_opcode op, UINT8 size, UINT8 condflags); +void drcuml_block_append_1(drcuml_block *block, drcuml_opcode op, UINT8 size, UINT8 condflags, drcuml_ptype p0type, drcuml_pvalue p0value); +void drcuml_block_append_2(drcuml_block *block, drcuml_opcode op, UINT8 size, UINT8 condflags, drcuml_ptype p0type, drcuml_pvalue p0value, drcuml_ptype p1type, drcuml_pvalue p1value); +void drcuml_block_append_3(drcuml_block *block, drcuml_opcode op, UINT8 size, UINT8 condflags, drcuml_ptype p0type, drcuml_pvalue p0value, drcuml_ptype p1type, drcuml_pvalue p1value, drcuml_ptype p2type, drcuml_pvalue p2value); +void drcuml_block_append_4(drcuml_block *block, drcuml_opcode op, UINT8 size, UINT8 condflags, drcuml_ptype p0type, drcuml_pvalue p0value, drcuml_ptype p1type, drcuml_pvalue p1value, drcuml_ptype p2type, drcuml_pvalue p2value, drcuml_ptype p3type, drcuml_pvalue p3value); + +/* complete a code block and commit it to the cache via the back-end */ +void drcuml_block_end(drcuml_block *block); + +/* abort a code block in progress due to cache allocation failure */ +void drcuml_block_abort(drcuml_block *block); + +/* return true if a hash entry exists for the given mode/pc */ +int drcuml_hash_exists(drcuml_state *drcuml, UINT32 mode, UINT32 pc); + + + +/* ----- code execution ----- */ + +/* execute at the given PC/mode for the specified cycles */ +int drcuml_execute(drcuml_state *drcuml, drcuml_codehandle *entry); + + + +/* ----- code handles ----- */ + +/* allocate a new handle */ +drcuml_codehandle *drcuml_handle_alloc(drcuml_state *drcuml, const char *name); + +/* set the handle pointer */ +void drcuml_handle_set_codeptr(drcuml_codehandle *handle, drccodeptr code); + +/* get the pointer from a handle */ +drccodeptr drcuml_handle_codeptr(const drcuml_codehandle *handle); + +/* get the address of the pointer from a handle */ +drccodeptr *drcuml_handle_codeptr_addr(drcuml_codehandle *handle); + +/* get the name of a handle */ +const char *drcuml_handle_name(const drcuml_codehandle *handle); + + + +/* ----- code logging ----- */ + +/* directly printf to the UML log if generated */ +void drcuml_log_printf(drcuml_state *state, const char *format, ...) ATTR_PRINTF(2,3); + +/* attach a comment to the current output location in the specified block */ +void drcuml_add_comment(drcuml_block *block, const char *format, ...) ATTR_PRINTF(2,3); + + +#endif diff --git a/src/emu/cpu/drcumld.c b/src/emu/cpu/drcumld.c new file mode 100644 index 00000000000..fb2d3b4ef49 --- /dev/null +++ b/src/emu/cpu/drcumld.c @@ -0,0 +1,453 @@ +/*************************************************************************** + + drcumld.c + + Universal machine language disassembler. + + Copyright Aaron Giles + Released for general non-commercial use under the MAME license + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include "drcumld.h" +#include "drcumlsh.h" +#include "memory.h" + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef char *(*output_function)(char *dest, int size, const drcuml_parameter *param); + + +typedef struct _drcuml_opdesc drcuml_opdesc; +struct _drcuml_opdesc +{ + UINT16 opcode; + const char * opstring; + output_function param[5]; +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static char *output_cond(char *dest, int size, const drcuml_parameter *param); +static char *output_flags(char *dest, int size, const drcuml_parameter *param); +static char *output_flags_cmptest(char *dest, int size, const drcuml_parameter *param); +static char *output_param(char *dest, int size, const drcuml_parameter *param); +static char *output_param_string(char *dest, int size, const drcuml_parameter *param); +static char *output_param_handle(char *dest, int size, const drcuml_parameter *param); +static char *output_param_space(char *dest, int size, const drcuml_parameter *param); + + + +/*************************************************************************** + TABLES +***************************************************************************/ + +/* these define the conditions for the UML */ +static const char *condname[] = +{ + "z", "nz", + "s", "ns", + "c", "nc", + "v", "nv", + "u", "nu", + "a", "be", + "g", "le", + "l", "ge" +}; + +/* the source opcode table */ +static const drcuml_opdesc opcode_source_table[] = +{ + /* Compile-time opcodes */ + { DRCUML_OP_HANDLE, "handle", { output_param_handle } }, + { DRCUML_OP_HASH, "hash", { output_param, output_param } }, + { DRCUML_OP_LABEL, "label", { output_param } }, + { DRCUML_OP_COMMENT, "comment", { output_param_string } }, + { DRCUML_OP_MAPVAR, "mapvar", { output_param, output_param } }, + + /* Control Flow Operations */ + { DRCUML_OP_DEBUG, "debug", { output_param } }, + { DRCUML_OP_EXIT, "exit", { output_param, output_cond } }, + { DRCUML_OP_HASHJMP, "hashjmp", { output_param, output_param, output_param_handle } }, + { DRCUML_OP_JMP, "jmp", { output_param, output_cond } }, + { DRCUML_OP_EXH, "exh", { output_param_handle, output_param, output_cond } } , + { DRCUML_OP_CALLH, "callh", { output_param_handle, output_cond } }, + { DRCUML_OP_RET, "ret", { output_cond } }, + { DRCUML_OP_CALLC, "callc", { output_param, output_param, output_cond } }, + { DRCUML_OP_RECOVER, "recover", { output_param, output_param } }, + + /* Internal Register Operations */ + { DRCUML_OP_SETFMOD, "setfmod", { output_param } }, + { DRCUML_OP_GETFMOD, "getfmod", { output_param } }, + { DRCUML_OP_GETEXP, "getexp", { output_param } }, + + /* Integer Operations */ + { DRCUML_OP_LOAD1U, "!load1u", { output_param, output_param, output_param } }, + { DRCUML_OP_LOAD1S, "!load1s", { output_param, output_param, output_param } }, + { DRCUML_OP_LOAD2U, "!load2u", { output_param, output_param, output_param } }, + { DRCUML_OP_LOAD2S, "!load2s", { output_param, output_param, output_param } }, + { DRCUML_OP_LOAD4U, "!load4u", { output_param, output_param, output_param } }, + { DRCUML_OP_LOAD4S, "!load4s", { output_param, output_param, output_param } }, + { DRCUML_OP_LOAD8U, "!load8u", { output_param, output_param, output_param } }, + { DRCUML_OP_STORE1, "!store1", { output_param, output_param, output_param } }, + { DRCUML_OP_STORE2, "!store2", { output_param, output_param, output_param } }, + { DRCUML_OP_STORE4, "!store4", { output_param, output_param, output_param } }, + { DRCUML_OP_STORE8, "!store8", { output_param, output_param, output_param } }, + { DRCUML_OP_READ1U, "!read1u", { output_param, output_param_space, output_param } }, + { DRCUML_OP_READ1S, "!read1s", { output_param, output_param_space, output_param } }, + { DRCUML_OP_READ2U, "!read2u", { output_param, output_param_space, output_param } }, + { DRCUML_OP_READ2S, "!read2s", { output_param, output_param_space, output_param } }, + { DRCUML_OP_READ2M, "!read2m", { output_param, output_param_space, output_param, output_param } }, + { DRCUML_OP_READ4U, "!read4u", { output_param, output_param_space, output_param } }, + { DRCUML_OP_READ4S, "!read4s", { output_param, output_param_space, output_param } }, + { DRCUML_OP_READ4M, "!read4m", { output_param, output_param_space, output_param, output_param } }, + { DRCUML_OP_READ8U, "!read8u", { output_param, output_param_space, output_param } }, + { DRCUML_OP_READ8M, "!read8m", { output_param, output_param_space, output_param, output_param } }, + { DRCUML_OP_WRITE1, "!write1", { output_param_space, output_param, output_param } }, + { DRCUML_OP_WRITE2, "!write2", { output_param_space, output_param, output_param } }, + { DRCUML_OP_WRIT2M, "!writ2m", { output_param_space, output_param, output_param, output_param } }, + { DRCUML_OP_WRITE4, "!write4", { output_param_space, output_param, output_param } }, + { DRCUML_OP_WRIT4M, "!writ4m", { output_param_space, output_param, output_param, output_param } }, + { DRCUML_OP_WRITE8, "!write8", { output_param_space, output_param, output_param } }, + { DRCUML_OP_WRIT8M, "!writ8m", { output_param_space, output_param, output_param, output_param } }, + { DRCUML_OP_FLAGS, "!flags", { output_param, output_param, output_param } }, + { DRCUML_OP_MOV, "!mov", { output_param, output_param, output_cond } }, + { DRCUML_OP_ZEXT1, "!zext1", { output_param, output_param } }, + { DRCUML_OP_ZEXT2, "!zext2", { output_param, output_param } }, + { DRCUML_OP_ZEXT4, "!zext4", { output_param, output_param } }, + { DRCUML_OP_SEXT1, "!sext1", { output_param, output_param } }, + { DRCUML_OP_SEXT2, "!sext2", { output_param, output_param } }, + { DRCUML_OP_SEXT4, "!sext4", { output_param, output_param } }, + { DRCUML_OP_ADD, "!add", { output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_ADDC, "!addc", { output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_SUB, "!sub", { output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_SUBB, "!subb", { output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_CMP, "!cmp", { output_param, output_param, output_flags_cmptest } }, + { DRCUML_OP_MULU, "!mulu", { output_param, output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_MULS, "!muls", { output_param, output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_DIVU, "!divu", { output_param, output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_DIVS, "!divs", { output_param, output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_AND, "!and", { output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_TEST, "!test", { output_param, output_param, output_flags_cmptest } }, + { DRCUML_OP_OR, "!or", { output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_XOR, "!xor", { output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_SHL, "!shl", { output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_SHR, "!shr", { output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_SAR, "!sar", { output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_ROL, "!rol", { output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_ROLC, "!rolc", { output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_ROR, "!ror", { output_param, output_param, output_param, output_flags } }, + { DRCUML_OP_RORC, "!rorc", { output_param, output_param, output_param, output_flags } }, + + /* Floating-Point Operations */ + { DRCUML_OP_FLOAD, "f!load", { output_param, output_param, output_param } }, + { DRCUML_OP_FSTORE, "f!store", { output_param, output_param, output_param } }, + { DRCUML_OP_FREAD, "f!read", { output_param, output_param_space, output_param } }, + { DRCUML_OP_FWRITE, "f!write", { output_param_space, output_param, output_param } }, + { DRCUML_OP_FMOV, "f!mov", { output_param, output_param, output_cond } }, + { DRCUML_OP_FTOI4, "f!toi4", { output_param, output_param } }, + { DRCUML_OP_FTOI4T, "f!toi4t", { output_param, output_param } }, + { DRCUML_OP_FTOI4R, "f!toi4r", { output_param, output_param } }, + { DRCUML_OP_FTOI4F, "f!toi4f", { output_param, output_param } }, + { DRCUML_OP_FTOI4C, "f!toi4c", { output_param, output_param } }, + { DRCUML_OP_FTOI8, "f!toi8", { output_param, output_param } }, + { DRCUML_OP_FTOI8T, "f!toi8t", { output_param, output_param } }, + { DRCUML_OP_FTOI8R, "f!toi8r", { output_param, output_param } }, + { DRCUML_OP_FTOI8F, "f!toi8f", { output_param, output_param } }, + { DRCUML_OP_FTOI8C, "f!toi8c", { output_param, output_param } }, + { DRCUML_OP_FFRFS, "f!frfs", { output_param, output_param } }, + { DRCUML_OP_FFRFD, "f!frfd", { output_param, output_param } }, + { DRCUML_OP_FFRI4, "f!fri4", { output_param, output_param } }, + { DRCUML_OP_FFRI8, "f!fri8", { output_param, output_param } }, + { DRCUML_OP_FADD, "f!add", { output_param, output_param, output_param } }, + { DRCUML_OP_FSUB, "f!sub", { output_param, output_param, output_param } }, + { DRCUML_OP_FCMP, "f!cmp", { output_param, output_param, output_flags_cmptest } }, + { DRCUML_OP_FMUL, "f!mul", { output_param, output_param, output_param } }, + { DRCUML_OP_FDIV, "f!div", { output_param, output_param, output_param } }, + { DRCUML_OP_FNEG, "f!neg", { output_param, output_param } }, + { DRCUML_OP_FABS, "f!abs", { output_param, output_param } }, + { DRCUML_OP_FSQRT, "f!sqrt", { output_param, output_param } }, + { DRCUML_OP_FRECIP, "f!recip", { output_param, output_param } }, + { DRCUML_OP_FRSQRT, "f!rsqrt", { output_param, output_param } }, +}; + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +static const drcuml_opdesc *opcode_table[DRCUML_OP_MAX]; + + + +/*************************************************************************** + CORE DISASSEMBLER +***************************************************************************/ + +/*------------------------------------------------- + drcuml_disasm - disassemble one instruction +-------------------------------------------------*/ + +void drcuml_disasm(const drcuml_instruction *inst, char *buffer) +{ + const drcuml_opdesc *opdesc; + const char *stringptr; + char *dest = buffer; + int pnum, iparam = 0; + + /* if this is the first time through, populate the opcode table */ + if (opcode_table[DRCUML_OP_HASH] == NULL) + { + int tabledex; + + for (tabledex = 0; tabledex < ARRAY_LENGTH(opcode_source_table); tabledex++) + opcode_table[opcode_source_table[tabledex].opcode] = &opcode_source_table[tabledex]; + } + + /* find the opcode in the list */ + opdesc = opcode_table[inst->opcode]; + + /* NULL means invalid opcode */ + if (opdesc == NULL) + { + strcpy(buffer, "???"); + return; + } + + /* start with the opcode itself */ + stringptr = opdesc->opstring; + + /* insert a prefix for integer operations */ + if (stringptr[0] == '!') + { + stringptr++; + switch (inst->size) + { + case 1: *dest++ = 'b'; break; + case 2: *dest++ = 'w'; break; + case 4: break; + case 8: *dest++ = 'd'; break; + default: *dest++ = '?'; break; + } + } + + /* insert a prefix for floating point operations */ + if (stringptr[0] == 'f' && stringptr[1] == '!') + { + *dest++ = *stringptr++; + stringptr++; + switch (inst->size) + { + case 4: *dest++ = 's'; break; + case 8: *dest++ = 'd'; break; + default: *dest++ = '?'; break; + } + } + + /* copy the rest of the string */ + dest += sprintf(dest, "%s", stringptr); + + /* iterate over parameters */ + for (pnum = 0; pnum < ARRAY_LENGTH(opdesc->param); pnum++) + { + output_function func = opdesc->param[pnum]; + drcuml_parameter param; + + /* stop when we hit NULL */ + if (func == NULL) + break; + + /* if we are outputting a condition but it's "always", skip it */ + if (func == output_cond) + { + if (inst->condflags == DRCUML_COND_ALWAYS) + continue; + param.value = inst->condflags; + } + + /* if we are outputting flags but they are "none", skip it */ + else if (func == output_flags) + { + if (inst->condflags == FLAGS_NONE) + continue; + param.value = inst->condflags; + } + + /* if we are outputting test/cmp flags but they are "all", skip it */ + else if (func == output_flags_cmptest) + { + if (inst->opcode == DRCUML_OP_CMP && inst->condflags == FLAGS_ALLI) + continue; + if (inst->opcode == DRCUML_OP_FCMP && inst->condflags == FLAGS_ALLF) + continue; + if (inst->opcode == DRCUML_OP_TEST && inst->condflags == (FLAGS_S|FLAGS_Z)) + continue; + param.value = inst->condflags; + } + + /* otherwise, we are fetching an actual parameter */ + else + param = inst->param[iparam++]; + + /* if we are less than 8 characters, pad to 8 */ + if (dest - buffer < 8) + dest += sprintf(dest, "%*s", 8 - (dest - buffer), ""); + + /* otherwise, add a comma */ + else + dest += sprintf(dest, ","); + + /* then append the parameter */ + dest = (*func)(dest, inst->size, ¶m); + } +} + + + +/*************************************************************************** + OUTPUT HELPERS +***************************************************************************/ + +/*------------------------------------------------- + output_cond - output condition +-------------------------------------------------*/ + +static char *output_cond(char *dest, int size, const drcuml_parameter *param) +{ + if (param->value >= DRCUML_COND_Z && param->value < DRCUML_COND_MAX) + return dest + sprintf(dest, "%s", condname[param->value - DRCUML_COND_Z]); + else + return dest + sprintf(dest, "??"); +} + + +/*------------------------------------------------- + output_flags - output flags +-------------------------------------------------*/ + +static char *output_flags(char *dest, int size, const drcuml_parameter *param) +{ + if ((param->value & (DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S)) == 0) + return dest + sprintf(dest, "noflags"); + if (param->value & DRCUML_FLAG_C) + *dest++ = 'C'; + if (param->value & DRCUML_FLAG_V) + *dest++ = 'V'; + if (param->value & DRCUML_FLAG_Z) + *dest++ = 'Z'; + if (param->value & DRCUML_FLAG_S) + *dest++ = 'S'; + *dest = 0; + return dest; +} + + +/*------------------------------------------------- + output_flags_cmptest - output flags +-------------------------------------------------*/ + +static char *output_flags_cmptest(char *dest, int size, const drcuml_parameter *param) +{ + if ((param->value & (DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S)) == (DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S)) + return dest + sprintf(dest, "allflags"); + return output_flags(dest, size, param); +} + + +/*------------------------------------------------- + output_param - generic parameter output +-------------------------------------------------*/ + +static char *output_param(char *dest, int size, const drcuml_parameter *param) +{ + UINT64 value; + switch (param->type) + { + case DRCUML_PTYPE_NONE: + return dest += sprintf(dest, "<none?>"); + + case DRCUML_PTYPE_IMMEDIATE: + value = param->value; + if (size == 1) value = (UINT8)value; + if (size == 2) value = (UINT16)value; + if (size == 4) value = (UINT32)value; + if ((UINT32)value == value) + return dest += sprintf(dest, "$%X", (UINT32)value); + else + return dest += sprintf(dest, "$%X%08X", (UINT32)(value >> 32), (UINT32)value); + + case DRCUML_PTYPE_MAPVAR: + if (param->value >= DRCUML_MAPVAR_M0 && param->value < DRCUML_MAPVAR_END) + return dest += sprintf(dest, "m%d", (UINT32)(param->value - DRCUML_MAPVAR_M0)); + else + return dest += sprintf(dest, "m(%X?)", (UINT32)param->value); + + case DRCUML_PTYPE_INT_REGISTER: + if (param->value >= DRCUML_REG_I0 && param->value < DRCUML_REG_I_END) + return dest += sprintf(dest, "i%d", (UINT32)(param->value - DRCUML_REG_I0)); + else + return dest += sprintf(dest, "i(%X?)", (UINT32)param->value); + + case DRCUML_PTYPE_FLOAT_REGISTER: + if (param->value >= DRCUML_REG_F0 && param->value < DRCUML_REG_F_END) + return dest += sprintf(dest, "f%d", (UINT32)(param->value - DRCUML_REG_F0)); + else + return dest += sprintf(dest, "f(%X?)", (UINT32)param->value); + + case DRCUML_PTYPE_MEMORY: + return dest += sprintf(dest, "[$%p]", (void *)(FPTR)param->value); + + default: + return dest += sprintf(dest, "???"); + } + return dest; +} + + +/*------------------------------------------------- + output_param_string - output parameter 0, + assuming it is a string +-------------------------------------------------*/ + +static char *output_param_string(char *dest, int size, const drcuml_parameter *param) +{ + if (param->type == DRCUML_PTYPE_MEMORY) + return dest += sprintf(dest, "%s", (char *)(FPTR)param->value); + return output_param(dest, size, param); +} + + +/*------------------------------------------------- + output_param_handle - output parameter 0, + assuming it is a handle +-------------------------------------------------*/ + +static char *output_param_handle(char *dest, int size, const drcuml_parameter *param) +{ + if (param->type == DRCUML_PTYPE_MEMORY) + return dest += sprintf(dest, "%s", drcuml_handle_name((drcuml_codehandle *)(FPTR)param->value)); + return output_param(dest, size, param); +} + + +/*------------------------------------------------- + output_param_space - output parameter 0, + assuming it is an address space +-------------------------------------------------*/ + +static char *output_param_space(char *dest, int size, const drcuml_parameter *param) +{ + if (param->type == DRCUML_PTYPE_IMMEDIATE && param->value >= ADDRESS_SPACE_PROGRAM && param->value <= ADDRESS_SPACE_IO) + return dest + sprintf(dest, "%s", address_space_names[param->value]); + return output_param(dest, size, param); +} diff --git a/src/emu/cpu/drcumld.h b/src/emu/cpu/drcumld.h new file mode 100644 index 00000000000..a8ef4fbac25 --- /dev/null +++ b/src/emu/cpu/drcumld.h @@ -0,0 +1,30 @@ +/*************************************************************************** + + drcumld.h + + Universal machine language disassembler. + + Copyright Aaron Giles + Released for general non-commercial use under the MAME license + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __DRCUMLD_H__ +#define __DRCUMLD_H__ + +#include "drcuml.h" + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* disassemble one instruction */ +void drcuml_disasm(const drcuml_instruction *inst, char *buffer); + + +#endif diff --git a/src/emu/cpu/drcumlsh.h b/src/emu/cpu/drcumlsh.h new file mode 100644 index 00000000000..b2ead90943c --- /dev/null +++ b/src/emu/cpu/drcumlsh.h @@ -0,0 +1,409 @@ +/*************************************************************************** + + drcumlsh.h + + Shorthand definitions for the universal machine language. + + Copyright Aaron Giles + Released for general non-commercial use under the MAME license + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __DRCUMLSH_H__ +#define __DRCUMLSH_H__ + +#include "drcuml.h" + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* shorthand for conditions */ +#define IF_Z DRCUML_COND_Z +#define IF_NZ DRCUML_COND_NZ +#define IF_S DRCUML_COND_S +#define IF_NS DRCUML_COND_NS +#define IF_C DRCUML_COND_C +#define IF_NC DRCUML_COND_NC +#define IF_V DRCUML_COND_V +#define IF_NV DRCUML_COND_NV +#define IF_U DRCUML_COND_U +#define IF_NU DRCUML_COND_NU +#define IF_E DRCUML_COND_E +#define IF_NE DRCUML_COND_NE +#define IF_A DRCUML_COND_A +#define IF_AE DRCUML_COND_AE +#define IF_B DRCUML_COND_B +#define IF_BE DRCUML_COND_BE +#define IF_G DRCUML_COND_G +#define IF_GE DRCUML_COND_GE +#define IF_L DRCUML_COND_L +#define IF_LE DRCUML_COND_LE +#define IF_ALWAYS DRCUML_COND_ALWAYS + +/* shorthand for flags */ +#define FLAGS_ALLI (DRCUML_FLAG_C | DRCUML_FLAG_V | DRCUML_FLAG_Z | DRCUML_FLAG_S) +#define FLAGS_ALLF (DRCUML_FLAG_C | DRCUML_FLAG_Z | DRCUML_FLAG_U) +#define FLAGS_NONE (0) + +/* flags needed for condition codes */ +#define FLAGS_Z (DRCUML_FLAG_Z) +#define FLAGS_NZ (DRCUML_FLAG_Z) +#define FLAGS_E (DRCUML_FLAG_Z) +#define FLAGS_NE (DRCUML_FLAG_Z) +#define FLAGS_S (DRCUML_FLAG_S) +#define FLAGS_NS (DRCUML_FLAG_S) +#define FLAGS_C (DRCUML_FLAG_C) +#define FLAGS_NC (DRCUML_FLAG_C) +#define FLAGS_V (DRCUML_FLAG_V) +#define FLAGS_NV (DRCUML_FLAG_V) +#define FLAGS_U (DRCUML_FLAG_U) +#define FLAGS_NU (DRCUML_FLAG_U) +#define FLAGS_A (DRCUML_FLAG_C | DRCUML_FLAG_Z) +#define FLAGS_BE (DRCUML_FLAG_C | DRCUML_FLAG_Z) +#define FLAGS_B (DRCUML_FLAG_C) +#define FLAGS_AE (DRCUML_FLAG_C) +#define FLAGS_G (DRCUML_FLAG_S | DRCUML_FLAG_V | DRCUML_FLAG_Z) +#define FLAGS_LE (DRCUML_FLAG_S | DRCUML_FLAG_V | DRCUML_FLAG_Z) +#define FLAGS_L (DRCUML_FLAG_S | DRCUML_FLAG_V) +#define FLAGS_GE (DRCUML_FLAG_S | DRCUML_FLAG_V) + + + +/*************************************************************************** + MACROS +***************************************************************************/ + +/* macros for wrapping parameters */ +#define NONE DRCUML_PTYPE_NONE, 0 +#define IMM(x) DRCUML_PTYPE_IMMEDIATE, (x) +#define IREG(x) DRCUML_PTYPE_INT_REGISTER, (DRCUML_REG_I0 + (x)) +#define FREG(x) DRCUML_PTYPE_FLOAT_REGISTER, (DRCUML_REG_F0 + (x)) +#define MVAR(x) DRCUML_PTYPE_MAPVAR, (DRCUML_MAPVAR_M0 + (x)) +#define MEM(x) DRCUML_PTYPE_MEMORY, (FPTR)(x) + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/* ----- Compile-time Opcodes ----- */ +#define UML_HANDLE(block, handle) do { drcuml_block_append_1(block, DRCUML_OP_HANDLE, 4, IF_ALWAYS, MEM(handle)); } while (0) +#define UML_HASH(block, mode, pc) do { drcuml_block_append_2(block, DRCUML_OP_HASH, 4, IF_ALWAYS, IMM(mode), IMM(pc)); } while (0) +#define UML_LABEL(block, label) do { drcuml_block_append_1(block, DRCUML_OP_LABEL, 4, IF_ALWAYS, IMM(label)); } while (0) +#define UML_COMMENT/*(block, format, ...)*/ drcuml_add_comment +#define UML_MAPVAR(block, mapvar, value) do { drcuml_block_append_2(block, DRCUML_OP_MAPVAR, 4, IF_ALWAYS, mapvar, IMM(value)); } while (0) + + +/* ----- Control Flow Operations ----- */ +#define UML_DEBUG(block, pc) do { drcuml_block_append_1(block, DRCUML_OP_DEBUG, 4, IF_ALWAYS, pc); } while (0) + +#define UML_EXIT(block, param) do { drcuml_block_append_1(block, DRCUML_OP_EXIT, 4, IF_ALWAYS, param); } while (0) +#define UML_EXITc(block, cond, param) do { drcuml_block_append_1(block, DRCUML_OP_EXIT, 4, cond, param); } while (0) + +#define UML_HASHJMP(block, mode, pc, handle) do { drcuml_block_append_3(block, DRCUML_OP_HASHJMP, 4, IF_ALWAYS, mode, pc, MEM(handle)); } while (0) + +#define UML_JMP(block, label) do { drcuml_block_append_1(block, DRCUML_OP_JMP, 4, IF_ALWAYS, IMM(label)); } while (0) +#define UML_JMPc(block, cond, label) do { drcuml_block_append_1(block, DRCUML_OP_JMP, 4, cond, IMM(label)); } while (0) + +#define UML_JMPH(block, handle) do { drcuml_block_append_1(block, DRCUML_OP_JMPH, 4, IF_ALWAYS, MEM(handle)); } while (0) +#define UML_JMPHc(block, cond, handle) do { drcuml_block_append_1(block, DRCUML_OP_JMPH, 4, cond, MEM(handle)); } while (0) +#define UML_EXH(block, handle, param) do { drcuml_block_append_2(block, DRCUML_OP_EXH, 4, IF_ALWAYS, MEM(handle), param); } while (0) +#define UML_EXHc(block, cond, handle, param) do { drcuml_block_append_2(block, DRCUML_OP_EXH, 4, cond, MEM(handle), param); } while (0) +#define UML_CALLH(block, handle) do { drcuml_block_append_1(block, DRCUML_OP_CALLH, 4, IF_ALWAYS, MEM(handle)); } while (0) +#define UML_CALLHc(block, cond, handle) do { drcuml_block_append_1(block, DRCUML_OP_CALLH, 4, cond, MEM(handle)); } while (0) +#define UML_RET(block) do { drcuml_block_append_0(block, DRCUML_OP_RET, 4, IF_ALWAYS); } while (0) +#define UML_RETc(block, cond) do { drcuml_block_append_0(block, DRCUML_OP_RET, 4, cond); } while (0) + +#define UML_CALLC(block, func, ptr) do { drcuml_block_append_2(block, DRCUML_OP_CALLC, 4, IF_ALWAYS, MEM(func), MEM(ptr)); } while (0) +#define UML_CALLCc(block, cond, func, ptr) do { drcuml_block_append_2(block, DRCUML_OP_CALLC, 4, cond, MEM(func), MEM(ptr)); } while (0) + +#define UML_RECOVER(block, dst, mapvar) do { drcuml_block_append_2(block, DRCUML_OP_RECOVER, 4, IF_ALWAYS, dst, mapvar); } while (0) + + +/* ----- Internal Register Operations ----- */ +#define UML_SETMODE(block, mode) do { drcuml_block_append_1(block, DRCUML_OP_SETMODE, 4, IF_ALWAYS, mode); } while (0) +#define UML_GETMODE(block, dst) do { drcuml_block_append_1(block, DRCUML_OP_GETMODE, 4, IF_ALWAYS, dst); } while (0) +#define UML_SETFMOD(block, mode) do { drcuml_block_append_1(block, DRCUML_OP_SETFMOD, 4, IF_ALWAYS, mode); } while (0) +#define UML_GETFMOD(block, dst) do { drcuml_block_append_1(block, DRCUML_OP_GETFMOD, 4, IF_ALWAYS, dst); } while (0) +#define UML_SUBICNT(block, delta, param) do { drcuml_block_append_2(block, DRCUML_OP_SUBICNT, 4, IF_ALWAYS, delta, param); } while (0) +#define UML_GETEXP(block, dst) do { drcuml_block_append_1(block, DRCUML_OP_GETEXP, 4, IF_ALWAYS, dst); } while (0) + + +/* ----- 32-Bit Integer Operations ----- */ +#define UML_LOAD1U(block, dst, base, index) do { drcuml_block_append_3(block, DRCUML_OP_LOAD1U, 4, IF_ALWAYS, dst, MEM(base), index); } while (0) +#define UML_LOAD1S(block, dst, base, index) do { drcuml_block_append_3(block, DRCUML_OP_LOAD1S, 4, IF_ALWAYS, dst, MEM(base), index); } while (0) +#define UML_LOAD2U(block, dst, base, index) do { drcuml_block_append_3(block, DRCUML_OP_LOAD2U, 4, IF_ALWAYS, dst, MEM(base), index); } while (0) +#define UML_LOAD2S(block, dst, base, index) do { drcuml_block_append_3(block, DRCUML_OP_LOAD2S, 4, IF_ALWAYS, dst, MEM(base), index); } while (0) +#define UML_LOAD4(block, dst, base, index) do { drcuml_block_append_3(block, DRCUML_OP_LOAD4U, 4, IF_ALWAYS, dst, MEM(base), index); } while (0) + +#define UML_STORE1(block, base, index, src1) do { drcuml_block_append_3(block, DRCUML_OP_STORE1, 4, IF_ALWAYS, MEM(base), index, src1); } while (0) +#define UML_STORE2(block, base, index, src1) do { drcuml_block_append_3(block, DRCUML_OP_STORE2, 4, IF_ALWAYS, MEM(base), index, src1); } while (0) +#define UML_STORE4(block, base, index, src1) do { drcuml_block_append_3(block, DRCUML_OP_STORE4, 4, IF_ALWAYS, MEM(base), index, src1); } while (0) + +#define UML_READ1U(block, dst, space, src1) do { drcuml_block_append_3(block, DRCUML_OP_READ1U, 4, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1); } while (0) +#define UML_READ1S(block, dst, space, src1) do { drcuml_block_append_3(block, DRCUML_OP_READ1S, 4, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1); } while (0) +#define UML_READ2U(block, dst, space, src1) do { drcuml_block_append_3(block, DRCUML_OP_READ2U, 4, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1); } while (0) +#define UML_READ2S(block, dst, space, src1) do { drcuml_block_append_3(block, DRCUML_OP_READ2S, 4, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1); } while (0) +#define UML_READ4(block, dst, space, src1) do { drcuml_block_append_3(block, DRCUML_OP_READ4U, 4, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1); } while (0) +#define UML_READ4M(block, dst, space, src1, mask) do { drcuml_block_append_4(block, DRCUML_OP_READ4M, 4, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1, mask); } while (0) + +#define UML_WRITE1(block, space, dst, src1) do { drcuml_block_append_3(block, DRCUML_OP_WRITE1, 4, IF_ALWAYS, IMM(ADDRESS_SPACE_##space), dst, src1); } while (0) +#define UML_WRITE2(block, space, dst, src1) do { drcuml_block_append_3(block, DRCUML_OP_WRITE2, 4, IF_ALWAYS, IMM(ADDRESS_SPACE_##space), dst, src1); } while (0) +#define UML_WRITE4(block, space, dst, src1) do { drcuml_block_append_3(block, DRCUML_OP_WRITE4, 4, IF_ALWAYS, IMM(ADDRESS_SPACE_##space), dst, src1); } while (0) +#define UML_WRIT4M(block, space, dst, mask, src1) do { drcuml_block_append_4(block, DRCUML_OP_WRIT4M, 4, IF_ALWAYS, IMM(ADDRESS_SPACE_##space), dst, mask, src1); } while (0) + +#define UML_FLAGS(block, dst, mask, table) do { drcuml_block_append_3(block, DRCUML_OP_FLAGS, 4, IF_ALWAYS, dst, IMM(mask), MEM(table)); } while (0) + +#define UML_MOV(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_MOV, 4, IF_ALWAYS, dst, src1); } while (0) +#define UML_MOVc(block, cond, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_MOV, 4, cond, dst, src1); } while (0) + +#define UML_ZEXT1(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_ZEXT1, 4, IF_ALWAYS, dst, src1); } while (0) +#define UML_ZEXT2(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_ZEXT2, 4, IF_ALWAYS, dst, src1); } while (0) + +#define UML_SEXT1(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_SEXT1, 4, IF_ALWAYS, dst, src1); } while (0) +#define UML_SEXT2(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_SEXT2, 4, IF_ALWAYS, dst, src1); } while (0) + +#define UML_NEG(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_NEG, 4, FLAGS_NONE, dst, src1); } while (0) +#define UML_NEGf(block, dst, src1, flags) do { drcuml_block_append_2(block, DRCUML_OP_NEG, 4, flags, dst, src1); } while (0) + +#define UML_ADD(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_ADD, 4, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_ADDf(block, dst, src1, src2, flags) do { drcuml_block_append_3(block, DRCUML_OP_ADD, 4, flags, dst, src1, src2); } while (0) +#define UML_ADDC(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_ADDC, 4, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_ADDCf(block, dst, src1, src2, flags) do { drcuml_block_append_3(block, DRCUML_OP_ADDC, 4, flags, dst, src1, src2); } while (0) + +#define UML_SUB(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_SUB, 4, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_SUBf(block, dst, src1, src2, flags) do { drcuml_block_append_3(block, DRCUML_OP_SUB, 4, flags, dst, src1, src2); } while (0) +#define UML_SUBB(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_SUBB, 4, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_SUBBf(block, dst, src1, src2, flags) do { drcuml_block_append_3(block, DRCUML_OP_SUBB, 4, flags, dst, src1, src2); } while (0) + +#define UML_CMP(block, src1, src2) do { drcuml_block_append_2(block, DRCUML_OP_CMP, 4, FLAGS_ALLI, src1, src2); } while (0) +#define UML_CMPf(block, src1, src2, flags) do { drcuml_block_append_2(block, DRCUML_OP_CMP, 4, flags, src1, src2); } while (0) + +#define UML_MULU(block, dst, edst, src1, src2) do { drcuml_block_append_4(block, DRCUML_OP_MULU, 4, FLAGS_NONE, dst, edst, src1, src2); } while (0) +#define UML_MULUf(block, dst, edst, src1, src2, flags) do { drcuml_block_append_4(block, DRCUML_OP_MULU, 4, flags, dst, edst, src1, src2); } while (0) +#define UML_MULS(block, dst, edst, src1, src2) do { drcuml_block_append_4(block, DRCUML_OP_MULS, 4, FLAGS_NONE, dst, edst, src1, src2); } while (0) +#define UML_MULSf(block, dst, edst, src1, src2, flags) do { drcuml_block_append_4(block, DRCUML_OP_MULS, 4, flags, dst, edst, src1, src2); } while (0) + +#define UML_DIVU(block, dst, edst, src1, src2) do { drcuml_block_append_4(block, DRCUML_OP_DIVU, 4, FLAGS_NONE, dst, edst, src1, src2); } while (0) +#define UML_DIVUf(block, dst, edst, src1, src2, flags) do { drcuml_block_append_4(block, DRCUML_OP_DIVU, 4, flags, dst, edst, src1, src2); } while (0) +#define UML_DIVS(block, dst, edst, src1, src2) do { drcuml_block_append_4(block, DRCUML_OP_DIVS, 4, FLAGS_NONE, dst, edst, src1, src2); } while (0) +#define UML_DIVSf(block, dst, edst, src1, src2, flags) do { drcuml_block_append_4(block, DRCUML_OP_DIVS, 4, flags, dst, edst, src1, src2); } while (0) + +#define UML_AND(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_AND, 4, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_ANDf(block, dst, src1, src2, flags) do { drcuml_block_append_3(block, DRCUML_OP_AND, 4, flags, dst, src1, src2); } while (0) + +#define UML_TEST(block, src1, src2) do { drcuml_block_append_2(block, DRCUML_OP_TEST, 4, FLAGS_S|FLAGS_Z,src1, src2); } while (0) +#define UML_TESTf(block, src1, src2, flags) do { drcuml_block_append_2(block, DRCUML_OP_TEST, 4, flags, src1, src2); } while (0) + +#define UML_OR(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_OR, 4, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_ORf(block, dst, src1, src2, flags) do { drcuml_block_append_3(block, DRCUML_OP_OR, 4, flags, dst, src1, src2); } while (0) + +#define UML_XOR(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_XOR, 4, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_XORf(block, dst, src1, src2, flags) do { drcuml_block_append_3(block, DRCUML_OP_XOR, 4, flags, dst, src1, src2); } while (0) + +#define UML_SHL(block, dst, src, count) do { drcuml_block_append_3(block, DRCUML_OP_SHL, 4, FLAGS_NONE, dst, src, count); } while (0) +#define UML_SHLf(block, dst, src, count, flags) do { drcuml_block_append_3(block, DRCUML_OP_SHL, 4, flags, dst, src, count); } while (0) + +#define UML_SHR(block, dst, src, count) do { drcuml_block_append_3(block, DRCUML_OP_SHR, 4, FLAGS_NONE, dst, src, count); } while (0) +#define UML_SHRf(block, dst, src, count, flags) do { drcuml_block_append_3(block, DRCUML_OP_SHR, 4, flags, dst, src, count); } while (0) + +#define UML_SAR(block, dst, src, count) do { drcuml_block_append_3(block, DRCUML_OP_SAR, 4, FLAGS_NONE, dst, src, count); } while (0) +#define UML_SARf(block, dst, src, count, flags) do { drcuml_block_append_3(block, DRCUML_OP_SAR, 4, flags, dst, src, count); } while (0) + +#define UML_ROL(block, dst, src, count) do { drcuml_block_append_3(block, DRCUML_OP_ROL, 4, FLAGS_NONE, dst, src, count); } while (0) +#define UML_ROLf(block, dst, src, count, flags) do { drcuml_block_append_3(block, DRCUML_OP_ROL, 4, flags, dst, src, count); } while (0) +#define UML_ROLC(block, dst, src, count) do { drcuml_block_append_3(block, DRCUML_OP_ROLC, 4, FLAGS_NONE, dst, src, count); } while (0) +#define UML_ROLCf(block, dst, src, count, flags) do { drcuml_block_append_3(block, DRCUML_OP_ROLC, 4, flags, dst, src, count); } while (0) + +#define UML_ROR(block, dst, src, count) do { drcuml_block_append_3(block, DRCUML_OP_ROR, 4, FLAGS_NONE, dst, src, count); } while (0) +#define UML_RORf(block, dst, src, count, flags) do { drcuml_block_append_3(block, DRCUML_OP_ROR, 4, flags, dst, src, count); } while (0) +#define UML_RORC(block, dst, src, count) do { drcuml_block_append_3(block, DRCUML_OP_RORC, 4, FLAGS_NONE, dst, src, count); } while (0) +#define UML_RORCf(block, dst, src, count, flags) do { drcuml_block_append_3(block, DRCUML_OP_RORC, 4, flags, dst, src, count); } while (0) + + +/* ----- 64-Bit Integer Operations ----- */ +#define UML_DLOAD1U(block, dst, base, index) do { drcuml_block_append_3(block, DRCUML_OP_LOAD1U, 8, IF_ALWAYS, dst, MEM(base), index); } while (0) +#define UML_DLOAD1S(block, dst, base, index) do { drcuml_block_append_3(block, DRCUML_OP_LOAD1S, 8, IF_ALWAYS, dst, MEM(base), index); } while (0) +#define UML_DLOAD2U(block, dst, base, index) do { drcuml_block_append_3(block, DRCUML_OP_LOAD2U, 8, IF_ALWAYS, dst, MEM(base), index); } while (0) +#define UML_DLOAD2S(block, dst, base, index) do { drcuml_block_append_3(block, DRCUML_OP_LOAD2S, 8, IF_ALWAYS, dst, MEM(base), index); } while (0) +#define UML_DLOAD4U(block, dst, base, index) do { drcuml_block_append_3(block, DRCUML_OP_LOAD4U, 8, IF_ALWAYS, dst, MEM(base), index); } while (0) +#define UML_DLOAD4S(block, dst, base, index) do { drcuml_block_append_3(block, DRCUML_OP_LOAD4S, 8, IF_ALWAYS, dst, MEM(base), index); } while (0) +#define UML_DLOAD8(block, dst, base, index) do { drcuml_block_append_3(block, DRCUML_OP_LOAD8U, 8, IF_ALWAYS, dst, MEM(base), index); } while (0) + +#define UML_DSTORE1(block, base, index, src1) do { drcuml_block_append_3(block, DRCUML_OP_STORE1, 8, IF_ALWAYS, MEM(base), index, src1); } while (0) +#define UML_DSTORE2(block, base, index, src1) do { drcuml_block_append_3(block, DRCUML_OP_STORE2, 8, IF_ALWAYS, MEM(base), index, src1); } while (0) +#define UML_DSTORE4(block, base, index, src1) do { drcuml_block_append_3(block, DRCUML_OP_STORE4, 8, IF_ALWAYS, MEM(base), index, src1); } while (0) +#define UML_DSTORE8(block, base, index, src1) do { drcuml_block_append_3(block, DRCUML_OP_STORE8, 8, IF_ALWAYS, MEM(base), index, src1); } while (0) + +#define UML_DREAD1U(block, dst, space, src1) do { drcuml_block_append_3(block, DRCUML_OP_READ1U, 8, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1); } while (0) +#define UML_DREAD1S(block, dst, space, src1) do { drcuml_block_append_3(block, DRCUML_OP_READ1S, 8, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1); } while (0) +#define UML_DREAD2U(block, dst, space, src1) do { drcuml_block_append_3(block, DRCUML_OP_READ2U, 8, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1); } while (0) +#define UML_DREAD2S(block, dst, space, src1) do { drcuml_block_append_3(block, DRCUML_OP_READ2S, 8, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1); } while (0) +#define UML_DREAD2M(block, dst, space, src1, mask) do { drcuml_block_append_4(block, DRCUML_OP_READ2M, 8, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1, mask); } while (0) +#define UML_DREAD4U(block, dst, space, src1) do { drcuml_block_append_3(block, DRCUML_OP_READ4U, 8, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1); } while (0) +#define UML_DREAD4S(block, dst, space, src1) do { drcuml_block_append_3(block, DRCUML_OP_READ4S, 8, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1); } while (0) +#define UML_DREAD4M(block, dst, space, src1, mask) do { drcuml_block_append_4(block, DRCUML_OP_READ4M, 8, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1, mask); } while (0) +#define UML_DREAD8(block, dst, space, src1) do { drcuml_block_append_3(block, DRCUML_OP_READ8U, 8, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1); } while (0) +#define UML_DREAD8M(block, dst, space, src1, mask) do { drcuml_block_append_4(block, DRCUML_OP_READ8M, 8, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1, mask); } while (0) + +#define UML_DWRITE1(block, space, dst, src1) do { drcuml_block_append_3(block, DRCUML_OP_WRITE1, 8, IF_ALWAYS, IMM(ADDRESS_SPACE_##space), dst, src1); } while (0) +#define UML_DWRITE2(block, space, dst, src1) do { drcuml_block_append_3(block, DRCUML_OP_WRITE2, 8, IF_ALWAYS, IMM(ADDRESS_SPACE_##space), dst, src1); } while (0) +#define UML_DWRIT2M(block, space, dst, mask, src1) do { drcuml_block_append_4(block, DRCUML_OP_WRIT2M, 8, IF_ALWAYS, IMM(ADDRESS_SPACE_##space), dst, mask, src1); } while (0) +#define UML_DWRITE4(block, space, dst, src1) do { drcuml_block_append_3(block, DRCUML_OP_WRITE4, 8, IF_ALWAYS, IMM(ADDRESS_SPACE_##space), dst, src1); } while (0) +#define UML_DWRIT4M(block, space, dst, mask, src1) do { drcuml_block_append_4(block, DRCUML_OP_WRIT4M, 8, IF_ALWAYS, IMM(ADDRESS_SPACE_##space), dst, mask, src1); } while (0) +#define UML_DWRITE8(block, space, dst, src1) do { drcuml_block_append_3(block, DRCUML_OP_WRITE8, 8, IF_ALWAYS, IMM(ADDRESS_SPACE_##space), dst, src1); } while (0) +#define UML_DWRIT8M(block, space, dst, mask, src1) do { drcuml_block_append_4(block, DRCUML_OP_WRIT8M, 8, IF_ALWAYS, IMM(ADDRESS_SPACE_##space), dst, mask, src1); } while (0) + +#define UML_DFLAGS(block, dst, mask, table) do { drcuml_block_append_3(block, DRCUML_OP_FLAGS, 8, IF_ALWAYS, dst, IMM(mask), MEM(table)); } while (0) + +#define UML_DMOV(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_MOV, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_DMOVc(block, cond, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_MOV, 8, cond, dst, src1); } while (0) + +#define UML_DZEXT1(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_ZEXT1, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_DZEXT2(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_ZEXT2, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_DZEXT4(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_ZEXT4, 8, IF_ALWAYS, dst, src1); } while (0) + +#define UML_DSEXT1(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_SEXT1, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_DSEXT2(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_SEXT2, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_DSEXT4(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_SEXT4, 8, IF_ALWAYS, dst, src1); } while (0) + +#define UML_DNEG(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_NEG, 8, FLAGS_NONE, dst, src1); } while (0) +#define UML_DNEGf(block, dst, src1, flags) do { drcuml_block_append_2(block, DRCUML_OP_NEG, 8, flags, dst, src1); } while (0) + +#define UML_DADD(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_ADD, 8, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_DADDf(block, dst, src1, src2, flags) do { drcuml_block_append_3(block, DRCUML_OP_ADD, 8, flags, dst, src1, src2); } while (0) +#define UML_DADDC(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_ADDC, 8, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_DADDCf(block, dst, src1, src2, flags) do { drcuml_block_append_3(block, DRCUML_OP_ADDC, 8, flags, dst, src1, src2); } while (0) + +#define UML_DSUB(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_SUB, 8, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_DSUBf(block, dst, src1, src2, flags) do { drcuml_block_append_3(block, DRCUML_OP_SUB, 8, flags, dst, src1, src2); } while (0) +#define UML_DSUBB(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_SUBB, 8, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_DSUBBf(block, dst, src1, src2, flags) do { drcuml_block_append_3(block, DRCUML_OP_SUBB, 8, flags, dst, src1, src2); } while (0) + +#define UML_DCMP(block, src1, src2) do { drcuml_block_append_2(block, DRCUML_OP_CMP, 8, FLAGS_ALLI, src1, src2); } while (0) +#define UML_DCMPf(block, src1, src2, flags) do { drcuml_block_append_2(block, DRCUML_OP_CMP, 8, flags, src1, src2); } while (0) + +#define UML_DMULU(block, dst, edst, src1, src2) do { drcuml_block_append_4(block, DRCUML_OP_MULU, 8, FLAGS_NONE, dst, edst, src1, src2); } while (0) +#define UML_DMULUf(block, dst, edst, src1, src2, flags) do { drcuml_block_append_4(block, DRCUML_OP_MULU, 8, flags, dst, edst, src1, src2); } while (0) +#define UML_DMULS(block, dst, edst, src1, src2) do { drcuml_block_append_4(block, DRCUML_OP_MULS, 8, FLAGS_NONE, dst, edst, src1, src2); } while (0) +#define UML_DMULSf(block, dst, edst, src1, src2, flags) do { drcuml_block_append_4(block, DRCUML_OP_MULS, 8, flags, dst, edst, src1, src2); } while (0) + +#define UML_DDIVU(block, dst, edst, src1, src2) do { drcuml_block_append_4(block, DRCUML_OP_DIVU, 8, FLAGS_NONE, dst, edst, src1, src2); } while (0) +#define UML_DDIVUf(block, dst, edst, src1, src2, flags) do { drcuml_block_append_4(block, DRCUML_OP_DIVU, 8, flags, dst, edst, src1, src2); } while (0) +#define UML_DDIVS(block, dst, edst, src1, src2) do { drcuml_block_append_4(block, DRCUML_OP_DIVS, 8, FLAGS_NONE, dst, edst, src1, src2); } while (0) +#define UML_DDIVSf(block, dst, edst, src1, src2, flags) do { drcuml_block_append_4(block, DRCUML_OP_DIVS, 8, flags, dst, edst, src1, src2); } while (0) + +#define UML_DAND(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_AND, 8, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_DANDf(block, dst, src1, src2, flags) do { drcuml_block_append_3(block, DRCUML_OP_AND, 8, flags, dst, src1, src2); } while (0) + +#define UML_DTEST(block, src1, src2) do { drcuml_block_append_2(block, DRCUML_OP_TEST, 8, FLAGS_S|FLAGS_Z,src1, src2); } while (0) +#define UML_DTESTf(block, src1, src2, flags) do { drcuml_block_append_2(block, DRCUML_OP_TEST, 8, flags, src1, src2); } while (0) + +#define UML_DOR(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_OR, 8, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_DORf(block, dst, src1, src2, flags) do { drcuml_block_append_3(block, DRCUML_OP_OR, 8, flags, dst, src1, src2); } while (0) + +#define UML_DXOR(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_XOR, 8, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_DXORf(block, dst, src1, src2, flags) do { drcuml_block_append_3(block, DRCUML_OP_XOR, 8, flags, dst, src1, src2); } while (0) + +#define UML_DSHL(block, dst, src, count) do { drcuml_block_append_3(block, DRCUML_OP_SHL, 8, FLAGS_NONE, dst, src, count); } while (0) +#define UML_DSHLf(block, dst, src, count, flags) do { drcuml_block_append_3(block, DRCUML_OP_SHL, 8, flags, dst, src, count); } while (0) + +#define UML_DSHR(block, dst, src, count) do { drcuml_block_append_3(block, DRCUML_OP_SHR, 8, FLAGS_NONE, dst, src, count); } while (0) +#define UML_DSHRf(block, dst, src, count, flags) do { drcuml_block_append_3(block, DRCUML_OP_SHR, 8, flags, dst, src, count); } while (0) + +#define UML_DSAR(block, dst, src, count) do { drcuml_block_append_3(block, DRCUML_OP_SAR, 8, FLAGS_NONE, dst, src, count); } while (0) +#define UML_DSARf(block, dst, src, count, flags) do { drcuml_block_append_3(block, DRCUML_OP_SAR, 8, flags, dst, src, count); } while (0) + +#define UML_DROL(block, dst, src, count) do { drcuml_block_append_3(block, DRCUML_OP_ROL, 8, FLAGS_NONE, dst, src, count); } while (0) +#define UML_DROLf(block, dst, src, count, flags) do { drcuml_block_append_3(block, DRCUML_OP_ROL, 8, flags, dst, src, count); } while (0) +#define UML_DROLC(block, dst, src, count) do { drcuml_block_append_3(block, DRCUML_OP_ROLC, 8, FLAGS_NONE, dst, src, count); } while (0) +#define UML_DROLCf(block, dst, src, count, flags) do { drcuml_block_append_3(block, DRCUML_OP_ROLC, 8, flags, dst, src, count); } while (0) + +#define UML_DROR(block, dst, src, count) do { drcuml_block_append_3(block, DRCUML_OP_ROR, 8, FLAGS_NONE, dst, src, count); } while (0) +#define UML_DRORf(block, dst, src, count, flags) do { drcuml_block_append_3(block, DRCUML_OP_ROR, 8, flags, dst, src, count); } while (0) +#define UML_DRORC(block, dst, src, count) do { drcuml_block_append_3(block, DRCUML_OP_RORC, 8, FLAGS_NONE, dst, src, count); } while (0) +#define UML_DRORCf(block, dst, src, count, flags) do { drcuml_block_append_3(block, DRCUML_OP_RORC, 8, flags, dst, src, count); } while (0) + + +/* ----- 32-bit Floating Point Arithmetic Operations ----- */ +#define UML_FSLOAD(block, dst, base, index) do { drcuml_block_append_3(block, DRCUML_OP_FLOAD, 4, IF_ALWAYS, dst, MEM(base), index); } while (0) +#define UML_FSSTORE(block, base, index, src1) do { drcuml_block_append_3(block, DRCUML_OP_FSTORE, 4, IF_ALWAYS, MEM(base), index, src1); } while (0) + +#define UML_FSREAD(block, dst, space, src1) do { drcuml_block_append_3(block, DRCUML_OP_FREAD, 4, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1); } while (0) +#define UML_FSWRITE(block, space, dst, src1) do { drcuml_block_append_3(block, DRCUML_OP_FWRITE, 4, IF_ALWAYS, IMM(ADDRESS_SPACE_##space), dst, src1); } while (0) + +#define UML_FSMOV(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FMOV, 4, IF_ALWAYS, dst, src1); } while (0) +#define UML_FSMOVc(block, cond, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FMOV, 4, cond, dst, src1); } while (0) + +#define UML_FSTOI4(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI4, 4, IF_ALWAYS, dst, src1); } while (0) +#define UML_FSTOI4T(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI4T, 4, IF_ALWAYS, dst, src1); } while (0) +#define UML_FSTOI4R(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI4R, 4, IF_ALWAYS, dst, src1); } while (0) +#define UML_FSTOI4C(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI4C, 4, IF_ALWAYS, dst, src1); } while (0) +#define UML_FSTOI4F(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI4F, 4, IF_ALWAYS, dst, src1); } while (0) +#define UML_FSTOI8(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI8, 4, IF_ALWAYS, dst, src1); } while (0) +#define UML_FSTOI8T(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI8T, 4, IF_ALWAYS, dst, src1); } while (0) +#define UML_FSTOI8R(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI8R, 4, IF_ALWAYS, dst, src1); } while (0) +#define UML_FSTOI8C(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI8C, 4, IF_ALWAYS, dst, src1); } while (0) +#define UML_FSTOI8F(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI8F, 4, IF_ALWAYS, dst, src1); } while (0) + +#define UML_FSFRFD(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FFRFD, 4, IF_ALWAYS, dst, src1); } while (0) +#define UML_FSFRI4(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FFRI4, 4, IF_ALWAYS, dst, src1); } while (0) +#define UML_FSFRI8(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FFRI8, 4, IF_ALWAYS, dst, src1); } while (0) + +#define UML_FSADD(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_FADD, 4, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_FSSUB(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_FSUB, 4, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_FSCMP(block, src1, src2) do { drcuml_block_append_2(block, DRCUML_OP_FCMP, 4, FLAGS_ALLF, src1, src2); } while (0) +#define UML_FSMUL(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_FMUL, 4, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_FSDIV(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_FDIV, 4, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_FSNEG(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FNEG, 4, FLAGS_NONE, dst, src1); } while (0) +#define UML_FSABS(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FABS, 4, FLAGS_NONE, dst, src1); } while (0) +#define UML_FSSQRT(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FSQRT, 4, FLAGS_NONE, dst, src1); } while (0) +#define UML_FSRECIP(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FRECIP, 4, FLAGS_NONE, dst, src1); } while (0) +#define UML_FSRSQRT(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FRSQRT, 4, FLAGS_NONE, dst, src1); } while (0) + + +/* ----- 64-bit Floating Point Arithmetic Operations ----- */ +#define UML_FDLOAD(block, dst, base, index) do { drcuml_block_append_3(block, DRCUML_OP_FLOAD, 8, IF_ALWAYS, dst, MEM(base), index); } while (0) +#define UML_FDSTORE(block, base, index, src1) do { drcuml_block_append_3(block, DRCUML_OP_FSTORE, 8, IF_ALWAYS, MEM(base), index, src1); } while (0) + +#define UML_FDREAD(block, dst, space, src1) do { drcuml_block_append_3(block, DRCUML_OP_FREAD, 8, IF_ALWAYS, dst, IMM(ADDRESS_SPACE_##space), src1); } while (0) +#define UML_FDWRITE(block, space, dst, src1) do { drcuml_block_append_3(block, DRCUML_OP_FWRITE, 8, IF_ALWAYS, IMM(ADDRESS_SPACE_##space), dst, src1); } while (0) + +#define UML_FDMOV(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FMOV, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_FDMOVc(block, cond, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FMOV, 8, cond, dst, src1); } while (0) + +#define UML_FDTOI4(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI4, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_FDTOI4T(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI4T, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_FDTOI4R(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI4R, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_FDTOI4C(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI4C, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_FDTOI4F(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI4F, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_FDTOI8(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI8, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_FDTOI8T(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI8T, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_FDTOI8R(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI8R, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_FDTOI8C(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI8C, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_FDTOI8F(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FTOI8F, 8, IF_ALWAYS, dst, src1); } while (0) + +#define UML_FDFRFS(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FFRFS, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_FDFRI4(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FFRI4, 8, IF_ALWAYS, dst, src1); } while (0) +#define UML_FDFRI8(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FFRI8, 8, IF_ALWAYS, dst, src1); } while (0) + +#define UML_FDADD(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_FADD, 8, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_FDSUB(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_FSUB, 8, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_FDCMP(block, src1, src2) do { drcuml_block_append_2(block, DRCUML_OP_FCMP, 8, FLAGS_ALLF, src1, src2); } while (0) +#define UML_FDMUL(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_FMUL, 8, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_FDDIV(block, dst, src1, src2) do { drcuml_block_append_3(block, DRCUML_OP_FDIV, 8, FLAGS_NONE, dst, src1, src2); } while (0) +#define UML_FDNEG(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FNEG, 8, FLAGS_NONE, dst, src1); } while (0) +#define UML_FDABS(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FABS, 8, FLAGS_NONE, dst, src1); } while (0) +#define UML_FDSQRT(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FSQRT, 8, FLAGS_NONE, dst, src1); } while (0) +#define UML_FDRECIP(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FRECIP, 8, FLAGS_NONE, dst, src1); } while (0) +#define UML_FDRSQRT(block, dst, src1) do { drcuml_block_append_2(block, DRCUML_OP_FRSQRT, 8, FLAGS_NONE, dst, src1); } while (0) + + +#endif diff --git a/src/emu/cpu/mips/mdrc64.c b/src/emu/cpu/mips/mdrc64.c deleted file mode 100644 index 52afd04f269..00000000000 --- a/src/emu/cpu/mips/mdrc64.c +++ /dev/null @@ -1,5730 +0,0 @@ -/*************************************************************************** - - mdrc64.c - - x64 MIPS III recompiler. - - Copyright Aaron Giles - Released for general use under the MAME license - Visit http://mamedev.org for licensing and usage restrictions. - -**************************************************************************** - - Conventions/rules: - - * Instructions are grouped into sequences by mips3fe; each - sequence of instructions is guaranteed to not contain any - branches to any instructions within the sequence apart from - the first. - - * Because of the above rule, assumptions are made during the - execution of a sequence in order to optimize the generated - code. - - * Because some state may be live and not flushed to memory at - the time an exception is generated, all exception-generating - instructions must jump to stub code that is appended to each - group of instructions which cleans up live registers before - running the exception code. - - * Page faults are trickier because they are detected in a - subroutine. In order to handle this, special markers are added - to the code consisting of one qword EXCEPTION_ADDRESS_COOKIE - followed by the return address of the call. When a page fault - needs to be generated, the common code scans forward from - the return address for this cookie/address pair and jumps to - the code immediately following. - - * Convention: jumps to non-constant addresses have their target - PC stored at [sp+SPOFFS_NEXTPC] - -**************************************************************************** - - Future improvements: - * problem: - lui r3,$b000 - lw r3,$100(r3) - if exception happens, then r3 is trashed - - * register allocator can use non-volatile registers if the - lifetime is only for this one instruction (only for - registers where (gpr.used & ~gpr.modified) - - * spec says if registers aren't sign-extended, then 32-bit ops have - undefined results; this can simplify our code - - * track which results are 32-bit sign extended versus 64-bit and - only promote when necessary - - * register aliasing - move from one to another either doesn't do - anything and tracks both, or else shifts ownership to new - target - - * generic drc: - * common code to load registers prior to instruction - - each register has a callback that generates the load - * generate code - * common code to write registers afterwards - * common register allocation routines (64 integer, 64 FPU) - * common OOB mechanism - * common self-mod code detection (via write bitmask) - * common code validation routines - -**************************************************************************** - - Temporary stack usage: - [esp+32].b = storage for CL in LWL/LWR/SWL/SWR - [esp+36].l = storage for new target PC in JR/JALR - -***************************************************************************/ - -#include "mips3fe.h" -#include "deprecat.h" - - -/*************************************************************************** - DEBUGGING -***************************************************************************/ - -#define DISABLE_FIXED_RAM_LOAD (0) -#define DISABLE_FIXED_RAM_STORE (0) -#define FLUSH_AFTER_EACH_INSTRUCTION (0) -#define PRINTF_EXCEPTIONS (0) -#define PROBE_ADDRESS (0) -#define LOG_HOTSPOTS (0) - - - -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -/* extra "conditions" used to augment the basic conditions for OOB handlers */ -#define COND_NONE 16 -#define COND_NO_JUMP 17 - -/* 64-bit cookie that is scanned for during exception processing */ -#define EXCEPTION_ADDRESS_COOKIE U64(0xcc90cc90cc90cc90) - -/* append_readwrite flags */ -#define ARW_READ 0x0000 -#define ARW_WRITE 0x0001 -#define ARW_UNSIGNED 0x0000 -#define ARW_SIGNED 0x0002 -#define ARW_MASKED 0x0004 -#define ARW_CACHED 0x0008 -#define ARW_UNCACHED 0x0010 - -/* compiler_register_allocate flags */ -#define ALLOC_SRC 0x0001 -#define ALLOC_DST 0x0002 -#define ALLOC_DIRTY 0x0004 - -/* x86 register tracking flags */ -#define X86REG_LIVE 0x80 /* register is live and contains something */ -#define X86REG_MIPSMASK 0x3f /* low 6 bits indicate which MIPS register we hold */ - -/* MIPS register tracking flags */ -#define MIPSREG_CONSTANT 0x8000 /* register is constant; low 10 bits select which one */ -#define MIPSREG_DIRTY 0x4000 /* register has been modified and needs to be flushed */ -#define MIPSREG_LIVE 0x2000 /* register is live in an x86 register */ -#define MIPSREG_X86MASK 0x000f /* low 4 bits indicate which x86 register holds us */ - - - -/*************************************************************************** - MACROS -***************************************************************************/ - -#define SPOFFS_SAVECL 32 -#define SPOFFS_NEXTPC 36 - -#define REGADDR(reg) MDRC(&mips3.core->r[reg]) -#define HIADDR MDRC(&mips3.core->r[REG_HI]) -#define LOADDR MDRC(&mips3.core->r[REG_LO]) -#define CPR0ADDR(reg) MDRC(&mips3.core->cpr[0][reg]) -#define CCR0ADDR(reg) MDRC(&mips3.core->ccr[0][reg]) -#define CPR1ADDR(reg) MDRC(&mips3.core->cpr[1][reg]) -#define CCR1ADDR(reg) MDRC(&mips3.core->ccr[1][reg]) -#define CPR2ADDR(reg) MDRC(&mips3.core->cpr[2][reg]) -#define CCR2ADDR(reg) MDRC(&mips3.core->ccr[2][reg]) -#define FPR32(reg) (IS_FR0 ? &((float *)&mips3.core->cpr[1][0])[reg] : (float *)&mips3.core->cpr[1][reg]) -#define FPR32ADDR(reg) MDRC(FPR32(reg)) -#define FPR64(reg) (IS_FR0 ? (double *)&mips3.core->cpr[1][(reg)/2] : (double *)&mips3.core->cpr[1][reg]) -#define FPR64ADDR(reg) MDRC(FPR64(reg)) -#define CF1ADDR(which) MDRC(&mips3.core->cf[1][(mips3.core->flavor < MIPS3_TYPE_MIPS_IV) ? 0 : (which)]) -#define PCADDR MDRC(&mips3.core->pc) -#define ICOUNTADDR MDRC(&mips3.core->icount) -#define COREADDR MDRC(mips3.core) - -/* macros for querying/accessing the tracking flags */ -#define ISCONST(x) (((x) & MIPSREG_CONSTANT) != 0) -#define ISDIRTY(x) (((x) & MIPSREG_DIRTY) != 0) -#define ISLIVE(x) (((x) & MIPSREG_LIVE) != 0) -#define CONSTVAL(x) ((INT32)((x) >> 32)) -#define X86REG(x) ((x) & MIPSREG_X86MASK) - -/* macro for defining a small positivec constant inline */ -#define MAKE_CONST(x) (MIPSREG_CONSTANT | ((INT64)(x) << 32)) -#define MAKE_DESTREG(x) (MIPSREG_DIRTY | ((x) & MIPSREG_X86MASK)) - - - -/*************************************************************************** - TYPEDEFS -***************************************************************************/ - -typedef struct _readwrite_handlers readwrite_handlers; -struct _readwrite_handlers -{ - x86code * read_byte_signed; - x86code * read_byte_unsigned; - x86code * read_half_signed; - x86code * read_half_unsigned; - x86code * read_word_signed; - x86code * read_word_unsigned; - x86code * read_word_masked; - x86code * read_double; - x86code * read_double_masked; - x86code * write_byte; - x86code * write_half; - x86code * write_word; - x86code * write_word_masked; - x86code * write_double; - x86code * write_double_masked; -}; - - -typedef INT64 mipsreg_state; -typedef UINT8 x86reg_state; - - -typedef struct _compiler_state compiler_state; -struct _compiler_state -{ - UINT32 cycles; /* accumulated cycles */ - x86reg_state x86regs[REG_MAX]; /* tracking what is in the x86 registers */ - mipsreg_state mipsregs[35]; /* tracking what is in the MIPS registers */ -}; - - -typedef struct _oob_handler oob_handler; - -typedef void (*oob_callback_func)(drc_core *drc, oob_handler *oob, void *param); - -struct _oob_handler -{ - oob_callback_func callback; /* pointer to callback function */ - void * param; /* callback parameter */ - const opcode_desc * desc; /* pointer to description of relevant instruction */ - emit_link link; /* link to source of the branch */ - compiler_state compiler; /* state of the compiler at this time */ -}; - - -struct _mips3drc_data -{ - /* misc data */ - UINT64 abs64mask[2]; - UINT32 abs32mask[4]; - - /* C functions */ - x86code * activecpu_gettotalcycles; - x86code * mips3com_update_cycle_counting; - x86code * mips3com_tlbr; - x86code * mips3com_tlbwi; - x86code * mips3com_tlbwr; - x86code * mips3com_tlbp; - - /* internal functions */ - x86code * generate_interrupt_exception; - x86code * generate_cop_exception; - x86code * generate_overflow_exception; - x86code * generate_invalidop_exception; - x86code * generate_syscall_exception; - x86code * generate_break_exception; - x86code * generate_trap_exception; - x86code * generate_tlbload_exception; - x86code * generate_tlbstore_exception; - x86code * handle_pc_tlb_mismatch; - x86code * find_exception_handler; - x86code * explode_ccr31; - x86code * recover_ccr31; - - /* read/write handlers */ - readwrite_handlers general; - readwrite_handlers cached; - readwrite_handlers uncached; - - /* out of bounds handlers */ - oob_handler ooblist[COMPILE_MAX_INSTRUCTIONS*2]; - int oobcount; - - /* C functions */ - x86code * execute_c_version; - - /* hotspot tracking */ -#if LOG_HOTSPOTS - struct - { - offs_t pc; - UINT32 opcode; - UINT64 count; - } hotspot[100000]; - int next_hotspot; -#endif -}; - - - -/*************************************************************************** - FUNCTION PROTOTYPES -***************************************************************************/ - -static void drc_reset_callback(drc_core *drc); -static void drc_recompile_callback(drc_core *drc); -static void drc_entrygen_callback(drc_core *drc); - -static void compile_one(drc_core *drc, compiler_state *compiler, const opcode_desc *desc); - -static void oob_exception_cleanup(drc_core *drc, oob_handler *oob, void *param); -static void oob_interrupt_cleanup(drc_core *drc, oob_handler *oob, void *param); - -static void compiler_register_set_constant(compiler_state *compiler, UINT8 mipsreg, UINT64 constval); -static mipsreg_state compiler_register_allocate(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 mipsreg, UINT8 flags); -static void compiler_register_flush(drc_core *drc, compiler_state *compiler, UINT8 mipsreg, int free); -static void compiler_register_flush_all(drc_core *drc, compiler_state *compiler); - -static void probe_printf(void); - -static void generate_read_write_handlers(drc_core *drc, readwrite_handlers *handlers, const char *type, UINT8 flags); -static void append_generate_exception(drc_core *drc, UINT8 exception); -static void append_readwrite_and_translate(drc_core *drc, int size, UINT8 flags, UINT32 ptroffs); -static void append_handle_pc_tlb_mismatch(drc_core *drc); -static void append_find_exception_handler(drc_core *drc); -static void append_explode_ccr31(drc_core *drc); -static void append_recover_ccr31(drc_core *drc); -static void append_check_interrupts(drc_core *drc, compiler_state *compiler, const opcode_desc *desc); -//static void append_check_sw_interrupts(drc_core *drc, int inline_generate); - -static void emit_update_cycles_pc_and_flush(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, offs_t destpc); - -static int compile_instruction(drc_core *drc, compiler_state *compiler, const opcode_desc *desc); -static int compile_special(drc_core *drc, compiler_state *compiler, const opcode_desc *desc); -static int compile_regimm(drc_core *drc, compiler_state *compiler, const opcode_desc *desc); -static int compile_idt(drc_core *drc, compiler_state *compiler, const opcode_desc *desc); -static int compile_cop0(drc_core *drc, compiler_state *compiler, const opcode_desc *desc); -static int compile_cop1(drc_core *drc, compiler_state *compiler, const opcode_desc *desc); -static int compile_cop1x(drc_core *drc, compiler_state *compiler, const opcode_desc *desc); - -static int compile_shift(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int shift_type, int shift_variable); -static int compile_dshift(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int shift_type, int shift_variable, int shift_const_plus32); -static int compile_movzn(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_movn); -static int compile_trapcc(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int condition, int immediate); -static int compile_branchcc(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int condition); -static int compile_branchcz(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int condition, int link); -static int compile_add(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions, int immediate); -static int compile_dadd(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions, int immediate); -static int compile_sub(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions); -static int compile_dsub(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions); -static int compile_mult(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned); -static int compile_dmult(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned); -static int compile_div(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned); -static int compile_ddiv(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned); -static int compile_logical(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int operation, int immediate); -static int compile_slt(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned, int immediate); -static int compile_load(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, int is_unsigned); -static int compile_load_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *dest); -static int compile_loadx_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *dest); -static int compile_load_word_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right); -static int compile_load_double_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right); -static int compile_store(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size); -static int compile_store_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *src); -static int compile_storex_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *src); -static int compile_store_word_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right); -static int compile_store_double_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right); - - - -/*************************************************************************** - PRIVATE GLOBAL VARIABLES -***************************************************************************/ - -static const UINT8 nvreg[] = { REG_NV0, REG_NV1, REG_NV2, REG_NV3, REG_NV4, REG_NV5, REG_NV6 }; - -static const char *const x86regname[] = {"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}; - - - -/*************************************************************************** - INLINE FUNCTIONS -***************************************************************************/ - -/*------------------------------------------------- - oob_request_callback - request an out-of- - bounds callback for later --------------------------------------------------*/ - -INLINE void oob_request_callback(drc_core *drc, UINT8 condition, oob_callback_func callback, const compiler_state *compiler, const opcode_desc *desc, void *param) -{ - oob_handler *oob = &mips3.drcdata->ooblist[mips3.drcdata->oobcount++]; - - /* fill in the next handler */ - oob->callback = callback; - oob->desc = desc; - oob->compiler = *compiler; - oob->param = param; - - /* emit an appropriate jump */ - if (condition == COND_NO_JUMP) - { - oob->link.size = 0; - oob->link.target = drc->cache_top; - } - else if (condition == COND_NONE) - emit_jmp_near_link(DRCTOP, &oob->link); // jmp <target> - else - emit_jcc_near_link(DRCTOP, condition, &oob->link); // jcc <target> -} - - -/*------------------------------------------------- - compiler_state_reset - reset the compiler - state --------------------------------------------------*/ - -INLINE void compiler_state_reset(compiler_state *compiler) -{ - memset(compiler, 0, sizeof(*compiler)); - compiler_register_set_constant(compiler, 0, 0); -} - - - -/*************************************************************************** - CORE RECOMPILER SYSTEMS -***************************************************************************/ - -/*------------------------------------------------- - mips3drc_init - initialize the drc-specific - state --------------------------------------------------*/ - -static void mips3drc_init(void) -{ - drc_config drconfig; - - /* fill in the config */ - memset(&drconfig, 0, sizeof(drconfig)); - drconfig.cache_base = mips3.cache; - drconfig.cache_size = CACHE_SIZE; - drconfig.max_instructions = (512 + 128) / 4; - drconfig.address_bits = 32; - drconfig.lsbs_to_ignore = 2; - drconfig.baseptr = &mips3.core->r[17]; - drconfig.pcptr = (UINT32 *)&mips3.core->pc; - drconfig.cb_reset = drc_reset_callback; - drconfig.cb_recompile = drc_recompile_callback; - drconfig.cb_entrygen = drc_entrygen_callback; - - /* initialize the compiler */ - mips3.drc = drc_init(cpu_getactivecpu(), &drconfig); - mips3.drcoptions = MIPS3DRC_FASTEST_OPTIONS; - - /* allocate our data out of the cache */ - mips3.drcdata = drc_alloc(mips3.drc, sizeof(*mips3.drcdata) + 16); - mips3.drcdata = (mips3drc_data *)((((FPTR)mips3.drcdata + 15) >> 4) << 4); - memset(mips3.drcdata, 0, sizeof(*mips3.drcdata)); - - /* set up constants */ - mips3.drcdata->abs64mask[0] = U64(0x7fffffffffffffff); - mips3.drcdata->abs32mask[0] = 0x7fffffff; - - /* get pointers to C functions */ - mips3.drcdata->activecpu_gettotalcycles = (x86code *)activecpu_gettotalcycles; - mips3.drcdata->mips3com_update_cycle_counting = (x86code *)mips3com_update_cycle_counting; - mips3.drcdata->mips3com_tlbr = (x86code *)mips3com_tlbr; - mips3.drcdata->mips3com_tlbwi = (x86code *)mips3com_tlbwi; - mips3.drcdata->mips3com_tlbwr = (x86code *)mips3com_tlbwr; - mips3.drcdata->mips3com_tlbp = (x86code *)mips3com_tlbp; - -#if COMPARE_AGAINST_C - mips3.drcdata->execute_c_version = (x86code *)execute_c_version; -#endif -} - - -/*------------------------------------------------- - mips3drc_exit - clean up the drc-specific - state --------------------------------------------------*/ - -static void mips3drc_exit(void) -{ - printf("Final code size = %d\n", (int)(mips3.drc->cache_top - mips3.drc->cache_base)); -#if LOG_HOTSPOTS - /* print top hotspots */ - { - int maxspot = 0; - int hotnum; - int count; - - for (count = 0; count < 10; count++) - { - for (hotnum = 1; hotnum < mips3.drcdata->next_hotspot; hotnum++) - if (mips3.drcdata->hotspot[hotnum].count > mips3.drcdata->hotspot[maxspot].count) - maxspot = hotnum; - printf("%08X%08X hits: add_speedup(0x%08X, 0x%08X);\n", - (UINT32)(mips3.drcdata->hotspot[maxspot].count >> 32), - (UINT32)mips3.drcdata->hotspot[maxspot].count, - mips3.drcdata->hotspot[maxspot].pc, - mips3.drcdata->hotspot[maxspot].opcode); - mips3.drcdata->hotspot[maxspot].count = 0; - } - } -#endif - drc_exit(mips3.drc); -} - - - -/*************************************************************************** - RECOMPILER CALLBACKS -***************************************************************************/ - -/*------------------------------------------------------------------ - drc_reset_callback -------------------------------------------------------------------*/ - -static void drc_reset_callback(drc_core *drc) -{ - if (LOG_CODE) - { - x86log_disasm_code_range(mips3.log, "entry_point:", (x86code *)drc->entry_point, drc->exit_point); - x86log_disasm_code_range(mips3.log, "exit_point:", drc->exit_point, drc->recompile); - x86log_disasm_code_range(mips3.log, "recompile:", drc->recompile, drc->dispatch); - x86log_disasm_code_range(mips3.log, "dispatch:", drc->dispatch, drc->flush); - x86log_disasm_code_range(mips3.log, "flush:", drc->flush, drc->cache_top); - } - - mips3.drcdata->generate_interrupt_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_INTERRUPT); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_interrupt_exception:", mips3.drcdata->generate_interrupt_exception, drc->cache_top); - - mips3.drcdata->generate_cop_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_BADCOP); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_cop_exception:", mips3.drcdata->generate_cop_exception, drc->cache_top); - - mips3.drcdata->generate_overflow_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_OVERFLOW); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_overflow_exception:", mips3.drcdata->generate_overflow_exception, drc->cache_top); - - mips3.drcdata->generate_invalidop_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_INVALIDOP); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_invalidop_exception:", mips3.drcdata->generate_invalidop_exception, drc->cache_top); - - mips3.drcdata->generate_syscall_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_SYSCALL); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_syscall_exception:", mips3.drcdata->generate_syscall_exception, drc->cache_top); - - mips3.drcdata->generate_break_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_BREAK); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_break_exception:", mips3.drcdata->generate_break_exception, drc->cache_top); - - mips3.drcdata->generate_trap_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_TRAP); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_trap_exception:", mips3.drcdata->generate_trap_exception, drc->cache_top); - - mips3.drcdata->generate_tlbload_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_TLBLOAD); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_tlbload_exception:", mips3.drcdata->generate_tlbload_exception, drc->cache_top); - - mips3.drcdata->generate_tlbstore_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_TLBSTORE); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_tlbstore_exception:", mips3.drcdata->generate_tlbstore_exception, drc->cache_top); - - mips3.drcdata->handle_pc_tlb_mismatch = drc->cache_top; - append_handle_pc_tlb_mismatch(drc); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "handle_pc_tlb_mismatch:", mips3.drcdata->handle_pc_tlb_mismatch, drc->cache_top); - - mips3.drcdata->find_exception_handler = drc->cache_top; - append_find_exception_handler(drc); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "find_exception_handler:", mips3.drcdata->find_exception_handler, drc->cache_top); - - mips3.drcdata->explode_ccr31 = drc->cache_top; - append_explode_ccr31(drc); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "explode_ccr31:", mips3.drcdata->explode_ccr31, drc->cache_top); - - mips3.drcdata->recover_ccr31 = drc->cache_top; - append_recover_ccr31(drc); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "recover_ccr31:", mips3.drcdata->recover_ccr31, drc->cache_top); - - generate_read_write_handlers(drc, &mips3.drcdata->general, "general", 0); - generate_read_write_handlers(drc, &mips3.drcdata->cached, "cached", ARW_CACHED); - generate_read_write_handlers(drc, &mips3.drcdata->uncached, "uncached", ARW_UNCACHED); -} - - -/*------------------------------------------------------------------ - drc_recompile_callback -------------------------------------------------------------------*/ - -static void drc_recompile_callback(drc_core *drc) -{ - int compiled_last_sequence = FALSE; - const opcode_desc *seqhead, *seqlast; - const opcode_desc *desclist; - void *start = drc->cache_top; - int override = FALSE; - int oobnum; - - (void)start; - - /* begin the sequence */ - drc_begin_sequence(drc, mips3.core->pc); - mips3.drcdata->oobcount = 0; - - /* get a description of this sequence */ - desclist = drcfe_describe_code(mips3.drcfe, mips3.core->pc); - if (LOG_CODE) - log_opcode_desc(mips3.log, desclist, 0); - - /* loop until we get through all instruction sequences */ - for (seqhead = desclist; seqhead != NULL; seqhead = seqlast->next) - { - const opcode_desc *curdesc; - compiler_state compiler; - - /* determine the last instruction in this sequence */ - for (seqlast = seqhead; seqlast != NULL; seqlast = seqlast->next) - if (seqlast->flags & OPFLAG_END_SEQUENCE) - break; - assert(seqlast != NULL); - - /* add this as an entry point */ - if (drc_add_entry_point(drc, seqhead->pc, override) && !override) - { - /* if this is the first sequence, it is a recompile request; allow overrides */ - if (seqhead == desclist) - { - override = TRUE; - drc_add_entry_point(drc, seqhead->pc, override); - } - - /* otherwise, just emit a jump to existing code */ - else - { - if (compiled_last_sequence) - drc_append_fixed_dispatcher(drc, seqhead->pc, TRUE); - compiled_last_sequence = FALSE; - continue; - } - } - compiled_last_sequence = TRUE; - - /* add a code log entry */ - if (LOG_CODE) - x86log_add_comment(mips3.log, drc->cache_top, "-------------------------"); - - /* validate this code block if we're not pointing into ROM */ - /* note that we assume P1 still contains the PC */ - if (memory_get_write_ptr(cpu_getactivecpu(), ADDRESS_SPACE_PROGRAM, seqhead->physpc) != NULL) - { - if (LOG_CODE) - x86log_add_comment(mips3.log, drc->cache_top, "[Validation for %08X]", seqhead->pc); - emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)seqhead->opptr.v); // mov rax,seqhead->opptr - for (curdesc = seqhead; curdesc != seqlast->next; curdesc = curdesc->next) - { - emit_cmp_m32_imm(DRCTOP, MBD(REG_RAX, curdesc->opptr.b - seqhead->opptr.b), *curdesc->opptr.l); - // cmp [code],val - emit_jcc(DRCTOP, COND_NE, mips3.drc->recompile); // jne recompile - } - } - - /* initialize the compiler state */ - compiler_state_reset(&compiler); - -#if LOG_HOTSPOTS - /* add hotspot tracking */ - { - int hotnum; - - drc_register_code_at_cache_top(drc, seqhead->pc); - - for (hotnum = 0; hotnum < mips3.drcdata->next_hotspot; hotnum++) - if (mips3.drcdata->hotspot[hotnum].pc == seqhead->pc && mips3.drcdata->hotspot[hotnum].opcode == *seqhead->opptr.l) - break; - if (hotnum == mips3.drcdata->next_hotspot) - { - assert(hotnum < ARRAY_LENGTH(mips3.drcdata->hotspot)); - mips3.drcdata->hotspot[hotnum].pc = seqhead->pc; - mips3.drcdata->hotspot[hotnum].opcode = *seqhead->opptr.l; - mips3.drcdata->hotspot[hotnum].count = 0; - mips3.drcdata->next_hotspot++; - } - emit_add_m64_imm(DRCTOP, MDRC(&mips3.drcdata->hotspot[hotnum].count), 1); - } -#endif - - /* iterate over instructions in the sequence and compile them */ - for (curdesc = seqhead; curdesc != seqlast->next; curdesc = curdesc->next) - compile_one(drc, &compiler, curdesc); - - /* at the end of the sequence; update the PC in memory and check cycle counts */ - if (!(seqlast->flags & (OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_WILL_CAUSE_EXCEPTION))) - { - UINT32 nextpc = seqlast->pc + (seqlast->skipslots + 1) * 4; - emit_update_cycles_pc_and_flush(drc, &compiler, NULL, nextpc); - } - - /* if this is a likely branch, the fall through case needs to skip the next instruction */ - if ((seqlast->flags & OPFLAG_IS_CONDITIONAL_BRANCH) && seqlast->skipslots > 0) - if (seqlast->next != NULL && seqlast->next->pc != seqlast->pc + 8) - { - compiler_register_flush_all(drc, &compiler); - drc_append_tentative_fixed_dispatcher(drc, seqlast->pc + 8, TRUE); // jmp <pc+8> - } - - /* if we need a redispatch, do it now */ - if (seqlast->flags & OPFLAG_REDISPATCH) - { - UINT32 nextpc = seqlast->pc + (seqlast->skipslots + 1) * 4; - compiler_register_flush_all(drc, &compiler); - drc_append_tentative_fixed_dispatcher(drc, nextpc, TRUE); // jmp <nextpc> - } - - /* if we need to return to the start, do it */ - if (seqlast->flags & OPFLAG_RETURN_TO_START) - { - compiler_register_flush_all(drc, &compiler); - drc_append_tentative_fixed_dispatcher(drc, mips3.core->pc, FALSE); // jmp <startpc> - } - } - - /* end the sequence */ - drc_end_sequence(drc); - - /* run the out-of-bounds handlers */ - for (oobnum = 0; oobnum < mips3.drcdata->oobcount; oobnum++) - { - oob_handler *oob = &mips3.drcdata->ooblist[oobnum]; - (*oob->callback)(drc, oob, oob->param); - } - - /* log the generated code */ - if (LOG_CODE) - { - char label[60]; - sprintf(label, "Code @ %08X (%08X physical)", desclist->pc, desclist->physpc); - x86log_disasm_code_range(mips3.log, label, start, drc->cache_top); - } -} - - -/*------------------------------------------------------------------ - drc_entrygen_callback -------------------------------------------------------------------*/ - -static void drc_entrygen_callback(drc_core *drc) -{ - append_check_interrupts(drc, NULL, NULL); -} - - - -/*************************************************************************** - RECOMPILER CORE -***************************************************************************/ - -/*------------------------------------------------------------------ - compile_one -------------------------------------------------------------------*/ - -static void compile_one(drc_core *drc, compiler_state *compiler, const opcode_desc *desc) -{ - int hotnum; - - /* register this instruction */ - if (!(desc->flags & OPFLAG_IN_DELAY_SLOT)) - drc_register_code_at_cache_top(drc, desc->pc); - - /* add an entry for the log */ - if (LOG_CODE) - log_add_disasm_comment(mips3.log, drc->cache_top, desc->pc, *desc->opptr.l); - - /* if we want a probe, add it here */ - if (desc->pc == PROBE_ADDRESS) - { - emit_mov_m32_imm(DRCTOP, PCADDR, desc->pc); // mov [pc],desc->pc - compiler_register_flush_all(drc, compiler); - emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)probe_printf); - emit_call_r64(DRCTOP, REG_RAX); - } - - /* if we are debugging, call the debugger */ - if (Machine->debug_mode || COMPARE_AGAINST_C) - { - emit_mov_m32_imm(DRCTOP, PCADDR, desc->pc); // mov [pc],desc->pc - if (COMPARE_AGAINST_C && !(desc->flags & OPFLAG_IN_DELAY_SLOT)) - emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->execute_c_version)); - if (Machine->debug_mode) - drc_append_call_debugger(drc); // <call debugger> - } - - /* validate our TLB entry at this PC; if we fail, we need to recompile */ - if ((desc->flags & OPFLAG_VALIDATE_TLB) && (desc->pc < 0x80000000 || desc->pc >= 0xc0000000)) - { - emit_cmp_m32_imm(DRCTOP, MDRC(&mips3.core->tlb_table[desc->pc >> 12]), mips3.core->tlb_table[desc->pc >> 12]); - // cmp tlb_table[pc>>12],<original value> - oob_request_callback(drc, COND_NE, oob_exception_cleanup, compiler, desc, mips3.drcdata->handle_pc_tlb_mismatch); - // jne handle_pc_tlb_mismatch - } - - /* accumulate total cycles */ - compiler->cycles += desc->cycles; - - /* is this a hotspot? */ - for (hotnum = 0; hotnum < MIPS3_MAX_HOTSPOTS; hotnum++) - if (desc->pc == mips3.hotspot[hotnum].pc && *desc->opptr.l == mips3.hotspot[hotnum].opcode) - { - compiler->cycles += mips3.hotspot[hotnum].cycles; - break; - } - - /* if this is an invalid opcode, generate the exception now */ - if (desc->flags & OPFLAG_INVALID_OPCODE) - oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_invalidop_exception); - - /* otherwise, it's a regular instruction */ - else - { - /* compile the instruction */ - if (!compile_instruction(drc, compiler, desc)) - fatalerror("Unimplemented op %08X (%02X,%02X)", *desc->opptr.l, *desc->opptr.l >> 26, *desc->opptr.l & 0x3f); - - /* flush */ - if (FLUSH_AFTER_EACH_INSTRUCTION) - compiler_register_flush_all(drc, compiler); - } -} - - - -/*************************************************************************** - OUT-OF-BOUNDS CODEGEN -***************************************************************************/ - -/*------------------------------------------------- - oob_exception_cleanup - out-of-bounds code - generator for cleaning up and generating an - exception with the EPC pointing to the - current instruction --------------------------------------------------*/ - -static void oob_exception_cleanup(drc_core *drc, oob_handler *oob, void *param) -{ - /* if this is non-jumping error, emit a unique signature */ - if (oob->link.size == 0) - { - /* align to an 8-byte boundary */ - while (((UINT64)drc->cache_top & 7) != 0) - emit_int_3(DRCTOP); - - /* logging */ - if (LOG_CODE) - { - x86log_add_comment(mips3.log, drc->cache_top, "[Exception path for %08X]", oob->desc->pc); - x86log_mark_as_data(mips3.log, drc->cache_top, drc->cache_top + 15, 8); - } - - /* output a pair of qwords */ - emit_qword(DRCTOP, EXCEPTION_ADDRESS_COOKIE); - emit_qword(DRCTOP, (UINT64)oob->link.target); - } - - /* otherwise, just resolve the link */ - else - { - resolve_link(DRCTOP, &oob->link); - if (LOG_CODE) - x86log_add_comment(mips3.log, drc->cache_top, "[Exception path for %08X]", oob->desc->pc); - } - - /* adjust for cycles */ - if (oob->compiler.cycles != 0) - emit_sub_m32_imm(DRCTOP, ICOUNTADDR, oob->compiler.cycles); // sub [icount],cycles - - /* update the PC with the instruction PC */ - if (!(oob->desc->flags & OPFLAG_IN_DELAY_SLOT)) - emit_mov_r32_imm(DRCTOP, REG_P1, oob->desc->pc); // mov p1,pc - else - emit_mov_r32_imm(DRCTOP, REG_P1, oob->desc->pc | 1); // mov p1,pc | 1 - - /* flush any dirty registers */ - compiler_register_flush_all(drc, &oob->compiler); // <flush registers> - - /* jump to the exception generator */ - emit_jmp(DRCTOP, param); // jmp <generate exception> -} - - -/*------------------------------------------------- - oob_interrupt_cleanup - out-of-bounds code - generator for cleaning up and generating an - interrupt with the EPC pointing to the - following instruction --------------------------------------------------*/ - -static void oob_interrupt_cleanup(drc_core *drc, oob_handler *oob, void *param) -{ - /* resolve the link */ - resolve_link(DRCTOP, &oob->link); - - /* add a code log entry */ - if (LOG_CODE) - x86log_add_comment(mips3.log, drc->cache_top, "[Interrupt path for %08X]", oob->desc->pc); - - /* adjust for cycles */ - if (oob->compiler.cycles != 0) - emit_sub_m32_imm(DRCTOP, ICOUNTADDR, oob->compiler.cycles); // sub [icount],cycles - - /* update the PC with the following instruction PC */ - if (!(oob->desc->flags & OPFLAG_IN_DELAY_SLOT)) - { - assert(!(oob->desc->flags & OPFLAG_IS_BRANCH)); - emit_mov_r32_imm(DRCTOP, REG_P1, oob->desc->pc + 4); // mov p1,pc + 4 - } - else - { - assert(oob->desc->branch != NULL); - if (oob->desc->branch->targetpc != BRANCH_TARGET_DYNAMIC) - emit_mov_r32_imm(DRCTOP, REG_P1, oob->desc->branch->targetpc); // mov p1,<targetpc> - else - emit_mov_r32_m32(DRCTOP, REG_P1, MBD(REG_RSP, SPOFFS_NEXTPC)); // mov p1,[rsp + nextpc] - } - - /* flush any dirty registers */ - compiler_register_flush_all(drc, &oob->compiler); // <flush registers> - - /* jump to the exception generator */ - emit_jmp(DRCTOP, param); // jmp <generate exception> -} - - - -/*************************************************************************** - REGISTER ALLOCATION ROUTINES -***************************************************************************/ - -/*------------------------------------------------- - compiler_register_set_constant - set a MIPS - register to a constant value --------------------------------------------------*/ - -static void compiler_register_set_constant(compiler_state *compiler, UINT8 mipsreg, UINT64 constval) -{ - mipsreg_state reg = compiler->mipsregs[mipsreg]; - - assert((INT32)constval == (INT64)constval); - - /* if the register was previously live, free it */ - if (ISLIVE(reg)) - { - assert(compiler->x86regs[X86REG(reg)] & X86REG_LIVE); - compiler->x86regs[X86REG(reg)] = 0; - } - - /* set up the constant */ - compiler->mipsregs[mipsreg] = MAKE_CONST(constval) | ((mipsreg == 0) ? 0 : MIPSREG_DIRTY); -} - - -/*------------------------------------------------- - compiler_register_allocate - allocate an x86 - register to hold a MIPS register --------------------------------------------------*/ - -static mipsreg_state compiler_register_allocate(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 mipsreg, UINT8 flags) -{ - UINT8 regindex, regnum = 0; - UINT64 livemask = 0; - - /* register 0 is always constant */ - if (mipsreg == 0) - return MAKE_CONST(0); - - /* first see if we are already allocated */ - if (ISLIVE(compiler->mipsregs[mipsreg])) - { - /* if we're not a source, make sure we are marked dirty */ - if (flags & ALLOC_DIRTY) - compiler->mipsregs[mipsreg] |= MIPSREG_DIRTY; - return compiler->mipsregs[mipsreg]; - } - - /* if we just want a source register, return a constant directly */ - if (flags == ALLOC_SRC && ISCONST(compiler->mipsregs[mipsreg])) - return compiler->mipsregs[mipsreg]; - - /* first look for a free entry */ - for (regindex = 0; regindex < NUM_NVREG; regindex++) - { - regnum = nvreg[regindex]; - if (!(compiler->x86regs[regnum] & X86REG_LIVE)) - goto allocateit; - else - livemask |= (UINT64)1 << (compiler->x86regs[regnum] & X86REG_MIPSMASK); - } - - /* can't have anything we need for this instruction, however */ - livemask &= ~desc->gpr.used & ~desc->gpr.modified; - desc = desc->next; - - /* next loop over the registers, figuring out who has the longest until being used */ - while (desc != NULL && livemask != 0) - { - /* if this instruction uses the last remaining register, we have to pick from among those */ - if ((livemask & ~desc->gpr.used) == 0) - break; - livemask &= ~desc->gpr.used; - - /* if this instruction modifies any of the registers, we want those */ - if ((livemask & desc->gpr.modified) != 0) - { - livemask &= desc->gpr.modified; - break; - } - - /* advance to the next instruction; stop if we hit end-of-sequence */ - if (desc->flags & OPFLAG_END_SEQUENCE) - break; - desc = desc->next; - } - - /* pick the first one */ - for (regindex = 0; regindex < NUM_NVREG; regindex++) - { - regnum = nvreg[regindex]; - if (livemask & ((UINT64)1 << (compiler->x86regs[regnum] & X86REG_MIPSMASK))) - break; - } - assert(regindex != NUM_NVREG); - - /* flush it */ - compiler_register_flush(drc, compiler, compiler->x86regs[regnum] & X86REG_MIPSMASK, TRUE); - -allocateit: - /* load the contents if we are a source */ - if (LOG_CODE) - x86log_add_comment(mips3.log, drc->cache_top, "allocate r%d (%s)", mipsreg, x86regname[regnum]); - if (flags & ALLOC_SRC) - { - mipsreg_state state = compiler->mipsregs[mipsreg]; - if (ISCONST(state)) - emit_mov_r64_imm(DRCTOP, regnum, CONSTVAL(state)); // mov <x86reg>,constant - else - emit_mov_r64_m64(DRCTOP, regnum, REGADDR(mipsreg)); // mov <x86reg>,[reg] - } - - /* mark it allocated */ - assert(!(compiler->x86regs[regnum] & X86REG_LIVE)); - compiler->x86regs[regnum] = X86REG_LIVE | mipsreg; - compiler->mipsregs[mipsreg] = MIPSREG_LIVE | regnum | ((flags & ALLOC_DIRTY) ? MIPSREG_DIRTY : 0); - return compiler->mipsregs[mipsreg]; -} - - -/*------------------------------------------------- - compiler_register_flush - flush and optionally - free a single MIPS register --------------------------------------------------*/ - -static void compiler_register_flush(drc_core *drc, compiler_state *compiler, UINT8 mipsreg, int free) -{ - mipsreg_state reg = compiler->mipsregs[mipsreg]; - - assert(ISCONST(reg) || ISLIVE(reg)); - - /* flush the value if dirty */ - if (ISDIRTY(reg)) - { - if (LOG_CODE) - x86log_add_comment(mips3.log, drc->cache_top, "flush r%d (%s)", mipsreg, ISCONST(reg) ? "constant" : x86regname[X86REG(reg)]); - - /* flush a constant value */ - if (ISCONST(reg)) - emit_mov_m64_imm(DRCTOP, REGADDR(mipsreg), CONSTVAL(reg)); // mov [reg],constant - - /* flush a live value */ - else if (ISLIVE(reg)) - emit_mov_m64_r64(DRCTOP, REGADDR(mipsreg), X86REG(reg)); // mov [reg],<x86reg> - } - - /* clear the dirty flag */ - if (!free) - compiler->mipsregs[mipsreg] &= ~MIPSREG_DIRTY; - else - { - if (ISLIVE(reg)) - compiler->x86regs[X86REG(reg)] = 0; - compiler->mipsregs[mipsreg] = 0; - } -} - - -/*------------------------------------------------- - compiler_register_flush_all - flush all dirty - registers --------------------------------------------------*/ - -static void compiler_register_flush_all(drc_core *drc, compiler_state *compiler) -{ - int mipsreg; - - /* flush all dirty registers */ - for (mipsreg = 1; mipsreg < 35; mipsreg++) - if (ISDIRTY(compiler->mipsregs[mipsreg])) - compiler_register_flush(drc, compiler, mipsreg, FALSE); -} - - - -/*************************************************************************** - COMPILER CALLBACKS -***************************************************************************/ - -/*------------------------------------------------- - probe_printf - print the current CPU state - and return --------------------------------------------------*/ - -static void probe_printf(void) -{ - printf(" PC=%08X r1=%08X%08X r2=%08X%08X r3=%08X%08X\n", - mips3.core->pc, - (UINT32)(mips3.core->r[1] >> 32), (UINT32)mips3.core->r[1], - (UINT32)(mips3.core->r[2] >> 32), (UINT32)mips3.core->r[2], - (UINT32)(mips3.core->r[3] >> 32), (UINT32)mips3.core->r[3]); - printf(" r4=%08X%08X r5=%08X%08X r6=%08X%08X r7=%08X%08X\n", - (UINT32)(mips3.core->r[4] >> 32), (UINT32)mips3.core->r[4], - (UINT32)(mips3.core->r[5] >> 32), (UINT32)mips3.core->r[5], - (UINT32)(mips3.core->r[6] >> 32), (UINT32)mips3.core->r[6], - (UINT32)(mips3.core->r[7] >> 32), (UINT32)mips3.core->r[7]); - printf(" r8=%08X%08X r9=%08X%08X r10=%08X%08X r11=%08X%08X\n", - (UINT32)(mips3.core->r[8] >> 32), (UINT32)mips3.core->r[8], - (UINT32)(mips3.core->r[9] >> 32), (UINT32)mips3.core->r[9], - (UINT32)(mips3.core->r[10] >> 32), (UINT32)mips3.core->r[10], - (UINT32)(mips3.core->r[11] >> 32), (UINT32)mips3.core->r[11]); - printf("r12=%08X%08X r13=%08X%08X r14=%08X%08X r15=%08X%08X\n", - (UINT32)(mips3.core->r[12] >> 32), (UINT32)mips3.core->r[12], - (UINT32)(mips3.core->r[13] >> 32), (UINT32)mips3.core->r[13], - (UINT32)(mips3.core->r[14] >> 32), (UINT32)mips3.core->r[14], - (UINT32)(mips3.core->r[15] >> 32), (UINT32)mips3.core->r[15]); - printf("r16=%08X%08X r17=%08X%08X r18=%08X%08X r19=%08X%08X\n", - (UINT32)(mips3.core->r[16] >> 32), (UINT32)mips3.core->r[16], - (UINT32)(mips3.core->r[17] >> 32), (UINT32)mips3.core->r[17], - (UINT32)(mips3.core->r[18] >> 32), (UINT32)mips3.core->r[18], - (UINT32)(mips3.core->r[19] >> 32), (UINT32)mips3.core->r[19]); - printf("r20=%08X%08X r21=%08X%08X r22=%08X%08X r23=%08X%08X\n", - (UINT32)(mips3.core->r[20] >> 32), (UINT32)mips3.core->r[20], - (UINT32)(mips3.core->r[21] >> 32), (UINT32)mips3.core->r[21], - (UINT32)(mips3.core->r[22] >> 32), (UINT32)mips3.core->r[22], - (UINT32)(mips3.core->r[23] >> 32), (UINT32)mips3.core->r[23]); - printf("r24=%08X%08X r25=%08X%08X r26=%08X%08X r27=%08X%08X\n", - (UINT32)(mips3.core->r[24] >> 32), (UINT32)mips3.core->r[24], - (UINT32)(mips3.core->r[25] >> 32), (UINT32)mips3.core->r[25], - (UINT32)(mips3.core->r[26] >> 32), (UINT32)mips3.core->r[26], - (UINT32)(mips3.core->r[27] >> 32), (UINT32)mips3.core->r[27]); - printf("r28=%08X%08X r29=%08X%08X r30=%08X%08X r31=%08X%08X\n", - (UINT32)(mips3.core->r[28] >> 32), (UINT32)mips3.core->r[28], - (UINT32)(mips3.core->r[29] >> 32), (UINT32)mips3.core->r[29], - (UINT32)(mips3.core->r[30] >> 32), (UINT32)mips3.core->r[30], - (UINT32)(mips3.core->r[31] >> 32), (UINT32)mips3.core->r[31]); - printf(" hi=%08X%08X lo=%08X%08X\n", - (UINT32)(mips3.core->r[REG_HI] >> 32), (UINT32)mips3.core->r[REG_HI], - (UINT32)(mips3.core->r[REG_LO] >> 32), (UINT32)mips3.core->r[REG_LO]); -} - - -/*------------------------------------------------- - exception_trap - log any exceptions that - aren't interrupts --------------------------------------------------*/ - -static void exception_trap(void) -{ - if ((mips3.core->cpr[0][COP0_Cause] & 0xff) != 0) - printf("Exception: EPC=%08X Cause=%08X BadVAddr=%08X Jmp=%08X\n", (UINT32)mips3.core->cpr[0][COP0_EPC], (UINT32)mips3.core->cpr[0][COP0_Cause], (UINT32)mips3.core->cpr[0][COP0_BadVAddr], mips3.core->pc); -} - - - -/*************************************************************************** - COMMON ROUTINES -***************************************************************************/ - -/*------------------------------------------------------------------ - generate_read_write_handlers -------------------------------------------------------------------*/ - -static void generate_read_write_handlers(drc_core *drc, readwrite_handlers *handlers, const char *type, UINT8 flags) -{ - char buffer[100]; - - handlers->read_byte_signed = drc->cache_top; - append_readwrite_and_translate(drc, 1, ARW_READ | ARW_SIGNED | flags, offsetof(readwrite_handlers, read_byte_signed)); - sprintf(buffer, "%s.read_byte_signed:", type); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, buffer, handlers->read_byte_signed, drc->cache_top); - - handlers->read_byte_unsigned = drc->cache_top; - append_readwrite_and_translate(drc, 1, ARW_READ | ARW_UNSIGNED | flags, offsetof(readwrite_handlers, read_byte_unsigned)); - sprintf(buffer, "%s.read_byte_unsigned:", type); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, buffer, handlers->read_byte_unsigned, drc->cache_top); - - handlers->read_half_signed = drc->cache_top; - append_readwrite_and_translate(drc, 2, ARW_READ | ARW_SIGNED | flags, offsetof(readwrite_handlers, read_half_signed)); - sprintf(buffer, "%s.read_half_signed:", type); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, buffer, handlers->read_half_signed, drc->cache_top); - - handlers->read_half_unsigned = drc->cache_top; - append_readwrite_and_translate(drc, 2, ARW_READ | ARW_UNSIGNED | flags, offsetof(readwrite_handlers, read_half_unsigned)); - sprintf(buffer, "%s.read_half_unsigned:", type); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, buffer, handlers->read_half_unsigned, drc->cache_top); - - handlers->read_word_signed = drc->cache_top; - append_readwrite_and_translate(drc, 4, ARW_READ | ARW_SIGNED | flags, offsetof(readwrite_handlers, read_word_signed)); - sprintf(buffer, "%s.read_word_signed:", type); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, buffer, handlers->read_word_signed, drc->cache_top); - - handlers->read_word_unsigned = drc->cache_top; - append_readwrite_and_translate(drc, 4, ARW_READ | ARW_UNSIGNED | flags, offsetof(readwrite_handlers, read_word_unsigned)); - sprintf(buffer, "%s.read_word_unsigned:", type); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, buffer, handlers->read_word_unsigned, drc->cache_top); - - handlers->read_word_masked = drc->cache_top; - append_readwrite_and_translate(drc, 4, ARW_READ | ARW_UNSIGNED | ARW_MASKED | flags, offsetof(readwrite_handlers, read_word_masked)); - sprintf(buffer, "%s.read_word_masked:", type); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, buffer, handlers->read_word_masked, drc->cache_top); - - handlers->read_double = drc->cache_top; - append_readwrite_and_translate(drc, 8, ARW_READ | flags, offsetof(readwrite_handlers, read_double)); - sprintf(buffer, "%s.read_double:", type); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, buffer, handlers->read_double, drc->cache_top); - - handlers->read_double_masked = drc->cache_top; - append_readwrite_and_translate(drc, 8, ARW_READ | ARW_MASKED | flags, offsetof(readwrite_handlers, read_double_masked)); - sprintf(buffer, "%s.read_double_masked:", type); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, buffer, handlers->read_double_masked, drc->cache_top); - - handlers->write_byte = drc->cache_top; - append_readwrite_and_translate(drc, 1, ARW_WRITE | flags, offsetof(readwrite_handlers, write_byte)); - sprintf(buffer, "%s.write_byte:", type); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, buffer, handlers->write_byte, drc->cache_top); - - handlers->write_half = drc->cache_top; - append_readwrite_and_translate(drc, 2, ARW_WRITE | flags, offsetof(readwrite_handlers, write_half)); - sprintf(buffer, "%s.write_half:", type); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, buffer, handlers->write_half, drc->cache_top); - - handlers->write_word = drc->cache_top; - append_readwrite_and_translate(drc, 4, ARW_WRITE | flags, offsetof(readwrite_handlers, write_word)); - sprintf(buffer, "%s.write_word:", type); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, buffer, handlers->write_word, drc->cache_top); - - handlers->write_word_masked = drc->cache_top; - append_readwrite_and_translate(drc, 4, ARW_WRITE | ARW_MASKED | flags, offsetof(readwrite_handlers, write_word_masked)); - sprintf(buffer, "%s.write_word_masked:", type); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, buffer, handlers->write_word_masked, drc->cache_top); - - handlers->write_double = drc->cache_top; - append_readwrite_and_translate(drc, 8, ARW_WRITE | flags, offsetof(readwrite_handlers, write_double)); - sprintf(buffer, "%s.write_double:", type); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, buffer, handlers->write_double, drc->cache_top); - - handlers->write_double_masked = drc->cache_top; - append_readwrite_and_translate(drc, 8, ARW_WRITE | ARW_MASKED | flags, offsetof(readwrite_handlers, write_double_masked)); - sprintf(buffer, "%s.write_double_masked:", type); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, buffer, handlers->write_double_masked, drc->cache_top); -} - - -/*------------------------------------------------------------------ - append_generate_exception -------------------------------------------------------------------*/ - -static void append_generate_exception(drc_core *drc, UINT8 exception) -{ - UINT32 offset = (exception >= EXCEPTION_TLBMOD && exception <= EXCEPTION_TLBSTORE) ? 0x00 : 0x180; - emit_link link1, link2; - - if (exception == EXCEPTION_TLBLOAD || exception == EXCEPTION_TLBSTORE) - { - /* assumes failed address is in P2 */ - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_BadVAddr), REG_P2); // mov [BadVAddr],p2 - emit_and_m32_imm(DRCTOP, CPR0ADDR(COP0_EntryHi), 0x000000ff); // and [EntryHi],0x000000ff - emit_and_r32_imm(DRCTOP, REG_P2, 0xffffe000); // and p2,0xffffe000 - emit_or_m32_r32(DRCTOP, CPR0ADDR(COP0_EntryHi), REG_P2); // or [EntryHi],p2 - emit_and_m32_imm(DRCTOP, CPR0ADDR(COP0_Context), 0xff800000); // and [Context],0xff800000 - emit_shr_r32_imm(DRCTOP, REG_P2, 9); // shr p2,9 - emit_or_m32_r32(DRCTOP, CPR0ADDR(COP0_Context), REG_P2); // or [Context],p2 - } - - /* on entry, exception PC must be in REG_P1, and low bit set if in a delay slot */ - emit_mov_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(COP0_Cause)); // mov eax,[Cause] - emit_and_r32_imm(DRCTOP, REG_EAX, ~0x800000ff); // and eax,~0x800000ff - emit_test_r32_imm(DRCTOP, REG_P1, 1); // test p1,1 - emit_jcc_short_link(DRCTOP, COND_Z, &link1); // jz skip - emit_or_r32_imm(DRCTOP, REG_EAX, 0x80000000); // or eax,0x80000000 - emit_sub_r32_imm(DRCTOP, REG_P1, 5); // sub p1,5 - - resolve_link(DRCTOP, &link1); // skip: - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_EPC), REG_P1); // mov [EPC],p1 - if (exception) - emit_or_r32_imm(DRCTOP, REG_EAX, exception << 2); // or eax,exception << 2 - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Cause), REG_EAX); // mov [Cause],eax - emit_mov_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(COP0_Status)); // mov eax,[SR] - emit_or_r32_imm(DRCTOP, REG_EAX, SR_EXL); // or eax,SR_EXL - emit_test_r32_imm(DRCTOP, REG_EAX, SR_BEV); // test eax,SR_BEV - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Status), REG_EAX); // mov [SR],eax - emit_mov_r32_imm(DRCTOP, REG_P1, 0xbfc00200 + offset); // mov p1,0xbfc00200+offset - emit_jcc_short_link(DRCTOP, COND_NZ, &link2); // jnz skip2 - emit_mov_r32_imm(DRCTOP, REG_P1, 0x80000000 + offset); // mov p1,0x80000000+offset - - resolve_link(DRCTOP, &link2); // skip2: - if (PRINTF_EXCEPTIONS) - { - emit_mov_m32_r32(DRCTOP, PCADDR, REG_P1); // mov [PC],p1 - emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)exception_trap); // mov rax,exception_trap - emit_call_r64(DRCTOP, REG_RAX); // call rax - emit_mov_r32_m32(DRCTOP, REG_P1, PCADDR); // mov p1,[PC] - } - drc_append_dispatcher(drc); // dispatch -} - - -/*------------------------------------------------------------------ - append_handle_pc_tlb_mismatch -------------------------------------------------------------------*/ - -static void append_handle_pc_tlb_mismatch(drc_core *drc) -{ -#if 0 - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_R15D); // mov eax,edi - emit_shr_r32_imm(DRCTOP, REG_EAX, 12); // shr eax,12 - emit_mov_r32_m32(DRCTOP, REG_EDX, MISD(REG_EAX, 4, &mips3.core->tlb_table)); // mov edx,tlb_table[eax*4] - emit_test_r32_imm(DRCTOP, REG_EDX, 2); // test edx,2 - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_R15D); // mov eax,edi - emit_jcc(DRCTOP, COND_NZ, mips3.drcdata->generate_tlbload_exception); // jnz generate_tlbload_exception - emit_mov_m32bd_r32(DRCTOP, drc->pcptr, REG_R15D); // mov [pcptr],edi - emit_jmp(DRCTOP, drc->recompile); // call recompile -#endif -} - - -/*------------------------------------------------------------------ - append_find_exception_handler -------------------------------------------------------------------*/ - -static void append_find_exception_handler(drc_core *drc) -{ - x86code *loop; - - emit_mov_r64_m64(DRCTOP, REG_RAX, MBD(REG_RSP, 0)); // mov rax,[rsp] - emit_mov_r64_imm(DRCTOP, REG_V5, EXCEPTION_ADDRESS_COOKIE); // mov v5,EXCEPTION_ADDRESS_COOKIE - emit_mov_r64_r64(DRCTOP, REG_V6, REG_RAX); // mov v6,rax - emit_and_r64_imm(DRCTOP, REG_RAX, ~7); // and rax,~7 - emit_add_r64_imm(DRCTOP, REG_RSP, 8); // add rsp,8 - emit_add_r64_imm(DRCTOP, REG_RAX, 8); // add rax,8 - loop = drc->cache_top; // loop: - emit_cmp_m64_r64(DRCTOP, MBD(REG_RAX, 0), REG_V5); // cmp [rax],v5 - emit_lea_r64_m64(DRCTOP, REG_RAX, MBD(REG_RAX, 8)); // lea rax,[rax+8] - emit_jcc(DRCTOP, COND_NE, loop); // jne loop - emit_cmp_m64_r64(DRCTOP, MBD(REG_RAX, 0), REG_V6); // cmp [rax],v6 - emit_lea_r64_m64(DRCTOP, REG_RAX, MBD(REG_RAX, 8)); // lea rax,[rax+8] - emit_jcc(DRCTOP, COND_NE, loop); // jne loop - emit_jmp_r64(DRCTOP, REG_RAX); // jmp rax -} - - -/*------------------------------------------------------------------ - append_direct_ram_read -------------------------------------------------------------------*/ - -static void append_direct_ram_read(drc_core *drc, int size, UINT8 flags, void *rambase, offs_t ramstart) -{ - void *adjbase = (UINT8 *)rambase - ramstart; - INT64 adjdisp = (UINT8 *)adjbase - (UINT8 *)drc->baseptr; - int raxrel = ((INT32)adjdisp != adjdisp); - - /* if we can't fit the delta to adjbase within an INT32, load RAX with the full address here */ - if (raxrel) - emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)adjbase); - - /* fast RAM byte read */ - if (size == 1) - { - /* adjust for endianness */ - if (mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_P1, 3); // xor p1,3 - - /* read rax-relative */ - if (raxrel) - { - if (flags & ARW_SIGNED) - emit_movsx_r64_m8(DRCTOP, REG_RAX, MBISD(REG_RAX, REG_P1, 1, 0)); // movsx rax,byte ptr [p1 + rax] - else - emit_movzx_r64_m8(DRCTOP, REG_RAX, MBISD(REG_RAX, REG_P1, 1, 0)); // movzx rax,byte ptr [p1 + rax] - } - - /* read drc-relative */ - else - { - if (flags & ARW_SIGNED) - emit_movsx_r64_m8(DRCTOP, REG_RAX, MDRCISD(adjbase, REG_P1, 1, 0)); // movsx rax,byte ptr [p1 + adjbase] - else - emit_movzx_r64_m8(DRCTOP, REG_RAX, MDRCISD(adjbase, REG_P1, 1, 0)); // movzx rax,byte ptr [p1 + adjbase] - } - } - - /* fast RAM halfword read */ - else if (size == 2) - { - /* adjust for endianness */ - if (mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_P1, 2); // xor p1,2 - - /* read rax-relative */ - if (raxrel) - { - if (flags & ARW_SIGNED) - emit_movsx_r64_m16(DRCTOP, REG_RAX, MBISD(REG_RAX, REG_P1, 1, 0)); // movsx rax,word ptr [p1 + rax] - else - emit_movzx_r64_m16(DRCTOP, REG_EAX, MBISD(REG_RAX, REG_P1, 1, 0)); // movzx rax,word ptr [p1 + rax] - } - - /* read drc-relative */ - else - { - if (flags & ARW_SIGNED) - emit_movsx_r64_m16(DRCTOP, REG_RAX, MDRCISD(adjbase, REG_P1, 1, 0)); // movsx rax,word ptr [p1 + adjbase] - else - emit_movzx_r64_m16(DRCTOP, REG_EAX, MDRCISD(adjbase, REG_P1, 1, 0)); // movzx rax,word ptr [p1 + adjbase] - } - } - - /* fast RAM dword read */ - else if (size == 4) - { - /* read rax-relative */ - if (raxrel) - { - if (flags & ARW_SIGNED) - emit_movsxd_r64_m32(DRCTOP, REG_RAX, MBISD(REG_RAX, REG_P1, 1, 0)); // movsxd rax,dword ptr [p1 + rax] - else - emit_mov_r32_m32(DRCTOP, REG_EAX, MBISD(REG_RAX, REG_P1, 1, 0)); // mov eax,word ptr [p1 + rax] - } - - /* read drc-relative */ - else - { - if (flags & ARW_SIGNED) - emit_movsxd_r64_m32(DRCTOP, REG_RAX, MDRCISD(adjbase, REG_P1, 1, 0)); // movsxd rax,dword ptr [p1 + adjbase] - else - emit_mov_r32_m32(DRCTOP, REG_EAX, MDRCISD(adjbase, REG_P1, 1, 0)); // mov eax,word ptr [p1 + adjbase] - } - } - - /* fast RAM qword read */ - else if (size == 8) - { - /* read rax-relative */ - if (raxrel) - emit_mov_r64_m64(DRCTOP, REG_RAX, MBISD(REG_RAX, REG_P1, 1, 0)); // mov rax,[p1 + rax] - - /* read drc-relative */ - else - emit_mov_r64_m64(DRCTOP, REG_RAX, MDRCISD(adjbase, REG_P1, 1, 0)); // mov rax,[p1 + adjbase] - - /* adjust for endianness */ - if (mips3.core->bigendian) - emit_ror_r64_imm(DRCTOP, REG_RAX, 32); // ror rax,32 - } -} - - -/*------------------------------------------------------------------ - append_direct_ram_write -------------------------------------------------------------------*/ - -static void append_direct_ram_write(drc_core *drc, int size, UINT8 flags, void *rambase, offs_t ramstart) -{ - void *adjbase = (UINT8 *)rambase - ramstart; - INT64 adjdisp = (UINT8 *)adjbase - (UINT8 *)drc->baseptr; - int raxrel = ((INT32)adjdisp != adjdisp); - - /* if we can't fit the delta to adjbase within an INT32, load RAX with the full address here */ - if (raxrel) - emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)adjbase); - - /* fast RAM byte write */ - if (size == 1) - { - /* adjust for endianness */ - if (mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_P1, 3); // xor p1,3 - - /* write either rax-relative or drc-relative */ - if (raxrel) - emit_mov_m8_r8(DRCTOP, MBISD(REG_RAX, REG_P1, 1, 0), REG_P2); // mov [p1 + rax],p2 - else - emit_mov_m8_r8(DRCTOP, MDRCISD(adjbase, REG_P1, 1, 0), REG_P2); // mov [p1 + adjbase],p2 - } - - /* fast RAM halfword write */ - else if (size == 2) - { - /* adjust for endianness */ - if (mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_P1, 2); // xor p1,2 - - /* write either rax-relative or drc-relative */ - if (raxrel) - emit_mov_m16_r16(DRCTOP, MBISD(REG_RAX, REG_P1, 1, 0), REG_P2); // mov [p1 + rax],p2 - else - emit_mov_m16_r16(DRCTOP, MDRCISD(adjbase, REG_P1, 1, 0), REG_P2); // mov [p1 + adjbase],p2 - } - - /* fast RAM dword write */ - else if (size == 4) - { - /* unmasked */ - if (!(flags & ARW_MASKED)) - { - if (raxrel) - emit_mov_m32_r32(DRCTOP, MBISD(REG_RAX, REG_P1, 1, 0), REG_P2); // mov [p1 + rax],p2 - else - emit_mov_m32_r32(DRCTOP, MDRCISD(adjbase, REG_P1, 1, 0), REG_P2); // mov [p1 + adjbase],p2 - } - - /* masked */ - else - { - emit_mov_r32_r32(DRCTOP, REG_P4, REG_P3); // mov p4,p3 - emit_not_r32(DRCTOP, REG_P3); // not p3 - emit_and_r32_r32(DRCTOP, REG_P4, REG_P2); // and p4,p2 - if (raxrel) - emit_and_r32_m32(DRCTOP, REG_P3, MBISD(REG_RAX, REG_P1, 1, 0)); // and p3,[p1 + rax] - else - emit_and_r32_m32(DRCTOP, REG_P3, MDRCISD(adjbase, REG_P1, 1, 0)); // and p3,[p1 + adjbase] - emit_or_r32_r32(DRCTOP, REG_P4, REG_P3); // or p4,p3 - if (raxrel) - emit_mov_m32_r32(DRCTOP, MBISD(REG_RAX, REG_P1, 1, 0), REG_P4); // mov [p1 + rax],p4 - else - emit_mov_m32_r32(DRCTOP, MDRCISD(adjbase, REG_P1, 1, 0), REG_P4); // mov [p1 + adjbase],p4 - } - } - - /* fast RAM qword write */ - else if (size == 8) - { - /* adjust for endianness */ - if (mips3.core->bigendian) - emit_ror_r64_imm(DRCTOP, REG_P2, 32); // ror p2,32 - - /* unmasked */ - if (!(flags & ARW_MASKED)) - { - if (raxrel) - emit_mov_m64_r64(DRCTOP, MBISD(REG_RAX, REG_P1, 1, 0), REG_P2); // mov [p1 + rax],p2 - else - emit_mov_m64_r64(DRCTOP, MDRCISD(adjbase, REG_P1, 1, 0), REG_P2); // mov [p1 + adjbase],p2 - } - - /* masked */ - else - { - if (mips3.core->bigendian) - emit_ror_r64_imm(DRCTOP, REG_P3, 32); // ror p3,32 - emit_mov_r64_r64(DRCTOP, REG_P4, REG_P3); // mov p4,p3 - emit_not_r64(DRCTOP, REG_P3); // not p3 - emit_and_r64_r64(DRCTOP, REG_P4, REG_P2); // and p4,p2 - if (raxrel) - emit_and_r64_m64(DRCTOP, REG_P3, MBISD(REG_RAX, REG_P1, 1, 0)); // and p3,[p1 + rax] - else - emit_and_r64_m64(DRCTOP, REG_P3, MDRCISD(adjbase, REG_P1, 1, 0)); // and p3,[p1 + adjbase] - emit_or_r64_r64(DRCTOP, REG_P4, REG_P3); // or p4,p3 - if (raxrel) - emit_mov_m64_r64(DRCTOP, MBISD(REG_RAX, REG_P1, 1, 0), REG_P4); // mov [p1 + rax],p4 - else - emit_mov_m64_r64(DRCTOP, MDRCISD(adjbase, REG_P1, 1, 0), REG_P4); // mov [p1 + adjbase],p4 - } - } -} - - -/*------------------------------------------------------------------ - append_readwrite_and_translate -------------------------------------------------------------------*/ - -static void append_readwrite_and_translate(drc_core *drc, int size, UINT8 flags, UINT32 ptroffs) -{ - UINT32 base = ((flags & ARW_CACHED) ? 0x80000000 : (flags & ARW_UNCACHED) ? 0xa0000000 : 0); - emit_link page_fault = { 0 }, handle_kernel = { 0 }, wrong_handler = { 0 }; - x86code *continue_mapped = NULL; - int ramnum; - - /* if the base is 0, this is a general case which may be paged; perform the translation first */ - /* the result of the translation produces the physical address inline in P1 */ - /* if a page fault occurs, we have to keep an original copy of the address in EAX */ - /* note that we use RAX and P4 as scratch registers */ - if (base == 0) - { - emit_test_r32_r32(DRCTOP, REG_P1, REG_P1); // test p1,p1 - emit_mov_r32_r32(DRCTOP, REG_P4, REG_P1); // mov p4,p1d - emit_jcc_near_link(DRCTOP, COND_S, &handle_kernel); // js handle_kernel - continue_mapped = drc->cache_top; // continue_mapped: - emit_mov_r32_r32(DRCTOP, REG_V5, REG_P1); // mov v5,p1 - emit_shr_r32_imm(DRCTOP, REG_P1, 12); // shr p1,12 - emit_mov_r32_m32(DRCTOP, REG_P1, MDRCISD(mips3.core->tlb_table, REG_P1, 4, 0)); // mov p1,tlb_table[p1*4] - emit_and_r32_imm(DRCTOP, REG_P4, 0xfff); // and p4,0xfff - emit_shr_r32_imm(DRCTOP, REG_P1, (flags & ARW_WRITE) ? 1 : 2); // shr p1,2/1 (read/write) - emit_lea_r32_m32(DRCTOP, REG_P1, MBISD(REG_P4, REG_P1, (flags & ARW_WRITE) ? 2 : 4, 0)); - // lea p1,[p4 + p1 * 4/2 (read/write)] - emit_jcc_near_link(DRCTOP, COND_C, &page_fault); // jc page_fault - } - - /* if the base is non-zero, this is a cached/uncached direct-mapped case; no translation necessary */ - /* just subtract the base from the address to get the physcial address */ - else - emit_sub_r32_imm(DRCTOP, REG_P1, base); // sub p1,base - - /* iterate over all fast RAM ranges looking for matches that can be handled directly */ - for (ramnum = 0; ramnum < MIPS3_MAX_FASTRAM; ramnum++) - { - const fast_ram_info *raminfo = &mips3.fastram[ramnum]; - if (!COMPARE_AGAINST_C && !Machine->debug_mode && raminfo->base != NULL && (!(flags & ARW_WRITE) || !raminfo->readonly)) - { - emit_link notram1 = { 0 }, notram2 = { 0 }; - - /* if we have an end, check it first */ - if (raminfo->end != 0xffffffff) - { - emit_cmp_r32_imm(DRCTOP, REG_P1, raminfo->end); // cmp p1,fastram_end - emit_jcc_short_link(DRCTOP, COND_A, ¬ram1); // ja notram1 - } - - /* if we have a non-0 start, check it next */ - if (raminfo->start != 0x00000000) - { - emit_cmp_r32_imm(DRCTOP, REG_P1, raminfo->start); // cmp p1,fastram_start - emit_jcc_short_link(DRCTOP, COND_B, ¬ram2); // jb notram2 - } - - /* handle a fast RAM read */ - if (!(flags & ARW_WRITE)) - { - append_direct_ram_read(drc, size, flags, raminfo->base, raminfo->start); // <direct read> - emit_ret(DRCTOP); // ret - } - - /* handle a fast RAM write */ - else - { - append_direct_ram_write(drc, size, flags, raminfo->base, raminfo->start); // <direct write> - emit_ret(DRCTOP); // ret - } - - /* resolve links that skipped over us */ - if (raminfo->end != 0xffffffff) - resolve_link(DRCTOP, ¬ram1); // notram1: - if (raminfo->start != 0x00000000) - resolve_link(DRCTOP, ¬ram2); // notram2: - } - } - - /* failed to find it in fast RAM; make sure we are within our expected range */ - if (base != 0) - { - emit_cmp_r32_imm(DRCTOP, REG_P1, 0x20000000); // cmp p1,0x20000000 - emit_jcc_short_link(DRCTOP, COND_AE, &wrong_handler); // jae wrong_handler - } - - /* handle a write */ - if (flags & ARW_WRITE) - { - if (size == 1) - emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.writebyte)); // jmp writebyte - else if (size == 2) - emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.writehalf)); // jmp writehalf - else if (size == 4) - { - if (!(flags & ARW_MASKED)) - emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.writeword)); // jmp writeword - else - emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.writeword_masked)); // jmp writeword_masked - } - else - { - if (!(flags & ARW_MASKED)) - emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.writedouble)); // jmp writedouble - else - emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.writedouble_masked)); // jmp writedouble_masked - } - } - - /* handle a read */ - else - { - if (size == 1) - { - emit_sub_r64_imm(DRCTOP, REG_RSP, 0x28); // sub rsp,0x28 - emit_call_m64(DRCTOP, MDRC(&mips3.core->memory.readbyte)); // call readbyte - if (flags & ARW_SIGNED) - emit_movsx_r64_r8(DRCTOP, REG_RAX, REG_AL); // movsx rax,al - else - emit_movzx_r64_r8(DRCTOP, REG_RAX, REG_AL); // movzx rax,al - emit_add_r64_imm(DRCTOP, REG_RSP, 0x28); // add rsp,0x28 - emit_ret(DRCTOP); // ret - } - else if (size == 2) - { - emit_sub_r64_imm(DRCTOP, REG_RSP, 0x28); // sub rsp,0x28 - emit_call_m64(DRCTOP, MDRC(&mips3.core->memory.readhalf)); // call readhalf - if (flags & ARW_SIGNED) - emit_movsx_r64_r16(DRCTOP, REG_RAX, REG_AX); // movsx rax,ax - else - emit_movzx_r64_r16(DRCTOP, REG_RAX, REG_AX); // movzx rax,ax - emit_add_r64_imm(DRCTOP, REG_RSP, 0x28); // add rsp,0x28 - emit_ret(DRCTOP); // ret - } - else if (size == 4) - { - if (!(flags & ARW_MASKED)) - { - if (!(flags & ARW_SIGNED)) - emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.readword)); // jmp readword - else - { - emit_sub_r64_imm(DRCTOP, REG_RSP, 0x28); // sub rsp,0x28 - emit_call_m64(DRCTOP, MDRC(&mips3.core->memory.readword)); // call readword - emit_movsxd_r64_r32(DRCTOP, REG_RAX, REG_EAX); // movsxd rax,eax - emit_add_r64_imm(DRCTOP, REG_RSP, 0x28); // add rsp,0x28 - emit_ret(DRCTOP); // ret - } - } - else - emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.readword_masked)); // jmp readword_masked - } - else - { - if (!(flags & ARW_MASKED)) - emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.readdouble)); // jmp readdouble - else - emit_jmp_m64(DRCTOP, MDRC(&mips3.core->memory.readdouble_masked)); // jmp readdouble_masked - } - } - - /* handle wrong handlers */ - if (base == 0) - { - resolve_link(DRCTOP, &handle_kernel); // handle_kernel: - emit_cmp_r32_imm(DRCTOP, REG_P1, 0xc0000000); // cmp p1,0xc0000000 - emit_jcc(DRCTOP, COND_AE, continue_mapped); // jae continue_mapped - emit_test_r32_imm(DRCTOP, REG_P1, 0x20000000); // test p1,0x20000000 - emit_cmovcc_r64_m64(DRCTOP, COND_Z, REG_RAX, MDRCD(&mips3.drcdata->cached, ptroffs)); - // mov rax,<cached version> - emit_cmovcc_r64_m64(DRCTOP, COND_NZ, REG_RAX, MDRCD(&mips3.drcdata->uncached, ptroffs)); - // mov rax,<uncached version> - emit_mov_r64_m64(DRCTOP, REG_P4, MBD(REG_RSP, 0)); // mov p4,[rsp] - emit_push_r64(DRCTOP, REG_RAX); // push rax - emit_sub_r64_r64(DRCTOP, REG_RAX, REG_P4); // sub rax,p4 - emit_mov_m32_r32(DRCTOP, MBD(REG_P4, -4), REG_EAX); // mov [p4-4],eax - emit_ret(DRCTOP); // ret - } - else - { - resolve_link(DRCTOP, &wrong_handler); // wrong_handler: - emit_mov_r64_m64(DRCTOP, REG_RAX, MDRCD(&mips3.drcdata->general, ptroffs)); - // mov rax,<general version> - emit_mov_r64_m64(DRCTOP, REG_P4, MBD(REG_RSP, 0)); // mov p4,[rsp] - emit_push_r64(DRCTOP, REG_RAX); // push rax - emit_sub_r64_r64(DRCTOP, REG_RAX, REG_P4); // sub rax,p4 - emit_mov_m32_r32(DRCTOP, MBD(REG_P4, -4), REG_EAX); // mov [p4-4],eax - emit_add_r32_imm(DRCTOP, REG_P1, base); // add p1,base - emit_ret(DRCTOP); // ret - } - - /* handle page faults */ - if (base == 0) - { - resolve_link(DRCTOP, &page_fault); // page_fault: - emit_mov_r32_r32(DRCTOP, REG_P2, REG_V5); // mov p2,v5 - emit_jmp(DRCTOP, mips3.drcdata->find_exception_handler); // jmp find_exception_handler - } -} - - -/*------------------------------------------------------------------ - append_explode_ccr31 -------------------------------------------------------------------*/ - -static void append_explode_ccr31(drc_core *drc) -{ - emit_link link1; - int i; - - /* new CCR31 value in EAX */ - emit_test_r32_imm(DRCTOP, REG_EAX, 1 << 23); // test eax,1 << 23 - emit_mov_m32_r32(DRCTOP, CCR1ADDR(31), REG_EAX); // mov ccr[31],eax - emit_setcc_m8(DRCTOP, COND_NZ, CF1ADDR(0)); // setnz fcc[0] - if (mips3.core->flavor >= MIPS3_TYPE_MIPS_IV) - for (i = 1; i <= 7; i++) - { - emit_test_r32_imm(DRCTOP, REG_EAX, 1 << (24 + i)); // test eax,1 << (24+i) - emit_setcc_m8(DRCTOP, COND_NZ, CF1ADDR(i)); // setnz fcc[i] - } - emit_and_r32_imm(DRCTOP, REG_EAX, 3); // and eax,3 - emit_test_r32_imm(DRCTOP, REG_EAX, 1); // test eax,1 - emit_jcc_near_link(DRCTOP, COND_Z, &link1); // jz skip - emit_xor_r32_imm(DRCTOP, REG_EAX, 2); // xor eax,2 - resolve_link(DRCTOP, &link1); // skip: - drc_append_set_sse_rounding(drc, REG_EAX); // set_rounding(EAX) - emit_ret(DRCTOP); // ret -} - - -/*------------------------------------------------------------------ - append_recover_ccr31 -------------------------------------------------------------------*/ - -static void append_recover_ccr31(drc_core *drc) -{ - int i; - - emit_movsxd_r64_m32(DRCTOP, REG_RAX, CCR1ADDR(31)); // movsxd rax,ccr1[rd] - emit_and_r64_imm(DRCTOP, REG_RAX, ~0xfe800000); // and rax,~0xfe800000 - emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx - emit_cmp_m8_imm(DRCTOP, CF1ADDR(0), 0); // cmp fcc[0],0 - emit_setcc_r8(DRCTOP, COND_NZ, REG_DL); // setnz dl - emit_shl_r32_imm(DRCTOP, REG_EDX, 23); // shl edx,23 - emit_or_r64_r64(DRCTOP, REG_RAX, REG_RDX); // or rax,rdx - if (mips3.core->flavor >= MIPS3_TYPE_MIPS_IV) - for (i = 1; i <= 7; i++) - { - emit_cmp_m8_imm(DRCTOP, CF1ADDR(i), 0); // cmp fcc[i],0 - emit_setcc_r8(DRCTOP, COND_NZ, REG_DL); // setnz dl - emit_shl_r32_imm(DRCTOP, REG_EDX, 24+i); // shl edx,24+i - emit_or_r64_r64(DRCTOP, REG_RAX, REG_RDX); // or rax,rdx - } - emit_ret(DRCTOP); // ret -} - - -/*------------------------------------------------------------------ - append_check_interrupts -------------------------------------------------------------------*/ - -static void append_check_interrupts(drc_core *drc, compiler_state *compiler, const opcode_desc *desc) -{ - emit_link link1, link2, link3 = { 0 }; - - emit_mov_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(COP0_Cause)); // mov eax,[Cause] - emit_and_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(COP0_Status)); // and eax,[Status] - emit_and_r32_imm(DRCTOP, REG_EAX, 0xfc00); // and eax,0xfc00 - emit_jcc_short_link(DRCTOP, COND_Z, &link1); // jz skip - emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_IE); // test [Status],SR_IE - emit_jcc_short_link(DRCTOP, COND_Z, &link2); // jz skip - emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_EXL | SR_ERL); // test [Status],SR_EXL | SR_ERL - if (desc == NULL) - { - emit_mov_r32_m32(DRCTOP, REG_P1, MDRC(drc->pcptr)); // mov p1,[pc] - emit_jcc_short_link(DRCTOP, COND_NZ, &link3); // jnz skip - emit_jmp_m64(DRCTOP, MDRC(&mips3.drcdata->generate_interrupt_exception)); // jmp generate_interrupt_exception - } - else - oob_request_callback(drc, COND_Z, oob_interrupt_cleanup, compiler, desc, mips3.drcdata->generate_interrupt_exception); - resolve_link(DRCTOP, &link1); // skip: - resolve_link(DRCTOP, &link2); - if (desc == NULL) - resolve_link(DRCTOP, &link3); -} - - - -/*************************************************************************** - RECOMPILATION HELPERS -***************************************************************************/ - -/*------------------------------------------------- - emit_load_register_value - load a register - either as a constant or as a register --------------------------------------------------*/ - -static void emit_load_register_value(drc_core *drc, compiler_state *compiler, UINT8 dstreg, mipsreg_state regstate) -{ - /* must be live or constant */ - assert(ISCONST(regstate) || ISLIVE(regstate)); - - /* if the register is constant, do an immediate load */ - if (ISCONST(regstate)) - { - INT32 mipsval = CONSTVAL(regstate); - if (mipsval != 0) - emit_mov_r64_imm(DRCTOP, dstreg, mipsval); - else - emit_xor_r32_r32(DRCTOP, dstreg, dstreg); - } - - /* otherwise, load from the allocated register, but only if different */ - else - { - UINT8 srcreg = X86REG(regstate); - if (dstreg != srcreg) - emit_mov_r64_r64(DRCTOP, dstreg, srcreg); - } -} - - -/*------------------------------------------------- - emit_set_constant_value - set a constant - value; if it is too large, allocate a - register and populate it --------------------------------------------------*/ - -static void emit_set_constant_value(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 mipsreg, UINT64 constval) -{ - /* ignore reigster 0 destination */ - if (mipsreg == 0) - return; - - /* if we fit in 32-bits, just continue the constant charade */ - if ((INT32)constval == (INT64)constval) - compiler_register_set_constant(compiler, mipsreg, constval); - - /* otherwise, allocate a new register and move the result into it */ - else - { - mipsreg_state resultreg = compiler_register_allocate(drc, compiler, desc, mipsreg, ALLOC_DST | ALLOC_DIRTY); - emit_mov_r64_imm(DRCTOP, X86REG(resultreg), constval); // mov <rt>,result - } -} - - -/*------------------------------------------------------------------ - emit_flush_cycles_before_instruction -------------------------------------------------------------------*/ - -static void emit_flush_cycles_before_instruction(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT32 cycles_to_leave) -{ - /* if we have cycles, subtract them now */ - if (compiler->cycles - cycles_to_leave != 0) - emit_sub_m32_imm(DRCTOP, ICOUNTADDR, compiler->cycles - cycles_to_leave); // sub [icount],cycles - - /* clear out compiler counts */ - compiler->cycles = cycles_to_leave; -} - - -/*------------------------------------------------------------------ - emit_update_cycles_pc_and_flush -------------------------------------------------------------------*/ - -static void emit_update_cycles_pc_and_flush(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, offs_t destpc) -{ - /* if we have cycles, subtract them now */ - emit_sub_m32_imm(DRCTOP, ICOUNTADDR, compiler->cycles); // sub [icount],cycles - - /* clear out compiler counts */ - compiler->cycles = 0; - - /* if we have an opcode descriptor, make this an oob */ - if (desc != NULL) - oob_request_callback(drc, COND_S, oob_interrupt_cleanup, compiler, desc, mips3.drc->exit_point); - - /* otherwise, do it inline */ - else - { - /* load the target PC into P1 */ - if (destpc != BRANCH_TARGET_DYNAMIC) - emit_mov_r32_imm(DRCTOP, REG_P1, destpc); // mov p1,destpc - else - emit_mov_r32_m32(DRCTOP, REG_P1, MBD(REG_RSP, SPOFFS_NEXTPC)); // mov p1,[esp+nextpc] - - /* flush any live registers */ - compiler_register_flush_all(drc, compiler); - - /* if we subtracted cycles and went negative, time to exit */ - emit_jcc(DRCTOP, COND_S, mips3.drc->exit_point); // js exit_point - } -} - - -/*------------------------------------------------------------------ - emit_delay_slot_and_branch -------------------------------------------------------------------*/ - -static void emit_delay_slot_and_branch(drc_core *drc, const compiler_state *compiler, const opcode_desc *desc, UINT8 linkreg) -{ - compiler_state compiler_temp = *compiler; - - /* set the link if needed */ - if (linkreg != 0) - compiler_register_set_constant(&compiler_temp, linkreg, (INT32)(desc->pc + 8)); // <link> = pc + 8 - - /* compile the delay slot using temporary compiler state */ - assert(desc->delay != NULL); - compile_one(drc, &compiler_temp, desc->delay); // <next instruction> - - /* update the cycles and load the new PC in case we need to stop here */ - emit_update_cycles_pc_and_flush(drc, &compiler_temp, NULL, desc->targetpc); - - /* otherwise, append a dispatcher */ - if (desc->targetpc != BRANCH_TARGET_DYNAMIC) - drc_append_tentative_fixed_dispatcher(drc, desc->targetpc, FALSE); // <redispatch> - else - drc_append_dispatcher(drc); // <redispatch> -} - - -/*------------------------------------------------------------------ - emit_fixed_byte_read -------------------------------------------------------------------*/ - -static int emit_fixed_byte_read(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 destreg, int is_unsigned, offs_t address) -{ - void *fastram = fastram_ptr(address, 1, FALSE); - - /* if we can't do fast RAM, we need to make the call */ - if (fastram == NULL) - { - emit_mov_r32_imm(DRCTOP, REG_P1, address); // mov p1,address - if (is_unsigned) - emit_call(DRCTOP, mips3.drcdata->general.read_byte_unsigned); // call read_byte_unsigned - else - emit_call(DRCTOP, mips3.drcdata->general.read_byte_signed); // call read_byte_signed - - /* handle exceptions */ - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception); - if (destreg != REG_RAX) - emit_mov_r64_r64(DRCTOP, destreg, REG_RAX); // mov <rt>,rax - } - - /* if we can do fastram, emit the right form */ - else - { - INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr; - int raxrel = ((INT32)fastdisp != fastdisp); - - /* if we can't fit the delta to fastram within an INT32, load RAX with the full address here */ - if (raxrel) - { - emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram - if (is_unsigned) - emit_movzx_r64_m8(DRCTOP, destreg, MBD(REG_RAX, 0)); // movzx destreg,byte ptr [rax] - else - emit_movsx_r64_m8(DRCTOP, destreg, MBD(REG_RAX, 0)); // movsx destreg,byte ptr [rax] - } - - /* read drc-relative */ - else - { - if (is_unsigned) - emit_movzx_r64_m8(DRCTOP, destreg, MDRC(fastram)); // movzx destreg,byte ptr [p1 + adjbase] - else - emit_movsx_r64_m8(DRCTOP, destreg, MDRC(fastram)); // movsx destreg,byte ptr [p1 + adjbase] - } - } - return (fastram != NULL); -} - - -/*------------------------------------------------------------------ - emit_fixed_half_read -------------------------------------------------------------------*/ - -static int emit_fixed_half_read(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 destreg, int is_unsigned, offs_t address) -{ - void *fastram = fastram_ptr(address & ~1, 2, FALSE); - - /* if we can't do fast RAM, we need to make the call */ - if (fastram == NULL) - { - emit_mov_r32_imm(DRCTOP, REG_P1, address & ~1); // mov p1,address & ~1 - if (is_unsigned) - emit_call(DRCTOP, mips3.drcdata->general.read_half_unsigned); // call read_half_unsigned - else - emit_call(DRCTOP, mips3.drcdata->general.read_half_signed); // call read_half_signed - - /* handle exceptions */ - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception); - if (destreg != REG_RAX) - emit_mov_r64_r64(DRCTOP, destreg, REG_RAX); // mov <rt>,rax - } - - /* if we can do fastram, emit the right form */ - else - { - INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr; - int raxrel = ((INT32)fastdisp != fastdisp); - - /* read rax-relative */ - if (raxrel) - { - emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram - if (is_unsigned) - emit_movzx_r64_m16(DRCTOP, destreg, MBD(REG_RAX, 0)); // movzx destreg,word ptr [rax] - else - emit_movsx_r64_m16(DRCTOP, destreg, MBD(REG_RAX, 0)); // movsx destreg,word ptr [rax] - } - - /* read drc-relative */ - else - { - if (is_unsigned) - emit_movzx_r64_m16(DRCTOP, destreg, MDRC(fastram)); // movzx destreg,word ptr [p1 + adjbase] - else - emit_movsx_r64_m16(DRCTOP, destreg, MDRC(fastram)); // movsx destreg,word ptr [p1 + adjbase] - } - } - return (fastram != NULL); -} - - -/*------------------------------------------------------------------ - emit_fixed_word_read -------------------------------------------------------------------*/ - -static int emit_fixed_word_read(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 destreg, int is_unsigned, offs_t address, UINT32 mask) -{ - void *fastram = fastram_ptr(address & ~3, 4, FALSE); - - /* if we can't do fast RAM, we need to make the call */ - if (fastram == NULL) - { - emit_mov_r32_imm(DRCTOP, REG_P1, address & ~3); // mov p1,address & ~3 - if (mask != 0xffffffff) - { - emit_mov_r32_imm(DRCTOP, REG_P2, (INT32)mask); // mov p2,mask - emit_call(DRCTOP, mips3.drcdata->general.read_word_masked); // call read_word_masked - } - else if (is_unsigned) - emit_call(DRCTOP, mips3.drcdata->general.read_word_unsigned); // call read_word_unsigned - else - emit_call(DRCTOP, mips3.drcdata->general.read_word_signed); // call read_word_signed - - /* handle exceptions */ - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception); - if (destreg != REG_RAX) - emit_mov_r64_r64(DRCTOP, destreg, REG_RAX); // mov <rt>,rax - } - - /* if we can do fastram, emit the right form */ - else - { - INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr; - int raxrel = ((INT32)fastdisp != fastdisp); - - /* if we can't fit the delta to fastram within an INT32, load RAX with the full address here */ - if (raxrel) - { - emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram - if (is_unsigned) - emit_mov_r32_m32(DRCTOP, destreg, MBD(REG_RAX, 0)); // mov destreg,word ptr [rax] - else - emit_movsxd_r64_m32(DRCTOP, destreg, MBD(REG_RAX, 0)); // movsxd destreg,dword ptr [rax] - } - - /* read drc-relative */ - else - { - if (is_unsigned) - emit_mov_r32_m32(DRCTOP, destreg, MDRC(fastram)); // mov destreg,word ptr [p1 + adjbase] - else - emit_movsxd_r64_m32(DRCTOP, destreg, MDRC(fastram)); // movsxd destreg,dword ptr [p1 + adjbase] - } - } - return (fastram != NULL); -} - - -/*------------------------------------------------------------------ - emit_fixed_double_read -------------------------------------------------------------------*/ - -static int emit_fixed_double_read(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 destreg, offs_t address, UINT64 mask) -{ - void *fastram = fastram_ptr(address & ~7, 8, FALSE); - - /* if we can't do fast RAM, we need to make the call */ - if (fastram == NULL) - { - emit_mov_r32_imm(DRCTOP, REG_P1, address & ~7); // mov p1,address & ~7 - if (mask != U64(0xffffffffffffffff)) - { - emit_mov_r64_imm(DRCTOP, REG_P2, mask); // mov p2,mask - emit_call(DRCTOP, mips3.drcdata->general.read_double_masked); // call read_double_masked - } - else - emit_call(DRCTOP, mips3.drcdata->general.read_double); // call read_double - - /* handle exceptions */ - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception); - if (destreg != REG_RAX) - emit_mov_r64_r64(DRCTOP, destreg, REG_RAX); // mov <rt>,rax - } - - /* if we can do fastram, emit the right form */ - else - { - INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr; - int raxrel = ((INT32)fastdisp != fastdisp); - - /* if we can't fit the delta to fastram within an INT32, load RAX with the full address here */ - if (raxrel) - { - emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram - emit_mov_r64_m64(DRCTOP, destreg, MBD(REG_RAX, 0)); // mov destreg,[rax] - } - - /* read drc-relative */ - else - emit_mov_r64_m64(DRCTOP, destreg, MDRC(fastram)); // mov destreg,[p1 + adjbase] - - /* adjust for endianness */ - if (mips3.core->bigendian) - emit_ror_r64_imm(DRCTOP, destreg, 32); // ror destreg,32 - } - return (fastram != NULL); -} - - -/*------------------------------------------------------------------ - emit_fixed_byte_write -------------------------------------------------------------------*/ - -static int emit_fixed_byte_write(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, mipsreg_state srcreg, offs_t address) -{ - void *fastram = fastram_ptr(address, 1, TRUE); - - assert(ISCONST(srcreg) || X86REG(srcreg != REG_P1)); - - /* if we can't do fast RAM, we need to make the call */ - if (fastram == NULL) - { - emit_mov_r32_imm(DRCTOP, REG_P1, address); // mov p1,address - if (ISCONST(srcreg)) - emit_mov_r64_imm(DRCTOP, REG_P2, CONSTVAL(srcreg)); // mov p2,<srcreg> - else if (X86REG(srcreg) != REG_P2) - emit_mov_r64_r64(DRCTOP, REG_P2, X86REG(srcreg)); // mov p2,<srcreg> - emit_call(DRCTOP, mips3.drcdata->general.write_byte); // call write_byte - - /* handle exceptions */ - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception); - } - - /* if we can do fastram, emit the right form */ - else - { - INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr; - int raxrel = ((INT32)fastdisp != fastdisp); - - /* if we can't fit the delta to fastram within an INT32, load RAX with the full address here */ - if (raxrel) - { - emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram - if (ISCONST(srcreg)) - emit_mov_m8_imm(DRCTOP, MBD(REG_RAX, 0), CONSTVAL(srcreg)); // mov [rax],constval - else - emit_mov_m8_r8(DRCTOP, MBD(REG_RAX, 0), X86REG(srcreg)); // mov [rax],<srcreg> - } - - /* write drc-relative */ - else - { - if (ISCONST(srcreg)) - emit_mov_m8_imm(DRCTOP, MDRC(fastram), CONSTVAL(srcreg)); // mov [fastram],constval - else - emit_mov_m8_r8(DRCTOP, MDRC(fastram), X86REG(srcreg)); // mov [fastram],srcreg - } - } - return (fastram != NULL); -} - - -/*------------------------------------------------------------------ - emit_fixed_half_write -------------------------------------------------------------------*/ - -static int emit_fixed_half_write(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, mipsreg_state srcreg, offs_t address) -{ - void *fastram = fastram_ptr(address & ~1, 2, TRUE); - - assert(ISCONST(srcreg) || X86REG(srcreg) != REG_P1); - - /* if we can't do fast RAM, we need to make the call */ - if (fastram == NULL) - { - emit_mov_r32_imm(DRCTOP, REG_P1, address & ~1); // mov p1,address & ~1 - if (ISCONST(srcreg)) - emit_mov_r64_imm(DRCTOP, REG_P2, CONSTVAL(srcreg)); // mov p2,constval - else if (X86REG(srcreg) != REG_P2) - emit_mov_r64_r64(DRCTOP, REG_P2, X86REG(srcreg)); // mov p2,srcreg - emit_call(DRCTOP, mips3.drcdata->general.write_half); // call write_half - - /* handle exceptions */ - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception); - } - - /* if we can do fastram, emit the right form */ - else - { - INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr; - int raxrel = ((INT32)fastdisp != fastdisp); - - /* if we can't fit the delta to fastram within an INT32, load RAX with the full address here */ - if (raxrel) - { - emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram - if (ISCONST(srcreg)) - emit_mov_m16_imm(DRCTOP, MBD(REG_RAX, 0), CONSTVAL(srcreg)); // mov [rax],constval - else - emit_mov_m16_r16(DRCTOP, MBD(REG_RAX, 0), X86REG(srcreg)); // mov [rax],srcreg - } - - /* write drc-relative */ - else - { - if (ISCONST(srcreg)) - emit_mov_m16_imm(DRCTOP, MDRC(fastram), CONSTVAL(srcreg)); // mov [fastram],constval - else - emit_mov_m16_r16(DRCTOP, MDRC(fastram), X86REG(srcreg)); // mov [fastram],srcreg - } - } - return (fastram != NULL); -} - - -/*------------------------------------------------------------------ - emit_fixed_word_write -------------------------------------------------------------------*/ - -static int emit_fixed_word_write(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, mipsreg_state srcreg, offs_t address, UINT32 mask) -{ - void *fastram = fastram_ptr(address & ~3, 4, TRUE); - - assert(ISCONST(srcreg) || X86REG(srcreg) != REG_P1); - assert(ISCONST(srcreg) || X86REG(srcreg) != REG_P3); - - /* if we can't do fast RAM, we need to make the call */ - if (fastram == NULL) - { - emit_mov_r32_imm(DRCTOP, REG_P1, address & ~3); // mov p1,address & ~3 - if (ISCONST(srcreg)) - emit_mov_r64_imm(DRCTOP, REG_P2, CONSTVAL(srcreg)); // mov p2,constval - else if (X86REG(srcreg) != REG_P2) - emit_mov_r64_r64(DRCTOP, REG_P2, X86REG(srcreg)); // mov p2,srcreg - if (mask == 0xffffffff) - emit_call(DRCTOP, mips3.drcdata->general.write_word); // call write_word - else - { - emit_mov_r32_imm(DRCTOP, REG_P3, mask); // mov p3,mask - emit_call(DRCTOP, mips3.drcdata->general.write_word_masked); // call write_word_masked - } - - /* handle exceptions */ - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception); - } - - /* if we can do fastram, emit the right form */ - else - { - INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr; - int raxrel = ((INT32)fastdisp != fastdisp); - - /* non-masked case */ - if (mask == 0xffffffff) - { - /* if we can't fit the delta to fastram within an INT32, load RAX with the full address here */ - if (raxrel) - { - emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram - if (ISCONST(srcreg)) - emit_mov_m32_imm(DRCTOP, MBD(REG_RAX, 0), CONSTVAL(srcreg)); // mov [rax],constval - else - emit_mov_m32_r32(DRCTOP, MBD(REG_RAX, 0), X86REG(srcreg)); // mov [rax],srcreg - } - - /* write drc-relative */ - else - { - if (ISCONST(srcreg)) - emit_mov_m32_imm(DRCTOP, MDRC(fastram), CONSTVAL(srcreg)); // mov [fastram],constval - else - emit_mov_m32_r32(DRCTOP, MDRC(fastram), X86REG(srcreg)); // mov [fastram],srcreg - } - } - - /* masked case */ - else - { - if (raxrel) - emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram - else - emit_lea_r64_m64(DRCTOP, REG_RAX, MDRC(fastram)); // lea rax,[fastram] - - /* always write rax-relative */ - if (ISCONST(srcreg)) - { - emit_mov_r32_m32(DRCTOP, REG_P2, MBD(REG_RAX, 0)); // mov p2,[rax] - emit_and_r32_imm(DRCTOP, REG_P2, ~mask); // and p2,~mask - emit_or_r32_imm(DRCTOP, REG_P2, CONSTVAL(srcreg) & mask); // or p2,constval & mask - emit_mov_m32_r32(DRCTOP, MBD(REG_RAX, 0), REG_P2); // mov [rax],p2 - } - else - { - emit_mov_r32_m32(DRCTOP, REG_P3, MBD(REG_RAX, 0)); // mov p3,[rax] - if (X86REG(srcreg) != REG_P2) - emit_mov_r32_r32(DRCTOP, REG_P2, X86REG(srcreg)); // mov p2,srcreg - emit_and_r32_imm(DRCTOP, REG_P3, ~mask); // and p3,~mask - emit_and_r32_imm(DRCTOP, REG_P2, mask); // and p2,mask - emit_or_r32_r32(DRCTOP, REG_P3, REG_P2); // or p3,p2 - emit_mov_m32_r32(DRCTOP, MBD(REG_RAX, 0), REG_P3); // mov [rax],p3 - } - } - } - return (fastram != NULL); -} - - -/*------------------------------------------------------------------ - emit_fixed_double_write -------------------------------------------------------------------*/ - -static int emit_fixed_double_write(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, mipsreg_state srcreg, offs_t address, UINT64 mask) -{ - void *fastram = fastram_ptr(address & ~7, 8, TRUE); - - assert(ISCONST(srcreg) || X86REG(srcreg) != REG_P1); - assert(ISCONST(srcreg) || X86REG(srcreg) != REG_P3); - assert((INT32)CONSTVAL(srcreg) == (INT64)CONSTVAL(srcreg)); - - /* if we can't do fast RAM, we need to make the call */ - if (fastram == NULL) - { - emit_mov_r32_imm(DRCTOP, REG_P1, address & ~7); // mov p1,address & ~7 - if (ISCONST(srcreg)) - emit_mov_r64_imm(DRCTOP, REG_P2, CONSTVAL(srcreg)); // mov p2,constval - else if (X86REG(srcreg) != REG_P2) - emit_mov_r64_r64(DRCTOP, REG_P2, X86REG(srcreg)); // mov p2,srcreg - if (mask == U64(0xffffffffffffffff)) - emit_call(DRCTOP, mips3.drcdata->general.write_double); // call write_double - else - { - emit_mov_r64_imm(DRCTOP, REG_P3, mask); // mov p3,mask - emit_call(DRCTOP, mips3.drcdata->general.write_double_masked); // call write_double_masked - } - - /* handle exceptions */ - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception); - } - - /* if we can do fastram, emit the right form */ - else - { - INT64 fastdisp = (UINT8 *)fastram - (UINT8 *)drc->baseptr; - int raxrel = ((INT32)fastdisp != fastdisp); - INT64 constval = CONSTVAL(srcreg); - - /* adjust values for endianness */ - if (mips3.core->bigendian) - { - if (ISCONST(srcreg)) - constval = (constval << 32) | (constval >> 32); - else - { - emit_mov_r64_r64(DRCTOP, REG_P2, X86REG(srcreg)); // mov p2,srcreg - emit_ror_r64_imm(DRCTOP, REG_P2, 32); // ror p2,32 - srcreg = REG_P2; - } - mask = (mask << 32) | (mask >> 32); - } - - /* non-masked case */ - if (mask == U64(0xffffffffffffffff)) - { - /* if we can't fit the delta to fastram within an INT32, load RAX with the full address here */ - if (raxrel) - { - emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram - if (ISCONST(srcreg)) - emit_mov_m64_imm(DRCTOP, MBD(REG_RAX, 0), constval); // mov [rax],constval - else - emit_mov_m64_r64(DRCTOP, MBD(REG_RAX, 0), X86REG(srcreg)); // mov [rax],srcreg - } - - /* write drc-relative */ - else - { - if (ISCONST(srcreg)) - emit_mov_m64_imm(DRCTOP, MDRC(fastram), constval); // mov [fastram],constval - else - emit_mov_m64_r64(DRCTOP, MDRC(fastram), X86REG(srcreg)); // mov [fastram],srcreg - } - } - - /* masked case */ - else - { - if (raxrel) - emit_mov_r64_imm(DRCTOP, REG_RAX, (UINT64)fastram); // mov rax,fastram - else - emit_lea_r64_m64(DRCTOP, REG_RAX, MDRC(fastram)); // lea rax,[fastram] - emit_mov_r64_imm(DRCTOP, REG_P4, ~mask); // mov p4,~mask - - /* always write rax-relative */ - if (ISCONST(srcreg)) - { - emit_and_r64_m64(DRCTOP, REG_P4, MBD(REG_RAX, 0)); // and p4,[rax] - emit_mov_r64_imm(DRCTOP, REG_P3, constval & mask); // mov p3,constval & mask - emit_or_r64_r64(DRCTOP, REG_P4, REG_P3); // or p4,p3 - emit_mov_m64_r64(DRCTOP, MBD(REG_RAX, 0), REG_P4); // mov [rax],p4 - } - else - { - emit_mov_r64_m64(DRCTOP, REG_P3, MBD(REG_RAX, 0)); // mov p3,[rax] - if (X86REG(srcreg) != REG_P2) - emit_mov_r64_r64(DRCTOP, REG_P2, X86REG(srcreg)); // mov p2,srcreg - emit_and_r64_r64(DRCTOP, REG_P3, REG_P4); // and p3,p4 - emit_not_r64(DRCTOP, REG_P4); // not p4 - emit_and_r64_r64(DRCTOP, REG_P2, REG_P4); // and p2,p4 - emit_or_r64_r64(DRCTOP, REG_P3, REG_P2); // or p3,p2 - emit_mov_m64_r64(DRCTOP, MBD(REG_RAX, 0), REG_P3); // mov [rax],p3 - } - } - } - return (fastram != NULL); -} - - - -/*************************************************************************** - CORE RECOMPILATION -***************************************************************************/ - -/*------------------------------------------------- - compile_instruction - master switch statement - for compiling a given opcode --------------------------------------------------*/ - -static int compile_instruction(drc_core *drc, compiler_state *compiler, const opcode_desc *desc) -{ - UINT32 op = *desc->opptr.l; - UINT8 opswitch = op >> 26; - - switch (opswitch) - { - /* ----- sub-groups ----- */ - - case 0x00: /* SPECIAL */ - return compile_special(drc, compiler, desc); - - case 0x01: /* REGIMM */ - return compile_regimm(drc, compiler, desc); - - case 0x1c: /* IDT-specific */ - return compile_idt(drc, compiler, desc); - - - /* ----- jumps and branches ----- */ - - case 0x02: /* J */ - case 0x03: /* JAL */ - emit_delay_slot_and_branch(drc, compiler, desc, (opswitch == 0x02) ? 0 : 31); // <next instruction + redispatch> - return TRUE; - - case 0x04: /* BEQ */ - case 0x14: /* BEQL */ - case 0x05: /* BNE */ - case 0x15: /* BNEL */ - return compile_branchcc(drc, compiler, desc, (opswitch & 1) ? COND_NE : COND_E); - - case 0x06: /* BLEZ */ - case 0x16: /* BLEZL */ - case 0x07: /* BGTZ */ - case 0x17: /* BGTZL */ - return compile_branchcz(drc, compiler, desc, (opswitch & 1) ? COND_G : COND_LE, FALSE); - - - /* ----- immediate arithmetic ----- */ - - case 0x0f: /* LUI */ - if (RTREG != 0) - emit_set_constant_value(drc, compiler, desc, RTREG, SIMMVAL << 16); - return TRUE; - - case 0x08: /* ADDI */ - case 0x09: /* ADDIU */ - return compile_add(drc, compiler, desc, opswitch & 1, TRUE); - - case 0x18: /* DADDI */ - case 0x19: /* DADDIU */ - return compile_dadd(drc, compiler, desc, opswitch & 1, TRUE); - - case 0x0c: /* ANDI */ - case 0x0d: /* ORI */ - case 0x0e: /* XORI */ - return compile_logical(drc, compiler, desc, opswitch & 3, TRUE); - - case 0x0a: /* SLTI */ - case 0x0b: /* SLTIU */ - return compile_slt(drc, compiler, desc, opswitch & 1, TRUE); - - - /* ----- memory load operations ----- */ - - case 0x20: /* LB */ - case 0x21: /* LH */ - case 0x23: /* LW */ - case 0x24: /* LBU */ - case 0x25: /* LHU */ - case 0x27: /* LWU */ - return compile_load(drc, compiler, desc, (opswitch & 3) + 1, opswitch & 4); - - case 0x37: /* LD */ - return compile_load(drc, compiler, desc, 8, FALSE); - - case 0x31: /* LWC1 */ - return compile_load_cop(drc, compiler, desc, 4, FPR32(RTREG)); - - case 0x35: /* LDC1 */ - return compile_load_cop(drc, compiler, desc, 8, FPR64(RTREG)); - - case 0x32: /* LWC2 */ - case 0x36: /* LDC2 */ - return compile_load_cop(drc, compiler, desc, (opswitch & 4) ? 8 : 4, &mips3.core->cpr[2][RTREG]); - - case 0x1a: /* LDL */ - case 0x1b: /* LDR */ - return compile_load_double_partial(drc, compiler, desc, opswitch & 1); - - case 0x22: /* LWL */ - case 0x26: /* LWR */ - return compile_load_word_partial(drc, compiler, desc, opswitch & 4); - - - /* ----- memory store operations ----- */ - - case 0x28: /* SB */ - case 0x29: /* SH */ - case 0x2b: /* SW */ - return compile_store(drc, compiler, desc, (opswitch & 3) + 1); - - case 0x3f: /* SD */ - return compile_store(drc, compiler, desc, 8); - - case 0x39: /* SWC1 */ - return compile_store_cop(drc, compiler, desc, 4, FPR32(RTREG)); - - case 0x3d: /* SDC1 */ - return compile_store_cop(drc, compiler, desc, 8, FPR64(RTREG)); - - case 0x3a: /* SWC2 */ - return compile_store_cop(drc, compiler, desc, (opswitch & 4) ? 8 : 4, &mips3.core->cpr[2][RTREG]); - - case 0x2a: /* SWL */ - case 0x2e: /* SWR */ - return compile_store_word_partial(drc, compiler, desc, opswitch & 4); - - case 0x2c: /* SDL */ - case 0x2d: /* SDR */ - return compile_store_double_partial(drc, compiler, desc, opswitch & 1); - - - /* ----- effective no-ops ----- */ - - case 0x2f: /* CACHE */ - case 0x33: /* PREF */ - return TRUE; - - - /* ----- coprocessor instructions ----- */ - - case 0x10: /* COP0 */ - return compile_cop0(drc, compiler, desc); - - case 0x11: /* COP1 */ - return compile_cop1(drc, compiler, desc); - - case 0x13: /* COP1X - R5000 */ - return compile_cop1x(drc, compiler, desc); - - case 0x12: /* COP2 */ - oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_invalidop_exception); - // jmp generate_invalidop_exception - return TRUE; - - - /* ----- unimplemented/illegal instructions ----- */ - -// case 0x30: /* LL */ logerror("mips3 Unhandled op: LL\n"); break; -// case 0x34: /* LLD */ logerror("mips3 Unhandled op: LLD\n"); break; -// case 0x38: /* SC */ logerror("mips3 Unhandled op: SC\n"); break; -// case 0x3c: /* SCD */ logerror("mips3 Unhandled op: SCD\n"); break; -// case 0x3b: /* SWC3 */ invalid_instruction(op); break; -// default: /* ??? */ invalid_instruction(op); break; - } - - return FALSE; -} - - -/*------------------------------------------------- - compile_special - compile opcodes in the - 'SPECIAL' group --------------------------------------------------*/ - -static int compile_special(drc_core *drc, compiler_state *compiler, const opcode_desc *desc) -{ - UINT32 op = *desc->opptr.l; - UINT8 opswitch = op & 63; - emit_link link1 = { 0 }; - - switch (opswitch) - { - /* ----- shift instructions ----- */ - - case 0x00: /* SLL - MIPS I */ - case 0x02: /* SRL - MIPS I */ - case 0x03: /* SRA - MIPS I */ - case 0x04: /* SLLV - MIPS I */ - case 0x06: /* SRLV - MIPS I */ - case 0x07: /* SRAV - MIPS I */ - return compile_shift(drc, compiler, desc, opswitch & 0x03, opswitch & 0x04); - - case 0x14: /* DSLLV - MIPS III */ - case 0x16: /* DSRLV - MIPS III */ - case 0x17: /* DSRAV - MIPS III */ - case 0x38: /* DSLL - MIPS III */ - case 0x3a: /* DSRL - MIPS III */ - case 0x3b: /* DSRA - MIPS III */ - case 0x3c: /* DSLL32 - MIPS III */ - case 0x3e: /* DSRL32 - MIPS III */ - case 0x3f: /* DSRA32 - MIPS III */ - return compile_dshift(drc, compiler, desc, opswitch & 0x03, ~opswitch & 0x20, opswitch & 0x04); - - - /* ----- basic arithmetic ----- */ - - case 0x20: /* ADD - MIPS I */ - case 0x21: /* ADDU - MIPS I */ - return compile_add(drc, compiler, desc, opswitch & 1, FALSE); - - case 0x2c: /* DADD - MIPS III */ - case 0x2d: /* DADDU - MIPS III */ - return compile_dadd(drc, compiler, desc, opswitch & 1, FALSE); - - case 0x22: /* SUB - MIPS I */ - case 0x23: /* SUBU - MIPS I */ - return compile_sub(drc, compiler, desc, opswitch & 1); - - case 0x2e: /* DSUB - MIPS III */ - case 0x2f: /* DSUBU - MIPS III */ - return compile_dsub(drc, compiler, desc, opswitch & 1); - - case 0x18: /* MULT - MIPS I */ - case 0x19: /* MULTU - MIPS I */ - return compile_mult(drc, compiler, desc, opswitch & 1); - - case 0x1c: /* DMULT - MIPS III */ - case 0x1d: /* DMULTU - MIPS III */ - return compile_dmult(drc, compiler, desc, opswitch & 1); - - case 0x1a: /* DIV - MIPS I */ - case 0x1b: /* DIVU - MIPS I */ - return compile_div(drc, compiler, desc, opswitch & 1); - - case 0x1e: /* DDIV - MIPS III */ - case 0x1f: /* DDIVU - MIPS III */ - return compile_ddiv(drc, compiler, desc, opswitch & 1); - - - /* ----- basic logical ops ----- */ - - case 0x24: /* AND - MIPS I */ - case 0x25: /* OR - MIPS I */ - case 0x26: /* XOR - MIPS I */ - case 0x27: /* NOR - MIPS I */ - return compile_logical(drc, compiler, desc, opswitch & 3, FALSE); - - - /* ----- basic comparisons ----- */ - - case 0x2a: /* SLT - MIPS I */ - case 0x2b: /* SLTU - MIPS I */ - return compile_slt(drc, compiler, desc, opswitch & 1, FALSE); - - - /* ----- conditional traps ----- */ - - case 0x30: /* TGE - MIPS II */ - case 0x31: /* TGEU - MIPS II */ - case 0x32: /* TLT - MIPS II */ - case 0x33: /* TLTU - MIPS II */ - case 0x34: /* TEQ - MIPS II */ - case 0x36: /* TNE - MIPS II */ - return compile_trapcc(drc, compiler, desc, opswitch & 7, FALSE); - - - /* ----- conditional moves ----- */ - - case 0x0a: /* MOVZ - MIPS IV */ - case 0x0b: /* MOVN - MIPS IV */ - return compile_movzn(drc, compiler, desc, opswitch & 1); - - case 0x01: /* MOVF - MIPS IV */ - if (RDREG != 0) - { - mipsreg_state rs, rd; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_SRC | ALLOC_DST | ALLOC_DIRTY); - - emit_cmp_m8_imm(DRCTOP, CF1ADDR((op >> 18) & 7), 0); // cmp fcc[x],0 - emit_jcc_short_link(DRCTOP, ((op >> 16) & 1) ? COND_Z : COND_NZ, &link1); // jz/nz skip - emit_load_register_value(drc, compiler, X86REG(rd), rs); // mov <rd>,<rs> - } - resolve_link(DRCTOP, &link1); // skip: - return TRUE; - - - /* ----- jumps and branches ----- */ - - case 0x08: /* JR - MIPS I */ - case 0x09: /* JALR - MIPS I */ - { - mipsreg_state rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - if (ISCONST(rs)) - emit_mov_m32_imm(DRCTOP, MBD(REG_RSP, SPOFFS_NEXTPC), CONSTVAL(rs)); // mov [esp+nextpc],rs - else - emit_mov_m32_r32(DRCTOP, MBD(REG_RSP, SPOFFS_NEXTPC), X86REG(rs)); // mov [esp+nextpc],rs - emit_delay_slot_and_branch(drc, compiler, desc, (opswitch == 0x09) ? RDREG : 0); - // <next instruction + redispatch> - } - return TRUE; - - - /* ----- system calls ----- */ - - case 0x0c: /* SYSCALL - MIPS I */ - oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_syscall_exception); - // jmp generate_syscall_exception - return TRUE; - - case 0x0d: /* BREAK - MIPS I */ - oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_break_exception); - // jmp generate_break_exception - return TRUE; - - - /* ----- effective no-ops ----- */ - - case 0x0f: /* SYNC - MIPS II */ - return TRUE; - - - /* ----- hi/lo register access ----- */ - - case 0x10: /* MFHI - MIPS I */ - if (RDREG != 0) - { - mipsreg_state hi = compiler_register_allocate(drc, compiler, desc, REG_HI, ALLOC_SRC); - mipsreg_state rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DST | ALLOC_DIRTY); - emit_load_register_value(drc, compiler, X86REG(rd), hi); // mov <rd>,<hi> - } - return TRUE; - - case 0x11: /* MTHI - MIPS I */ - if (RSREG != 0) - { - mipsreg_state rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - mipsreg_state hi = compiler_register_allocate(drc, compiler, desc, REG_HI, ALLOC_DST | ALLOC_DIRTY); - emit_load_register_value(drc, compiler, X86REG(hi), rs); // mov hi,<rs> - } - return TRUE; - - case 0x12: /* MFLO - MIPS I */ - if (RDREG != 0) - { - mipsreg_state lo = compiler_register_allocate(drc, compiler, desc, REG_LO, ALLOC_SRC); - mipsreg_state rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DST | ALLOC_DIRTY); - emit_load_register_value(drc, compiler, X86REG(rd), lo); // mov <rd>,<lo> - } - return TRUE; - - case 0x13: /* MTLO - MIPS I */ - if (RSREG != 0) - { - mipsreg_state rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - mipsreg_state lo = compiler_register_allocate(drc, compiler, desc, REG_LO, ALLOC_DST | ALLOC_DIRTY); - emit_load_register_value(drc, compiler, X86REG(lo), rs); // mov lo,<rs> - } - return TRUE; - } - return FALSE; -} - - -/*------------------------------------------------- - compile_regimm - compile opcodes in the - 'REGIMM' group --------------------------------------------------*/ - -static int compile_regimm(drc_core *drc, compiler_state *compiler, const opcode_desc *desc) -{ - UINT32 op = *desc->opptr.l; - UINT8 opswitch = RTREG; - - switch (opswitch) - { - case 0x00: /* BLTZ */ - case 0x01: /* BGEZ */ - case 0x02: /* BLTZL */ - case 0x03: /* BGEZL */ - case 0x10: /* BLTZAL */ - case 0x11: /* BGEZAL */ - case 0x12: /* BLTZALL */ - case 0x13: /* BGEZALL */ - return compile_branchcz(drc, compiler, desc, (opswitch & 1) ? COND_GE : COND_L, opswitch & 0x10); - - case 0x08: /* TGEI */ - case 0x09: /* TGEIU */ - case 0x0a: /* TLTI */ - case 0x0b: /* TLTIU */ - case 0x0c: /* TEQI */ - case 0x0e: /* TNEI */ - return compile_trapcc(drc, compiler, desc, opswitch & 7, TRUE); - } - return FALSE; -} - - -/*------------------------------------------------- - compile_idt - compile opcodes in the IDT- - specific group --------------------------------------------------*/ - -static int compile_idt(drc_core *drc, compiler_state *compiler, const opcode_desc *desc) -{ - fatalerror("Unimplemented IDT instructions"); -#if 0 - UINT32 op = *desc->opptr.l; - UINT8 opswitch = op & 0x1f; - - switch (opswitch) - { - case 0: /* MAD */ - if (RSREG != 0 && RTREG != 0) - { - emit_mov_r32_m32(REG_EAX, REGADDR(RSREG)); // mov eax,[rsreg] - emit_mov_r32_m32(REG_EDX, REGADDR(RTREG)); // mov edx,[rtreg] - emit_imul_r32(DRCTOP, REG_EDX); // imul edx - emit_add_r32_m32(DRCTOP, LOADDR); // add eax,[lo] - emit_adc_r32_m32(DRCTOP, HIADDR); // adc edx,[hi] - emit_mov_r32_r32(DRCTOP, REG_EBX, REG_EDX); // mov ebx,edx - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, LOADDR, REG_EAX); // mov [lo],edx:eax - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EBX); // mov eax,ebx - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, HIADDR, REG_EAX); // mov [hi],edx:eax - } - return compile_SUCCESSFUL_CP(3,4); - - case 1: /* MADU */ - if (RSREG != 0 && RTREG != 0) - { - emit_mov_r32_m32(REG_EAX, REGADDR(RSREG)); // mov eax,[rsreg] - emit_mov_r32_m32(REG_EDX, REGADDR(RTREG)); // mov edx,[rtreg] - emit_mul_r32(DRCTOP, REG_EDX); // mul edx - emit_add_r32_m32(DRCTOP, LOADDR); // add eax,[lo] - emit_adc_r32_m32(DRCTOP, HIADDR); // adc edx,[hi] - emit_mov_r32_r32(DRCTOP, REG_EBX, REG_EDX); // mov ebx,edx - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, LOADDR, REG_EDX, REG_EAX); // mov [lo],edx:eax - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EBX); // mov eax,ebx - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, HIADDR, REG_EDX, REG_EAX); // mov [hi],edx:eax - } - return compile_SUCCESSFUL_CP(3,4); - - case 2: /* MUL */ - if (RDREG != 0) - { - if (RSREG != 0 && RTREG != 0) - { - emit_mov_r32_m32(REG_EAX, REGADDR(RSREG)); // mov eax,[rsreg] - emit_imul_r32_m32(REG_EAX, REGADDR(RTREG)); // imul eax,[rtreg] - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(REG_ESI, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rd],edx:eax - } - else - emit_zero_m64(REG_ESI, REGADDR(RDREG)); - } - return compile_SUCCESSFUL_CP(3,4); - } -#endif - return FALSE; -} - - - -/*************************************************************************** - COP0 RECOMPILATION -***************************************************************************/ - -/*------------------------------------------------- - compile_set_cop0_reg - generate code to - handle special COP0 registers --------------------------------------------------*/ - -static int compile_set_cop0_reg(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 reg) -{ - emit_link link1; - - switch (reg) - { - case COP0_Cause: - emit_mov_r32_m32(DRCTOP, REG_EDX, CPR0ADDR(COP0_Cause)); // mov edx,[Cause] - emit_and_r32_imm(DRCTOP, REG_EAX, ~0xfc00); // and eax,~0xfc00 - emit_and_r32_imm(DRCTOP, REG_EDX, 0xfc00); // and edx,0xfc00 - emit_or_r32_r32(DRCTOP, REG_EAX, REG_EDX); // or eax,edx - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Cause), REG_EAX); // mov [Cause],eax - emit_and_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(COP0_Status)); // and eax,[SR] - emit_test_r32_imm(DRCTOP, REG_EAX, 0x300); // test eax,0x300 - oob_request_callback(drc, COND_NZ, oob_interrupt_cleanup, compiler, desc, mips3.drcdata->generate_interrupt_exception); - // jnz generate_interrupt_exception - return TRUE; - - case COP0_Status: - emit_flush_cycles_before_instruction(drc, compiler, desc, 1); - emit_mov_r32_m32(DRCTOP, REG_EDX, CPR0ADDR(COP0_Status)); // mov edx,[Status] - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Status), REG_EAX); // mov [Status],eax - emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EAX); // xor edx,eax - emit_test_r32_imm(DRCTOP, REG_EDX, SR_IMEX5); // test edx,0x8000 - emit_jcc_short_link(DRCTOP, COND_Z, &link1); // jz skip - emit_lea_r64_m64(DRCTOP, REG_P1, COREADDR); // lea p1,[mips3.core] - emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->mips3com_update_cycle_counting)); // call mips3com_update_cycle_counting - resolve_link(DRCTOP, &link1); // skip: - append_check_interrupts(drc, compiler, desc); // <check interrupts> - return TRUE; - - case COP0_Count: - emit_flush_cycles_before_instruction(drc, compiler, desc, 1); - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Count), REG_EAX); // mov [Count],eax - emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->activecpu_gettotalcycles)); // call activecpu_gettotalcycles - emit_mov_r32_m32(DRCTOP, REG_EDX, CPR0ADDR(COP0_Count)); // mov edx,[Count] - emit_sub_r64_r64(DRCTOP, REG_RAX, REG_RDX); // sub rax,rdx - emit_sub_r64_r64(DRCTOP, REG_RAX, REG_RDX); // sub rax,rdx - emit_mov_m64_r64(DRCTOP, MDRC(&mips3.core->count_zero_time), REG_RAX); // mov [count_zero_time],rax - emit_lea_r64_m64(DRCTOP, REG_P1, COREADDR); // lea p1,[mips3.core] - emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->mips3com_update_cycle_counting)); // call mips3com_update_cycle_counting - return TRUE; - - case COP0_Compare: - emit_flush_cycles_before_instruction(drc, compiler, desc, 1); - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Compare), REG_EAX); // mov [Compare],eax - emit_and_m32_imm(DRCTOP, CPR0ADDR(COP0_Cause), ~0x8000); // and [Cause],~0x8000 - emit_lea_r64_m64(DRCTOP, REG_P1, COREADDR); // lea p1,[mips3.core] - emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->mips3com_update_cycle_counting)); // call mips3com_update_cycle_counting - return TRUE; - - case COP0_PRId: - return TRUE; - - case COP0_Config: - emit_mov_r32_m32(DRCTOP, REG_EDX, CPR0ADDR(COP0_Config)); // mov edx,[Config] - emit_and_r32_imm(DRCTOP, REG_EAX, 0x0007); // and eax,0x0007 - emit_and_r32_imm(DRCTOP, REG_EDX, ~0x0007); // and edx,~0x0007 - emit_or_r32_r32(DRCTOP, REG_EAX, REG_EDX); // or eax,edx - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Config), REG_EAX); // mov [Config],eax - return TRUE; - - default: - emit_mov_m32_r32(DRCTOP, CPR0ADDR(reg), REG_EAX); // mov cpr0[reg],eax - return TRUE; - } -} - - -/*------------------------------------------------- - compile_get_cop0_reg - generate code to - read special COP0 registers --------------------------------------------------*/ - -static int compile_get_cop0_reg(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, UINT8 reg) -{ - emit_link link1, link2; - - switch (reg) - { - case COP0_Count: - compiler->cycles += MIPS3_COUNT_READ_CYCLES; - emit_flush_cycles_before_instruction(drc, compiler, desc, MIPS3_COUNT_READ_CYCLES); - emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->activecpu_gettotalcycles)); // call activecpu_gettotalcycles - emit_sub_r64_m64(DRCTOP, REG_RAX, MDRC(&mips3.core->count_zero_time)); // sub rax,[count_zero_time] - emit_shr_r64_imm(DRCTOP, REG_RAX, 1); // shr rax,1 - emit_movsxd_r64_r32(DRCTOP, REG_RAX, REG_EAX); // movsxd rax,eax - return TRUE; - - case COP0_Cause: - compiler->cycles += MIPS3_CAUSE_READ_CYCLES; - emit_movsxd_r64_m32(DRCTOP, REG_RAX, CPR0ADDR(COP0_Cause)); // movsxd rax,[Cause] - return TRUE; - - case COP0_Random: - emit_flush_cycles_before_instruction(drc, compiler, desc, 1); - emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->activecpu_gettotalcycles)); // call activecpu_gettotalcycles - emit_mov_r32_m32(DRCTOP, REG_ECX, CPR0ADDR(COP0_Wired)); // mov ecx,[Wired] - emit_mov_r32_imm(DRCTOP, REG_R8D, 48); // mov r8d,48 - emit_and_r32_imm(DRCTOP, REG_ECX, 0x3f); // and ecx,0x3f - emit_sub_r64_m64(DRCTOP, REG_RAX, MDRC(&mips3.core->count_zero_time)); // sub rax,[count_zero_time] - emit_sub_r32_r32(DRCTOP, REG_R8D, REG_ECX); // sub r8d,ecx - emit_jcc_short_link(DRCTOP, COND_BE, &link1); // jbe link1 - emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx - emit_div_r64(DRCTOP, REG_R8); // div r8 - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EDX); // mov eax,edx - emit_add_r32_r32(DRCTOP, REG_EAX, REG_ECX); // add eax,ecx - emit_and_r32_imm(DRCTOP, REG_EAX, 0x3f); // and eax,0x3f - emit_jmp_short_link(DRCTOP, &link2); // jmp link2 - resolve_link(DRCTOP, &link1); // link1: - emit_mov_r32_imm(DRCTOP, REG_EAX, 47); // mov eax,47 - resolve_link(DRCTOP, &link2); // link2: - return TRUE; - - default: - emit_movsxd_r64_m32(DRCTOP, REG_RAX, CPR0ADDR(reg)); // movsxd rax,cpr0[reg] - return TRUE; - } -} - - -/*------------------------------------------------- - compile_cop0 - compile COP0 opcodes --------------------------------------------------*/ - -static int compile_cop0(drc_core *drc, compiler_state *compiler, const opcode_desc *desc) -{ - UINT32 op = *desc->opptr.l; - UINT8 opswitch = RSREG; - - /* generate an exception if COP0 is disabled or we are not in kernel mode */ - if (mips3.drcoptions & MIPS3DRC_STRICT_COP0) - { - emit_link checklink; - - emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_KSU_MASK); // test [SR],SR_KSU_MASK - emit_jcc_short_link(DRCTOP, COND_Z, &checklink); // jz okay - emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_COP0); // test [SR],SR_COP0 - oob_request_callback(drc, COND_Z, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_cop_exception); - // jz generate_cop_exception - resolve_link(DRCTOP, &checklink); // okay: - } - - switch (opswitch) - { - case 0x00: /* MFCz */ - case 0x01: /* DMFCz */ - if (RTREG != 0) - { - mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_DST | ALLOC_DIRTY); - compile_get_cop0_reg(drc, compiler, desc, RDREG); // mov eax,cpr0[rd]) - if (!(opswitch & 1)) - emit_movsxd_r64_r32(DRCTOP, X86REG(rt), REG_EAX); // movsxd <rt>,eax - else - emit_mov_r64_r64(DRCTOP, X86REG(rt), REG_RAX); // mov <rt>,rax - } - return TRUE; - - case 0x02: /* CFCz */ - if (RTREG != 0) - { - mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_DST | ALLOC_DIRTY); - emit_movsxd_r64_m32(DRCTOP, X86REG(rt), CCR0ADDR(RDREG)); // movsxd <rt>,ccr1[rd] - } - return TRUE; - - case 0x04: /* MTCz */ - case 0x05: /* DMTCz */ - { - mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - emit_load_register_value(drc, compiler, REG_EAX, rt); // mov eax,<rt> - compile_set_cop0_reg(drc, compiler, desc, RDREG); // mov cpr0[rd],rax - } - return TRUE; - - case 0x06: /* CTCz */ - { - mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - emit_load_register_value(drc, compiler, REG_EAX, rt); // mov eax,<rt> - emit_mov_m32_r32(DRCTOP, CCR0ADDR(RDREG), REG_EAX); // mov ccr1[rd],eax - } - return TRUE; - - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: /* COP */ - switch (op & 0x01ffffff) - { - case 0x01: /* TLBR */ - emit_lea_r64_m64(DRCTOP, REG_P1, COREADDR); // lea p1,[mips3.core] - emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->mips3com_tlbr)); // call mips3com_tlbr - return TRUE; - - case 0x02: /* TLBWI */ - emit_lea_r64_m64(DRCTOP, REG_P1, COREADDR); // lea p1,[mips3.core] - emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->mips3com_tlbwi)); // call mips3com_tlbwi - return TRUE; - - case 0x06: /* TLBWR */ - emit_lea_r64_m64(DRCTOP, REG_P1, COREADDR); // lea p1,[mips3.core] - emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->mips3com_tlbwr)); // call mips3com_tlbwr - return TRUE; - - case 0x08: /* TLBP */ - emit_lea_r64_m64(DRCTOP, REG_P1, COREADDR); // lea p1,[mips3.core] - emit_call_m64(DRCTOP, MDRC(&mips3.drcdata->mips3com_tlbp)); // call mips3com_tlbp - return TRUE; - - case 0x18: /* ERET */ - emit_mov_r32_m32(DRCTOP, REG_P1, CPR0ADDR(COP0_EPC)); // mov p1,[EPC] - emit_and_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), ~SR_EXL); // and [SR],~SR_EXL - compiler_register_flush_all(drc, compiler); // <flush registers> - drc_append_dispatcher(drc); // <dispatch> - return TRUE; - - case 0x20: /* WAIT */ - return TRUE; - } - break; - } - - return FALSE; -} - - - -/*************************************************************************** - COP1 RECOMPILATION -***************************************************************************/ - -/*------------------------------------------------- - compile_cop1 - compile COP1 opcodes --------------------------------------------------*/ - -static int compile_cop1(drc_core *drc, compiler_state *compiler, const opcode_desc *desc) -{ - UINT32 op = *desc->opptr.l; - emit_link link1; - - /* generate an exception if COP1 is disabled */ - if (mips3.drcoptions & MIPS3DRC_STRICT_COP1) - { - emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_COP1); // test [mips3.core->cpr[0][COP0_Status]],SR_COP1 - oob_request_callback(drc, COND_Z, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_cop_exception); - // jz generate_cop_exception - } - - switch (RSREG) - { - case 0x00: /* MFCz */ - if (RTREG != 0) - { - mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_DST | ALLOC_DIRTY); - emit_movsxd_r64_m32(DRCTOP, X86REG(rt), FPR32ADDR(RDREG)); // movsxd <rt>,cpr[rd] - } - return TRUE; - - case 0x01: /* DMFCz */ - if (RTREG != 0) - { - mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_DST | ALLOC_DIRTY); - emit_mov_r64_m64(DRCTOP, X86REG(rt), FPR64ADDR(RDREG)); // mov <rt>,cpr[rd] - } - return TRUE; - - case 0x02: /* CFCz */ - if (RTREG != 0) - { - mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_DST | ALLOC_DIRTY); - if (RDREG != 31) - emit_movsxd_r64_m32(DRCTOP, X86REG(rt), CCR1ADDR(RDREG)); // movsxd <rt>,ccr1[rd] - else - { - emit_call(DRCTOP, mips3.drcdata->recover_ccr31); // call recover_ccr31 - emit_mov_r64_r64(DRCTOP, X86REG(rt), REG_RAX); // mov [X86REG(rt)],rax - } - } - return TRUE; - - case 0x04: /* MTCz */ - { - mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - UINT8 tempreg = ISCONST(rt) ? REG_EAX : X86REG(rt); - emit_load_register_value(drc, compiler, tempreg, rt); // mov <tempreg>,<rt> - emit_mov_m32_r32(DRCTOP, FPR32ADDR(RDREG), tempreg); // mov cpr1[rd],<tempreg> - } - return TRUE; - - case 0x05: /* DMTCz */ - { - mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - UINT8 tempreg = ISCONST(rt) ? REG_EAX : X86REG(rt); - emit_load_register_value(drc, compiler, tempreg, rt); // mov <tempreg>,<rt> - emit_mov_m64_r64(DRCTOP, FPR64ADDR(RDREG), tempreg); // mov cpr1[rd],<tempreg> - } - return TRUE; - - case 0x06: /* CTCz */ - { - mipsreg_state rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - emit_load_register_value(drc, compiler, REG_EAX, rt); // mov eax,<rt> - if (RDREG != 31) - emit_mov_m32_r32(DRCTOP, CCR1ADDR(RDREG), REG_EAX); // mov ccr1[rd],eax - else - emit_call(DRCTOP, mips3.drcdata->explode_ccr31); // call explode_ccr31 - } - return TRUE; - - case 0x08: /* BC */ - switch ((op >> 16) & 3) - { - case 0x00: /* BCzF */ - case 0x02: /* BCzFL */ - emit_cmp_m8_imm(DRCTOP, CF1ADDR((op >> 18) & 7), 0); // cmp fcc[which],0 - emit_jcc_near_link(DRCTOP, COND_NZ, &link1); // jnz link1 - emit_delay_slot_and_branch(drc, compiler, desc, 0); // <next instruction + redispatch> - resolve_link(DRCTOP, &link1); // skip: - return TRUE; - - case 0x01: /* BCzT */ - case 0x03: /* BCzTL */ - emit_cmp_m8_imm(DRCTOP, CF1ADDR((op >> 18) & 7), 0); // cmp fcc[which],0 - emit_jcc_near_link(DRCTOP, COND_Z, &link1); // jz link1 - emit_delay_slot_and_branch(drc, compiler, desc, 0); // <next instruction + redispatch> - resolve_link(DRCTOP, &link1); // skip: - return TRUE; - } - break; - - default: - switch (op & 0x3f) - { - case 0x00: - if (IS_SINGLE(op)) /* ADD.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_addss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // addss xmm0,[ftreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else /* ADD.D */ - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_addsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // addsd xmm0,[ftreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - return TRUE; - - case 0x01: - if (IS_SINGLE(op)) /* SUB.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_subss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // subss xmm0,[ftreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else /* SUB.D */ - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_subsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // subsd xmm0,[ftreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - return TRUE; - - case 0x02: - if (IS_SINGLE(op)) /* MUL.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_mulss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // mulss xmm0,[ftreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else /* MUL.D */ - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_mulsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // mulsd xmm0,[ftreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - return TRUE; - - case 0x03: - if (IS_SINGLE(op)) /* DIV.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_divss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // divss xmm0,[ftreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else /* DIV.D */ - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_divsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // divsd xmm0,[ftreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - return TRUE; - - case 0x04: - if (IS_SINGLE(op)) /* SQRT.S */ - { - emit_sqrtss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // sqrtss xmm0,[fsreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else /* SQRT.D */ - { - emit_sqrtsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // sqrtsd xmm0,[fsreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - return TRUE; - - case 0x05: - if (IS_SINGLE(op)) /* ABS.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_andps_r128_m128(DRCTOP, REG_XMM0, MDRC(mips3.drcdata->abs32mask)); - // andps xmm0,[abs32mask] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else /* ABS.D */ - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_andps_r128_m128(DRCTOP, REG_XMM0, MDRC(mips3.drcdata->abs64mask)); - // andpd xmm0,[abs64mask] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - return TRUE; - - case 0x06: - if (IS_SINGLE(op)) /* MOV.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else /* MOV.D */ - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - return TRUE; - - case 0x07: - if (IS_SINGLE(op)) /* NEG.S */ - { - emit_xorps_r128_r128(DRCTOP, REG_XMM0, REG_XMM0); // xorps xmm0,xmm0 - emit_subss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // subss xmm0,[fsreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else /* NEG.D */ - { - emit_xorps_r128_r128(DRCTOP, REG_XMM0, REG_XMM0); // xorps xmm0,xmm0 - emit_subsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // subsd xmm0,[fsreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - return TRUE; - - case 0x08: - /* FUTURE: On Penryn, we can use ROUNDPS/ROUNDPD */ - drc_append_set_temp_sse_rounding(drc, FPRND_NEAR); // ldmxcsr [round] - if (IS_SINGLE(op)) /* ROUND.L.S */ - emit_cvtss2si_r64_m32(DRCTOP, REG_RAX, FPR32ADDR(FSREG)); // cvtss2si rax,[fsreg] - else /* ROUND.L.D */ - emit_cvtsd2si_r64_m64(DRCTOP, REG_RAX, FPR64ADDR(FSREG)); // cvtsd2si rax,[fsreg] - emit_mov_m64_r64(DRCTOP, FPR64ADDR(FDREG), REG_RAX); // mov [fdreg],rax - drc_append_restore_sse_rounding(drc); // ldmxcsr [mode] - return TRUE; - - case 0x09: - if (IS_SINGLE(op)) /* TRUNC.L.S */ - emit_cvttss2si_r64_m32(DRCTOP, REG_RAX, FPR32ADDR(FSREG)); // cvttss2si rax,[fsreg] - else /* TRUNC.L.D */ - emit_cvttsd2si_r64_m64(DRCTOP, REG_RAX, FPR64ADDR(FSREG)); // cvttsd2si rax,[fsreg] - emit_mov_m64_r64(DRCTOP, FPR64ADDR(FDREG), REG_RAX); // mov [fdreg],rax - return TRUE; - - case 0x0a: - /* FUTURE: On Penryn, we can use ROUNDPS/ROUNDPD */ - drc_append_set_temp_sse_rounding(drc, FPRND_UP); // ldmxcsr [round] - if (IS_SINGLE(op)) /* CEIL.L.S */ - emit_cvtss2si_r64_m32(DRCTOP, REG_RAX, FPR32ADDR(FSREG)); // cvtss2si rax,[fsreg] - else /* CEIL.L.D */ - emit_cvtsd2si_r64_m64(DRCTOP, REG_RAX, FPR64ADDR(FSREG)); // cvtsd2si rax,[fsreg] - emit_mov_m64_r64(DRCTOP, FPR64ADDR(FDREG), REG_RAX); // mov [fdreg],rax - drc_append_restore_sse_rounding(drc); // ldmxcsr [mode] - return TRUE; - - case 0x0b: - /* FUTURE: On Penryn, we can use ROUNDPS/ROUNDPD */ - drc_append_set_temp_sse_rounding(drc, FPRND_DOWN); // ldmxcsr [round] - if (IS_SINGLE(op)) /* FLOOR.L.S */ - emit_cvtss2si_r64_m32(DRCTOP, REG_RAX, FPR32ADDR(FSREG)); // cvtss2si rax,[fsreg] - else /* FLOOR.L.D */ - emit_cvtsd2si_r64_m64(DRCTOP, REG_RAX, FPR64ADDR(FSREG)); // cvtsd2si rax,[fsreg] - emit_mov_m64_r64(DRCTOP, FPR64ADDR(FDREG), REG_RAX); // mov [fdreg],rax - drc_append_restore_sse_rounding(drc); // ldmxcsr [mode] - return TRUE; - - case 0x0c: - /* FUTURE: On Penryn, we can use ROUNDPS/ROUNDPD */ - drc_append_set_temp_sse_rounding(drc, FPRND_NEAR); // ldmxcsr [round] - if (IS_SINGLE(op)) /* ROUND.W.S */ - emit_cvtss2si_r32_m32(DRCTOP, REG_EAX, FPR32ADDR(FSREG)); // cvtss2si eax,[fsreg] - else /* ROUND.W.D */ - emit_cvtsd2si_r32_m64(DRCTOP, REG_EAX, FPR64ADDR(FSREG)); // cvtsd2si eax,[fsreg] - emit_mov_m32_r32(DRCTOP, FPR32ADDR(FDREG), REG_EAX); // mov [fdreg],eax - drc_append_restore_sse_rounding(drc); // ldmxcsr [mode] - return TRUE; - - case 0x0d: - if (IS_SINGLE(op)) /* TRUNC.W.S */ - emit_cvttss2si_r32_m32(DRCTOP, REG_EAX, FPR32ADDR(FSREG)); // cvttss2si eax,[fsreg] - else /* TRUNC.W.D */ - emit_cvttsd2si_r32_m64(DRCTOP, REG_EAX, FPR64ADDR(FSREG)); // cvttsd2si eax,[fsreg] - emit_mov_m32_r32(DRCTOP, FPR32ADDR(FDREG), REG_EAX); // mov [fdreg],eax - return TRUE; - - case 0x0e: - /* FUTURE: On Penryn, we can use ROUNDPS/ROUNDPD */ - drc_append_set_temp_sse_rounding(drc, FPRND_UP); // ldmxcsr [round] - if (IS_SINGLE(op)) /* CEIL.W.S */ - emit_cvtss2si_r32_m32(DRCTOP, REG_EAX, FPR32ADDR(FSREG)); // cvtss2si eax,[fsreg] - else /* CEIL.W.D */ - emit_cvtsd2si_r32_m64(DRCTOP, REG_EAX, FPR64ADDR(FSREG)); // cvtsd2si eax,[fsreg] - emit_mov_m32_r32(DRCTOP, FPR32ADDR(FDREG), REG_EAX); // mov [fdreg],eax - drc_append_restore_sse_rounding(drc); // ldmxcsr [mode] - return TRUE; - - case 0x0f: - /* FUTURE: On Penryn, we can use ROUNDPS/ROUNDPD */ - drc_append_set_temp_sse_rounding(drc, FPRND_DOWN); // ldmxcsr [round] - if (IS_SINGLE(op)) /* FLOOR.W.S */ - emit_cvtss2si_r32_m32(DRCTOP, REG_EAX, FPR32ADDR(FSREG)); // cvtss2si eax,[fsreg] - else /* FLOOR.W.D */ - emit_cvtsd2si_r32_m64(DRCTOP, REG_EAX, FPR64ADDR(FSREG)); // cvtsd2si eax,[fsreg] - emit_mov_m32_r32(DRCTOP, FPR32ADDR(FDREG), REG_EAX); // mov [fdreg],eax - drc_append_restore_sse_rounding(drc); // ldmxcsr [mode] - return TRUE; - - case 0x11: /* R5000 */ - emit_cmp_m8_imm(DRCTOP, CF1ADDR((op >> 18) & 7), 0); // cmp fcc[which],0 - emit_jcc_short_link(DRCTOP, ((op >> 16) & 1) ? COND_Z : COND_NZ, &link1); - // jz/nz skip - if (IS_SINGLE(op)) /* MOVT/F.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else /* MOVT/F.D */ - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - resolve_link(DRCTOP, &link1); // skip: - return TRUE; - - case 0x12: /* R5000 */ - emit_cmp_m64_imm(DRCTOP, REGADDR(RTREG), 0); // cmp [X86REG(rt)],0 - emit_jcc_short_link(DRCTOP, COND_NZ, &link1); // jnz skip - if (IS_SINGLE(op)) /* MOVZ.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else /* MOVZ.D */ - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - resolve_link(DRCTOP, &link1); // skip: - return TRUE; - - case 0x13: /* R5000 */ - emit_cmp_m64_imm(DRCTOP, REGADDR(RTREG), 0); // cmp [X86REG(rt)],0 - emit_jcc_short_link(DRCTOP, COND_Z, &link1); // jz skip - if (IS_SINGLE(op)) /* MOVN.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else /* MOVN.D */ - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - resolve_link(DRCTOP, &link1); // skip: - return TRUE; - - case 0x15: /* R5000 */ - if (IS_SINGLE(op)) /* RECIP.S */ - { - emit_rcpss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // rcpss xmm0,[fsreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else /* RECIP.D */ - { - emit_cvtsd2ss_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // cvtsd2ss xmm0,[fsreg] - emit_rcpss_r128_r128(DRCTOP, REG_XMM0, REG_XMM0); // rcpss xmm0,xmm0 - emit_cvtss2sd_r128_r128(DRCTOP, REG_XMM0, REG_XMM0); // cvtss2sd xmm0,xmm0 - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - return TRUE; - - case 0x16: /* R5000 */ - if (IS_SINGLE(op)) /* RSQRT.S */ - { - emit_rsqrtss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // rsqrtss xmm0,[fsreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else /* RSQRT.D */ - { - emit_cvtsd2ss_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // cvtss2sd xmm0,[fsreg] - emit_rsqrtss_r128_r128(DRCTOP, REG_XMM0, REG_XMM0); // rsqrtss xmm0,xmm0 - emit_cvtss2sd_r128_r128(DRCTOP, REG_XMM0, REG_XMM0); // cvtsd2ss xmm0,xmm0 - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - return TRUE; - - case 0x20: - if (IS_INTEGRAL(op)) - { - if (IS_SINGLE(op)) /* CVT.S.W */ - emit_cvtsi2ss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // cvtsi2ss xmm0,[fsreg] - else /* CVT.S.L */ - emit_cvtsi2ss_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // cvtsi2ss xmm0,[fsreg] - } - else /* CVT.S.D */ - emit_cvtsd2ss_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // cvtsd2ss xmm0,[fsreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - return TRUE; - - case 0x21: - if (IS_INTEGRAL(op)) - { - if (IS_SINGLE(op)) /* CVT.D.W */ - emit_cvtsi2sd_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // cvtsi2sd xmm0,[fsreg] - else /* CVT.D.L */ - emit_cvtsi2sd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // cvtsi2sd xmm0,[fsreg] - } - else /* CVT.D.S */ - emit_cvtss2sd_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // cvtss2sd xmm0,[fsreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - return TRUE; - - case 0x24: - if (IS_SINGLE(op)) /* CVT.W.S */ - emit_cvtss2si_r32_m32(DRCTOP, REG_EAX, FPR32ADDR(FSREG)); // cvtss2si eax,[fsreg] - else /* CVT.W.D */ - emit_cvtsd2si_r32_m64(DRCTOP, REG_EAX, FPR64ADDR(FSREG)); // cvtsd2si eax,[fsreg] - emit_mov_m32_r32(DRCTOP, FPR32ADDR(FDREG), REG_EAX); // mov [fdreg],eax - return TRUE; - - case 0x25: - if (IS_SINGLE(op)) /* CVT.L.S */ - emit_cvtss2si_r64_m32(DRCTOP, REG_RAX, FPR32ADDR(FSREG)); // cvtss2si rax,[fsreg] - else /* CVT.L.D */ - emit_cvtsd2si_r64_m64(DRCTOP, REG_RAX, FPR64ADDR(FSREG)); // cvtsd2si rax,[fsreg] - emit_mov_m64_r64(DRCTOP, FPR32ADDR(FDREG), REG_RAX); // mov [fdreg],rax - return TRUE; - - case 0x30: - case 0x38: - emit_mov_m8_imm(DRCTOP, CF1ADDR((op >> 8) & 7), 0); /* C.F.S/D */ - return TRUE; - - case 0x31: - case 0x39: - emit_mov_m8_imm(DRCTOP, CF1ADDR((op >> 8) & 7), 0); /* C.F.S/D */ - return TRUE; - - case 0x32: - case 0x3a: - if (IS_SINGLE(op)) /* C.EQ.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_comiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // comiss xmm0,[ftreg] - } - else /* C.EQ.D */ - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_comisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // comisd xmm0,[ftreg] - } - emit_setcc_m8(DRCTOP, COND_E, CF1ADDR((op >> 8) & 7)); // sete fcc[x] - return TRUE; - - case 0x33: - case 0x3b: - if (IS_SINGLE(op)) /* C.UEQ.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_ucomiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // ucomiss xmm0,[ftreg] - } - else /* C.UEQ.D */ - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_ucomisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // ucomisd xmm0,[ftreg] - } - emit_setcc_m8(DRCTOP, COND_E, CF1ADDR((op >> 8) & 7)); // sete fcc[x] - return TRUE; - - case 0x34: - case 0x3c: - if (IS_SINGLE(op)) /* C.OLT.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_comiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // comiss xmm0,[ftreg] - } - else /* C.OLT.D */ - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_comisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // comisd xmm0,[ftreg] - } - emit_setcc_m8(DRCTOP, COND_B, CF1ADDR((op >> 8) & 7)); // setb fcc[x] - return TRUE; - - case 0x35: - case 0x3d: - if (IS_SINGLE(op)) /* C.ULT.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_ucomiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // ucomiss xmm0,[ftreg] - } - else /* C.ULT.D */ - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_ucomisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // ucomisd xmm0,[ftreg] - } - emit_setcc_m8(DRCTOP, COND_B, CF1ADDR((op >> 8) & 7)); // setb fcc[x] - return TRUE; - - case 0x36: - case 0x3e: - if (IS_SINGLE(op)) /* C.OLE.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_comiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // comiss xmm0,[ftreg] - } - else /* C.OLE.D */ - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_comisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // comisd xmm0,[ftreg] - } - emit_setcc_m8(DRCTOP, COND_BE, CF1ADDR((op >> 8) & 7)); // setbe fcc[x] - return TRUE; - - case 0x37: - case 0x3f: - if (IS_SINGLE(op)) /* C.ULE.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_ucomiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // ucomiss xmm0,[ftreg] - } - else /* C.ULE.D */ - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_ucomisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // ucomisd xmm0,[ftreg] - } - emit_setcc_m8(DRCTOP, COND_BE, CF1ADDR((op >> 8) & 7)); // setbe fcc[x] - return TRUE; - } - break; - } - return FALSE; -} - - - -/*************************************************************************** - COP1X RECOMPILATION -***************************************************************************/ - -/*------------------------------------------------- - compile_cop1x - compile COP1X opcodes --------------------------------------------------*/ - -static int compile_cop1x(drc_core *drc, compiler_state *compiler, const opcode_desc *desc) -{ - UINT32 op = *desc->opptr.l; - - if (mips3.drcoptions & MIPS3DRC_STRICT_COP1) - { - emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_COP1); // test [mips3.core->cpr[0][COP0_Status]],SR_COP1 - oob_request_callback(drc, COND_Z, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_cop_exception); - // jz generate_cop_exception - } - - switch (op & 0x3f) - { - case 0x00: /* LWXC1 */ - return compile_loadx_cop(drc, compiler, desc, 4, FPR32(FDREG)); - - case 0x01: /* LDXC1 */ - return compile_loadx_cop(drc, compiler, desc, 8, FPR64(FDREG)); - - case 0x08: /* SWXC1 */ - return compile_storex_cop(drc, compiler, desc, 4, FPR32(FDREG)); - - case 0x09: /* SDXC1 */ - return compile_storex_cop(drc, compiler, desc, 8, FPR64(FDREG)); - - case 0x0f: /* PREFX */ - return TRUE; - - case 0x20: /* MADD.S */ - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_mulss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // mulss xmm0,[ftreg] - emit_addss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FRREG)); // addss xmm0,[frreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - return TRUE; - - case 0x21: /* MADD.D */ - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_mulsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // mulsd xmm0,[ftreg] - emit_addsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FRREG)); // addsd xmm0,[frreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - return TRUE; - - case 0x28: /* MSUB.S */ - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_mulss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // mulss xmm0,[ftreg] - emit_subss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FRREG)); // subss xmm0,[frreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - return TRUE; - - case 0x29: /* MSUB.D */ - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_mulsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // mulsd xmm0,[ftreg] - emit_subsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FRREG)); // subsd xmm0,[frreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - return TRUE; - - case 0x30: /* NMADD.S */ - emit_xorps_r128_r128(DRCTOP, REG_XMM1, REG_XMM1); // xorps xmm1,xmm1 - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_mulss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // mulss xmm0,[ftreg] - emit_addss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FRREG)); // addss xmm0,[frreg] - emit_subss_r128_r128(DRCTOP, REG_XMM1, REG_XMM0); // subss xmm1,xmm0 - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM1); // movss [fdreg],xmm1 - return TRUE; - - case 0x31: /* NMADD.D */ - emit_xorps_r128_r128(DRCTOP, REG_XMM1, REG_XMM1); // xorps xmm1,xmm1 - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_mulsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // mulsd xmm0,[ftreg] - emit_addsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FRREG)); // addsd xmm0,[frreg] - emit_subsd_r128_r128(DRCTOP, REG_XMM1, REG_XMM0); // subsd xmm1,xmm0 - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM1); // movsd [fdreg],xmm1 - return TRUE; - - case 0x38: /* NMSUB.S */ - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_mulss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // mulss xmm0,[ftreg] - emit_movss_r128_m32(DRCTOP, REG_XMM1, FPR32ADDR(FRREG)); // movss xmm1,[frreg] - emit_subss_r128_r128(DRCTOP, REG_XMM1, REG_XMM0); // subss xmm1,xmm0 - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM1); // movss [fdreg],xmm1 - return TRUE; - - case 0x39: /* NMSUB.D */ - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_mulsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // mulsd xmm0,[ftreg] - emit_movsd_r128_m64(DRCTOP, REG_XMM1, FPR64ADDR(FRREG)); // movsd xmm1,[frreg] - emit_subsd_r128_r128(DRCTOP, REG_XMM1, REG_XMM0); // subsd xmm1,xmm0 - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM1); // movsd [fdreg],xmm1 - return TRUE; - - case 0x24: /* MADD.W */ - case 0x25: /* MADD.L */ - case 0x2c: /* MSUB.W */ - case 0x2d: /* MSUB.L */ - case 0x34: /* NMADD.W */ - case 0x35: /* NMADD.L */ - case 0x3c: /* NMSUB.W */ - case 0x3d: /* NMSUB.L */ - default: - fprintf(stderr, "cop1x %X\n", op); - break; - } - return FALSE; -} - - - -/*************************************************************************** - INDIVIDUAL INSTRUCTION COMPILATION -***************************************************************************/ - -/*------------------------------------------------- - compile_shift - compile a 32-bit - shift, both fixed and variable counts - - 0: sll/sllv rD,rT,rS - 2: srl/srlv rD,rT,rS - 3: sra/srav rD,rT,rS --------------------------------------------------*/ - -static int compile_shift(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int shift_type, int shift_variable) -{ - UINT32 op = *desc->opptr.l; - mipsreg_state rd, rs, rt; - - assert(shift_type == 0 || shift_type == 2 || shift_type == 3); - - /* if the destination is r0, bail */ - if (RDREG == 0) - return TRUE; - - /* allocate source registers */ - rs = shift_variable ? compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC) : MAKE_CONST(SHIFT); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if rS and rT are both constant, we can compute the constant result */ - if (ISCONST(rs) && ISCONST(rt)) - { - if (shift_type == 0) - emit_set_constant_value(drc, compiler, desc, RDREG, (INT32)((UINT32)CONSTVAL(rt) << CONSTVAL(rs))); - else if (shift_type == 2) - emit_set_constant_value(drc, compiler, desc, RDREG, (INT32)((UINT32)CONSTVAL(rt) >> CONSTVAL(rs))); - else - emit_set_constant_value(drc, compiler, desc, RDREG, (INT32)((INT32)CONSTVAL(rt) >> CONSTVAL(rs))); - } - - /* otherwise, handle generically */ - else - { - /* allocate the register and load rT into it */ - /* if rS is non-const, must load it first in case rD == rS */ - rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DST | ALLOC_DIRTY); - if (!ISCONST(rs)) - emit_load_register_value(drc, compiler, REG_ECX, rs); // mov ecx,<rs> - emit_load_register_value(drc, compiler, X86REG(rd), rt); // mov <rd>,<rt> - - /* if rs is constant, use immediate forms of the shift */ - if (ISCONST(rs)) - { - if (shift_type == 0) - emit_shl_r32_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 31); // shl <rd>,<rs> - else if (shift_type == 2) - emit_shr_r32_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 31); // shr <rd>,<rs> - else - emit_sar_r32_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 31); // sar <rd>,<rs> - } - - /* otherwise, load rs into cl so we can do a variable shift */ - else - { - if (shift_type == 0) - emit_shl_r32_cl(DRCTOP, X86REG(rd)); // shl <rd>,cl - else if (shift_type == 2) - emit_shr_r32_cl(DRCTOP, X86REG(rd)); // shr <rd>,cl - else - emit_sar_r32_cl(DRCTOP, X86REG(rd)); // sar <rd>,cl - } - emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rd)); // movsxd <rd>,<rd> - } - return TRUE; -} - - -/*------------------------------------------------- - compile_dshift - compile a 64-bit - shift, both fixed and variable counts - - 0: dsll/dsll32/dsllv rD,rT,rS - 2: dsrl/dsrl32/dsrlv rD,rT,rS - 3: dsra/dsra32/dsrav rD,rT,rS --------------------------------------------------*/ - -static int compile_dshift(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int shift_type, int shift_variable, int shift_const_plus32) -{ - UINT32 op = *desc->opptr.l; - mipsreg_state rd, rs, rt; - - assert(shift_type == 0 || shift_type == 2 || shift_type == 3); - - /* if the destination is r0, bail */ - if (RDREG == 0) - return TRUE; - - /* allocate source registers */ - rs = shift_variable ? compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC) : MAKE_CONST(SHIFT + (shift_const_plus32 ? 32 : 0)); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if rS and rT are both constant, we can compute the constant result */ - if (ISCONST(rs) && ISCONST(rt)) - { - if (shift_type == 0) - emit_set_constant_value(drc, compiler, desc, RDREG, (UINT64)CONSTVAL(rt) << CONSTVAL(rs)); - else if (shift_type == 2) - emit_set_constant_value(drc, compiler, desc, RDREG, (UINT64)CONSTVAL(rt) >> CONSTVAL(rs)); - else - emit_set_constant_value(drc, compiler, desc, RDREG, (INT64)CONSTVAL(rt) >> CONSTVAL(rs)); - } - - /* otherwise, handle generically */ - else - { - /* allocate the register and load rT into it */ - /* if rS is non-const, must load it first in case rD == rS */ - rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DST | ALLOC_DIRTY); - if (!ISCONST(rs)) - emit_load_register_value(drc, compiler, REG_ECX, rs); // mov ecx,<rs> - emit_load_register_value(drc, compiler, X86REG(rd), rt); // mov <rd>,<rt> - - /* if rs is constant, use immediate forms of the shift */ - if (ISCONST(rs)) - { - if (shift_type == 0) - emit_shl_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 63); // shl <rd>,<rs> - else if (shift_type == 2) - emit_shr_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 63); // shr <rd>,<rs> - else - emit_sar_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rs) & 63); // sar <rd>,<rs> - } - - /* otherwise, load rs into cl so we can do a variable shift */ - else - { - if (shift_type == 0) - emit_shl_r64_cl(DRCTOP, X86REG(rd)); // shl <rd>,cl - else if (shift_type == 2) - emit_shr_r64_cl(DRCTOP, X86REG(rd)); // shr <rd>,cl - else - emit_sar_r64_cl(DRCTOP, X86REG(rd)); // sar <rd>,cl - } - } - return TRUE; -} - - -/*------------------------------------------------- - compile_movzn - compile a conditional - move based on compare against 0 - - movz/movn rD,rS,rT --------------------------------------------------*/ - -static int compile_movzn(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_movn) -{ - UINT32 op = *desc->opptr.l; - mipsreg_state rd, rs, rt; - emit_link skip = { 0 }; - - /* if the destination is r0, bail */ - if (RDREG == 0) - return TRUE; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_SRC | ALLOC_DST | ALLOC_DIRTY); - - /* if rT is constant and fails the condition, we're done */ - if (ISCONST(rt)) - { - INT32 rtval = CONSTVAL(rt); - if ((is_movn && rtval == 0) || (!is_movn && rtval != 0)) - return TRUE; - } - - /* if rT is not constant, check the condition */ - else - { - emit_test_r64_r64(DRCTOP, X86REG(rt), X86REG(rt)); // test <rt>,<rt> - emit_jcc_short_link(DRCTOP, is_movn ? COND_Z : COND_NZ, &skip); // jz/nz skip - } - - /* load rS into rD */ - emit_load_register_value(drc, compiler, X86REG(rd), rs); // mov <rd>,<rs> - if (!ISCONST(rt)) - resolve_link(DRCTOP, &skip); // skip: - - return TRUE; -} - - -/*------------------------------------------------- - compile_trapcc - compile a conditional - trap comparing two registers - - 0/1: tge/tgeu rS,rT / tgei/tgeiu rS,SIMM - 2/3: tlt/tltu rS,rT / tlti/tltiu rS,SIMM - 4: teq rS,rT / teqi rS,SIMM - 6: tne rS,rT / tnei rS,SIMM --------------------------------------------------*/ - -static int compile_trapcc(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int condition, int immediate) -{ - static const UINT8 condtype[] = { COND_GE, COND_AE, COND_L, COND_B, COND_E, COND_NONE, COND_NE, COND_NONE }; - UINT32 op = *desc->opptr.l; - mipsreg_state rs, rt; - - assert(condition < ARRAY_LENGTH(condtype) && condtype[condition] != COND_NONE); - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = immediate ? MAKE_CONST(SIMMVAL) : compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if both constant, we know the answer up front */ - if (ISCONST(rs) && ISCONST(rt)) - { - INT32 rsval = CONSTVAL(rs); - INT32 rtval = CONSTVAL(rt); - int result = FALSE; - - /* determine the fixed answer */ - switch (condition) - { - case 0: result = ((INT64)rsval >= (INT64)rtval); break; - case 1: result = ((UINT64)rsval >= (UINT64)rtval); break; - case 2: result = ((INT64)rsval < (INT64)rtval); break; - case 3: result = ((UINT64)rsval < (UINT64)rtval); break; - case 4: result = (rsval == rtval); break; - case 6: result = (rsval != rtval); break; - } - - /* if we're not trapping, just return; otherwise, always trap */ - if (!result) - return TRUE; - condition = COND_NONE; - } - - /* if rT is constant do an immediate compare */ - else if (ISCONST(rt)) - { - emit_cmp_r64_imm(DRCTOP, X86REG(rs), CONSTVAL(rt)); // cmp <rs>,<rt> - condition = condtype[condition]; - } - - /* if rS is constant do an immediate compare with opposite condition */ - else if (ISCONST(rs)) - { - emit_cmp_r64_imm(DRCTOP, X86REG(rt), CONSTVAL(rs)); // cmp <rt>,<rs> - condition = condtype[condition] ^ 1; - } - - /* otherwise, compare the two registers with a normal condition */ - else - { - emit_cmp_r64_imm(DRCTOP, X86REG(rs), X86REG(rt)); // cmp <rs>,<rt> - condition = condtype[condition]; - } - - /* generate an OOB request if the trap is taken */ - oob_request_callback(drc, condition, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_trap_exception); - return TRUE; -} - - -/*------------------------------------------------- - compile_branchcc - compile a conditional - branch comparing two registers - - beq rS,rT,dest - bne rS,rT,dest --------------------------------------------------*/ - -static int compile_branchcc(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int condition) -{ - UINT32 op = *desc->opptr.l; - emit_link skip = { 0 }; - mipsreg_state rs, rt; - - assert(condition == COND_E || condition == COND_NE); - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if both constant, we know the answer up front */ - if (ISCONST(rs) && ISCONST(rt)) - { - INT32 rsval = CONSTVAL(rs); - INT32 rtval = CONSTVAL(rt); - if ((condition == COND_E && rsval != rtval) || (condition == COND_NE && rsval == rtval)) - return TRUE; - } - - /* if rT is constant do an immediate compare */ - else if (ISCONST(rt)) - { - emit_cmp_r64_imm(DRCTOP, X86REG(rs), CONSTVAL(rt)); // cmp <rs>,<rt> - emit_jcc_near_link(DRCTOP, condition ^ 1, &skip); // jncc skip - } - - /* if rS is constant do an immediate compare */ - else if (ISCONST(rs)) - { - emit_cmp_r64_imm(DRCTOP, X86REG(rt), CONSTVAL(rs)); // cmp <rt>,<rs> - emit_jcc_near_link(DRCTOP, condition ^ 1, &skip); // jncc skip - } - - /* otherwise, compare the two registers with a normal condition */ - else - { - emit_cmp_r64_r64(DRCTOP, X86REG(rs), X86REG(rt)); // cmp <rs>,<rt> - emit_jcc_near_link(DRCTOP, condition ^ 1, &skip); // jncc skip - } - - /* append the branch logic */ - emit_delay_slot_and_branch(drc, compiler, desc, 0); // <next instruction + redispatch> - - if (!(ISCONST(rs) && ISCONST(rt))) - resolve_link(DRCTOP, &skip); // skip: - return TRUE; -} - - -/*------------------------------------------------- - compile_branchcz - compile a conditional - branch comparing one register against 0 - - blez rS,dest - blezl rS,dest - bgtz rS,dest - bgtzl rS,dest - bltz rS,dest - bltzl rS,dest - bltzal rS,dest - bltzall rS,dest - bgez rS,dest - bgezl rS,dest - bgezal rS,dest - bgezall rS,dest --------------------------------------------------*/ - -static int compile_branchcz(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int condition, int link) -{ - UINT32 op = *desc->opptr.l; - emit_link skip = { 0 }; - mipsreg_state rs; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - - /* if the register is constant, we know the answer up front */ - if (ISCONST(rs)) - { - INT32 rsval = CONSTVAL(rs); - if ((condition == COND_LE && rsval > 0) || (condition == COND_G && rsval <= 0) || - (condition == COND_L && rsval >= 0) || (condition == COND_GE && rsval < 0)) - return TRUE; - } - - /* otherwise, perform the compare */ - else - { - emit_cmp_r64_imm(DRCTOP, X86REG(rs), 0); // cmp <rs>,0 - emit_jcc_near_link(DRCTOP, condition ^ 1, &skip); // jncc skip - } - - /* append the branch logic */ - emit_delay_slot_and_branch(drc, compiler, desc, link ? 31 : 0); // <next instruction + redispatch> - - if (!ISCONST(rs)) - resolve_link(DRCTOP, &skip); // skip: - return TRUE; -} - - -/*------------------------------------------------- - compile_add - compile an add with or without - overflow - - add rD,rS,rT / addi rT,rS,SIMM - addu rD,rS,rT / addiu rT,rS,SIMM --------------------------------------------------*/ - -static int compile_add(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions, int immediate) -{ - UINT32 op = *desc->opptr.l; - UINT8 destreg = immediate ? RTREG : RDREG; - mipsreg_state rd, rs, rt; - - /* note that we still must perform this operation even if destreg == 0 - in the overflow case */ - if (destreg == 0 && suppress_exceptions) - return TRUE; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = immediate ? MAKE_CONST(SIMMVAL) : compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if both constant, we know the answer up front */ - if (ISCONST(rs) && ISCONST(rt)) - { - INT64 result = (INT64)CONSTVAL(rs) + (INT64)CONSTVAL(rt); - - /* if we overflow, always generate an exception */ - if (!suppress_exceptions && (INT32)result != result) - oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception); - - /* else just set the new constant value */ - else if (destreg != 0) - emit_set_constant_value(drc, compiler, desc, destreg, (INT32)result); - } - - /* otherwise, handle generically */ - else - { - rd = compiler_register_allocate(drc, compiler, desc, destreg, ALLOC_DST); - - /* perform the operating in EAX so we don't trash rD if an exception happens */ - if (!suppress_exceptions) - { - emit_load_register_value(drc, compiler, REG_EAX, rs); // mov eax,<rs> - if (!ISCONST(rt)) - emit_add_r32_r32(DRCTOP, REG_EAX, X86REG(rt)); // add eax,<rt> - else if (CONSTVAL(rt) != 0) - emit_add_r32_imm(DRCTOP, REG_EAX, CONSTVAL(rt)); // add eax,<rt> - oob_request_callback(drc, COND_O, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception); - // jo generate_overflow_exception - if (destreg != 0) - emit_movsxd_r64_r32(DRCTOP, X86REG(rd), REG_EAX); // movsxd <rd>,eax - } - else - { - if (ISCONST(rs) && CONSTVAL(rs) == 0) - emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rt)); // movsxd <rd>,<rt> - else if (ISCONST(rt) && CONSTVAL(rt) == 0) - emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rs)); // movsxd <rd>,<rs> - else if (ISCONST(rs)) - { - emit_lea_r32_m32(DRCTOP, X86REG(rd), MBD(X86REG(rt), CONSTVAL(rs))); // lea <rd>,[<rt> + <rs>] - emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rd)); // movsxd <rd>,<rd> - } - else if (ISCONST(rt)) - { - emit_lea_r32_m32(DRCTOP, X86REG(rd), MBD(X86REG(rs), CONSTVAL(rt))); // lea <rd>,[<rs> + <rt>] - emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rd)); // movsxd <rd>,<rd> - } - else - { - emit_lea_r32_m32(DRCTOP, X86REG(rd), MBISD(X86REG(rs), X86REG(rt), 1, 0)); // lea <rd>,[<rs> + <rt>] - emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rd)); // movsxd <rd>,<rd> - } - } - - /* only mark it dirty after the exception risk is over */ - compiler_register_allocate(drc, compiler, desc, destreg, ALLOC_DIRTY); - } - return TRUE; -} - - -/*------------------------------------------------- - compile_dadd - compile an add with or without - overflow - - dadd rD,rS,rT - daddu rD,rS,rT --------------------------------------------------*/ - -static int compile_dadd(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions, int immediate) -{ - UINT32 op = *desc->opptr.l; - UINT8 destreg = immediate ? RTREG : RDREG; - mipsreg_state rd, rs, rt; - - /* note that we still must perform this operation even if destreg == 0 - in the overflow case */ - if (destreg == 0 && suppress_exceptions) - return TRUE; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = immediate ? MAKE_CONST(SIMMVAL) : compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if both constant, we know the answer up front */ - if (ISCONST(rs) && ISCONST(rt)) - { - INT64 result = (INT64)CONSTVAL(rs) + (INT64)CONSTVAL(rt); - - /* if we overflow, always generate an exception */ - if (!suppress_exceptions && (INT32)result != result) - oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception); - - /* else just set the new constant value */ - else if (destreg != 0) - emit_set_constant_value(drc, compiler, desc, destreg, result); - } - - /* otherwise, handle generically */ - else - { - rd = compiler_register_allocate(drc, compiler, desc, destreg, ALLOC_DST); - - /* perform the operating in EAX so we don't trash RTREG if an exception happens */ - if (!suppress_exceptions) - { - emit_load_register_value(drc, compiler, REG_RAX, rs); // mov rax,<rs> - if (!ISCONST(rt)) - emit_add_r64_r64(DRCTOP, REG_RAX, X86REG(rt)); // add rax,<rt> - else if (CONSTVAL(rt) != 0) - emit_add_r64_imm(DRCTOP, REG_RAX, CONSTVAL(rt)); // add rax,<rt> - oob_request_callback(drc, COND_O, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception); - // jo generate_overflow_exception - if (destreg != 0) - emit_mov_r64_r64(DRCTOP, X86REG(rd), REG_RAX); // mov <rd>,rax - } - else - { - if (ISCONST(rs)) - emit_lea_r64_m64(DRCTOP, X86REG(rd), MBD(X86REG(rt), CONSTVAL(rs))); // lea <rd>,[<rt> + <rs>] - else if (ISCONST(rt)) - emit_lea_r64_m64(DRCTOP, X86REG(rd), MBD(X86REG(rs), CONSTVAL(rt))); // lea <rd>,[<rs> + <rt>] - else - emit_lea_r64_m64(DRCTOP, X86REG(rd), MBISD(X86REG(rs), X86REG(rt), 1, 0)); // lea <rd>,[<rs> + <rt>] - } - - /* only mark it dirty after the exception risk is over */ - compiler_register_allocate(drc, compiler, desc, destreg, ALLOC_DIRTY); - } - return TRUE; -} - - -/*------------------------------------------------- - compile_sub - compile an add with or without - overflow - - sub rD,rS,rT - subu rD,rS,rT --------------------------------------------------*/ - -static int compile_sub(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions) -{ - UINT32 op = *desc->opptr.l; - mipsreg_state rd, rs, rt; - - /* note that we still must perform this operation even if RDREG == 0 - in the overflow case */ - if (RDREG == 0 && suppress_exceptions) - return TRUE; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if both constant, we know the answer up front */ - if (ISCONST(rs) && ISCONST(rt)) - { - INT64 result = (INT64)CONSTVAL(rs) - (INT64)CONSTVAL(rt); - - /* if we overflow, always generate an exception */ - if (!suppress_exceptions && (INT32)result != result) - oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception); - - /* else just set the new constant value */ - else if (RDREG != 0) - emit_set_constant_value(drc, compiler, desc, RDREG, (INT32)result); - } - - /* if rS == rT, the result is 0 */ - else if (RSREG == RTREG) - { - if (RDREG != 0) - emit_set_constant_value(drc, compiler, desc, RDREG, 0); - } - - /* otherwise, handle generically */ - else - { - rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DST); - - /* perform the operating in EAX so we don't trash RTREG if an exception happens */ - if (!suppress_exceptions) - { - emit_load_register_value(drc, compiler, REG_EAX, rs); // mov eax,<rs> - if (!ISCONST(rt)) - emit_sub_r32_r32(DRCTOP, REG_EAX, X86REG(rt)); // sub eax,<rt> - else if (CONSTVAL(rt) != 0) - emit_sub_r32_imm(DRCTOP, REG_EAX, CONSTVAL(rt)); // sub eax,<rt> - if (!ISCONST(rt) || CONSTVAL(rt) != 0) - oob_request_callback(drc, COND_O, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception); - // jo generate_overflow_exception - if (RDREG != 0) - emit_movsxd_r64_r32(DRCTOP, X86REG(rd), REG_EAX); // movsxd <rd>,eax - } - else - { - /* if rD and rT aren't the same, it is pretty basic */ - if (X86REG(rd) != X86REG(rt)) - { - emit_load_register_value(drc, compiler, X86REG(rd), rs); // mov <rd>,<rs> - if (!ISCONST(rt)) - emit_sub_r32_r32(DRCTOP, X86REG(rd), X86REG(rt)); // sub <rd>,<rt> - else if (CONSTVAL(rt) != 0) - emit_sub_r32_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // sub <rd>,<rt> - } - - /* otherwise, we negate rT and add rS */ - else - { - emit_neg_r32(DRCTOP, X86REG(rd)); // neg <rd> - if (!ISCONST(rs)) - emit_add_r32_r32(DRCTOP, X86REG(rd), X86REG(rs)); // add <rd>,<rs> - else if (CONSTVAL(rs) != 0) - emit_add_r32_imm(DRCTOP, X86REG(rd), CONSTVAL(rs)); // add <rd>,<rs> - } - emit_movsxd_r64_r32(DRCTOP, X86REG(rd), X86REG(rd)); // movsxd <rd>,<rd> - } - - /* only mark it dirty after the exception risk is over */ - compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DIRTY); - } - return TRUE; -} - - -/*------------------------------------------------- - compile_dsub - compile an add with or without - overflow - - dsub rD,rS,rT - dsubu rD,rS,rT --------------------------------------------------*/ - -static int compile_dsub(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int suppress_exceptions) -{ - UINT32 op = *desc->opptr.l; - mipsreg_state rd, rs, rt; - - /* note that we still must perform this operation even if RDREG == 0 - in the overflow case */ - if (RDREG == 0 && suppress_exceptions) - return TRUE; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if both constant, we know the answer up front */ - if (ISCONST(rs) && ISCONST(rt)) - { - INT64 result = (INT64)CONSTVAL(rs) - (INT64)CONSTVAL(rt); - - /* if we overflow, always generate an exception */ - if (!suppress_exceptions && (INT32)result != result) - oob_request_callback(drc, COND_NONE, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception); - - /* else just set the new constant value */ - else if (RDREG != 0) - emit_set_constant_value(drc, compiler, desc, RDREG, result); - } - - /* if rS == rT, the result is 0 */ - else if (RSREG == RTREG) - { - if (RDREG != 0) - emit_set_constant_value(drc, compiler, desc, RDREG, 0); - } - - /* otherwise, handle generically */ - else - { - rd = compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DST); - - /* perform the operating in EAX so we don't trash RTREG if an exception happens */ - if (!suppress_exceptions) - { - emit_load_register_value(drc, compiler, REG_EAX, rs); // mov eax,<rs> - if (!ISCONST(rt)) - emit_sub_r64_r64(DRCTOP, REG_RAX, X86REG(rt)); // sub rax,<rt> - else if (CONSTVAL(rt) != 0) - emit_sub_r64_imm(DRCTOP, REG_RAX, CONSTVAL(rt)); // sub rax,<rt> - if (!ISCONST(rt) || CONSTVAL(rt) != 0) - oob_request_callback(drc, COND_O, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_overflow_exception); - // jo generate_overflow_exception - if (RDREG != 0) - emit_mov_r64_r64(DRCTOP, X86REG(rd), REG_RAX); // mov <rd>,rax - } - else - { - /* if rD and rT aren't the same, it is pretty basic */ - if (X86REG(rd) != X86REG(rt)) - { - emit_load_register_value(drc, compiler, X86REG(rd), rs); // mov <rd>,<rs> - if (!ISCONST(rt)) - emit_sub_r64_r64(DRCTOP, X86REG(rd), X86REG(rt)); // sub <rd>,<rt> - else if (CONSTVAL(rt) != 0) - emit_sub_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // sub <rd>,<rt> - } - - /* otherwise, we negate rT and add rS */ - else - { - emit_neg_r64(DRCTOP, X86REG(rd)); // neg <rd> - if (!ISCONST(rs)) - emit_add_r64_r64(DRCTOP, X86REG(rd), X86REG(rs)); // add <rd>,<rs> - else if (CONSTVAL(rs) != 0) - emit_add_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rs)); // add <rd>,<rs> - } - } - - /* only mark it dirty after the exception risk is over */ - compiler_register_allocate(drc, compiler, desc, RDREG, ALLOC_DIRTY); - } - return TRUE; -} - - -/*------------------------------------------------- - compile_mult - compile a 32-bit multiply - instruction - - mult rS,rT - multu rS,rT --------------------------------------------------*/ - -static int compile_mult(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned) -{ - UINT32 op = *desc->opptr.l; - mipsreg_state rs, rt, lo, hi; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if both constant, we know the answer up front */ - if (ISCONST(rs) && ISCONST(rt)) - { - UINT64 result = is_unsigned ? ((UINT64)CONSTVAL(rs) * (UINT64)CONSTVAL(rt)) : ((INT64)CONSTVAL(rs) * (INT64)CONSTVAL(rt)); - emit_set_constant_value(drc, compiler, desc, REG_LO, (INT32)result); - emit_set_constant_value(drc, compiler, desc, REG_HI, (INT32)(result >> 32)); - } - - /* otherwise, handle generically */ - else - { - UINT8 mulreg = ISCONST(rt) ? REG_ECX : X86REG(rt); - - hi = compiler_register_allocate(drc, compiler, desc, REG_HI, ALLOC_DST | ALLOC_DIRTY); - lo = compiler_register_allocate(drc, compiler, desc, REG_LO, ALLOC_DST | ALLOC_DIRTY); - - emit_load_register_value(drc, compiler, REG_EAX, rs); // mov eax,<rs> - emit_load_register_value(drc, compiler, mulreg, rt); // mov <mulreg>,<rt> - if (is_unsigned) - emit_mul_r32(DRCTOP, mulreg); // mul <mulreg> - else - emit_imul_r32(DRCTOP, mulreg); // imul <mulreg> - emit_movsxd_r64_r32(DRCTOP, X86REG(hi), REG_EDX); // movsxd <hireg>,edx - emit_movsxd_r64_r32(DRCTOP, X86REG(lo), REG_EAX); // movsxd <loreg>,eax - } - return TRUE; -} - - -/*------------------------------------------------- - compile_dmult - compile a 64-bit multiply - instruction - - dmult rS,rT - dmultu rS,rT --------------------------------------------------*/ - -static int compile_dmult(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned) -{ - UINT32 op = *desc->opptr.l; - mipsreg_state rs, rt, lo, hi; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if both constant, we know the answer up front */ - if (ISCONST(rs) && ISCONST(rt)) - { - UINT64 result = is_unsigned ? ((UINT64)CONSTVAL(rs) * (UINT64)CONSTVAL(rt)) : ((INT64)CONSTVAL(rs) * (INT64)CONSTVAL(rt)); - emit_set_constant_value(drc, compiler, desc, REG_LO, (INT64)result); - emit_set_constant_value(drc, compiler, desc, REG_HI, (INT64)(result >> 63)); - } - - /* otherwise, handle generically */ - else - { - UINT8 mulreg = ISCONST(rt) ? REG_ECX : X86REG(rt); - - hi = compiler_register_allocate(drc, compiler, desc, REG_HI, ALLOC_DST | ALLOC_DIRTY); - lo = compiler_register_allocate(drc, compiler, desc, REG_LO, ALLOC_DST | ALLOC_DIRTY); - - emit_load_register_value(drc, compiler, REG_RAX, rs); // mov rax,<rs> - emit_load_register_value(drc, compiler, mulreg, rt); // mov <mulreg>,<rt> - if (is_unsigned) - emit_mul_r64(DRCTOP, mulreg); // mul <mulreg> - else - emit_imul_r64(DRCTOP, mulreg); // imul <mulreg> - emit_mov_r64_r64(DRCTOP, X86REG(hi), REG_RDX); // mov <hi>,rdx - emit_mov_r64_r64(DRCTOP, X86REG(lo), REG_RAX); // mov <lo>,rax - } - return TRUE; -} - - -/*------------------------------------------------- - compile_div - compile a 32-bit divide - instruction - - div rS,rT - divu rS,rT --------------------------------------------------*/ - -static int compile_div(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned) -{ - UINT32 op = *desc->opptr.l; - mipsreg_state rs, rt, lo, hi; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if rT is constant and 0, just skip */ - if (ISCONST(rt) && CONSTVAL(rt) == 0) - return TRUE; - - /* if both constant, we know the answer up front */ - if (ISCONST(rs) && ISCONST(rt)) - { - UINT32 result = is_unsigned ? ((UINT32)CONSTVAL(rs) / (UINT32)CONSTVAL(rt)) : ((INT32)CONSTVAL(rs) / (INT32)CONSTVAL(rt)); - UINT32 remainder = is_unsigned ? ((UINT32)CONSTVAL(rs) % (UINT32)CONSTVAL(rt)) : ((INT32)CONSTVAL(rs) % (INT32)CONSTVAL(rt)); - - emit_set_constant_value(drc, compiler, desc, REG_LO, (INT32)result); - emit_set_constant_value(drc, compiler, desc, REG_HI, (INT32)remainder); - } - - /* otherwise, handle generically */ - else - { - UINT8 divreg = ISCONST(rt) ? REG_ECX : X86REG(rt); - emit_link skip = { 0 }; - - hi = compiler_register_allocate(drc, compiler, desc, REG_HI, ALLOC_DST | ALLOC_DIRTY); - lo = compiler_register_allocate(drc, compiler, desc, REG_LO, ALLOC_DST | ALLOC_DIRTY); - - emit_load_register_value(drc, compiler, REG_RAX, rs); // mov rax,<rs> - emit_load_register_value(drc, compiler, divreg, rt); // mov <divreg>,<rt> - if (!ISCONST(rt)) - { - emit_test_r32_r32(DRCTOP, divreg, divreg); // test <divreg>,<divreg> - emit_jcc_short_link(DRCTOP, COND_Z, &skip); // jz skip - } - if (is_unsigned) - { - emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx - emit_div_r32(DRCTOP, divreg); // div <divreg> - } - else - { - emit_cdq(DRCTOP); // cdq - emit_idiv_r32(DRCTOP, divreg); // idiv <divreg> - } - emit_movsxd_r64_r32(DRCTOP, X86REG(hi), REG_EDX); // movsxd <hi>,edx - emit_movsxd_r64_r32(DRCTOP, X86REG(lo), REG_EAX); // movsxd <lo>,eax - if (!ISCONST(rt)) - resolve_link(DRCTOP, &skip); // skip: - } - return TRUE; -} - - -/*------------------------------------------------- - compile_ddiv - compile a 64-bit divide - instruction - - ddiv rS,rT - ddivu rS,rT --------------------------------------------------*/ - -static int compile_ddiv(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned) -{ - UINT32 op = *desc->opptr.l; - mipsreg_state rs, rt, lo, hi; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if rT is constant and 0, just skip */ - if (ISCONST(rt) && CONSTVAL(rt) == 0) - return TRUE; - - /* if both constant, we know the answer up front */ - if (ISCONST(rs) && ISCONST(rt)) - { - UINT32 result = is_unsigned ? ((UINT32)CONSTVAL(rs) / (UINT32)CONSTVAL(rt)) : ((INT32)CONSTVAL(rs) / (INT32)CONSTVAL(rt)); - UINT32 remainder = is_unsigned ? ((UINT32)CONSTVAL(rs) % (UINT32)CONSTVAL(rt)) : ((INT32)CONSTVAL(rs) % (INT32)CONSTVAL(rt)); - - emit_set_constant_value(drc, compiler, desc, REG_LO, (INT32)result); - emit_set_constant_value(drc, compiler, desc, REG_HI, (INT32)remainder); - } - - /* if one is a constant, load the constant into EAX and multiple from there */ - else - { - UINT8 divreg = ISCONST(rt) ? REG_ECX : X86REG(rt); - emit_link skip = { 0 }; - - hi = compiler_register_allocate(drc, compiler, desc, REG_HI, ALLOC_DST | ALLOC_DIRTY); - lo = compiler_register_allocate(drc, compiler, desc, REG_LO, ALLOC_DST | ALLOC_DIRTY); - - emit_load_register_value(drc, compiler, REG_RAX, rs); // mov rax,<rs> - emit_load_register_value(drc, compiler, divreg, rt); // mov <divreg>,<rt> - emit_test_r64_r64(DRCTOP, divreg, divreg); // test <divreg>,<divreg> - emit_jcc_short_link(DRCTOP, COND_Z, &skip); // jz skip - if (is_unsigned) - { - emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx - emit_div_r64(DRCTOP, divreg); // div <divreg> - } - else - { - emit_cqo(DRCTOP); // cqo - emit_idiv_r64(DRCTOP, divreg); // idiv <divreg> - } - emit_mov_r64_r64(DRCTOP, X86REG(hi), REG_EDX); // mov <hi>,edx - emit_mov_r64_r64(DRCTOP, X86REG(lo), REG_EAX); // mov <loreg>,eax - resolve_link(DRCTOP, &skip); // skip: - } - return TRUE; -} - - -/*------------------------------------------------- - compile_logical - compile a logical - operation between two registers - - 0: and rD,rS,rT / and rT,rS,UIMM - 1: or rD,rS,rT / or rT,rS,UIMM - 2: xor rD,rS,rT / xor rT,rS,UIMM - 3: nor rD,rS,rT --------------------------------------------------*/ - -static int compile_logical(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int operation, int immediate) -{ - UINT32 op = *desc->opptr.l; - UINT8 destreg = immediate ? RTREG : RDREG; - mipsreg_state rd, rs, rt; - - assert(operation >= 0 && operation <= 3); - assert(!immediate || operation != 3); - - /* if the destination is r0, bail */ - if (destreg == 0) - return TRUE; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = immediate ? MAKE_CONST(UIMMVAL) : compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if both constant, we know the answer up front */ - if (ISCONST(rs) && ISCONST(rt)) - { - UINT64 result = (operation == 0) ? (CONSTVAL(rs) & CONSTVAL(rt)) : (operation == 1) ? (CONSTVAL(rs) | CONSTVAL(rt)) : (operation == 2) ? (CONSTVAL(rs) ^ CONSTVAL(rt)) : ~(CONSTVAL(rs) | CONSTVAL(rt)); - emit_set_constant_value(drc, compiler, desc, destreg, result); - } - - /* an AND against 0 is constant as well */ - else if (operation == 0 && ISCONST(rt) && CONSTVAL(rt) == 0) - emit_set_constant_value(drc, compiler, desc, destreg, 0); - - /* else do the operation in code */ - else - { - rd = compiler_register_allocate(drc, compiler, desc, destreg, ALLOC_DST | ALLOC_DIRTY); - - /* if rD matches rT, or if rS is const and rT isn't, then swap rT and rS */ - if (X86REG(rd) == X86REG(rt) || (ISCONST(rs) && !ISCONST(rt))) - { - mipsreg_state temp = rs; - rs = rt; - rt = temp; - } - - /* load rS into rD */ - emit_load_register_value(drc, compiler, X86REG(rd), rs); // mov <rd>,<rs> - - /* if rD does not match rT, we can work directly in rD */ - if (!ISCONST(rt)) - { - if (operation == 0) - emit_and_r64_r64(DRCTOP, X86REG(rd), X86REG(rt)); // and <rd>,<rt> - else if (operation == 1) - emit_or_r64_r64(DRCTOP, X86REG(rd), X86REG(rt)); // or <rd>,<rt> - else if (operation == 2) - emit_xor_r64_r64(DRCTOP, X86REG(rd), X86REG(rt)); // xor <rd>,<rt> - else if (operation == 3) - { - emit_or_r64_r64(DRCTOP, X86REG(rd), X86REG(rt)); // or <rd>,<rt> - emit_not_r64(DRCTOP, X86REG(rd)); // not <rd> - } - } - else - { - if (operation == 0) - emit_and_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // and <rd>,CONSTVAL(rt) - else if (operation == 1 && CONSTVAL(rt) != 0) - emit_or_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // or <rd>,CONSTVAL(rt) - else if (operation == 2 && CONSTVAL(rt) != 0) - emit_xor_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // xor <rd>,CONSTVAL(rt) - else if (operation == 3) - { - if (CONSTVAL(rt) != 0) - emit_or_r64_imm(DRCTOP, X86REG(rd), CONSTVAL(rt)); // or <rd>,CONSTVAL(rt) - emit_not_r64(DRCTOP, X86REG(rd)); // not <rd> - } - } - } - return TRUE; -} - - -/*------------------------------------------------- - compile_slt - compile a set if less - than btween two registers - - slti rD,rS,rT / slti rT,rS,SIMM - sltiu rD,rS,rT / sltiu rT,rS,SIMM --------------------------------------------------*/ - -static int compile_slt(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_unsigned, int immediate) -{ - UINT32 op = *desc->opptr.l; - UINT8 destreg = immediate ? RTREG : RDREG; - mipsreg_state rd, rs, rt; - - /* if the destination is r0, bail */ - if (destreg == 0) - return TRUE; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = immediate ? MAKE_CONST(SIMMVAL) : compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if the source is constant, compute as constant */ - if (ISCONST(rs) && ISCONST(rt)) - { - INT64 result = is_unsigned ? ((UINT32)CONSTVAL(rs) < (UINT32)CONSTVAL(rt)) : ((INT32)CONSTVAL(rs) < (INT32)CONSTVAL(rt)); - emit_set_constant_value(drc, compiler, desc, destreg, result); - } - - /* else do the operation in code */ - else - { - UINT8 rsreg = ISCONST(rs) ? REG_RAX : X86REG(rs); - - rd = compiler_register_allocate(drc, compiler, desc, destreg, ALLOC_DST | ALLOC_DIRTY); - - emit_load_register_value(drc, compiler, rsreg, rs); // mov <rsreg>,<rs> - if (ISCONST(rt)) - emit_cmp_r64_imm(DRCTOP, rsreg, CONSTVAL(rt)); // cmp <rsreg>,<rt> - else - emit_cmp_r64_r64(DRCTOP, rsreg, X86REG(rt)); // cmp <rsreg>,<rt> - emit_setcc_r8(DRCTOP, is_unsigned ? COND_B : COND_L, X86REG(rd)); // setb/l <rd> - emit_movzx_r32_r8(DRCTOP, X86REG(rd), X86REG(rd)); // movzx <rd>,<rd> - } - return TRUE; -} - - -/*------------------------------------------------- - compile_load - compile a memory load - instruction - - lb rT,SIMM(rS) - lbu rT,SIMM(rS) - lh rT,SIMM(rS) - lhu rT,SIMM(rS) - lw rT,SIMM(rS) - lwu rT,SIMM(rS) - ld rT,SIMM(rS) --------------------------------------------------*/ - -static int compile_load(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, int is_unsigned) -{ - UINT32 op = *desc->opptr.l; - mipsreg_state rs, rt; - int was_fixed = FALSE; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_DST); - - /* if rS is constant, we know the address at compile time; this simplifies the code */ - if (ISCONST(rs) && !DISABLE_FIXED_RAM_LOAD) - { - UINT8 destreg = (RTREG == 0) ? REG_RAX : X86REG(rt); - - /* perform a fixed read if we can */ - if (size == 1) - was_fixed = emit_fixed_byte_read(drc, compiler, desc, destreg, is_unsigned, CONSTVAL(rs) + SIMMVAL); - else if (size == 2) - was_fixed = emit_fixed_half_read(drc, compiler, desc, destreg, is_unsigned, CONSTVAL(rs) + SIMMVAL); - else if (size == 4) - was_fixed = emit_fixed_word_read(drc, compiler, desc, destreg, is_unsigned, CONSTVAL(rs) + SIMMVAL, 0xffffffff); - else - was_fixed = emit_fixed_double_read(drc, compiler, desc, destreg, CONSTVAL(rs) + SIMMVAL, U64(0xffffffffffffffff)); - } - - /* otherwise, we just need to call the appropriate subroutine */ - else - { - if (ISCONST(rs)) - emit_mov_r32_imm(DRCTOP, REG_P1, CONSTVAL(rs) + SIMMVAL); // mov p1,<rs> + SIMMVAL - else - emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), SIMMVAL)); // lea p1,[<rs> + SIMMVAL] - if (size == 1 && is_unsigned) - emit_call(DRCTOP, mips3.drcdata->general.read_byte_unsigned); // call general.read_byte_unsigned - else if (size == 1 && !is_unsigned) - emit_call(DRCTOP, mips3.drcdata->general.read_byte_signed); // call general.read_byte_signed - else if (size == 2 && is_unsigned) - emit_call(DRCTOP, mips3.drcdata->general.read_half_unsigned); // call general.read_half_unsigned - else if (size == 2 && !is_unsigned) - emit_call(DRCTOP, mips3.drcdata->general.read_half_signed); // call general.read_half_signed - else if (size == 4 && is_unsigned) - emit_call(DRCTOP, mips3.drcdata->general.read_word_unsigned); // call general.read_word_unsigned - else if (size == 4 && !is_unsigned) - emit_call(DRCTOP, mips3.drcdata->general.read_word_signed); // call general.read_word_signed - else - emit_call(DRCTOP, mips3.drcdata->general.read_double); // call general.read_double - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception); - if (RTREG != 0) - emit_mov_r64_r64(DRCTOP, X86REG(rt), REG_RAX); // mov <rt>,rax - } - - /* only mark dirty after the exception risk is over */ - compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_DIRTY); - - /* update cycle counts */ - if (!was_fixed) - emit_update_cycles_pc_and_flush(drc, compiler, desc, 0); - return TRUE; -} - - -/*------------------------------------------------- - compile_load_cop - compile a COP memory load - instruction - - lwc1 rT,SIMM(rs) - lwc2 rT,SIMM(rs) - ldc1 rT,SIMM(rs) - ldc2 rT,SIMM(rs) --------------------------------------------------*/ - -static int compile_load_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *dest) -{ - UINT32 op = *desc->opptr.l; - int was_fixed = FALSE; - mipsreg_state rs; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - - /* if rS is constant, we know the address at compile time; this simplifies the code */ - if (ISCONST(rs) && !DISABLE_FIXED_RAM_LOAD) - { - /* perform a fixed read if we can */ - if (size == 4) - was_fixed = emit_fixed_word_read(drc, compiler, desc, REG_RAX, FALSE, CONSTVAL(rs) + SIMMVAL, 0xffffffff); - else - was_fixed = emit_fixed_double_read(drc, compiler, desc, REG_RAX, CONSTVAL(rs) + SIMMVAL, U64(0xffffffffffffffff)); - } - - /* otherwise, we just need to call the appropriate subroutine */ - else - { - if (ISCONST(rs)) - emit_mov_r32_imm(DRCTOP, REG_P1, CONSTVAL(rs) + SIMMVAL); // mov p1,<rs> + SIMMVAL - else - emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), SIMMVAL)); // lea p1,[<rs> + SIMMVAL] - if (size == 4) - emit_call(DRCTOP, mips3.drcdata->general.read_word_unsigned); // call general.read_word_unsigned - else - emit_call(DRCTOP, mips3.drcdata->general.read_double); // call general.read_double - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception); - } - - /* complete the load by moving to the destination address */ - if (size == 4) - emit_mov_m32_r32(DRCTOP, MDRC(dest), REG_EAX); // mov [dest],eax - else - emit_mov_m64_r64(DRCTOP, MDRC(dest), REG_RAX); // mov [dest],rax - - /* update cycle counts */ - if (!was_fixed) - emit_update_cycles_pc_and_flush(drc, compiler, desc, 0); - return TRUE; -} - - -/*------------------------------------------------- - compile_loadx_cop - compile a COP indexed - memory load instruction - - lwxc1 fD,rS,rT - ldxc1 fD,rS,rT --------------------------------------------------*/ - -static int compile_loadx_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *dest) -{ - UINT32 op = *desc->opptr.l; - int was_fixed = FALSE; - mipsreg_state rs, rt; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if rS is constant, we know the address at compile time; this simplifies the code */ - if (ISCONST(rs) && ISCONST(rt) && !DISABLE_FIXED_RAM_LOAD) - { - /* perform a fixed read if we can */ - if (size == 4) - was_fixed = emit_fixed_word_read(drc, compiler, desc, REG_RAX, FALSE, CONSTVAL(rs) + CONSTVAL(rt), 0xffffffff); - else - was_fixed = emit_fixed_double_read(drc, compiler, desc, REG_RAX, CONSTVAL(rs) + CONSTVAL(rt), U64(0xffffffffffffffff)); - } - - /* otherwise, we just need to call the appropriate subroutine */ - else - { - if (ISCONST(rs) && ISCONST(rt)) - emit_mov_r32_imm(DRCTOP, REG_P1, CONSTVAL(rs) + CONSTVAL(rt)); // mov p1,<rs> + <rt> - else if (ISCONST(rs)) - emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rt), CONSTVAL(rs))); // lea p1,[<rt> + <rs>] - else if (ISCONST(rt)) - emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), CONSTVAL(rt))); // lea p1,[<rs> + <rt>] - else - emit_lea_r32_m32(DRCTOP, REG_P1, MBISD(X86REG(rs), X86REG(rt), 1, 0)); // lea p1,[<rs> + <rt>] - if (size == 4) - emit_call(DRCTOP, mips3.drcdata->general.read_word_unsigned); // call general.read_word_unsigned - else - emit_call(DRCTOP, mips3.drcdata->general.read_double); // call general.read_double - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception); - } - - /* complete the load by moving to the destination address */ - if (size == 4) - emit_mov_m32_r32(DRCTOP, MDRC(dest), REG_EAX); // mov [dest],eax - else - emit_mov_m64_r64(DRCTOP, MDRC(dest), REG_RAX); // mov [dest],rax - - /* update cycle counts */ - if (!was_fixed) - emit_update_cycles_pc_and_flush(drc, compiler, desc, 0); - return TRUE; -} - - -/*------------------------------------------------- - compile_load_word_partial - compile a lwl/lwr - instruction - - lwl rT,SIMM(rS) - lwr rT,SIMM(rS) --------------------------------------------------*/ - -static int compile_load_word_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right) -{ - UINT32 op = *desc->opptr.l; - int was_fixed = FALSE; - mipsreg_state rs, rt; - - /* allocate source/dest registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC | ALLOC_DST | ALLOC_DIRTY); - - /* if rS is constant, we know the address at compile time; this simplifies the code */ - if (ISCONST(rs) && !DISABLE_FIXED_RAM_LOAD) - { - INT32 addr = CONSTVAL(rs) + SIMMVAL; - int shift = (addr & 3) * 8; - - /* shift the opposite amount depending on right/left and endianness */ - if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right)) - shift ^= 0x18; - - /* read the double value into rax, handling exceptions */ - was_fixed = emit_fixed_word_read(drc, compiler, desc, REG_RAX, FALSE, addr, is_right ? ((UINT32)-1 << shift) : ((UINT32)-1 >> shift)); - - /* handle writing back the register */ - if (RTREG != 0) - { - UINT32 mask = is_right ? (~((UINT32)-1 >> shift)) : (~((UINT32)-1 << shift)); - - /* AND rT with the mask */ - emit_and_r32_imm(DRCTOP, X86REG(rt), mask); // and <rt>,mask - - /* shift and merge rax into the X86REG(rt) */ - if (!is_right) - emit_shl_r32_imm(DRCTOP, REG_EAX, shift); // shl eax,shift - else - emit_shr_r32_imm(DRCTOP, REG_EAX, shift); // shr eax,shift - emit_or_r32_r32(DRCTOP, X86REG(rt), REG_EAX); // or <rtdst>,eax - emit_movsxd_r64_r32(DRCTOP, X86REG(rt), X86REG(rt)); // movsxd <rt>,<rt> - } - } - - /* otherwise, we need to compute everything at runtime */ - else - { - /* compute the address and mask values */ - if (ISCONST(rs)) - emit_mov_r32_imm(DRCTOP, REG_EAX, CONSTVAL(rs) + SIMMVAL); // mov eax,<rs> + SIMMVAL - else - emit_lea_r32_m32(DRCTOP, REG_EAX, MBD(X86REG(rs), SIMMVAL)); // lea eax,[<rs> + SIMMVAL] - emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_RAX, 8, 0)); // lea ecx,[rax*8] - emit_or_r32_imm(DRCTOP, REG_P2, -1); // or p2,-1 - if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right)) - emit_xor_r32_imm(DRCTOP, REG_ECX, 0x18); // xor ecx,0x18 - emit_and_r32_imm(DRCTOP, REG_EAX, ~3); // and eax,~3 - if (!is_right) - emit_shr_r32_cl(DRCTOP, REG_P2); // shr p2,cl - else - emit_shl_r32_cl(DRCTOP, REG_P2); // shl p2,cl - emit_mov_m8_r8(DRCTOP, MBD(REG_RSP, SPOFFS_SAVECL), REG_CL); // mov [sp+savecl],cl - emit_mov_r32_r32(DRCTOP, REG_P1, REG_EAX); // mov p1,eax - emit_call(DRCTOP, mips3.drcdata->general.read_word_masked); // call general.read_word_masked - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception); - - /* handle writing back the register */ - if (RTREG != 0) - { - /* recompute the mask */ - emit_mov_r8_m8(DRCTOP, REG_CL, MBD(REG_RSP, SPOFFS_SAVECL)); // mov cl,[sp+savecl] - emit_or_r32_imm(DRCTOP, REG_P2, -1); // or p2,-1 - if (!is_right) - emit_shl_r32_cl(DRCTOP, REG_P2); // shl p2,cl - else - emit_shr_r32_cl(DRCTOP, REG_P2); // shr p2,cl - - /* apply the shifts and masks */ - emit_not_r32(DRCTOP, REG_P2); // not p2 - if (!is_right) - emit_shl_r32_cl(DRCTOP, REG_RAX); // shl rax,cl - else - emit_shr_r32_cl(DRCTOP, REG_RAX); // shr rax,cl - emit_and_r32_r32(DRCTOP, X86REG(rt), REG_P2); // and <rt>,p2 - emit_or_r32_r32(DRCTOP, X86REG(rt), REG_RAX); // or <rt>,rax - emit_movsxd_r64_r32(DRCTOP, X86REG(rt), X86REG(rt)); // movsxd <rt>,<rt> - } - } - - /* update cycle counts */ - if (!was_fixed) - emit_update_cycles_pc_and_flush(drc, compiler, desc, 0); - return TRUE; -} - - -/*------------------------------------------------- - compile_load_double_partial - compile a - ldl/ldr instruction - - ldl rT,SIMM(rS) - ldr rT,SIMM(rS) --------------------------------------------------*/ - -static int compile_load_double_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right) -{ - UINT32 op = *desc->opptr.l; - int was_fixed = FALSE; - mipsreg_state rs, rt; - - /* allocate source/dest registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC | ALLOC_DST | ALLOC_DIRTY); - - /* if rS is constant, we know the address at compile time; this simplifies the code */ - if (ISCONST(rs) && !DISABLE_FIXED_RAM_LOAD) - { - INT32 addr = CONSTVAL(rs) + SIMMVAL; - int shift = (addr & 7) * 8; - - /* shift the opposite amount depending on right/left and endianness */ - if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right)) - shift ^= 0x38; - - /* read the double value into rax, handling exceptions */ - was_fixed = emit_fixed_double_read(drc, compiler, desc, REG_RAX, addr, is_right ? ((UINT64)-1 << shift) : ((UINT64)-1 >> shift)); - - /* handle writing back the register */ - if (RTREG != 0) - { - UINT64 mask = is_right ? (~((UINT64)-1 >> shift)) : (~((UINT64)-1 << shift)); - - /* AND rT with the mask, using 32 bits if we can */ - if ((INT32)mask == (INT64)mask) - emit_and_r64_imm(DRCTOP, X86REG(rt), (INT32)mask); // and <rt>,mask - else - { - emit_mov_r64_imm(DRCTOP, REG_P2, mask); // mov p2,mask - emit_and_r64_r64(DRCTOP, X86REG(rt), REG_P2); // and <rt>,p2 - } - - /* shift and merge rax into the X86REG(rt) */ - if (!is_right) - emit_shl_r64_imm(DRCTOP, REG_RAX, shift); // shl rax,shift - else - emit_shr_r64_imm(DRCTOP, REG_RAX, shift); // shr rax,shift - emit_or_r64_r64(DRCTOP, X86REG(rt), REG_RAX); // or <rt>,rax - } - } - - /* otherwise, we need to compute everything at runtime */ - else - { - /* compute the address and mask values */ - if (ISCONST(rs)) - emit_mov_r32_imm(DRCTOP, REG_EAX, CONSTVAL(rs) + SIMMVAL); // mov eax,<rs> + SIMMVAL - else - emit_lea_r32_m32(DRCTOP, REG_EAX, MBD(X86REG(rs), SIMMVAL)); // lea eax,[<rs> + SIMMVAL] - emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_RAX, 8, 0)); // lea ecx,[rax*8] - emit_or_r64_imm(DRCTOP, REG_P2, -1); // or p2,-1 - if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right)) - emit_xor_r32_imm(DRCTOP, REG_ECX, 0x38); // xor ecx,0x38 - emit_and_r32_imm(DRCTOP, REG_EAX, ~7); // and eax,~7 - if (!is_right) - emit_shr_r64_cl(DRCTOP, REG_P2); // shr p2,cl - else - emit_shl_r64_cl(DRCTOP, REG_P2); // shl p2,cl - emit_mov_m8_r8(DRCTOP, MBD(REG_RSP, SPOFFS_SAVECL), REG_CL); // mov [sp+savecl],cl - emit_mov_r32_r32(DRCTOP, REG_P1, REG_EAX); // mov p1,eax - emit_call(DRCTOP, mips3.drcdata->general.read_double_masked); // call general.read_double_masked - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbload_exception); - - /* handle writing back the register */ - if (RTREG != 0) - { - /* recompute the mask */ - emit_mov_r8_m8(DRCTOP, REG_CL, MBD(REG_RSP, SPOFFS_SAVECL)); // mov cl,[sp+savecl] - emit_or_r64_imm(DRCTOP, REG_P2, -1); // or p2,-1 - if (!is_right) - emit_shl_r64_cl(DRCTOP, REG_P2); // shl p2,cl - else - emit_shr_r64_cl(DRCTOP, REG_P2); // shr p2,cl - - /* apply the shifts and masks */ - emit_not_r64(DRCTOP, REG_P2); // not p2 - if (!is_right) - emit_shl_r64_cl(DRCTOP, REG_RAX); // shl rax,cl - else - emit_shr_r64_cl(DRCTOP, REG_RAX); // shr rax,cl - emit_and_r64_r64(DRCTOP, X86REG(rt), REG_P2); // and <rt>,p2 - emit_or_r64_r64(DRCTOP, X86REG(rt), REG_RAX); // or <rt>,rax - } - } - - /* update cycle counts */ - if (!was_fixed) - emit_update_cycles_pc_and_flush(drc, compiler, desc, 0); - return TRUE; -} - - -/*------------------------------------------------- - compile_store - compile a memory store - instruction - - sb rT,SIMM(rS) - sh rT,SIMM(rS) - sw rT,SIMM(rS) - sd rT,SIMM(rS) --------------------------------------------------*/ - -static int compile_store(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size) -{ - UINT32 op = *desc->opptr.l; - int was_fixed = FALSE; - mipsreg_state rs, rt; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if rS is constant, we know the address at compile time; this simplifies the code */ - if (ISCONST(rs) && !DISABLE_FIXED_RAM_STORE) - { - /* perform a fixed write if we can */ - if (size == 1) - was_fixed = emit_fixed_byte_write(drc, compiler, desc, rt, CONSTVAL(rs) + SIMMVAL); - else if (size == 2) - was_fixed = emit_fixed_half_write(drc, compiler, desc, rt, CONSTVAL(rs) + SIMMVAL); - else if (size == 4) - was_fixed = emit_fixed_word_write(drc, compiler, desc, rt, CONSTVAL(rs) + SIMMVAL, 0xffffffff); - else - was_fixed = emit_fixed_double_write(drc, compiler, desc, rt, CONSTVAL(rs) + SIMMVAL, U64(0xffffffffffffffff)); - } - - /* otherwise, we just need to call the appropriate subroutine */ - else - { - if (ISCONST(rs)) - emit_mov_r32_imm(DRCTOP, REG_P1, CONSTVAL(rs) + SIMMVAL); // mov p1,<rs> + SIMMVAL - else - emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), SIMMVAL)); // lea p1,[<rs> + SIMMVAL] - emit_load_register_value(drc, compiler, REG_P2, rt); // mov p2,<rt> - if (size == 1) - emit_call(DRCTOP, mips3.drcdata->general.write_byte); // call general.write_byte - else if (size == 2) - emit_call(DRCTOP, mips3.drcdata->general.write_half); // call general.write_half - else if (size == 4) - emit_call(DRCTOP, mips3.drcdata->general.write_word); // call general.write_word - else if (size == 8) - emit_call(DRCTOP, mips3.drcdata->general.write_double); // call general.write_double - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception); - } - - /* update cycle counts */ - if (!was_fixed) - emit_update_cycles_pc_and_flush(drc, compiler, desc, 0); - return TRUE; -} - - -/*------------------------------------------------- - compile_store_cop - compile a COP memory - store instruction - - swc1 rT,SIMM(rs) - swc2 rT,SIMM(rs) - sdc1 rT,SIMM(rs) - sdc2 rT,SIMM(rs) --------------------------------------------------*/ - -static int compile_store_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *src) -{ - UINT32 op = *desc->opptr.l; - int was_fixed = FALSE; - mipsreg_state rs; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - - /* load the value into P2 */ - if (size == 4) - emit_mov_r32_m32(DRCTOP, REG_P2, MDRC(src)); // mov p2,[src] - else - emit_mov_r64_m64(DRCTOP, REG_P2, MDRC(src)); // mov p2,[src] - - /* if rS is constant, we know the address at compile time; this simplifies the code */ - if (ISCONST(rs) && !DISABLE_FIXED_RAM_STORE) - { - /* perform a fixed write if we can */ - if (size == 4) - was_fixed = emit_fixed_word_write(drc, compiler, desc, REG_P2, CONSTVAL(rs) + SIMMVAL, 0xffffffff); - else - was_fixed = emit_fixed_double_write(drc, compiler, desc, REG_P2, CONSTVAL(rs) + SIMMVAL, U64(0xffffffffffffffff)); - } - - /* otherwise, we just need to call the appropriate subroutine */ - else - { - if (ISCONST(rs)) - emit_mov_r32_imm(DRCTOP, REG_P1, CONSTVAL(rs) + SIMMVAL); // mov p1,<rs> + SIMMVAL - else - emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), SIMMVAL)); // lea p1,[<rs> + SIMMVAL] - if (size == 4) - emit_call(DRCTOP, mips3.drcdata->general.write_word); // call general.write_word - else if (size == 8) - emit_call(DRCTOP, mips3.drcdata->general.write_double); // call general.write_double - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception); - } - - /* update cycle counts */ - if (!was_fixed) - emit_update_cycles_pc_and_flush(drc, compiler, desc, 0); - return TRUE; -} - - -/*------------------------------------------------- - compile_storex_cop - compile a COP indexed - memory store instruction - - swxc1 fD,rS,rT - sdxc1 fD,rS,rT --------------------------------------------------*/ - -static int compile_storex_cop(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int size, void *src) -{ - UINT32 op = *desc->opptr.l; - int was_fixed = FALSE; - mipsreg_state rs, rt; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* load the value into P2 */ - if (size == 4) - emit_mov_r32_m32(DRCTOP, REG_P2, MDRC(src)); // mov p2,[src] - else - emit_mov_r64_m64(DRCTOP, REG_P2, MDRC(src)); // mov p2,[src] - - /* if rS is constant, we know the address at compile time; this simplifies the code */ - if (ISCONST(rs) && ISCONST(rt) && !DISABLE_FIXED_RAM_STORE) - { - /* perform a fixed write if we can */ - if (size == 4) - was_fixed = emit_fixed_word_write(drc, compiler, desc, REG_P2, CONSTVAL(rs) + CONSTVAL(rt), 0xffffffff); - else - was_fixed = emit_fixed_double_write(drc, compiler, desc, REG_P2, CONSTVAL(rs) + CONSTVAL(rt), U64(0xffffffffffffffff)); - } - - /* otherwise, we just need to call the appropriate subroutine */ - else - { - if (ISCONST(rs) && ISCONST(rt)) - emit_mov_r32_imm(DRCTOP, REG_P1, CONSTVAL(rs) + CONSTVAL(rt)); // mov p1,<rs> + <rt> - else if (ISCONST(rs)) - emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rt), CONSTVAL(rs))); // lea p1,[<rt> + <rs>] - else if (ISCONST(rt)) - emit_lea_r32_m32(DRCTOP, REG_P1, MBD(X86REG(rs), CONSTVAL(rt))); // lea p1,[<rs> + <rt>] - else - emit_lea_r32_m32(DRCTOP, REG_P1, MBISD(X86REG(rs), X86REG(rt), 1, 0)); // lea p1,[<rs> + <rt>] - if (size == 4) - emit_call(DRCTOP, mips3.drcdata->general.write_word); // call general.write_word - else if (size == 8) - emit_call(DRCTOP, mips3.drcdata->general.write_double); // call general.write_double - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception); - } - - /* update cycle counts */ - if (!was_fixed) - emit_update_cycles_pc_and_flush(drc, compiler, desc, 0); - return TRUE; -} - - -/*------------------------------------------------- - compile_store_word_partial - compile a swl/swr - instruction - - swl rT,SIMM(rS) - swr rT,SIMM(rS) --------------------------------------------------*/ - -static int compile_store_word_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right) -{ - UINT32 op = *desc->opptr.l; - int was_fixed = FALSE; - mipsreg_state rs, rt; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if rS is constant, we know the address at compile time; this simplifies the code */ - if (ISCONST(rs) && !DISABLE_FIXED_RAM_STORE) - { - INT32 addr = CONSTVAL(rs) + SIMMVAL; - int shift = (addr & 3) * 8; - UINT32 mask; - - /* shift the opposite amount depending on right/left and endianness */ - if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right)) - shift ^= 0x18; - - /* if rT is not constant, load and shift it */ - if (!ISCONST(rt)) - { - emit_mov_r32_r32(DRCTOP, REG_EAX, X86REG(rt)); // mov eax,X86REG(rt) - emit_shr_r32_imm(DRCTOP, REG_EAX, shift); // shr eax,shift - rt = MAKE_DESTREG(REG_EAX); - } - - /* compute shifted value and mask */ - if (!is_right) - { - if (ISCONST(rt)) - rt = MAKE_CONST((UINT32)CONSTVAL(rt) >> shift); - mask = (UINT32)-1 >> shift; - } - else - { - if (ISCONST(rt)) - rt = MAKE_CONST((UINT32)CONSTVAL(rt) << shift); - mask = (UINT32)-1 << shift; - } - - /* perform a fixed write */ - was_fixed = emit_fixed_word_write(drc, compiler, desc, rt, addr & ~3, mask); - } - - /* otherwise, we need to compute everything at runtime */ - else - { - /* compute the address and mask values */ - emit_lea_r32_m32(DRCTOP, REG_EAX, MBD(X86REG(rs), SIMMVAL)); // lea eax,[<rs> + SIMMVAL] - emit_or_r32_imm(DRCTOP, REG_P3, -1); // or p3,-1 - emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_RAX, 8, 0)); // lea ecx,[rax*8] - emit_load_register_value(drc, compiler, REG_P2, rt); // mov p2,<rt> - if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right)) - emit_xor_r32_imm(DRCTOP, REG_ECX, 0x18); // xor ecx,0x18 - emit_and_r32_imm(DRCTOP, REG_EAX, ~3); // and eax,~3 - if (!is_right) - { - emit_shr_r32_cl(DRCTOP, REG_P3); // shr p3,cl - emit_shr_r32_cl(DRCTOP, REG_P2); // shr p2,cl - } - else - { - emit_shl_r32_cl(DRCTOP, REG_P3); // shl p3,cl - emit_shl_r32_cl(DRCTOP, REG_P2); // shl p2,cl - } - emit_mov_r32_r32(DRCTOP, REG_P1, REG_EAX); // mov p1,eax - - /* perform the write */ - emit_call(DRCTOP, mips3.drcdata->general.write_word_masked); // call general.write_word_masked - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception); - } - - /* update cycle counts */ - if (!was_fixed) - emit_update_cycles_pc_and_flush(drc, compiler, desc, 0); - return TRUE; -} - - -/*------------------------------------------------- - compile_store_double_partial - compile a - sdl/sdr instruction - - sdl rT,SIMM(rS) - sdr rT,SIMM(rS) --------------------------------------------------*/ - -static int compile_store_double_partial(drc_core *drc, compiler_state *compiler, const opcode_desc *desc, int is_right) -{ - UINT32 op = *desc->opptr.l; - int was_fixed = FALSE; - mipsreg_state rs, rt; - - /* allocate source registers */ - rs = compiler_register_allocate(drc, compiler, desc, RSREG, ALLOC_SRC); - rt = compiler_register_allocate(drc, compiler, desc, RTREG, ALLOC_SRC); - - /* if rS is constant, we know the address at compile time; this simplifies the code */ - if (ISCONST(rs) && !DISABLE_FIXED_RAM_STORE) - { - INT32 addr = CONSTVAL(rs) + SIMMVAL; - int shift = (addr & 7) * 8; - UINT64 mask; - - /* shift the opposite amount depending on right/left and endianness */ - if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right)) - shift ^= 0x38; - - /* if rT is not constant, load and shift it */ - if (!ISCONST(rt)) - { - emit_mov_r64_r64(DRCTOP, REG_RAX, X86REG(rt)); // mov rax,X86REG(rt) - emit_shr_r64_imm(DRCTOP, REG_RAX, shift); // shr rax,shift - rt = MAKE_DESTREG(REG_RAX); - } - - /* compute shifted value and mask */ - if (!is_right) - { - if (ISCONST(rt)) - rt = MAKE_CONST((UINT64)CONSTVAL(rt) >> shift); - mask = (UINT64)-1 >> shift; - } - else - { - if (ISCONST(rt)) - rt = MAKE_CONST((UINT64)CONSTVAL(rt) << shift); - mask = (UINT64)-1 << shift; - } - - /* perform a fixed write */ - was_fixed = emit_fixed_double_write(drc, compiler, desc, rt, addr & ~7, mask); - } - - /* otherwise, we need to compute everything at runtime */ - else - { - /* compute the address and mask values */ - emit_lea_r32_m32(DRCTOP, REG_EAX, MBD(X86REG(rs), SIMMVAL)); // lea eax,[<rs> + SIMMVAL] - emit_or_r64_imm(DRCTOP, REG_P3, -1); // or p3,-1 - emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_RAX, 8, 0)); // lea ecx,[rax*8] - emit_load_register_value(drc, compiler, REG_P2, rt); // mov p2,<rt> - if ((!mips3.core->bigendian && !is_right) || (mips3.core->bigendian && is_right)) - emit_xor_r32_imm(DRCTOP, REG_ECX, 0x38); // xor ecx,0x38 - emit_and_r32_imm(DRCTOP, REG_EAX, ~7); // and eax,~7 - if (!is_right) - { - emit_shr_r64_cl(DRCTOP, REG_P3); // shr p3,cl - emit_shr_r64_cl(DRCTOP, REG_P2); // shr p2,cl - } - else - { - emit_shl_r64_cl(DRCTOP, REG_P3); // shl p3,cl - emit_shl_r64_cl(DRCTOP, REG_P2); // shl p2,cl - } - emit_mov_r32_r32(DRCTOP, REG_P1, REG_EAX); // mov p1,eax - - /* perform the write */ - emit_call(DRCTOP, mips3.drcdata->general.write_double_masked); // call general.write_double_masked - oob_request_callback(drc, COND_NO_JUMP, oob_exception_cleanup, compiler, desc, mips3.drcdata->generate_tlbstore_exception); - } - - /* update cycle counts */ - if (!was_fixed) - emit_update_cycles_pc_and_flush(drc, compiler, desc, 0); - return TRUE; -} diff --git a/src/emu/cpu/mips/mdrcold.c b/src/emu/cpu/mips/mdrcold.c deleted file mode 100644 index e00aa1eece8..00000000000 --- a/src/emu/cpu/mips/mdrcold.c +++ /dev/null @@ -1,4872 +0,0 @@ - - -/*************************************************************************** - CONFIGURATION -***************************************************************************/ - -#define STRIP_NOPS 1 /* 1 is faster */ -#define USE_SSE 0 /* can't tell any speed difference here */ - - - -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -#define MAX_INSTRUCTIONS ((512 + 128) / 4) - -/* recompiler flags */ -#define RECOMPILE_UNIMPLEMENTED 0x0000 -#define RECOMPILE_SUCCESSFUL 0x0001 -#define RECOMPILE_SUCCESSFUL_CP(c,p) (RECOMPILE_SUCCESSFUL | (((c) & 0xfff) << 8) | (((p) & 0xfff) << 20)) -#define RECOMPILE_MAY_CAUSE_EXCEPTION 0x0002 -#define RECOMPILE_END_OF_STRING 0x0004 -#define RECOMPILE_CHECK_INTERRUPTS 0x0008 -#define RECOMPILE_CHECK_SW_INTERRUPTS 0x0010 -#define RECOMPILE_ADD_DISPATCH 0x0020 - -/* append_readwrite flags */ -#define ARW_READ 0x0000 -#define ARW_WRITE 0x0001 -#define ARW_UNSIGNED 0x0000 -#define ARW_SIGNED 0x0002 -#define ARW_MASKED 0x0004 - - - -/*************************************************************************** - MACROS -***************************************************************************/ - -#define REGDISP(reg) (offsetof(mips3_state, r[reg]) - offsetof(mips3_state, r[17])) -#define REGADDR(reg) MBD(REG_ESI, REGDISP(reg)) -#define REGADDRLO(reg) MBD(REG_ESI, REGDISP(reg)) -#define REGADDRHI(reg) MBD(REG_ESI, REGDISP(reg)+4) - -#define HIADDR REGADDR(REG_HI) -#define HIADDRLO REGADDRLO(REG_HI) -#define HIADDRHI REGADDRHI(REG_HI) - -#define LOADDR REGADDR(REG_LO) -#define LOADDRLO REGADDRLO(REG_LO) -#define LOADDRHI REGADDRHI(REG_LO) - -#define CPR0ADDR(reg) MABS((UINT8 *)&mips3.core->cpr[0][reg]) -#define CPR0ADDRLO(reg) MABS((UINT8 *)&mips3.core->cpr[0][reg]) -#define CPR0ADDRHI(reg) MABS((UINT8 *)&mips3.core->cpr[0][reg] + 4) - -#define CCR0ADDR(reg) MABS((UINT8 *)&mips3.core->ccr[0][reg]) -#define CCR0ADDRLO(reg) MABS((UINT8 *)&mips3.core->ccr[0][reg]) -#define CCR0ADDRHI(reg) MABS((UINT8 *)&mips3.core->ccr[0][reg] + 4) - -#define CPR1ADDR(reg) MABS((UINT8 *)&mips3.core->cpr[1][reg]) -#define CPR1ADDRLO(reg) MABS((UINT8 *)&mips3.core->cpr[1][reg]) -#define CPR1ADDRHI(reg) MABS((UINT8 *)&mips3.core->cpr[1][reg] + 4) - -#define CCR1ADDR(reg) MABS((UINT8 *)&mips3.core->ccr[1][reg]) -#define CCR1ADDRLO(reg) MABS((UINT8 *)&mips3.core->ccr[1][reg]) -#define CCR1ADDRHI(reg) MABS((UINT8 *)&mips3.core->ccr[1][reg] + 4) - -#define CPR2ADDR(reg) MABS((UINT8 *)&mips3.core->cpr[2][reg]) -#define CPR2ADDRLO(reg) MABS((UINT8 *)&mips3.core->cpr[2][reg]) -#define CPR2ADDRHI(reg) MABS((UINT8 *)&mips3.core->cpr[2][reg] + 4) - -#define CCR2ADDR(reg) MABS((UINT8 *)&mips3.core->ccr[2][reg]) -#define CCR2ADDRLO(reg) MABS((UINT8 *)&mips3.core->ccr[2][reg]) -#define CCR2ADDRHI(reg) MABS((UINT8 *)&mips3.core->ccr[2][reg] + 4) - -#define FPR32(reg) (IS_FR0 ? &((float *)&mips3.core->cpr[1][0])[reg] : (float *)&mips3.core->cpr[1][reg]) -#define FPR32ADDR(reg) MABS(FPR32(reg)) -#define FPR32ADDRLO(reg) MABS(FPR32(reg)) -#define FPR32ADDRHI(reg) MABS((UINT8 *)FPR32(reg) + 4) - -#define FPR64(reg) (IS_FR0 ? (double *)&mips3.core->cpr[1][(reg)/2] : (double *)&mips3.core->cpr[1][reg]) -#define FPR64ADDR(reg) MABS(FPR64(reg)) -#define FPR64ADDR(reg) MABS(FPR64(reg)) -#define FPR64ADDRLO(reg) MABS(FPR64(reg)) -#define FPR64ADDRHI(reg) MABS((UINT8 *)FPR64(reg) + 4) - -#define CF1ADDR(which) MABS(&mips3.core->cf[1][(mips3.core->flavor < MIPS3_TYPE_MIPS_IV) ? 0 : (which)]) - -#define ICOUNTADDR MABS(&mips3.core->icount) - - -/*************************************************************************** - USEFUL PRIMITIVES -***************************************************************************/ - -INLINE void emit_mov_r64_m64(x86code **emitptr, UINT8 reghi, UINT8 reglo, DECLARE_MEMPARAMS) -{ - emit_mov_r32_m32(emitptr, reglo, MEMPARAMS); - emit_mov_r32_m32(emitptr, reghi, base, index, scale, disp+4); -} - -INLINE void emit_mov_m64_r64(x86code **emitptr, DECLARE_MEMPARAMS, UINT8 reghi, UINT8 reglo) -{ - emit_mov_m32_r32(emitptr, MEMPARAMS, reglo); - emit_mov_m32_r32(emitptr, base, index, scale, disp+4, reghi); -} - -INLINE void emit_mov_m64_imm32(x86code **emitptr, DECLARE_MEMPARAMS, INT32 imm) -{ - emit_mov_m32_imm(emitptr, MEMPARAMS, imm); - emit_mov_m32_imm(emitptr, base, index, scale, disp+4, (INT32)imm >> 31); -} - -INLINE void emit_zero_m64(x86code **emitptr, DECLARE_MEMPARAMS) -{ - if (USE_SSE) - { - emit_xorpd_r128_r128(emitptr, REG_XMM0, REG_XMM0); - emit_movsd_m64_r128(emitptr, MEMPARAMS, REG_XMM0); - } - else - emit_mov_m64_imm32(emitptr, MEMPARAMS, 0); -} - -INLINE void emit_mov_m64_m64(x86code **emitptr, DECLARE_MEMPARAMS, UINT8 base2, UINT8 index2, UINT8 scale2, INT32 disp2) -{ - if (USE_SSE) - { - emit_movsd_r128_m64(emitptr, REG_XMM0, base2, index2, scale2, disp2); - emit_movsd_m64_r128(emitptr, MEMPARAMS, REG_XMM0); - } - else - { - emit_mov_r64_m64(emitptr, REG_EDX, REG_EAX, base2, index2, scale2, disp2); - emit_mov_m64_r64(emitptr, MEMPARAMS, REG_EDX, REG_EAX); - } -} - -#define emit_save_pc_before_call(e) \ -do { \ - if (mips3.drcoptions & MIPS3DRC_FLUSH_PC) \ - emit_mov_m32_r32(e, MABS(drc->pcptr), REG_EDI); \ -} while (0) - - - - -/*************************************************************************** - TYPEDEFS -***************************************************************************/ - -struct _mips3drc_data -{ - x86code * generate_interrupt_exception; - x86code * generate_cop_exception; - x86code * generate_overflow_exception; - x86code * generate_invalidop_exception; - x86code * generate_syscall_exception; - x86code * generate_break_exception; - x86code * generate_trap_exception; - x86code * generate_tlbload_exception; - x86code * generate_tlbstore_exception; - x86code * handle_pc_tlb_mismatch; - x86code * read_and_translate_byte_signed; - x86code * read_and_translate_byte_unsigned; - x86code * read_and_translate_half_signed; - x86code * read_and_translate_half_unsigned; - x86code * read_and_translate_word; - x86code * read_and_translate_word_masked; - x86code * read_and_translate_double; - x86code * read_and_translate_double_masked; - x86code * write_and_translate_byte; - x86code * write_and_translate_half; - x86code * write_and_translate_word; - x86code * write_and_translate_word_masked; - x86code * write_and_translate_double; - x86code * write_and_translate_double_masked; -}; - - - -/*************************************************************************** - FUNCTION PROTOTYPES -***************************************************************************/ - -static void drc_reset_callback(drc_core *drc); -static void drc_recompile_callback(drc_core *drc); -static void drc_entrygen_callback(drc_core *drc); - -static UINT32 compile_one(drc_core *drc, UINT32 pc, UINT32 physpc); - -static void append_generate_exception(drc_core *drc, UINT8 exception); -static void append_readwrite_and_translate(drc_core *drc, int size, UINT8 flags); -static void append_tlb_verify(drc_core *drc, UINT32 pc, void *target); -static void append_update_cycle_counting(drc_core *drc); -static void append_check_interrupts(drc_core *drc, int inline_generate); -static void append_check_sw_interrupts(drc_core *drc, int inline_generate); - -static UINT32 recompile_instruction(drc_core *drc, UINT32 pc, UINT32 physpc); -static UINT32 recompile_special(drc_core *drc, UINT32 pc, UINT32 op); -static UINT32 recompile_regimm(drc_core *drc, UINT32 pc, UINT32 op); - -static UINT32 recompile_cop0(drc_core *drc, UINT32 pc, UINT32 op); -static UINT32 recompile_cop1(drc_core *drc, UINT32 pc, UINT32 op); -static UINT32 recompile_cop1x(drc_core *drc, UINT32 pc, UINT32 op); - - - -/*************************************************************************** - PRIVATE GLOBAL VARIABLES -***************************************************************************/ - -static UINT64 dmult_temp1; -static UINT64 dmult_temp2; -static UINT32 jr_temp; - -static UINT8 in_delay_slot = 0; - - - -/*************************************************************************** - CORE RECOMPILER SYSTEMS -***************************************************************************/ - -/*------------------------------------------------- - mips3drc_init - initialize the drc-specific - state --------------------------------------------------*/ - -static void mips3drc_init(void) -{ - drc_config drconfig; - - /* fill in the config */ - memset(&drconfig, 0, sizeof(drconfig)); - drconfig.cache_base = mips3.cache; - drconfig.cache_size = CACHE_SIZE; - drconfig.max_instructions = MAX_INSTRUCTIONS; - drconfig.address_bits = 32; - drconfig.lsbs_to_ignore = 2; - drconfig.uses_fp = 1; - drconfig.uses_sse = USE_SSE; - drconfig.pc_in_memory = 0; - drconfig.icount_in_memory = 0; - drconfig.pcptr = (UINT32 *)&mips3.core->pc; - drconfig.icountptr = (UINT32 *)&mips3.core->icount; - drconfig.esiptr = NULL; - drconfig.cb_reset = drc_reset_callback; - drconfig.cb_recompile = drc_recompile_callback; - drconfig.cb_entrygen = drc_entrygen_callback; - - /* initialize the compiler */ - mips3.drc = drc_init(cpu_getactivecpu(), &drconfig); - mips3.drcoptions = MIPS3DRC_FASTEST_OPTIONS; - - /* allocate our data out of the cache */ - mips3.drcdata = drc_alloc(mips3.drc, sizeof(*mips3.drcdata)); - memset(mips3.drcdata, 0, sizeof(*mips3.drcdata)); -} - - -/*------------------------------------------------- - mips3drc_exit - clean up the drc-specific - state --------------------------------------------------*/ - -static void mips3drc_exit(void) -{ - drc_exit(mips3.drc); -} - - - -/*************************************************************************** - RECOMPILER CALLBACKS -***************************************************************************/ - -/*------------------------------------------------------------------ - drc_reset_callback -------------------------------------------------------------------*/ - -static void drc_reset_callback(drc_core *drc) -{ - if (LOG_CODE) - { - x86log_disasm_code_range(mips3.log, "entry_point:", (x86code *)drc->entry_point, drc->out_of_cycles); - x86log_disasm_code_range(mips3.log, "out_of_cycles:", drc->out_of_cycles, drc->recompile); - x86log_disasm_code_range(mips3.log, "recompile:", drc->recompile, drc->dispatch); - x86log_disasm_code_range(mips3.log, "dispatch:", drc->dispatch, drc->flush); - x86log_disasm_code_range(mips3.log, "flush:", drc->flush, drc->cache_top); - } - - mips3.drcdata->generate_interrupt_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_INTERRUPT); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_interrupt_exception:", mips3.drcdata->generate_interrupt_exception, drc->cache_top); - - mips3.drcdata->generate_cop_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_BADCOP); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_cop_exception:", mips3.drcdata->generate_cop_exception, drc->cache_top); - - mips3.drcdata->generate_overflow_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_OVERFLOW); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_overflow_exception:", mips3.drcdata->generate_overflow_exception, drc->cache_top); - - mips3.drcdata->generate_invalidop_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_INVALIDOP); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_invalidop_exception:", mips3.drcdata->generate_invalidop_exception, drc->cache_top); - - mips3.drcdata->generate_syscall_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_SYSCALL); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_syscall_exception:", mips3.drcdata->generate_syscall_exception, drc->cache_top); - - mips3.drcdata->generate_break_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_BREAK); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_break_exception:", mips3.drcdata->generate_break_exception, drc->cache_top); - - mips3.drcdata->generate_trap_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_TRAP); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_trap_exception:", mips3.drcdata->generate_trap_exception, drc->cache_top); - - mips3.drcdata->generate_tlbload_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_TLBLOAD); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_tlbload_exception:", mips3.drcdata->generate_tlbload_exception, drc->cache_top); - - mips3.drcdata->generate_tlbstore_exception = drc->cache_top; - append_generate_exception(drc, EXCEPTION_TLBSTORE); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "generate_tlbstore_exception:", mips3.drcdata->generate_tlbstore_exception, drc->cache_top); - - mips3.drcdata->handle_pc_tlb_mismatch = drc->cache_top; - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EDI); // mov eax,edi - emit_shr_r32_imm(DRCTOP, REG_EAX, 12); // shr eax,12 - emit_mov_r32_m32(DRCTOP, REG_EBX, MISD(REG_EAX, 4, mips3.core->tlb_table)); // mov ebx,tlb_table[eax*4] - emit_test_r32_imm(DRCTOP, REG_EBX, 2); // test ebx,2 - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EDI); // mov eax,edi - emit_jcc(DRCTOP, COND_NZ, mips3.drcdata->generate_tlbload_exception); // jnz generate_tlbload_exception - emit_mov_m32_r32(DRCTOP, MABS(drc->pcptr), REG_EDI); // mov [pcptr],edi - emit_jmp(DRCTOP, drc->recompile); // jmp recompile - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "handle_pc_tlb_mismatch:", mips3.drcdata->handle_pc_tlb_mismatch, drc->cache_top); - - mips3.drcdata->read_and_translate_byte_signed = drc->cache_top; - append_readwrite_and_translate(drc, 1, ARW_READ | ARW_SIGNED); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "read_and_translate_byte_signed:", mips3.drcdata->read_and_translate_byte_signed, drc->cache_top); - - mips3.drcdata->read_and_translate_byte_unsigned = drc->cache_top; - append_readwrite_and_translate(drc, 1, ARW_READ | ARW_UNSIGNED); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "read_and_translate_byte_unsigned:", mips3.drcdata->read_and_translate_byte_unsigned, drc->cache_top); - - mips3.drcdata->read_and_translate_half_signed = drc->cache_top; - append_readwrite_and_translate(drc, 2, ARW_READ | ARW_SIGNED); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "read_and_translate_half_signed:", mips3.drcdata->read_and_translate_half_signed, drc->cache_top); - - mips3.drcdata->read_and_translate_half_unsigned = drc->cache_top; - append_readwrite_and_translate(drc, 2, ARW_READ | ARW_UNSIGNED); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "read_and_translate_half_unsigned:", mips3.drcdata->read_and_translate_half_unsigned, drc->cache_top); - - mips3.drcdata->read_and_translate_word = drc->cache_top; - append_readwrite_and_translate(drc, 4, ARW_READ); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "read_and_translate_word:", mips3.drcdata->read_and_translate_word, drc->cache_top); - - mips3.drcdata->read_and_translate_word_masked = drc->cache_top; - append_readwrite_and_translate(drc, 4, ARW_READ | ARW_MASKED); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "read_and_translate_word_masked:", mips3.drcdata->read_and_translate_word_masked, drc->cache_top); - - mips3.drcdata->read_and_translate_double = drc->cache_top; - append_readwrite_and_translate(drc, 8, ARW_READ); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "read_and_translate_double:", mips3.drcdata->read_and_translate_double, drc->cache_top); - - mips3.drcdata->read_and_translate_double_masked = drc->cache_top; - append_readwrite_and_translate(drc, 8, ARW_READ | ARW_MASKED); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "read_and_translate_double_masked:", mips3.drcdata->read_and_translate_double_masked, drc->cache_top); - - mips3.drcdata->write_and_translate_byte = drc->cache_top; - append_readwrite_and_translate(drc, 1, ARW_WRITE); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "write_and_translate_byte:", mips3.drcdata->write_and_translate_byte, drc->cache_top); - - mips3.drcdata->write_and_translate_half = drc->cache_top; - append_readwrite_and_translate(drc, 2, ARW_WRITE); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "write_and_translate_half:", mips3.drcdata->write_and_translate_half, drc->cache_top); - - mips3.drcdata->write_and_translate_word = drc->cache_top; - append_readwrite_and_translate(drc, 4, ARW_WRITE); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "write_and_translate_word:", mips3.drcdata->write_and_translate_word, drc->cache_top); - - mips3.drcdata->write_and_translate_word_masked = drc->cache_top; - append_readwrite_and_translate(drc, 4, ARW_WRITE | ARW_MASKED); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "write_and_translate_word_masked:", mips3.drcdata->write_and_translate_word_masked, drc->cache_top); - - mips3.drcdata->write_and_translate_double = drc->cache_top; - append_readwrite_and_translate(drc, 8, ARW_WRITE); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "write_and_translate_double:", mips3.drcdata->write_and_translate_double, drc->cache_top); - - mips3.drcdata->write_and_translate_double_masked = drc->cache_top; - append_readwrite_and_translate(drc, 8, ARW_WRITE | ARW_MASKED); - if (LOG_CODE) - x86log_disasm_code_range(mips3.log, "write_and_translate_double_masked:", mips3.drcdata->write_and_translate_double_masked, drc->cache_top); -} - - -/*------------------------------------------------------------------ - drc_recompile_callback -------------------------------------------------------------------*/ - -static void drc_recompile_callback(drc_core *drc) -{ - int remaining = MAX_INSTRUCTIONS; - x86code *start = drc->cache_top; - UINT32 pc = mips3.core->pc; - UINT32 lastpc = pc; - UINT32 physpc; - - (void)start; - - /* begin the sequence */ - drc_begin_sequence(drc, pc); - - /* make sure our FR flag matches the last time */ - emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_FR); // test [COP0_Status],SR_FR - emit_jcc(DRCTOP, IS_FR0 ? COND_NZ : COND_Z, drc->recompile); // jnz/z recompile - - /* ensure that we are still mapped */ - append_tlb_verify(drc, pc, mips3.drcdata->handle_pc_tlb_mismatch); - physpc = pc; - if (!mips3com_translate_address(mips3.core, ADDRESS_SPACE_PROGRAM, &physpc)) - { - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EDI); // mov eax,edi - emit_jmp(DRCTOP, mips3.drcdata->generate_tlbload_exception); // jmp tlbload_exception - goto exit; - } - change_pc(physpc); - - /* loose verification case: one verification here only */ - if (!(mips3.drcoptions & MIPS3DRC_STRICT_VERIFY)) - drc_append_verify_code(drc, cpu_opptr(physpc), 4); - - /* loop until we hit an unconditional branch */ - while (--remaining != 0) - { - UINT32 result; - - /* if we crossed a page boundary, check the mapping */ - if ((pc ^ lastpc) & 0xfffff000) - { - physpc = pc; - if (!mips3com_translate_address(mips3.core, ADDRESS_SPACE_PROGRAM, &physpc)) - break; - append_tlb_verify(drc, pc, drc->dispatch); - change_pc(physpc); - } - - /* compile one instruction */ - physpc = (physpc & 0xfffff000) | (pc & 0xfff); - result = compile_one(drc, pc, physpc); - lastpc = pc; - pc += (INT32)result >> 20; - if (result & RECOMPILE_END_OF_STRING) - break; - } - - /* add dispatcher just in case */ - if (remaining == 0) - drc_append_dispatcher(drc); - -exit: - /* end the sequence */ - drc_end_sequence(drc); - - /* log the generated code */ - if (LOG_CODE) - { - char label[60]; - physpc = mips3.core->pc; - mips3com_translate_address(mips3.core, ADDRESS_SPACE_PROGRAM, &physpc); - sprintf(label, "Code @ %08X (%08X physical)", mips3.core->pc, physpc); - x86log_disasm_code_range(mips3.log, label, start, drc->cache_top); - } -} - - -/*------------------------------------------------------------------ - drc_entrygen_callback -------------------------------------------------------------------*/ - -static void drc_entrygen_callback(drc_core *drc) -{ - emit_mov_r32_imm(DRCTOP, REG_ESI, (FPTR)&mips3.core->r[17]); - append_check_interrupts(drc, 1); -} - - - -/*************************************************************************** - RECOMPILER CORE -***************************************************************************/ - -/*------------------------------------------------------------------ - compile_one -------------------------------------------------------------------*/ - -static UINT32 compile_one(drc_core *drc, UINT32 pc, UINT32 physpc) -{ - int pcdelta, cycles, hotnum; - UINT32 *opptr; - UINT32 result; - - /* register this instruction */ - drc_register_code_at_cache_top(drc, pc); - - /* get a pointer to the current instruction */ - change_pc(physpc); - opptr = cpu_opptr(physpc); - - /* emit debugging and self-modifying code checks */ - drc_append_call_debugger(drc); - if (mips3.drcoptions & MIPS3DRC_STRICT_VERIFY) - drc_append_verify_code(drc, opptr, 4); - - /* is this a hotspot? */ - for (hotnum = 0; hotnum < MIPS3_MAX_HOTSPOTS; hotnum++) - if (pc == mips3.hotspot[hotnum].pc && *(UINT32 *)opptr == mips3.hotspot[hotnum].opcode) - break; - - /* compile the instruction */ - result = recompile_instruction(drc, pc, physpc); - - /* handle the results */ - if (!(result & RECOMPILE_SUCCESSFUL)) - fatalerror("Unimplemented op %08X (%02X,%02X)", *opptr, *opptr >> 26, *opptr & 0x3f); - - pcdelta = (INT32)result >> 20; - cycles = (result >> 8) & 0xfff; - - /* absorb any NOPs following */ - if (STRIP_NOPS && !Machine->debug_mode) - { - if (!(result & (RECOMPILE_END_OF_STRING | RECOMPILE_CHECK_INTERRUPTS | RECOMPILE_CHECK_SW_INTERRUPTS))) - while ((((pc + pcdelta) ^ pc) & 0xfffff000) == 0 && pcdelta < 120 && opptr[pcdelta/4] == 0) - { - pcdelta += 4; - cycles += 1; - } - } - - /* epilogue */ - if (hotnum != MIPS3_MAX_HOTSPOTS) - cycles += mips3.hotspot[hotnum].cycles; - drc_append_standard_epilogue(drc, cycles, pcdelta, 1); - - /* check interrupts */ - if (result & RECOMPILE_CHECK_INTERRUPTS) - append_check_interrupts(drc, 0); - if (result & RECOMPILE_CHECK_SW_INTERRUPTS) - append_check_sw_interrupts(drc, 0); - if (result & RECOMPILE_ADD_DISPATCH) - drc_append_dispatcher(drc); - - return (result & 0xff) | ((cycles & 0xfff) << 8) | ((pcdelta & 0xfff) << 20); -} - - - -/*************************************************************************** - COMMON ROUTINES -***************************************************************************/ - -/*------------------------------------------------------------------ - append_generate_exception -------------------------------------------------------------------*/ - -static void append_generate_exception(drc_core *drc, UINT8 exception) -{ - UINT32 offset = (exception >= EXCEPTION_TLBMOD && exception <= EXCEPTION_TLBSTORE) ? 0x00 : 0x180; - emit_link link1, link2; - - /* assumes address is in EAX */ - if (exception == EXCEPTION_TLBLOAD || exception == EXCEPTION_TLBSTORE) - { - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_BadVAddr), REG_EAX); // mov [BadVAddr],eax - emit_and_m32_imm(DRCTOP, CPR0ADDR(COP0_EntryHi), 0x000000ff); // and [EntryHi],0x000000ff - emit_and_r32_imm(DRCTOP, REG_EAX, 0xffffe000); // and eax,0xffffe000 - emit_or_m32_r32(DRCTOP, CPR0ADDR(COP0_EntryHi), REG_EAX); // or [EntryHi],eax - emit_and_m32_imm(DRCTOP, CPR0ADDR(COP0_Context), 0xff800000); // and [Context],0xff800000 - emit_shr_r32_imm(DRCTOP, REG_EAX, 9); // shr eax,9 - emit_or_m32_r32(DRCTOP, CPR0ADDR(COP0_Context), REG_EAX); // or [Context],eax - } - - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_EPC), REG_EDI); // mov [COP0_EPC],edi - emit_mov_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(COP0_Cause)); // mov eax,[COP0_Cause] - emit_and_r32_imm(DRCTOP, REG_EAX, ~0x800000ff); // and eax,~0x800000ff - if (exception) - emit_or_r32_imm(DRCTOP, REG_EAX, exception << 2); // or eax,exception << 2 - emit_cmp_m32_imm(DRCTOP, MABS(&mips3.nextpc), ~0); // cmp [mips3.nextpc],~0 - emit_jcc_short_link(DRCTOP, COND_E, &link1); // je skip - emit_mov_m32_imm(DRCTOP, MABS(&mips3.nextpc), ~0); // mov [mips3.nextpc],~0 - emit_sub_m32_imm(DRCTOP, CPR0ADDR(COP0_EPC), 4); // sub [COP0_EPC],4 - emit_or_r32_imm(DRCTOP, REG_EAX, 0x80000000); // or eax,0x80000000 - resolve_link(DRCTOP, &link1); // skip: - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Cause), REG_EAX); // mov [COP0_Cause],eax - emit_mov_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(COP0_Status)); // mov eax,[COP0_Status] - emit_or_r32_imm(DRCTOP, REG_EAX, SR_EXL); // or eax,SR_EXL - emit_test_r32_imm(DRCTOP, REG_EAX, SR_BEV); // test eax,SR_BEV - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Status), REG_EAX); // mov [COP0_Status],eax - emit_mov_r32_imm(DRCTOP, REG_EDI, 0xbfc00200 + offset); // mov edi,0xbfc00200+offset - emit_jcc_short_link(DRCTOP, COND_NZ, &link2); // jnz skip2 - emit_mov_r32_imm(DRCTOP, REG_EDI, 0x80000000 + offset); // mov edi,0x80000000+offset - resolve_link(DRCTOP, &link2); // skip2: - drc_append_dispatcher(drc); // dispatch -} - - -/*------------------------------------------------------------------ - append_readwrite_and_translate -------------------------------------------------------------------*/ - -static void append_readwrite_and_translate(drc_core *drc, int size, UINT8 flags) -{ - emit_link link1 = { 0 }, link2 = { 0 }, link3 = { 0 }; - int ramnum; - - emit_mov_r32_m32(DRCTOP, REG_EAX, MBD(REG_ESP, 4)); // mov eax,[esp+4] - emit_mov_r32_r32(DRCTOP, REG_EBX, REG_EAX); // mov ebx,eax - emit_shr_r32_imm(DRCTOP, REG_EBX, 12); // shr ebx,12 - emit_mov_r32_m32(DRCTOP, REG_EBX, MISD(REG_EBX, 4, mips3.core->tlb_table)); // mov ebx,tlb_table[ebx*4] - emit_and_r32_imm(DRCTOP, REG_EAX, 0xfff); // and eax,0xfff - emit_shr_r32_imm(DRCTOP, REG_EBX, ((flags & ARW_WRITE) ? 1 : 2)); // shr ebx,2/1 (read/write) - emit_lea_r32_m32(DRCTOP, REG_EBX, MBISD(REG_EAX, REG_EBX, ((flags & ARW_WRITE) ? 2 : 4), 0)); - // lea ebx,[eax+ebx*4/2] (read/write) - emit_jcc_near_link(DRCTOP, COND_C, &link1); // jc error - for (ramnum = 0; ramnum < MIPS3_MAX_FASTRAM; ramnum++) - if (!Machine->debug_mode && mips3.fastram[ramnum].base && (!(flags & ARW_WRITE) || !mips3.fastram[ramnum].readonly)) - { - UINT32 fastbase = (UINT32)((UINT8 *)mips3.fastram[ramnum].base - mips3.fastram[ramnum].start); - if (mips3.fastram[ramnum].end != 0xffffffff) - { - emit_cmp_r32_imm(DRCTOP, REG_EBX, mips3.fastram[ramnum].end); // cmp ebx,fastram_end - emit_jcc_short_link(DRCTOP, COND_A, &link2); // ja notram - } - if (mips3.fastram[ramnum].start != 0x00000000) - { - emit_cmp_r32_imm(DRCTOP, REG_EBX, mips3.fastram[ramnum].start); // cmp ebx,fastram_start - emit_jcc_short_link(DRCTOP, COND_B, &link3); // jb notram - } - - if (!(flags & ARW_WRITE)) - { - if (size == 1) - { - if (mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_EBX, 3); // xor ebx,3 - if (flags & ARW_SIGNED) - emit_movsx_r32_m8(DRCTOP, REG_EAX, MBD(REG_EBX, fastbase)); // movsx eax,byte ptr [ebx+fastbase] - else - emit_movzx_r32_m8(DRCTOP, REG_EAX, MBD(REG_EBX, fastbase)); // movzx eax,byte ptr [ebx+fastbase] - } - else if (size == 2) - { - if (mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_EBX, 2); // xor ebx,2 - if (flags & ARW_SIGNED) - emit_movsx_r32_m16(DRCTOP, REG_EAX, MBD(REG_EBX, fastbase)); // movsx eax,word ptr [ebx+fastbase] - else - emit_movzx_r32_m16(DRCTOP, REG_EAX, MBD(REG_EBX, fastbase)); // movzx eax,word ptr [ebx+fastbase] - } - else if (size == 4) - emit_mov_r32_m32(DRCTOP, REG_EAX, MBD(REG_EBX, fastbase)); // mov eax,[ebx+fastbase] - else if (size == 8) - { - if (mips3.core->bigendian) - emit_mov_r64_m64(DRCTOP, REG_EAX, REG_EDX, MBD(REG_EBX, (FPTR)fastbase)); - // mov eax:edx,[ebx+fastbase] - else - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, MBD(REG_EBX, (FPTR)fastbase)); - // mov edx:eax,[ebx+fastbase] - } - emit_ret(DRCTOP); // ret - } - else - { - if (size == 1) - { - emit_mov_r8_m8(DRCTOP, REG_AL, MBD(REG_ESP, 8)); // mov al,[esp+8] - if (mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_EBX, 3); // xor ebx,3 - emit_mov_m8_r8(DRCTOP, MBD(REG_EBX, fastbase), REG_AL); // mov [ebx+fastbase],al - } - else if (size == 2) - { - emit_mov_r16_m16(DRCTOP, REG_AX, MBD(REG_ESP, 8)); // mov ax,[esp+8] - if (mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_EBX, 2); // xor ebx,2 - emit_mov_m16_r16(DRCTOP, MBD(REG_EBX, fastbase), REG_AX); // mov [ebx+fastbase],ax - } - else if (size == 4) - { - if (!(flags & ARW_MASKED)) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, MBD(REG_ESP, 8)); // mov eax,[esp+8] - emit_mov_m32_r32(DRCTOP, MBD(REG_EBX, fastbase), REG_EAX); // mov [ebx+fastbase],eax - } - else - { - emit_mov_r32_m32(DRCTOP, REG_ECX, MBD(REG_ESP, 12)); // mov ecx,[esp+12] - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_ECX); // mov eax,ecx - emit_not_r32(DRCTOP, REG_ECX); // not ecx - emit_and_r32_m32(DRCTOP, REG_EAX, MBD(REG_ESP, 8)); // and eax,[esp+8] - emit_and_r32_m32(DRCTOP, REG_ECX, MBD(REG_EBX, fastbase)); // and ecx,[ebx+fastbase] - emit_or_r32_r32(DRCTOP, REG_EAX, REG_ECX); // or eax,ecx - emit_mov_m32_r32(DRCTOP, MBD(REG_EBX, fastbase), REG_EAX); // mov [ebx+fastbase],eax - } - } - else if (size == 8) - { - if (!(flags & ARW_MASKED)) - { - if (mips3.core->bigendian) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, MBD(REG_ESP, 8)); // mov eax,[esp+8] - emit_mov_m32_r32(DRCTOP, MBD(REG_EBX, fastbase+4), REG_EAX); // mov [ebx+fastbase+4],eax - emit_mov_r32_m32(DRCTOP, REG_EAX, MBD(REG_ESP, 12)); // mov eax,[esp+12] - emit_mov_m32_r32(DRCTOP, MBD(REG_EBX, fastbase), REG_EAX); // mov [ebx+fastbase],eax - } - else - { - emit_mov_r32_m32(DRCTOP, REG_EAX, MBD(REG_ESP, 8)); // mov eax,[esp+8] - emit_mov_m32_r32(DRCTOP, MBD(REG_EBX, fastbase), REG_EAX); // mov [ebx+fastbase],eax - emit_mov_r32_m32(DRCTOP, REG_EAX, MBD(REG_ESP, 12)); // mov eax,[esp+12] - emit_mov_m32_r32(DRCTOP, MBD(REG_EBX, fastbase+4), REG_EAX); // mov [ebx+fastbase+4],eax - } - } - else - { - if (mips3.core->bigendian) - { - emit_mov_r32_m32(DRCTOP, REG_ECX, MBD(REG_ESP, 16)); // mov ecx,[esp+16] - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_ECX); // mov eax,ecx - emit_not_r32(DRCTOP, REG_ECX); // not ecx - emit_and_r32_m32(DRCTOP, REG_EAX, MBD(REG_ESP, 8)); // and eax,[esp+8] - emit_and_r32_m32(DRCTOP, REG_ECX, MBD(REG_EBX, fastbase+4)); // and ecx,[ebx+fastbase+4] - emit_or_r32_r32(DRCTOP, REG_EAX, REG_ECX); // or eax,ecx - emit_mov_m32_r32(DRCTOP, MBD(REG_EBX, fastbase+4), REG_EAX); // mov [ebx+fastbase+4],eax - emit_mov_r32_m32(DRCTOP, REG_ECX, MBD(REG_ESP, 20)); // mov ecx,[esp+20] - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_ECX); // mov eax,ecx - emit_not_r32(DRCTOP, REG_ECX); // not ecx - emit_and_r32_m32(DRCTOP, REG_EAX, MBD(REG_ESP, 12)); // and eax,[esp+12] - emit_and_r32_m32(DRCTOP, REG_ECX, MBD(REG_EBX, fastbase)); // and ecx,[ebx+fastbase] - emit_or_r32_r32(DRCTOP, REG_EAX, REG_ECX); // or eax,ecx - emit_mov_m32_r32(DRCTOP, MBD(REG_EBX, fastbase), REG_EAX); // mov [ebx+fastbase],eax - } - else - { - emit_mov_r32_m32(DRCTOP, REG_ECX, MBD(REG_ESP, 16)); // mov ecx,[esp+16] - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_ECX); // mov eax,ecx - emit_not_r32(DRCTOP, REG_ECX); // not ecx - emit_and_r32_m32(DRCTOP, REG_EAX, MBD(REG_ESP, 8)); // and eax,[esp+8] - emit_and_r32_m32(DRCTOP, REG_ECX, MBD(REG_EBX, fastbase)); // and ecx,[ebx+fastbase] - emit_or_r32_r32(DRCTOP, REG_EAX, REG_ECX); // or eax,ecx - emit_mov_m32_r32(DRCTOP, MBD(REG_EBX, fastbase), REG_EAX); // mov [ebx+fastbase],eax - emit_mov_r32_m32(DRCTOP, REG_ECX, MBD(REG_ESP, 20)); // mov ecx,[esp+20] - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_ECX); // mov eax,ecx - emit_not_r32(DRCTOP, REG_ECX); // not ecx - emit_and_r32_m32(DRCTOP, REG_EAX, MBD(REG_ESP, 12)); // and eax,[esp+12] - emit_and_r32_m32(DRCTOP, REG_ECX, MBD(REG_EBX, fastbase+4)); // and ecx,[ebx+fastbase+4] - emit_or_r32_r32(DRCTOP, REG_EAX, REG_ECX); // or eax,ecx - emit_mov_m32_r32(DRCTOP, MBD(REG_EBX, fastbase+4), REG_EAX); // mov [ebx+fastbase+4],eax - } - } - } - emit_ret(DRCTOP); // ret - } - - if (mips3.fastram[ramnum].end != 0xffffffff) // notram: - resolve_link(DRCTOP, &link2); - if (mips3.fastram[ramnum].start != 0x00000000) - resolve_link(DRCTOP, &link3); - } - - if (flags & ARW_WRITE) - { - emit_mov_m32_r32(DRCTOP, MBD(REG_ESP, 4), REG_EBX); // mov [esp+4],ebx - if (size == 1) - emit_jmp(DRCTOP, (x86code *)mips3.core->memory.writebyte); // jmp writebyte - else if (size == 2) - emit_jmp(DRCTOP, (x86code *)mips3.core->memory.writehalf); // jmp writehalf - else if (size == 4) - { - if (!(flags & ARW_MASKED)) - emit_jmp(DRCTOP, (x86code *)mips3.core->memory.writeword); // jmp writeword - else - emit_jmp(DRCTOP, (x86code *)mips3.core->memory.writeword_masked); // jmp writeword_masked - } - else - { - if (!(flags & ARW_MASKED)) - emit_jmp(DRCTOP, (x86code *)mips3.core->memory.writedouble); // jmp writedouble - else - emit_jmp(DRCTOP, (x86code *)mips3.core->memory.writedouble_masked); // jmp writedouble_masked - } - } - else - { - if (size == 1) - { - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - emit_push_r32(DRCTOP, REG_EBX); // push ebx - emit_call(DRCTOP, (x86code *)mips3.core->memory.readbyte); // call readbyte - if (flags & ARW_SIGNED) - emit_movsx_r32_r8(DRCTOP, REG_EAX, REG_AL); // movsx eax,al - else - emit_movzx_r32_r8(DRCTOP, REG_EAX, REG_AL); // movzx eax,al - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_ret(DRCTOP); // ret - } - else if (size == 2) - { - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - emit_push_r32(DRCTOP, REG_EBX); // push ebx - emit_call(DRCTOP, (x86code *)mips3.core->memory.readhalf); // call readhalf - if (flags & ARW_SIGNED) - emit_movsx_r32_r16(DRCTOP, REG_EAX, REG_AX); // movsx eax,ax - else - emit_movzx_r32_r16(DRCTOP, REG_EAX, REG_AX); // movzx eax,ax - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_ret(DRCTOP); // ret - } - else if (size == 4) - { - emit_mov_m32_r32(DRCTOP, MBD(REG_ESP, 4), REG_EBX); // mov [esp+4],ebx - if (!(flags & ARW_MASKED)) - emit_jmp(DRCTOP, (x86code *)mips3.core->memory.readword); // jmp readword - else - emit_jmp(DRCTOP, (x86code *)mips3.core->memory.readword_masked); // jmp readword_masked - } - else - { - emit_mov_m32_r32(DRCTOP, MBD(REG_ESP, 4), REG_EBX); // mov [esp+4],ebx - if (!(flags & ARW_MASKED)) - emit_jmp(DRCTOP, (x86code *)mips3.core->memory.readdouble); // jmp readdouble - else - emit_jmp(DRCTOP, (x86code *)mips3.core->memory.readdouble_masked); // jmp readdouble_masked - } - } - { - int valuebytes = (size == 8) ? 8 : 4; - int stackbytes = 4 + ((flags & ARW_WRITE) ? 4 + valuebytes : 4) + ((flags & ARW_MASKED) ? valuebytes : 0); - resolve_link(DRCTOP, &link1); // error: - emit_mov_r32_m32(DRCTOP, REG_EAX, MBD(REG_ESP, 4)); // mov eax,[esp+4] - emit_add_r32_imm(DRCTOP, REG_ESP, stackbytes); // add esp,stack_bytes - emit_jmp(DRCTOP, (flags & ARW_WRITE) ? (x86code *)mips3.drcdata->generate_tlbstore_exception : (x86code *)mips3.drcdata->generate_tlbload_exception); - // jmp generate_exception - } -} - - -/*------------------------------------------------------------------ - append_tlb_verify -------------------------------------------------------------------*/ - -static void append_tlb_verify(drc_core *drc, UINT32 pc, void *target) -{ - /* addresses 0x80000000-0xbfffffff are direct-mapped; no checking needed */ - if (pc < 0x80000000 || pc >= 0xc0000000) - { - emit_cmp_m32_imm(DRCTOP, MABS(&mips3.core->tlb_table[pc >> 12]), mips3.core->tlb_table[pc >> 12]); - // cmp tlbtable[pc >> 12],physpc & 0xfffff000 - emit_jcc(DRCTOP, COND_NE, target); // jne handle_pc_tlb_mismatch - } -} - - -/*------------------------------------------------------------------ - append_update_cycle_counting -------------------------------------------------------------------*/ - -static void append_update_cycle_counting(drc_core *drc) -{ - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_push_imm(DRCTOP, (FPTR)mips3.core); // push mips3.core - emit_call(DRCTOP, (x86code *)mips3com_update_cycle_counting); // call update_cycle_counting - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] -} - - -/*------------------------------------------------------------------ - append_check_interrupts -------------------------------------------------------------------*/ - -static void append_check_interrupts(drc_core *drc, int inline_generate) -{ - emit_link link1, link2, link3; - emit_mov_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(COP0_Cause)); // mov eax,[COP0_Cause] - emit_and_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(COP0_Status)); // and eax,[COP0_Status] - emit_and_r32_imm(DRCTOP, REG_EAX, 0xfc00); // and eax,0xfc00 - if (!inline_generate) - emit_jcc_short_link(DRCTOP, COND_Z, &link1); // jz skip - else - emit_jcc_near_link(DRCTOP, COND_Z, &link1); // jz skip - emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_IE); // test [COP0_Status,SR_IE - if (!inline_generate) - emit_jcc_short_link(DRCTOP, COND_Z, &link2); // jz skip - else - emit_jcc_near_link(DRCTOP, COND_Z, &link2); // jz skip - emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_EXL | SR_ERL); // test [COP0_Status,SR_EXL | SR_ERL - if (!inline_generate) - emit_jcc(DRCTOP, COND_Z, mips3.drcdata->generate_interrupt_exception); // jz generate_interrupt_exception - else - { - emit_jcc_near_link(DRCTOP, COND_NZ, &link3); // jnz skip - append_generate_exception(drc, EXCEPTION_INTERRUPT); // <generate exception> - resolve_link(DRCTOP, &link3); // skip: - } - resolve_link(DRCTOP, &link1); // skip: - resolve_link(DRCTOP, &link2); -} - - -/*------------------------------------------------------------------ - append_check_sw_interrupts -------------------------------------------------------------------*/ - -static void append_check_sw_interrupts(drc_core *drc, int inline_generate) -{ - emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Cause), 0x300); // test [COP0_Cause],0x300 - emit_jcc(DRCTOP, COND_NZ, mips3.drcdata->generate_interrupt_exception); // jnz generate_interrupt_exception -} - - -/*------------------------------------------------------------------ - append_branch_or_dispatch -------------------------------------------------------------------*/ - -static void append_branch_or_dispatch(drc_core *drc, UINT32 newpc, int cycles) -{ - void *code = drc_get_code_at_pc(drc, newpc); - emit_mov_r32_imm(DRCTOP, REG_EDI, newpc); // mov edi,newpc - drc_append_standard_epilogue(drc, cycles, 0, 1); // <epilogue> - - if (code) - emit_jmp(DRCTOP, code); // jmp code - else - drc_append_tentative_fixed_dispatcher(drc, newpc); // <dispatch> -} - - - -/*************************************************************************** - CORE RECOMPILATION -***************************************************************************/ - -static void ddiv(INT64 *rs, INT64 *rt) -{ - if (*rt) - { - mips3.core->r[REG_LO] = *rs / *rt; - mips3.core->r[REG_HI] = *rs % *rt; - } -} - -static void ddivu(UINT64 *rs, UINT64 *rt) -{ - if (*rt) - { - mips3.core->r[REG_LO] = *rs / *rt; - mips3.core->r[REG_HI] = *rs % *rt; - } -} - - -/*------------------------------------------------------------------ - recompile_delay_slot -------------------------------------------------------------------*/ - -static int recompile_delay_slot(drc_core *drc, UINT32 pc) -{ - UINT8 *saved_top; - UINT32 result; - UINT32 physpc; - -#ifdef ENABLE_DEBUGGER - if (Machine->debug_mode) - { - /* emit debugging */ - emit_mov_r32_imm(DRCTOP, REG_EDI, pc); - drc_append_call_debugger(drc); - } -#endif - - saved_top = drc->cache_top; - - /* recompile the instruction as-is */ - in_delay_slot = 1; - physpc = pc; - mips3com_translate_address(mips3.core, ADDRESS_SPACE_PROGRAM, &physpc); - result = recompile_instruction(drc, pc, physpc); // <next instruction> - in_delay_slot = 0; - - /* if the instruction can cause an exception, recompile setting nextpc */ - if (result & RECOMPILE_MAY_CAUSE_EXCEPTION) - { - drc->cache_top = saved_top; - emit_mov_m32_imm(DRCTOP, MABS(&mips3.nextpc), 0); // bogus nextpc for exceptions - result = recompile_instruction(drc, pc, physpc); // <next instruction> - emit_mov_m32_imm(DRCTOP, MABS(&mips3.nextpc), ~0); // reset nextpc - } - - return (result >> 8) & 0xfff; -} - - -/*------------------------------------------------------------------ - recompile_instruction -------------------------------------------------------------------*/ - -static UINT32 recompile_instruction(drc_core *drc, UINT32 pc, UINT32 physpc) -{ - emit_link link1, link2 = { 0 }, link3; - UINT32 op = cpu_readop32(physpc); - int cycles; - - if (LOG_CODE) - log_add_disasm_comment(mips3.log, drc->cache_top, pc, op); - - switch (op >> 26) - { - case 0x00: /* SPECIAL */ - return recompile_special(drc, pc, op); - - case 0x01: /* REGIMM */ - return recompile_regimm(drc, pc, op); - - case 0x02: /* J */ - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, (pc & 0xf0000000) | (LIMMVAL << 2), 1+cycles); // <branch or dispatch> - return RECOMPILE_SUCCESSFUL_CP(0,0) | RECOMPILE_END_OF_STRING; - - case 0x03: /* JAL */ - emit_mov_m64_imm32(DRCTOP, REGADDR(31), pc + 8); // mov [31],pc + 8 - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, (pc & 0xf0000000) | (LIMMVAL << 2), 1+cycles); // <branch or dispatch> - return RECOMPILE_SUCCESSFUL_CP(0,0) | RECOMPILE_END_OF_STRING; - - case 0x04: /* BEQ */ - if (RSREG == RTREG) - { - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - return RECOMPILE_SUCCESSFUL_CP(0,0) | RECOMPILE_END_OF_STRING; - } - else if (RSREG == 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - emit_or_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // or eax,[rtreg].hi - emit_jcc_near_link(DRCTOP, COND_NZ, &link1); // jnz skip - } - else if (RTREG == 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_or_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // or eax,[rsreg].hi - emit_jcc_near_link(DRCTOP, COND_NZ, &link1); // jnz skip - } - else - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // cmp eax,[rtreg].lo - emit_jcc_near_link(DRCTOP, COND_NE, &link1); // jne skip - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // mov eax,[rsreg].hi - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // cmp eax,[rtreg].hi - emit_jcc_near_link(DRCTOP, COND_NE, &link2); // jne skip - } - - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - if (RSREG != 0 && RTREG != 0) - resolve_link(DRCTOP, &link2); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x05: /* BNE */ - if (RSREG == 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - emit_or_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // or eax,[rtreg].hi - emit_jcc_near_link(DRCTOP, COND_Z, &link1); // jz skip - } - else if (RTREG == 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_or_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // or eax,[rsreg].hi - emit_jcc_near_link(DRCTOP, COND_Z, &link1); // jz skip - } - else - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // cmp eax,[rtreg].lo - emit_jcc_short_link(DRCTOP, COND_NE, &link2); // jne takeit - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // mov eax,[rsreg].hi - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // cmp eax,[rtreg].hi - emit_jcc_near_link(DRCTOP, COND_E, &link1); // je skip - resolve_link(DRCTOP, &link2); // takeit: - } - - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x06: /* BLEZ */ - if (RSREG == 0) - { - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - return RECOMPILE_SUCCESSFUL_CP(0,0) | RECOMPILE_END_OF_STRING; - } - else - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), 0); // cmp [rsreg].hi,0 - emit_jcc_near_link(DRCTOP, COND_G, &link1); // jg skip - emit_jcc_short_link(DRCTOP, COND_L, &link2); // jl takeit - emit_cmp_m32_imm(DRCTOP, REGADDRLO(RSREG), 0); // cmp [rsreg].lo,0 - emit_jcc_near_link(DRCTOP, COND_NE, &link3); // jne skip - resolve_link(DRCTOP, &link2); // takeit: - } - - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - resolve_link(DRCTOP, &link3); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x07: /* BGTZ */ - if (RSREG == 0) - return RECOMPILE_SUCCESSFUL_CP(1,4); - else - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), 0); // cmp [rsreg].hi,0 - emit_jcc_near_link(DRCTOP, COND_L, &link1); // jl skip - emit_jcc_short_link(DRCTOP, COND_G, &link2); // jg takeit - emit_cmp_m32_imm(DRCTOP, REGADDRLO(RSREG), 0); // cmp [rsreg].lo,0 - emit_jcc_near_link(DRCTOP, COND_E, &link3); // je skip - resolve_link(DRCTOP, &link2); // takeit: - } - - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - resolve_link(DRCTOP, &link3); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x08: /* ADDI */ - if (RSREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_jcc(DRCTOP, COND_O, mips3.drcdata->generate_overflow_exception); // jo generate_overflow_exception - if (RTREG != 0) - { - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - } - else if (RTREG != 0) - emit_mov_m64_imm32(DRCTOP, REGADDR(RTREG), SIMMVAL); // mov [rtreg],const - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x09: /* ADDIU */ - if (RTREG != 0) - { - if (RSREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - else - emit_mov_m64_imm32(DRCTOP, REGADDR(RTREG), SIMMVAL); // mov [rtreg],const - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x0a: /* SLTI */ - if (RTREG != 0) - { - if (RSREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - emit_sub_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // sub eax,[rtreg].lo - emit_sbb_r32_imm(DRCTOP, REG_EDX, ((INT32)SIMMVAL >> 31)); // sbb edx,[rtreg].lo - emit_shr_r32_imm(DRCTOP, REG_EDX, 31); // shr edx,31 - emit_mov_m32_r32(DRCTOP, REGADDRLO(RTREG), REG_EDX); // mov [rdreg].lo,edx - emit_mov_m32_imm(DRCTOP, REGADDRHI(RTREG), 0); // mov [rdreg].hi,0 - } - else - { - emit_mov_m32_imm(DRCTOP, REGADDRLO(RTREG), (0 < SIMMVAL)); // mov [rtreg].lo,const - emit_mov_m32_imm(DRCTOP, REGADDRHI(RTREG), 0); // mov [rtreg].hi,sign-extend(const) - } - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x0b: /* SLTIU */ - if (RTREG != 0) - { - if (RSREG != 0) - { - emit_xor_r32_r32(DRCTOP, REG_ECX, REG_ECX); // xor ecx,ecx - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), ((INT32)SIMMVAL >> 31)); // cmp [rsreg].hi,upper - emit_jcc_short_link(DRCTOP, COND_B, &link1); // jb takeit - emit_jcc_short_link(DRCTOP, COND_A, &link2); // ja skip - emit_cmp_m32_imm(DRCTOP, REGADDRLO(RSREG), SIMMVAL); // cmp [rsreg].lo,lower - emit_jcc_short_link(DRCTOP, COND_AE, &link3); // jae skip - resolve_link(DRCTOP, &link1); // takeit: - emit_add_r32_imm(DRCTOP, REG_ECX, 1); // add ecx,1 - resolve_link(DRCTOP, &link2); // skip: - resolve_link(DRCTOP, &link3); // skip: - emit_mov_m32_r32(DRCTOP, REGADDRLO(RTREG), REG_ECX); // mov [rtreg].lo,ecx - emit_mov_m32_imm(DRCTOP, REGADDRHI(RTREG), 0); // mov [rtreg].hi,sign-extend(const) - } - else - { - emit_mov_m32_imm(DRCTOP, REGADDRLO(RTREG), (0 < SIMMVAL)); // mov [rtreg].lo,const - emit_mov_m32_imm(DRCTOP, REGADDRHI(RTREG), 0); // mov [rtreg].hi,sign-extend(const) - } - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x0c: /* ANDI */ - if (RTREG != 0) - { - if (RSREG == RTREG) - { - emit_and_m32_imm(DRCTOP, REGADDRLO(RTREG), UIMMVAL); // and [rtreg],UIMMVAL - emit_mov_m32_imm(DRCTOP, REGADDRHI(RTREG), 0); // mov [rtreg].hi,0 - } - else if (RSREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_and_r32_imm(DRCTOP, REG_EAX, UIMMVAL); // and eax,UIMMVAL - emit_mov_m32_r32(DRCTOP, REGADDRLO(RTREG), REG_EAX); // mov [rtreg].lo,eax - emit_mov_m32_imm(DRCTOP, REGADDRHI(RTREG), 0); // mov [rtreg].hi,0 - } - else - emit_mov_m64_imm32(DRCTOP, REGADDR(RTREG), 0); // mov [rtreg],0 - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x0d: /* ORI */ - if (RTREG != 0) - { - if (RSREG == RTREG) - emit_or_m32_imm(DRCTOP, REGADDRLO(RTREG), UIMMVAL); // or [rtreg].lo,UIMMVAL - else if (RSREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - emit_or_r32_imm(DRCTOP, REG_EAX, UIMMVAL); // or eax,UIMMVAL - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - else - emit_mov_m64_imm32(DRCTOP, REGADDR(RTREG), UIMMVAL); // mov [rtreg],const - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x0e: /* XORI */ - if (RTREG != 0) - { - if (RSREG == RTREG) - emit_xor_m32_imm(DRCTOP, REGADDRLO(RTREG), UIMMVAL); // xor [rtreg].lo,UIMMVAL - else if (RSREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - emit_xor_r32_imm(DRCTOP, REG_EAX, UIMMVAL); // xor eax,UIMMVAL - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - else - emit_mov_m64_imm32(DRCTOP, REGADDR(RTREG), UIMMVAL); // mov [rtreg],const - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x0f: /* LUI */ - if (RTREG != 0) - emit_mov_m64_imm32(DRCTOP, REGADDR(RTREG), UIMMVAL << 16); // mov [rtreg],const << 16 - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x10: /* COP0 */ - return recompile_cop0(drc, pc, op) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x11: /* COP1 */ - return recompile_cop1(drc, pc, op) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x12: /* COP2 */ - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_MAY_CAUSE_EXCEPTION | RECOMPILE_END_OF_STRING; - - case 0x13: /* COP1X - R5000 */ - if (mips3.core->flavor < MIPS3_TYPE_MIPS_IV) - { - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_MAY_CAUSE_EXCEPTION | RECOMPILE_END_OF_STRING; - } - return recompile_cop1x(drc, pc, op) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x14: /* BEQL */ - if (RSREG == RTREG) - { - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - return RECOMPILE_SUCCESSFUL_CP(0,0) | RECOMPILE_END_OF_STRING; - } - else if (RSREG == 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - emit_or_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // or eax,[rtreg].hi - emit_jcc_near_link(DRCTOP, COND_NZ, &link1); // jnz skip - } - else if (RTREG == 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_or_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // or eax,[rsreg].hi - emit_jcc_near_link(DRCTOP, COND_NZ, &link1); // jnz skip - } - else - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // cmp eax,[rtreg].lo - emit_jcc_near_link(DRCTOP, COND_NE, &link1); // jne skip - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // mov eax,[rsreg].hi - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // cmp eax,[rtreg].hi - emit_jcc_near_link(DRCTOP, COND_NE, &link2); // jne skip - } - - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - if (RSREG != 0 && RTREG != 0) - resolve_link(DRCTOP, &link2); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,8); - - case 0x15: /* BNEL */ - if (RSREG == 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - emit_or_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // or eax,[rtreg].hi - emit_jcc_near_link(DRCTOP, COND_Z, &link1); // jz skip - } - else if (RTREG == 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_or_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // or eax,[rsreg].hi - emit_jcc_near_link(DRCTOP, COND_Z, &link1); // jz skip - } - else - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // cmp eax,[rtreg].lo - emit_jcc_short_link(DRCTOP, COND_NE, &link2); // jne takeit - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // mov eax,[rsreg].hi - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // cmp eax,[rtreg].hi - emit_jcc_near_link(DRCTOP, COND_E, &link1); // je skip - resolve_link(DRCTOP, &link2); // takeit: - } - - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,8); - - case 0x16: /* BLEZL */ - if (RSREG == 0) - { - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - return RECOMPILE_SUCCESSFUL_CP(0,0) | RECOMPILE_END_OF_STRING; - } - else - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), 0); // cmp [rsreg].hi,0 - emit_jcc_near_link(DRCTOP, COND_G, &link1); // jg skip - emit_jcc_short_link(DRCTOP, COND_L, &link2); // jl takeit - emit_cmp_m32_imm(DRCTOP, REGADDRLO(RSREG), 0); // cmp [rsreg].lo,0 - emit_jcc_near_link(DRCTOP, COND_NE, &link3); // jne skip - resolve_link(DRCTOP, &link2); // takeit: - } - - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - resolve_link(DRCTOP, &link3); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,8); - - case 0x17: /* BGTZL */ - if (RSREG == 0) - return RECOMPILE_SUCCESSFUL_CP(1,8); - else - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), 0); // cmp [rsreg].hi,0 - emit_jcc_near_link(DRCTOP, COND_L, &link1); // jl skip - emit_jcc_short_link(DRCTOP, COND_G, &link2); // jg takeit - emit_cmp_m32_imm(DRCTOP, REGADDRLO(RSREG), 0); // cmp [rsreg].lo,0 - emit_jcc_near_link(DRCTOP, COND_E, &link3); // je skip - resolve_link(DRCTOP, &link2); // takeit: - } - - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - resolve_link(DRCTOP, &link3); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,8); - - case 0x18: /* DADDI */ - if (RSREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_adc_r32_imm(DRCTOP, REG_EDX, (SIMMVAL < 0) ? -1 : 0); // adc edx,signext(SIMMVAL) - emit_jcc(DRCTOP, COND_O, mips3.drcdata->generate_overflow_exception); // jo generate_overflow_exception - if (RTREG != 0) - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - else if (RTREG != 0) - emit_mov_m64_imm32(DRCTOP, REGADDR(RTREG), SIMMVAL); // mov [rtreg],const - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x19: /* DADDIU */ - if (RTREG != 0) - { - if (RSREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_adc_r32_imm(DRCTOP, REG_EDX, (SIMMVAL < 0) ? -1 : 0); // adc edx,signext(SIMMVAL) - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - else - emit_mov_m64_imm32(DRCTOP, REGADDR(RTREG), SIMMVAL); // mov [rtreg],const - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x1a: /* LDL */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - if (SIMMVAL) - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_EAX, 4, 0)); // lea ecx,[eax*4] - emit_and_r32_imm(DRCTOP, REG_EAX, ~7); // and eax,~7 - emit_mov_r32_imm(DRCTOP, REG_EBX, -1); // mov ebx,-1 - emit_mov_r32_imm(DRCTOP, REG_EBP, -1); // mov ebp,-1 - if (!mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_ECX, 0x1c); // xor ecx,0x1c - emit_shr_r32_cl(DRCTOP, REG_EBP); // shr ebp,cl - emit_shrd_r32_r32_cl(DRCTOP, REG_EBX, REG_EBP); // shrd ebx,ebp,cl - emit_shr_r32_cl(DRCTOP, REG_EBP); // shr ebp,cl - emit_push_r32(DRCTOP, REG_ECX); // push ecx - emit_push_r32(DRCTOP, REG_EBP); // push ebp - emit_push_r32(DRCTOP, REG_EBX); // push ebx - emit_push_r32(DRCTOP, REG_EAX); // push eax - emit_call(DRCTOP, mips3.drcdata->read_and_translate_double_masked); // call read_and_translate_double_masked - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_pop_r32(DRCTOP, REG_ECX); // pop ecx - - if (RTREG != 0) - { - emit_mov_r32_imm(DRCTOP, REG_EBX, -1); // mov ebx,-1 - emit_mov_r32_imm(DRCTOP, REG_EBP, -1); // mov ebp,-1 - emit_shl_r32_cl(DRCTOP, REG_EBX); // shl ebx,cl - emit_shld_r32_r32_cl(DRCTOP, REG_EBP, REG_EBX); // shld ebp,ebx,cl - emit_shl_r32_cl(DRCTOP, REG_EBX); // shl ebx,cl - emit_not_r32(DRCTOP, REG_EBX); // not ebx - emit_not_r32(DRCTOP, REG_EBP); // not ebp - emit_shld_r32_r32_cl(DRCTOP, REG_EDX, REG_EAX); // shld edx,eax,cl - emit_shl_r32_cl(DRCTOP, REG_EAX); // shl eax,cl - emit_shld_r32_r32_cl(DRCTOP, REG_EDX, REG_EAX); // shld edx,eax,cl - emit_shl_r32_cl(DRCTOP, REG_EAX); // shl eax,cl - emit_and_r32_m32(DRCTOP, REG_EBX, REGADDRLO(RTREG)); // and ebx,[rtreg].lo - emit_and_r32_m32(DRCTOP, REG_EBP, REGADDRHI(RTREG)); // and ebp,[rtreg].hi - emit_or_r32_r32(DRCTOP, REG_EAX, REG_EBX); // or eax,ebx - emit_or_r32_r32(DRCTOP, REG_EDX, REG_EBP); // or edx,ebp - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x1b: /* LDR */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - if (SIMMVAL) - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_EAX, 4, 0)); // lea ecx,[eax*4] - emit_and_r32_imm(DRCTOP, REG_EAX, ~7); // and eax,~7 - emit_mov_r32_imm(DRCTOP, REG_EBX, -1); // mov ebx,-1 - emit_mov_r32_imm(DRCTOP, REG_EBP, -1); // mov ebp,-1 - if (mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_ECX, 0x1c); // xor ecx,0x1c - emit_shl_r32_cl(DRCTOP, REG_EBX); // shl ebx,cl - emit_shld_r32_r32_cl(DRCTOP, REG_EBP, REG_EBX); // shld ebp,ebx,cl - emit_shl_r32_cl(DRCTOP, REG_EBX); // shl ebx,cl - emit_push_r32(DRCTOP, REG_ECX); // push ecx - emit_push_r32(DRCTOP, REG_EBP); // push ebp - emit_push_r32(DRCTOP, REG_EBX); // push ebx - emit_push_r32(DRCTOP, REG_EAX); // push eax - emit_call(DRCTOP, mips3.drcdata->read_and_translate_double_masked); // call read_and_translate_double_masked - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_pop_r32(DRCTOP, REG_ECX); // pop ecx - - if (RTREG != 0) - { - emit_mov_r32_imm(DRCTOP, REG_EBX, -1); // mov ebx,-1 - emit_mov_r32_imm(DRCTOP, REG_EBP, -1); // mov ebp,-1 - emit_shr_r32_cl(DRCTOP, REG_EBP); // shr ebp,cl - emit_shrd_r32_r32_cl(DRCTOP, REG_EBX, REG_EBP); // shrd ebx,ebp,cl - emit_shr_r32_cl(DRCTOP, REG_EBP); // shr ebp,cl - emit_not_r32(DRCTOP, REG_EBX); // not ebx - emit_not_r32(DRCTOP, REG_EBP); // not ebp - emit_shrd_r32_r32_cl(DRCTOP, REG_EAX, REG_EDX); // shld eax,edx,cl - emit_shr_r32_cl(DRCTOP, REG_EDX); // shl edx,cl - emit_shrd_r32_r32_cl(DRCTOP, REG_EAX, REG_EDX); // shld eax,edx,cl - emit_shr_r32_cl(DRCTOP, REG_EDX); // shl edx,cl - emit_and_r32_m32(DRCTOP, REG_EBX, REGADDRLO(RTREG)); // and ebx,[rtreg].lo - emit_and_r32_m32(DRCTOP, REG_EBP, REGADDRHI(RTREG)); // and ebp,[rtreg].hi - emit_or_r32_r32(DRCTOP, REG_EAX, REG_EBX); // or eax,ebx - emit_or_r32_r32(DRCTOP, REG_EDX, REG_EBP); // or edx,ebp - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x1c: /* IDT-specific opcodes: mad/madu/mul on R4640/4650, msub on RC32364 */ - switch (op & 0x1f) - { - case 0: /* MAD */ - if (RSREG != 0 && RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg] - emit_mov_r32_m32(DRCTOP, REG_EDX, REGADDRLO(RTREG)); // mov edx,[rtreg] - emit_imul_r32(DRCTOP, REG_EDX); // imul edx - emit_add_r32_m32(DRCTOP, REG_EAX, LOADDRLO); // add eax,[lo] - emit_adc_r32_m32(DRCTOP, REG_EDX, HIADDRLO); // adc edx,[hi] - emit_mov_r32_r32(DRCTOP, REG_EBX, REG_EDX); // mov ebx,edx - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, LOADDR, REG_EDX, REG_EAX); // mov [lo],edx:eax - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EBX); // mov eax,ebx - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, HIADDR, REG_EDX, REG_EAX); // mov [hi],edx:eax - } - return RECOMPILE_SUCCESSFUL_CP(3,4); - - case 1: /* MADU */ - if (RSREG != 0 && RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg] - emit_mov_r32_m32(DRCTOP, REG_EDX, REGADDRLO(RTREG)); // mov edx,[rtreg] - emit_mul_r32(DRCTOP, REG_EDX); // mul edx - emit_add_r32_m32(DRCTOP, REG_EAX, LOADDRLO); // add eax,[lo] - emit_adc_r32_m32(DRCTOP, REG_EDX, HIADDRLO); // adc edx,[hi] - emit_mov_r32_r32(DRCTOP, REG_EBX, REG_EDX); // mov ebx,edx - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, LOADDR, REG_EDX, REG_EAX); // mov [lo],edx:eax - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EBX); // mov eax,ebx - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, HIADDR, REG_EDX, REG_EAX); // mov [hi],edx:eax - } - return RECOMPILE_SUCCESSFUL_CP(3,4); - - case 2: /* MUL */ - if (RDREG != 0) - { - if (RSREG != 0 && RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_imul_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // imul eax,[rtreg].lo - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); // mov [rdreg],0 - } - return RECOMPILE_SUCCESSFUL_CP(3,4); - } - break; - - case 0x20: /* LB */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->read_and_translate_byte_signed); // call read_and_translate_byte_signed - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - if (RTREG != 0) - { - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x21: /* LH */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->read_and_translate_half_signed); // call read_and_translate_half_signed - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - if (RTREG != 0) - { - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x22: /* LWL */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 4); // sub esp,4 - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - if (SIMMVAL) - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_EAX, 8, 0)); // lea ecx,[eax*8] - emit_and_r32_imm(DRCTOP, REG_EAX, ~3); // and eax,~3 - emit_mov_r32_imm(DRCTOP, REG_EBX, -1); // mov ebx,-1 - if (!mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_ECX, 0x18); // xor ecx,0x18 - emit_shr_r32_cl(DRCTOP, REG_EBX); // shr ebx,cl - emit_push_r32(DRCTOP, REG_ECX); // push ecx - emit_push_r32(DRCTOP, REG_EBX); // push ebx - emit_push_r32(DRCTOP, REG_EAX); // push eax - emit_call(DRCTOP, mips3.drcdata->read_and_translate_word_masked); // call read_and_translate_word_masked - emit_add_r32_imm(DRCTOP, REG_ESP, 8); // add esp,8 - emit_pop_r32(DRCTOP, REG_ECX); // pop ecx - - if (RTREG != 0) - { - emit_mov_r32_imm(DRCTOP, REG_EBX, -1); // mov ebx,-1 - emit_shl_r32_cl(DRCTOP, REG_EBX); // shl ebx,cl - emit_not_r32(DRCTOP, REG_EBX); // not ebx - emit_shl_r32_cl(DRCTOP, REG_EAX); // shl eax,cl - emit_and_r32_m32(DRCTOP, REG_EBX, REGADDRLO(RTREG)); // and ebx,[rtreg].lo - emit_or_r32_r32(DRCTOP, REG_EAX, REG_EBX); // or eax,ebx - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - emit_add_r32_imm(DRCTOP, REG_ESP, 4); // add esp,4 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x23: /* LW */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->read_and_translate_word); // call read_and_translate_word - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - if (RTREG != 0) - { - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x24: /* LBU */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->read_and_translate_byte_unsigned); // call read_and_translate_byte_unsigned - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - if (RTREG != 0) - { - emit_mov_m32_imm(DRCTOP, REGADDRHI(RTREG), 0); // mov [rtreg].hi,0 - emit_mov_m32_r32(DRCTOP, REGADDRLO(RTREG), REG_EAX); // mov [rtreg].lo,eax - } - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x25: /* LHU */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->read_and_translate_half_unsigned); // call read_and_translate_half_unsigned - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - if (RTREG != 0) - { - emit_mov_m32_imm(DRCTOP, REGADDRHI(RTREG), 0); // mov [rtreg].hi,0 - emit_mov_m32_r32(DRCTOP, REGADDRLO(RTREG), REG_EAX); // mov [rtreg].lo,eax - } - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x26: /* LWR */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 4); // sub esp,4 - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - if (SIMMVAL) - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_EAX, 8, 0)); // lea ecx,[eax*8] - emit_and_r32_imm(DRCTOP, REG_EAX, ~3); // and eax,~3 - emit_mov_r32_imm(DRCTOP, REG_EBX, -1); // mov ebx,-1 - if (mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_ECX, 0x18); // xor ecx,0x18 - emit_shl_r32_cl(DRCTOP, REG_EBX); // shl ebx,cl - emit_push_r32(DRCTOP, REG_ECX); // push ecx - emit_push_r32(DRCTOP, REG_EBX); // push ebx - emit_push_r32(DRCTOP, REG_EAX); // push eax - emit_call(DRCTOP, mips3.drcdata->read_and_translate_word_masked); // call read_and_translate_word_masked - emit_add_r32_imm(DRCTOP, REG_ESP, 8); // add esp,8 - emit_pop_r32(DRCTOP, REG_ECX); // pop ecx - - if (RTREG != 0) - { - emit_mov_r32_imm(DRCTOP, REG_EBX, -1); // mov ebx,-1 - emit_shr_r32_cl(DRCTOP, REG_EBX); // shr ebx,cl - emit_not_r32(DRCTOP, REG_EBX); // not ebx - emit_shr_r32_cl(DRCTOP, REG_EAX); // shr eax,cl - emit_and_r32_m32(DRCTOP, REG_EBX, REGADDRLO(RTREG)); // and ebx,[rtreg].lo - emit_or_r32_r32(DRCTOP, REG_EAX, REG_EBX); // or eax,ebx - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - emit_add_r32_imm(DRCTOP, REG_ESP, 4); // add esp,4 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x27: /* LWU */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->read_and_translate_word); // call read_and_translate_word - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - if (RTREG != 0) - { - emit_mov_m32_imm(DRCTOP, REGADDRHI(RTREG), 0); // mov [rtreg].hi,0 - emit_mov_m32_r32(DRCTOP, REGADDRLO(RTREG), REG_EAX); // mov [rtreg].lo,eax - } - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x28: /* SB */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 4); // sub esp,4 - if (RTREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RTREG)); // push dword [rtreg].lo - else - emit_push_imm(DRCTOP, 0); // push 0 - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->write_and_translate_byte); // call writebyte - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x29: /* SH */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 4); // sub esp,4 - if (RTREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RTREG)); // push dword [rtreg].lo - else - emit_push_imm(DRCTOP, 0); // push 0 - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->write_and_translate_half); // call writehalf - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x2a: /* SWL */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 4); // sub esp,4 - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - if (SIMMVAL) - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_EAX, 8, 0)); // lea ecx,[eax*8] - emit_and_r32_imm(DRCTOP, REG_EAX, ~3); // and eax,~3 - emit_mov_r32_imm(DRCTOP, REG_EBX, -1); // mov ebx,-1 - if (!mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_ECX, 0x18); // xor ecx,0x18 - emit_shr_r32_cl(DRCTOP, REG_EBX); // shr ebx,cl - emit_push_r32(DRCTOP, REG_EBX); // push ebx - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EDX, REGADDRLO(RTREG)); // mov edx,[rtreg].lo - emit_shr_r32_cl(DRCTOP, REG_EDX); // shr edx,cl - emit_push_r32(DRCTOP, REG_EDX); // push edx - } - else - emit_push_imm(DRCTOP, 0); // push 0 - emit_push_r32(DRCTOP, REG_EAX); // push eax - emit_call(DRCTOP, mips3.drcdata->write_and_translate_word_masked); // call write_and_translate_word_masked - emit_add_r32_imm(DRCTOP, REG_ESP, 16); // add esp,16 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x2b: /* SW */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 4); // sub esp,4 - if (RTREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RTREG)); // push dword [rtreg].lo - else - emit_push_imm(DRCTOP, 0); // push 0 - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->write_and_translate_word); // call write_and_translate_word - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x2c: /* SDL */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 12); // sub esp,12 - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - if (SIMMVAL) - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_EAX, 4, 0)); // lea ecx,[eax*4] - emit_and_r32_imm(DRCTOP, REG_EAX, ~7); // and eax,~7 - emit_mov_r32_imm(DRCTOP, REG_EBX, -1); // mov ebx,-1 - emit_mov_r32_imm(DRCTOP, REG_EBP, -1); // mov ebp,-1 - if (!mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_ECX, 0x1c); // xor ecx,0x1c - emit_shr_r32_cl(DRCTOP, REG_EBP); // shr ebp,cl - emit_shrd_r32_r32_cl(DRCTOP, REG_EBX, REG_EBP); // shrd ebx,ebp,cl - emit_shr_r32_cl(DRCTOP, REG_EBP); // shr ebp,cl - emit_push_r32(DRCTOP, REG_EBP); // push ebp - emit_push_r32(DRCTOP, REG_EBX); // push ebx - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EBX, REGADDRLO(RTREG)); // mov ebx,[rtreg].lo - emit_mov_r32_m32(DRCTOP, REG_EDX, REGADDRHI(RTREG)); // mov edx,[rtreg].hi - emit_shrd_r32_r32_cl(DRCTOP, REG_EBX, REG_EDX); // shrd ebx,edx,cl - emit_shr_r32_cl(DRCTOP, REG_EDX); // shr edx,cl - emit_shrd_r32_r32_cl(DRCTOP, REG_EBX, REG_EDX); // shrd ebx,edx,cl - emit_shr_r32_cl(DRCTOP, REG_EDX); // shr edx,cl - emit_push_r32(DRCTOP, REG_EDX); // push edx - emit_push_r32(DRCTOP, REG_EBX); // push ebx - } - else - { - emit_push_imm(DRCTOP, 0); // push 0 - emit_push_imm(DRCTOP, 0); // push 0 - } - emit_push_r32(DRCTOP, REG_EAX); // push eax - emit_call(DRCTOP, mips3.drcdata->write_and_translate_double_masked); // call write_and_translate_double_masked - emit_add_r32_imm(DRCTOP, REG_ESP, 32); // add esp,32 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x2d: /* SDR */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 12); // sub esp,12 - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - if (SIMMVAL) - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_EAX, 4, 0)); // lea ecx,[eax*4] - emit_and_r32_imm(DRCTOP, REG_EAX, ~7); // and eax,~7 - emit_mov_r32_imm(DRCTOP, REG_EBX, -1); // mov ebx,-1 - emit_mov_r32_imm(DRCTOP, REG_EBP, -1); // mov ebp,-1 - if (mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_ECX, 0x1c); // xor ecx,0x1c - emit_shl_r32_cl(DRCTOP, REG_EBX); // shl ebx,cl - emit_shld_r32_r32_cl(DRCTOP, REG_EBP, REG_EBX); // shld ebp,ebx,cl - emit_shl_r32_cl(DRCTOP, REG_EBX); // shl ebx,cl - emit_push_r32(DRCTOP, REG_EBP); // push ebp - emit_push_r32(DRCTOP, REG_EBX); // push ebx - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EBX, REGADDRLO(RTREG)); // mov ebx,[rtreg].lo - emit_mov_r32_m32(DRCTOP, REG_EDX, REGADDRHI(RTREG)); // mov edx,[rtreg].hi - emit_shld_r32_r32_cl(DRCTOP, REG_EDX, REG_EBX); // shld edx,ebx,cl - emit_shl_r32_cl(DRCTOP, REG_EBX); // shl ebx,cl - emit_shld_r32_r32_cl(DRCTOP, REG_EDX, REG_EBX); // shld edx,ebx,cl - emit_shl_r32_cl(DRCTOP, REG_EBX); // shl ebx,cl - emit_push_r32(DRCTOP, REG_EDX); // push edx - emit_push_r32(DRCTOP, REG_EBX); // push ebx - } - else - { - emit_push_imm(DRCTOP, 0); // push 0 - emit_push_imm(DRCTOP, 0); // push 0 - } - emit_push_r32(DRCTOP, REG_EAX); // push eax - emit_call(DRCTOP, mips3.drcdata->write_and_translate_double_masked); // call write_and_translate_double_masked - emit_add_r32_imm(DRCTOP, REG_ESP, 32); // add esp,32 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x2e: /* SWR */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 4); // sub esp,4 - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - if (SIMMVAL) - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_lea_r32_m32(DRCTOP, REG_ECX, MISD(REG_EAX, 8, 0)); // lea ecx,[eax*8] - emit_and_r32_imm(DRCTOP, REG_EAX, ~3); // and eax,~3 - emit_mov_r32_imm(DRCTOP, REG_EBX, -1); // mov ebx,-1 - if (mips3.core->bigendian) - emit_xor_r32_imm(DRCTOP, REG_ECX, 0x18); // xor ecx,0x18 - emit_shl_r32_cl(DRCTOP, REG_EBX); // shl ebx,cl - emit_push_r32(DRCTOP, REG_EBX); // push ebx - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EDX, REGADDRLO(RTREG)); // mov edx,[rtreg].lo - emit_shl_r32_cl(DRCTOP, REG_EDX); // shl edx,cl - emit_push_r32(DRCTOP, REG_EDX); // push edx - } - else - emit_push_imm(DRCTOP, 0); // push 0 - emit_push_r32(DRCTOP, REG_EAX); // push eax - emit_call(DRCTOP, mips3.drcdata->write_and_translate_word_masked); // call write_and_translate_word_masked - emit_add_r32_imm(DRCTOP, REG_ESP, 16); // add esp,16 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x2f: /* CACHE */ - return RECOMPILE_SUCCESSFUL_CP(1,4); - -// case 0x30: /* LL */ logerror("mips3 Unhandled op: LL\n"); break; - - case 0x31: /* LWC1 */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].los - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->read_and_translate_word); // call read_and_translate_word - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_m32_r32(DRCTOP, FPR32ADDR(RTREG), REG_EAX); // mov [rtreg],eax - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x32: /* LWC2 */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->read_and_translate_word); // call read_and_translate_word - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_m32_r32(DRCTOP, CPR2ADDR(RTREG), REG_EAX); // mov [rtreg],eax - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x33: /* PREF */ - if (mips3.core->flavor >= MIPS3_TYPE_MIPS_IV) - return RECOMPILE_SUCCESSFUL_CP(1,4); - else - { - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_MAY_CAUSE_EXCEPTION | RECOMPILE_END_OF_STRING; - } - -// case 0x34: /* LLD */ logerror("mips3 Unhandled op: LLD\n"); break; - - case 0x35: /* LDC1 */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->read_and_translate_double); // call read_and_translate_double - emit_mov_m64_r64(DRCTOP, FPR64ADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x36: /* LDC2 */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->read_and_translate_double); // call read_and_translate_word - emit_mov_m64_r64(DRCTOP, CPR2ADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x37: /* LD */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->read_and_translate_double); // call read_and_translate_double - if (RTREG != 0) - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - -// case 0x38: /* SC */ logerror("mips3 Unhandled op: SC\n"); break; - - case 0x39: /* SWC1 */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 4); // sub esp,4 - emit_push_m32(DRCTOP, FPR32ADDR(RTREG)); // push dword [rtreg] - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->write_and_translate_word); // call write_and_translate_word - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x3a: /* SWC2 */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 4); // sub esp,4 - emit_push_m32(DRCTOP, CPR2ADDR(RTREG)); // push dword [rtreg] - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->write_and_translate_word); // call write_and_translate_word - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - -// case 0x3b: /* SWC3 */ invalid_instruction(op); break; -// case 0x3c: /* SCD */ logerror("mips3 Unhandled op: SCD\n"); break; - - case 0x3d: /* SDC1 */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_push_m32(DRCTOP, FPR64ADDRHI(RTREG)); // push dword [rtreg].hi - emit_push_m32(DRCTOP, FPR64ADDRLO(RTREG)); // push dword [rtreg].lo - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->write_and_translate_double); // call write_and_translate_double - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x3e: /* SDC2 */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_push_m32(DRCTOP, CPR2ADDRHI(RTREG)); // push dword [rtreg].hi - emit_push_m32(DRCTOP, CPR2ADDRLO(RTREG)); // push dword [rtreg].lo - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->write_and_translate_double); // call write_and_translate_double - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x3f: /* SD */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - if (RTREG != 0) - { - emit_push_m32(DRCTOP, REGADDRHI(RTREG)); // push dword [rtreg].hi - emit_push_m32(DRCTOP, REGADDRLO(RTREG)); // push dword [rtreg].lo - } - else - { - emit_push_imm(DRCTOP, 0); // push 0 - emit_push_imm(DRCTOP, 0); // push 0 - } - if (RSREG != 0 && SIMMVAL != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // add eax,SIMMVAL - emit_push_r32(DRCTOP, REG_EAX); // push eax - } - else if (RSREG != 0) - emit_push_m32(DRCTOP, REGADDRLO(RSREG)); // push [rsreg].lo - else - emit_push_imm(DRCTOP, SIMMVAL); // push SIMMVAL - emit_call(DRCTOP, mips3.drcdata->write_and_translate_double); // call write_and_translate_double - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - -// default: /* ??? */ invalid_instruction(op); break; - } - return RECOMPILE_UNIMPLEMENTED; -} - - -/*------------------------------------------------------------------ - recompile_special -------------------------------------------------------------------*/ - -static UINT32 recompile_special1(drc_core *drc, UINT32 pc, UINT32 op) -{ - emit_link link1, link2, link3; - int cycles; - - switch (op & 63) - { - case 0x00: /* SLL */ - if (RDREG != 0) - { - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - if (SHIFT != 0) - emit_shl_r32_imm(DRCTOP, REG_EAX, SHIFT); // shl eax,SHIFT - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x01: /* MOVF - R5000*/ - if (mips3.core->flavor < MIPS3_TYPE_MIPS_IV) - { - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_MAY_CAUSE_EXCEPTION | RECOMPILE_END_OF_STRING; - } - emit_cmp_m8_imm(DRCTOP, CF1ADDR((op >> 18) & 7), 0); // cmp [cf[x]],0 - emit_jcc_short_link(DRCTOP, ((op >> 16) & 1) ? COND_Z : COND_NZ, &link1); // jz/nz skip - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x02: /* SRL */ - if (RDREG != 0) - { - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - if (SHIFT != 0) - emit_shr_r32_imm(DRCTOP, REG_EAX, SHIFT); // shr eax,SHIFT - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x03: /* SRA */ - if (RDREG != 0) - { - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - if (SHIFT != 0) - emit_sar_r32_imm(DRCTOP, REG_EAX, SHIFT); // sar eax,SHIFT - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x04: /* SLLV */ - if (RDREG != 0) - { - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - if (RSREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_ECX, REGADDRLO(RSREG)); // mov ecx,[rsreg].lo - emit_shl_r32_cl(DRCTOP, REG_EAX); // shl eax,cl - } - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x06: /* SRLV */ - if (RDREG != 0) - { - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - if (RSREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_ECX, REGADDRLO(RSREG)); // mov ecx,[rsreg].lo - emit_shr_r32_cl(DRCTOP, REG_EAX); // shr eax,cl - } - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x07: /* SRAV */ - if (RDREG != 0) - { - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - if (RSREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_ECX, REGADDRLO(RSREG)); // mov ecx,[rsreg].lo - emit_sar_r32_cl(DRCTOP, REG_EAX); // sar eax,cl - } - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x08: /* JR */ - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_mov_m32_r32(DRCTOP, MABS(&jr_temp), REG_EAX); // mov jr_temp,eax - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - emit_mov_r32_m32(DRCTOP, REG_EDI, MABS(&jr_temp)); // mov edi,jr_temp - return RECOMPILE_SUCCESSFUL_CP(1+cycles,0) | RECOMPILE_END_OF_STRING | RECOMPILE_ADD_DISPATCH; - - case 0x09: /* JALR */ - if (RDREG != 0) - emit_mov_m64_imm32(DRCTOP, REGADDR(RDREG), pc + 8); // mov [rdreg],pc + 8 - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - emit_mov_r32_m32(DRCTOP, REG_EDI, REGADDRLO(RSREG)); // mov edi,[rsreg].lo - return RECOMPILE_SUCCESSFUL_CP(1+cycles,0) | RECOMPILE_END_OF_STRING | RECOMPILE_ADD_DISPATCH; - - case 0x0a: /* MOVZ - R5000 */ - if (mips3.core->flavor < MIPS3_TYPE_MIPS_IV) - { - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_MAY_CAUSE_EXCEPTION | RECOMPILE_END_OF_STRING; - } - if (RDREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - emit_or_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // or eax,[rtreg].hi - emit_jcc_short_link(DRCTOP, COND_NZ, &link1); // jnz skip - emit_mov_m64_m64(DRCTOP, REGADDR(RDREG), REGADDR(RSREG)); - // mov [rdreg],[rsreg] - resolve_link(DRCTOP, &link1); // skip: - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x0b: /* MOVN - R5000 */ - if (mips3.core->flavor < MIPS3_TYPE_MIPS_IV) - { - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_MAY_CAUSE_EXCEPTION | RECOMPILE_END_OF_STRING; - } - if (RDREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - emit_or_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // or eax,[rtreg].hi - emit_jcc_short_link(DRCTOP, COND_Z, &link1); // jz skip - emit_mov_m64_m64(DRCTOP, REGADDR(RDREG), REGADDR(RSREG)); - // mov [rdreg],[rsreg] - resolve_link(DRCTOP, &link1); // skip: - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x0c: /* SYSCALL */ - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_syscall_exception); // jmp generate_syscall_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_MAY_CAUSE_EXCEPTION | RECOMPILE_END_OF_STRING; - - case 0x0d: /* BREAK */ - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_break_exception); // jmp generate_break_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_MAY_CAUSE_EXCEPTION | RECOMPILE_END_OF_STRING; - - case 0x0f: /* SYNC */ - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x10: /* MFHI */ - if (RDREG != 0) - emit_mov_m64_m64(DRCTOP, REGADDR(RDREG), HIADDR); // mov [rdreg],[hi] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x11: /* MTHI */ - emit_mov_m64_m64(DRCTOP, HIADDR, REGADDR(RSREG)); // mov [hi],[rsreg] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x12: /* MFLO */ - if (RDREG != 0) - emit_mov_m64_m64(DRCTOP, REGADDR(RDREG), LOADDR); // mov [rdreg],[lo] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x13: /* MTLO */ - emit_mov_m64_m64(DRCTOP, LOADDR, REGADDR(RSREG)); // mov [lo],[rsreg] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x14: /* DSLLV */ - if (RDREG != 0) - { - if (RTREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RTREG)); // mov edx:eax,[rtreg] - if (RSREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_ECX, REGADDRLO(RSREG)); // mov ecx,[rsreg].lo - emit_test_r32_imm(DRCTOP, REG_ECX, 0x20); // test ecx,0x20 - emit_jcc_short_link(DRCTOP, COND_Z, &link1); // jz skip - emit_mov_r32_r32(DRCTOP, REG_EDX, REG_EAX); // mov edx,eax - emit_xor_r32_r32(DRCTOP, REG_EAX, REG_EAX); // xor eax,eax - resolve_link(DRCTOP, &link1); // skip: - emit_shld_r32_r32_cl(DRCTOP, REG_EDX, REG_EAX); // shld edx,eax,cl - emit_shl_r32_cl(DRCTOP, REG_EAX); // shl eax,cl - } - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x16: /* DSRLV */ - if (RDREG != 0) - { - if (RTREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RTREG)); // mov edx:eax,[rtreg] - if (RSREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_ECX, REGADDRLO(RSREG)); // mov ecx,[rsreg].lo - emit_test_r32_imm(DRCTOP, REG_ECX, 0x20); // test ecx,0x20 - emit_jcc_short_link(DRCTOP, COND_Z, &link1); // jz skip - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EDX); // mov eax,edx - emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx - resolve_link(DRCTOP, &link1); // skip: - emit_shrd_r32_r32_cl(DRCTOP, REG_EAX, REG_EDX); // shrd eax,edx,cl - emit_shr_r32_cl(DRCTOP, REG_EDX); // shr edx,cl - } - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x17: /* DSRAV */ - if (RDREG != 0) - { - if (RTREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RTREG)); // mov edx:eax,[rtreg] - if (RSREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_ECX, REGADDRLO(RSREG)); // mov ecx,[rsreg].lo - emit_test_r32_imm(DRCTOP, REG_ECX, 0x20); // test ecx,0x20 - emit_jcc_short_link(DRCTOP, COND_Z, &link1); // jz skip - emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EDX); // mov eax,edx - emit_cdq(DRCTOP); // cdq - resolve_link(DRCTOP, &link1); // skip: - emit_shrd_r32_r32_cl(DRCTOP, REG_EAX, REG_EDX); // shrd eax,edx,cl - emit_sar_r32_cl(DRCTOP, REG_EDX); // sar edx,cl - } - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x18: /* MULT */ - emit_mov_r32_m32(DRCTOP, REG_ECX, REGADDRLO(RTREG)); // mov ecx,[rtreg].lo - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_imul_r32(DRCTOP, REG_ECX); // imul ecx - emit_push_r32(DRCTOP, REG_EDX); // push edx - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, LOADDR, REG_EDX, REG_EAX); // mov [lo],edx:eax - emit_pop_r32(DRCTOP, REG_EAX); // pop eax - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, HIADDR, REG_EDX, REG_EAX); // mov [hi],edx:eax - return RECOMPILE_SUCCESSFUL_CP(4,4); - - case 0x19: /* MULTU */ - emit_mov_r32_m32(DRCTOP, REG_ECX, REGADDRLO(RTREG)); // mov ecx,[rtreg].lo - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_mul_r32(DRCTOP, REG_ECX); // mul ecx - emit_push_r32(DRCTOP, REG_EDX); // push edx - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, LOADDR, REG_EDX, REG_EAX); // mov [lo],edx:eax - emit_pop_r32(DRCTOP, REG_EAX); // pop eax - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, HIADDR, REG_EDX, REG_EAX); // mov [hi],edx:eax - return RECOMPILE_SUCCESSFUL_CP(4,4); - - case 0x1a: /* DIV */ - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_ECX, REGADDRLO(RTREG)); // mov ecx,[rtreg].lo - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_cdq(DRCTOP); // cdq - emit_cmp_r32_imm(DRCTOP, REG_ECX, 0); // cmp ecx,0 - emit_jcc_short_link(DRCTOP, COND_E, &link1); // je skip - emit_idiv_r32(DRCTOP, REG_ECX); // idiv ecx - emit_push_r32(DRCTOP, REG_EDX); // push edx - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, LOADDR, REG_EDX, REG_EAX); // mov [lo],edx:eax - emit_pop_r32(DRCTOP, REG_EAX); // pop eax - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, HIADDR, REG_EDX, REG_EAX); // mov [hi],edx:eax - resolve_link(DRCTOP, &link1); // skip: - } - return RECOMPILE_SUCCESSFUL_CP(36,4); - - case 0x1b: /* DIVU */ - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_ECX, REGADDRLO(RTREG)); // mov ecx,[rtreg].lo - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx - emit_cmp_r32_imm(DRCTOP, REG_ECX, 0); // cmp ecx,0 - emit_jcc_short_link(DRCTOP, COND_E, &link1); // je skip - emit_div_r32(DRCTOP, REG_ECX); // div ecx - emit_push_r32(DRCTOP, REG_EDX); // push edx - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, LOADDR, REG_EDX, REG_EAX); // mov [lo],edx:eax - emit_pop_r32(DRCTOP, REG_EAX); // pop eax - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, HIADDR, REG_EDX, REG_EAX); // mov [hi],edx:eax - resolve_link(DRCTOP, &link1); // skip: - } - return RECOMPILE_SUCCESSFUL_CP(36,4); - - case 0x1c: /* DMULT */ - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - emit_cmp_r32_imm(DRCTOP, REG_EDX, 0); // cmp edx,0 - emit_jcc_short_link(DRCTOP, COND_GE, &link1); // jge skip1 - emit_mov_r32_r32(DRCTOP, REG_ECX, REG_EDX); // mov ecx,edx - emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx - emit_neg_r32(DRCTOP, REG_EAX); // neg eax - emit_sbb_r32_r32(DRCTOP, REG_EDX, REG_ECX); // sbb edx,ecx - resolve_link(DRCTOP, &link1); // skip1: - emit_mov_m64_r64(DRCTOP, MABS(&dmult_temp1), REG_EDX, REG_EAX); // mov [dmult_temp1],edx:eax - - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RTREG)); // mov edx:eax,[rtreg] - emit_cmp_r32_imm(DRCTOP, REG_EDX, 0); // cmp edx,0 - emit_jcc_short_link(DRCTOP, COND_GE, &link2); // jge skip2 - emit_mov_r32_r32(DRCTOP, REG_ECX, REG_EDX); // mov ecx,edx - emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx - emit_neg_r32(DRCTOP, REG_EAX); // neg eax - emit_sbb_r32_r32(DRCTOP, REG_EDX, REG_ECX); // sbb edx,ecx - resolve_link(DRCTOP, &link2); // skip2: - emit_mov_m64_r64(DRCTOP, MABS(&dmult_temp2), REG_EDX, REG_EAX); // mov [dmult_temp2],edx:eax - - emit_mov_r32_m32(DRCTOP, REG_EAX, MABS(LO(&dmult_temp1))); // mov eax,[dmult_temp1].lo - emit_mul_m32(DRCTOP, MABS(LO(&dmult_temp2))); // mul [dmult_temp2].lo - emit_mov_r32_r32(DRCTOP, REG_ECX, REG_EDX); // mov ecx,edx - emit_xor_r32_r32(DRCTOP, REG_EBX, REG_EBX); // xor ebx,ebx - emit_mov_m32_r32(DRCTOP, LOADDRLO, REG_EAX); // mov [lo].lo,eax - - emit_mov_r32_m32(DRCTOP, REG_EAX, MABS(HI(&dmult_temp1))); // mov eax,[dmult_temp1].hi - emit_mul_m32(DRCTOP, MABS(LO(&dmult_temp2))); // mul [dmult_temp2].lo - emit_add_r32_r32(DRCTOP, REG_ECX, REG_EAX); // add ecx,eax - emit_adc_r32_r32(DRCTOP, REG_EBX, REG_EDX); // adc ebx,edx - - emit_mov_r32_m32(DRCTOP, REG_EAX, MABS(LO(&dmult_temp1))); // mov eax,[dmult_temp1].lo - emit_mul_m32(DRCTOP, MABS(HI(&dmult_temp2))); // mul [dmult_temp2].hi - emit_add_r32_r32(DRCTOP, REG_ECX, REG_EAX); // add ecx,eax - emit_adc_r32_r32(DRCTOP, REG_EBX, REG_EDX); // adc ebx,edx - emit_mov_m32_r32(DRCTOP, LOADDRHI, REG_ECX); // mov [lo].hi,ecx - - emit_mov_r32_m32(DRCTOP, REG_EAX, MABS(HI(&dmult_temp1))); // mov eax,[dmult_temp1].hi - emit_mul_m32(DRCTOP, MABS(HI(&dmult_temp2))); // mul [dmult_temp2].hi - emit_add_r32_r32(DRCTOP, REG_EBX, REG_EAX); // add ebx,eax - emit_adc_r32_imm(DRCTOP, REG_EDX, 0); // adc edx,0 - emit_mov_m32_r32(DRCTOP, HIADDRLO, REG_EBX); // mov [hi].lo,ebx - emit_mov_m32_r32(DRCTOP, HIADDRHI, REG_EDX); // mov [hi].hi,edx - - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // mov eax,[rsreg].hi - emit_xor_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // xor eax,[rtreg].hi - emit_jcc_short_link(DRCTOP, COND_NS, &link3); // jns noflip - emit_xor_r32_r32(DRCTOP, REG_EAX, REG_EAX); // xor eax,eax - emit_xor_r32_r32(DRCTOP, REG_EBX, REG_EBX); // xor ebx,ebx - emit_xor_r32_r32(DRCTOP, REG_ECX, REG_ECX); // xor ecx,ecx - emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx - emit_sub_r32_m32(DRCTOP, REG_EAX, LOADDRLO); // sub eax,[lo].lo - emit_sbb_r32_m32(DRCTOP, REG_EBX, LOADDRHI); // sbb ebx,[lo].hi - emit_sbb_r32_m32(DRCTOP, REG_ECX, HIADDRLO); // sbb ecx,[hi].lo - emit_sbb_r32_m32(DRCTOP, REG_EDX, HIADDRHI); // sbb edx,[hi].hi - emit_mov_m64_r64(DRCTOP, LOADDR, REG_EBX, REG_EAX); // mov [lo],ebx:eax - emit_mov_m64_r64(DRCTOP, HIADDR, REG_EDX, REG_ECX); // mov [hi],edx:ecx - resolve_link(DRCTOP, &link3); // noflip: - return RECOMPILE_SUCCESSFUL_CP(8,4); - - case 0x1d: /* DMULTU */ - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_mul_m32(DRCTOP, REGADDRLO(RTREG)); // mul [rtreg].lo - emit_mov_r32_r32(DRCTOP, REG_ECX, REG_EDX); // mov ecx,edx - emit_xor_r32_r32(DRCTOP, REG_EBX, REG_EBX); // xor ebx,ebx - emit_mov_m32_r32(DRCTOP, LOADDRLO, REG_EAX); // mov [lo].lo,eax - - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // mov eax,[rsreg].hi - emit_mul_m32(DRCTOP, REGADDRLO(RTREG)); // mul [rtreg].lo - emit_add_r32_r32(DRCTOP, REG_ECX, REG_EAX); // add ecx,eax - emit_adc_r32_r32(DRCTOP, REG_EBX, REG_EDX); // adc ebx,edx - - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_mul_m32(DRCTOP, REGADDRHI(RTREG)); // mul [rtreg].hi - emit_add_r32_r32(DRCTOP, REG_ECX, REG_EAX); // add ecx,eax - emit_adc_r32_r32(DRCTOP, REG_EBX, REG_EDX); // adc ebx,edx - emit_mov_m32_r32(DRCTOP, LOADDRHI, REG_ECX); // mov [lo].hi,ecx - - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // mov eax,[rsreg].hi - emit_mul_m32(DRCTOP, REGADDRHI(RTREG)); // mul [rtreg].hi - emit_add_r32_r32(DRCTOP, REG_EBX, REG_EAX); // add ebx,eax - emit_adc_r32_imm(DRCTOP, REG_EDX, 0); // adc edx,0 - emit_mov_m32_r32(DRCTOP, HIADDRLO, REG_EBX); // mov [hi].lo,ebx - emit_mov_m32_r32(DRCTOP, HIADDRHI, REG_EDX); // mov [hi].hi,edx - return RECOMPILE_SUCCESSFUL_CP(8,4); - - case 0x1e: /* DDIV */ - emit_sub_r32_imm(DRCTOP, REG_ESP, 4); // sub esp,4 - emit_push_imm(DRCTOP, (FPTR)&mips3.core->r[RTREG]); // push [rtreg] - emit_push_imm(DRCTOP, (FPTR)&mips3.core->r[RSREG]); // push [rsreg] - emit_call(DRCTOP, (x86code *)ddiv); // call ddiv - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - return RECOMPILE_SUCCESSFUL_CP(68,4); - - case 0x1f: /* DDIVU */ - emit_sub_r32_imm(DRCTOP, REG_ESP, 4); // sub esp,4 - emit_push_imm(DRCTOP, (FPTR)&mips3.core->r[RTREG]); // push [rtreg] - emit_push_imm(DRCTOP, (FPTR)&mips3.core->r[RSREG]); // push [rsreg] - emit_call(DRCTOP, (x86code *)ddivu); // call ddivu - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - return RECOMPILE_SUCCESSFUL_CP(68,4); - } - - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_END_OF_STRING; -} - -static UINT32 recompile_special2(drc_core *drc, UINT32 pc, UINT32 op) -{ - emit_link link1, link2, link3; - - switch (op & 63) - { - case 0x20: /* ADD */ - if (RSREG != 0 && RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // add eax,[rtreg].lo - emit_jcc(DRCTOP, COND_O, mips3.drcdata->generate_overflow_exception); // jo generate_overflow_exception - if (RDREG != 0) - { - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - } - else if (RDREG != 0) - { - if (RSREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x21: /* ADDU */ - if (RDREG != 0) - { - if (RSREG != 0 && RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_add_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // add eax,[rtreg].lo - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else if (RSREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x22: /* SUB */ - if (RSREG != 0 && RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_sub_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // sub eax,[rtreg].lo - emit_jcc(DRCTOP, COND_O, mips3.drcdata->generate_overflow_exception); // jo generate_overflow_exception - if (RDREG != 0) - { - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - } - else if (RDREG != 0) - { - if (RSREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - emit_neg_r32(DRCTOP, REG_EAX); // neg eax - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x23: /* SUBU */ - if (RDREG != 0) - { - if (RSREG != 0 && RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_sub_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // sub eax,[rtreg].lo - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else if (RSREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - emit_neg_r32(DRCTOP, REG_EAX); // neg eax - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x24: /* AND */ - if (RDREG != 0) - { - if (RSREG != 0 && RTREG != 0) - { - if (USE_SSE) - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, REGADDR(RSREG)); // movsd xmm0,[rsreg] - emit_movsd_r128_m64(DRCTOP, REG_XMM1, REGADDR(RTREG)); // movsd xmm1,[rtreg] - emit_andpd_r128_r128(DRCTOP, REG_XMM0, REG_XMM1); // andpd xmm0,xmm1 - emit_movsd_m64_r128(DRCTOP, REGADDR(RDREG), REG_XMM0); // mov [rdreg],xmm0 - } - else - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); - // mov edx:eax,[rsreg] - emit_and_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // and eax,[rtreg].lo - emit_and_r32_m32(DRCTOP, REG_EDX, REGADDRHI(RTREG)); // and edx,[rtreg].hi - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x25: /* OR */ - if (RDREG != 0) - { - if (RSREG != 0 && RTREG != 0) - { - if (USE_SSE) - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, REGADDR(RSREG)); // movsd xmm0,[rsreg] - emit_movsd_r128_m64(DRCTOP, REG_XMM1, REGADDR(RTREG)); // movsd xmm1,[rtreg] - emit_orpd_r128_r128(DRCTOP, REG_XMM0, REG_XMM1); // orpd xmm0,xmm1 - emit_movsd_m64_r128(DRCTOP, REGADDR(RDREG), REG_XMM0); // mov [rdreg],xmm0 - } - else - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); - // mov edx:eax,[rsreg] - emit_or_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // or eax,[rtreg].lo - emit_or_r32_m32(DRCTOP, REG_EDX, REGADDRHI(RTREG)); // or edx,[rtreg].hi - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - } - else if (RSREG != 0) - emit_mov_m64_m64(DRCTOP, REGADDR(RDREG), REGADDR(RSREG)); // mov [rdreg],[rsreg] - else if (RTREG != 0) - emit_mov_m64_m64(DRCTOP, REGADDR(RDREG), REGADDR(RTREG)); // mov [rdreg],[rtreg] - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x26: /* XOR */ - if (RDREG != 0) - { - if (RSREG != 0 && RTREG != 0) - { - if (USE_SSE) - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, REGADDR(RSREG)); // movsd xmm0,[rsreg] - emit_movsd_r128_m64(DRCTOP, REG_XMM1, REGADDR(RTREG)); // movsd xmm1,[rtreg] - emit_xorpd_r128_r128(DRCTOP, REG_XMM0, REG_XMM1); // xorpd xmm0,xmm1 - emit_movsd_m64_r128(DRCTOP, REGADDR(RDREG), REG_XMM0); // mov [rdreg],xmm0 - } - else - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); - // mov edx:eax,[rsreg] - emit_xor_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // xor eax,[rtreg].lo - emit_xor_r32_m32(DRCTOP, REG_EDX, REGADDRHI(RTREG)); // xor edx,[rtreg].hi - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - } - else if (RSREG != 0) - emit_mov_m64_m64(DRCTOP, REGADDR(RDREG), REGADDR(RSREG)); // mov [rdreg],[rsreg] - else if (RTREG != 0) - emit_mov_m64_m64(DRCTOP, REGADDR(RDREG), REGADDR(RTREG)); // mov [rdreg],[rtreg] - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x27: /* NOR */ - if (RDREG != 0) - { - if (RSREG != 0 && RTREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - emit_or_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // or eax,[rtreg].lo - emit_or_r32_m32(DRCTOP, REG_EDX, REGADDRHI(RTREG)); // or edx,[rtreg].hi - emit_not_r32(DRCTOP, REG_EDX); // not edx - emit_not_r32(DRCTOP, REG_EAX); // not eax - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else if (RSREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - emit_not_r32(DRCTOP, REG_EDX); // not edx - emit_not_r32(DRCTOP, REG_EAX); // not eax - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else if (RTREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RTREG)); // mov edx:eax,[rtreg] - emit_not_r32(DRCTOP, REG_EDX); // not edx - emit_not_r32(DRCTOP, REG_EAX); // not eax - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - { - emit_mov_m32_imm(DRCTOP, REGADDRLO(RDREG), ~0); // mov [rtreg].lo,~0 - emit_mov_m32_imm(DRCTOP, REGADDRHI(RDREG), ~0); // mov [rtreg].hi,~0 - } - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x2a: /* SLT */ - if (RDREG != 0) - { - if (RSREG != 0) - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - else - { - emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx - emit_xor_r32_r32(DRCTOP, REG_EAX, REG_EAX); // xor eax,eax - } - if (RTREG != 0) - { - emit_sub_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // sub eax,[rtreg].lo - emit_sbb_r32_m32(DRCTOP, REG_EDX, REGADDRHI(RTREG)); // sbb edx,[rtreg].lo - } - emit_shr_r32_imm(DRCTOP, REG_EDX, 31); // shr edx,31 - emit_mov_m32_r32(DRCTOP, REGADDRLO(RDREG), REG_EDX); // mov [rdreg].lo,edx - emit_mov_m32_imm(DRCTOP, REGADDRHI(RDREG), 0); // mov [rdreg].hi,0 - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x2b: /* SLTU */ - if (RDREG != 0) - { - emit_xor_r32_r32(DRCTOP, REG_ECX, REG_ECX); // xor ecx,ecx - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // mov eax,[rsreg].hi - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // cmp eax,[rtreg].hi - emit_jcc_short_link(DRCTOP, COND_B, &link1); // jb setit - emit_jcc_short_link(DRCTOP, COND_A, &link2); // ja skipit - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // cmp eax,[rtreg].lo - emit_jcc_short_link(DRCTOP, COND_AE, &link3); // jae skipit - resolve_link(DRCTOP, &link1); // setit: - emit_add_r32_imm(DRCTOP, REG_ECX, 1); // add ecx,1 - resolve_link(DRCTOP, &link2); // skipit: - resolve_link(DRCTOP, &link3); // skipit: - emit_mov_m32_r32(DRCTOP, REGADDRLO(RDREG), REG_ECX); // mov [rdreg].lo,ecx - emit_mov_m32_imm(DRCTOP, REGADDRHI(RDREG), 0); // mov [rdreg].hi,0 - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x2c: /* DADD */ - if (RSREG != 0 && RTREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - emit_add_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // add eax,[rtreg].lo - emit_adc_r32_m32(DRCTOP, REG_EDX, REGADDRHI(RTREG)); // adc edx,[rtreg].hi - emit_jcc(DRCTOP, COND_O, mips3.drcdata->generate_overflow_exception); // jo generate_overflow_exception - if (RDREG != 0) - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else if (RDREG != 0) - { - if (RSREG != 0) - emit_mov_m64_m64(DRCTOP, REGADDR(RDREG), REGADDR(RSREG)); // mov [rdreg],[rsreg] - else if (RTREG != 0) - emit_mov_m64_m64(DRCTOP, REGADDR(RDREG), REGADDR(RTREG)); // mov [rdreg],[rtreg] - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x2d: /* DADDU */ - if (RDREG != 0) - { - if (RSREG != 0 && RTREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - emit_add_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // add eax,[rtreg].lo - emit_adc_r32_m32(DRCTOP, REG_EDX, REGADDRHI(RTREG)); // adc edx,[rtreg].hi - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else if (RSREG != 0) - emit_mov_m64_m64(DRCTOP, REGADDR(RDREG), REGADDR(RSREG)); - // mov [rdreg],[rsreg] - else if (RTREG != 0) - emit_mov_m64_m64(DRCTOP, REGADDR(RDREG), REGADDR(RTREG)); - // mov [rdreg],[rtreg] - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x2e: /* DSUB */ - if (RSREG != 0 && RTREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - emit_sub_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // sub eax,[rtreg].lo - emit_sbb_r32_m32(DRCTOP, REG_EDX, REGADDRHI(RTREG)); // sbb edx,[rtreg].hi - emit_jcc(DRCTOP, COND_O, mips3.drcdata->generate_overflow_exception); // jo generate_overflow_exception - if (RDREG != 0) - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else if (RDREG != 0) - { - if (RSREG != 0) - emit_mov_m64_m64(DRCTOP, REGADDR(RDREG), REGADDR(RSREG)); // mov [rdreg],[rsreg] - else if (RTREG != 0) - { - emit_xor_r32_r32(DRCTOP, REG_EAX, REG_EAX); // xor eax,eax - emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx - emit_sub_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // sub eax,[rtreg].lo - emit_sbb_r32_m32(DRCTOP, REG_EDX, REGADDRHI(RTREG)); // sbb edx,[rtreg].hi - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x2f: /* DSUBU */ - if (RDREG != 0) - { - if (RSREG != 0 && RTREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - emit_sub_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // sub eax,[rtreg].lo - emit_sbb_r32_m32(DRCTOP, REG_EDX, REGADDRHI(RTREG)); // sbb edx,[rtreg].hi - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else if (RSREG != 0) - emit_mov_m64_m64(DRCTOP, REGADDR(RDREG), REGADDR(RSREG)); // mov [rdreg],[rsreg] - else if (RTREG != 0) - { - emit_xor_r32_r32(DRCTOP, REG_EAX, REG_EAX); // xor eax,eax - emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx - emit_sub_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // sub eax,[rtreg].lo - emit_sbb_r32_m32(DRCTOP, REG_EDX, REGADDRHI(RTREG)); // sbb edx,[rtreg].hi - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x30: /* TGE */ - if (RSREG != 0) - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - else - { - emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx - emit_xor_r32_r32(DRCTOP, REG_EAX, REG_EAX); // xor eax,eax - } - if (RTREG != 0) - { - emit_sub_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // sub eax,[rtreg].lo - emit_sbb_r32_m32(DRCTOP, REG_EDX, REGADDRHI(RTREG)); // sbb edx,[rtreg].hi - } - else - emit_cmp_r32_imm(DRCTOP, REG_EDX, 0); // cmp edx,0 - emit_jcc(DRCTOP, COND_GE, mips3.drcdata->generate_trap_exception); // jge generate_trap_exception - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x31: /* TGEU */ - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // mov eax,[rsreg].hi - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // cmp eax,[rtreg].hi - emit_jcc(DRCTOP, COND_A, mips3.drcdata->generate_trap_exception); // ja generate_trap_exception - emit_jcc_short_link(DRCTOP, COND_B, &link1); // jb skipit - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // cmp eax,[rtreg].lo - emit_jcc(DRCTOP, COND_AE, mips3.drcdata->generate_trap_exception); // jae generate_trap_exception - resolve_link(DRCTOP, &link1); // skipit: - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x32: /* TLT */ - if (RSREG != 0) - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - else - { - emit_xor_r32_r32(DRCTOP, REG_EDX, REG_EDX); // xor edx,edx - emit_xor_r32_r32(DRCTOP, REG_EAX, REG_EAX); // xor eax,eax - } - if (RTREG != 0) - { - emit_sub_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // sub eax,[rtreg].lo - emit_sbb_r32_m32(DRCTOP, REG_EDX, REGADDRHI(RTREG)); // sbb edx,[rtreg].hi - } - else - emit_cmp_r32_imm(DRCTOP, REG_EDX, 0); // cmp edx,0 - emit_jcc(DRCTOP, COND_L, mips3.drcdata->generate_trap_exception); // jl generate_trap_exception - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x33: /* TLTU */ - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // mov eax,[rsreg].hi - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // cmp eax,[rtreg].hi - emit_jcc(DRCTOP, COND_B, mips3.drcdata->generate_trap_exception); // jb generate_trap_exception - emit_jcc_short_link(DRCTOP, COND_A, &link1); // ja skipit - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // cmp eax,[rtreg].lo - emit_jcc(DRCTOP, COND_B, mips3.drcdata->generate_trap_exception); // jb generate_trap_exception - resolve_link(DRCTOP, &link1); // skipit: - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x34: /* TEQ */ - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // mov eax,[rsreg].hi - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // cmp eax,[rtreg].hi - emit_jcc_short_link(DRCTOP, COND_NE, &link1); // jne skipit - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // cmp eax,[rtreg].lo - emit_jcc(DRCTOP, COND_E, mips3.drcdata->generate_trap_exception); // je generate_trap_exception - resolve_link(DRCTOP, &link1); // skipit: - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x36: /* TNE */ - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RSREG)); // mov eax,[rsreg].hi - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // cmp eax,[rtreg].hi - emit_jcc_short_link(DRCTOP, COND_E, &link1); // je skipit - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg].lo - emit_cmp_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // cmp eax,[rtreg].lo - emit_jcc(DRCTOP, COND_NE, mips3.drcdata->generate_trap_exception); // jne generate_trap_exception - resolve_link(DRCTOP, &link1); // skipit: - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x38: /* DSLL */ - if (RDREG != 0) - { - if (RTREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RTREG)); // mov edx:eax,[rtreg] - if (SHIFT != 0) - { - emit_shld_r32_r32_imm(DRCTOP, REG_EDX, REG_EAX, SHIFT); // shld edx,eax,SHIFT - emit_shl_r32_imm(DRCTOP, REG_EAX, SHIFT); // shl eax,SHIFT - } - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x3a: /* DSRL */ - if (RDREG != 0) - { - if (RTREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RTREG)); // mov edx:eax,[rtreg] - if (SHIFT != 0) - { - emit_shrd_r32_r32_imm(DRCTOP, REG_EAX, REG_EDX, SHIFT); // shrd eax,edx,SHIFT - emit_shr_r32_imm(DRCTOP, REG_EDX, SHIFT); // shr edx,SHIFT - } - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x3b: /* DSRA */ - if (RDREG != 0) - { - if (RTREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RTREG)); // mov edx:eax,[rtreg] - if (SHIFT != 0) - { - emit_shrd_r32_r32_imm(DRCTOP, REG_EAX, REG_EDX, SHIFT); // shrd eax,edx,SHIFT - emit_sar_r32_imm(DRCTOP, REG_EDX, SHIFT); // sar edx,SHIFT - } - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x3c: /* DSLL32 */ - if (RDREG != 0) - { - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - if (SHIFT != 0) - emit_shl_r32_imm(DRCTOP, REG_EAX, SHIFT); // shl eax,SHIFT - emit_mov_m32_imm(DRCTOP, REGADDRLO(RDREG), 0); // mov [rdreg].lo,0 - emit_mov_m32_r32(DRCTOP, REGADDRHI(RDREG), REG_EAX); // mov [rdreg].hi,eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x3e: /* DSRL32 */ - if (RDREG != 0) - { - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // mov eax,[rtreg].hi - if (SHIFT != 0) - emit_shr_r32_imm(DRCTOP, REG_EAX, SHIFT); // shr eax,SHIFT - emit_mov_m32_imm(DRCTOP, REGADDRHI(RDREG), 0); // mov [rdreg].hi,0 - emit_mov_m32_r32(DRCTOP, REGADDRLO(RDREG), REG_EAX); // mov [rdreg].lo,eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x3f: /* DSRA32 */ - if (RDREG != 0) - { - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // mov eax,[rtreg].hi - emit_cdq(DRCTOP); // cdq - if (SHIFT != 0) - emit_sar_r32_imm(DRCTOP, REG_EAX, SHIFT); // sar eax,SHIFT - emit_mov_m64_r64(DRCTOP, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rdreg],edx:eax - } - else - emit_zero_m64(DRCTOP, REGADDR(RDREG)); - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - } - - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_END_OF_STRING; -} - - -static UINT32 recompile_special(drc_core *drc, UINT32 pc, UINT32 op) -{ - if (!(op & 32)) - return recompile_special1(drc, pc, op); - else - return recompile_special2(drc, pc, op); -} - - -/*------------------------------------------------------------------ - recompile_regimm -------------------------------------------------------------------*/ - -static UINT32 recompile_regimm(drc_core *drc, UINT32 pc, UINT32 op) -{ - emit_link link1; - int cycles; - - switch (RTREG) - { - case 0x00: /* BLTZ */ - if (RSREG == 0) - return RECOMPILE_SUCCESSFUL_CP(1,4); - else - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), 0); // cmp [rsreg].hi,0 - emit_jcc_near_link(DRCTOP, COND_GE, &link1); // jge skip - } - - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x01: /* BGEZ */ - if (RSREG == 0) - { - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - return RECOMPILE_SUCCESSFUL_CP(0,0) | RECOMPILE_END_OF_STRING; - } - else - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), 0); // cmp [rsreg].hi,0 - emit_jcc_near_link(DRCTOP, COND_L, &link1); // jl skip - } - - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x02: /* BLTZL */ - if (RSREG == 0) - return RECOMPILE_SUCCESSFUL_CP(1,4); - else - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), 0); // cmp [rsreg].hi,0 - emit_jcc_near_link(DRCTOP, COND_GE, &link1); // jge skip - } - - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,8); - - case 0x03: /* BGEZL */ - if (RSREG == 0) - { - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - return RECOMPILE_SUCCESSFUL_CP(0,0) | RECOMPILE_END_OF_STRING; - } - else - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), 0); // cmp [rsreg].hi,0 - emit_jcc_near_link(DRCTOP, COND_L, &link1); // jl skip - } - - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,8); - - case 0x08: /* TGEI */ - if (RSREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - emit_sub_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // sub eax,[rtreg].lo - emit_sbb_r32_imm(DRCTOP, REG_EDX, ((INT32)SIMMVAL >> 31)); // sbb edx,[rtreg].lo - emit_jcc(DRCTOP, COND_GE, mips3.drcdata->generate_trap_exception); // jge generate_trap_exception - } - else - { - if (0 >= SIMMVAL) - emit_jmp(DRCTOP, mips3.drcdata->generate_trap_exception); // jmp generate_trap_exception - } - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x09: /* TGEIU */ - if (RSREG != 0) - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), ((INT32)SIMMVAL >> 31)); - // cmp [rsreg].hi,upper - emit_jcc(DRCTOP, COND_A, mips3.drcdata->generate_trap_exception); // ja generate_trap_exception - emit_jcc_short_link(DRCTOP, COND_B, &link1); // jb skip - emit_cmp_m32_imm(DRCTOP, REGADDRLO(RSREG), SIMMVAL); // cmp [rsreg].lo,lower - emit_jcc(DRCTOP, COND_AE, mips3.drcdata->generate_trap_exception); // jae generate_trap_exception - resolve_link(DRCTOP, &link1); // skip: - } - else - { - if (0 >= SIMMVAL) - emit_jmp(DRCTOP, mips3.drcdata->generate_trap_exception); // jmp generate_trap_exception - } - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x0a: /* TLTI */ - if (RSREG != 0) - { - emit_mov_r64_m64(DRCTOP, REG_EDX, REG_EAX, REGADDR(RSREG)); // mov edx:eax,[rsreg] - emit_sub_r32_imm(DRCTOP, REG_EAX, SIMMVAL); // sub eax,[rtreg].lo - emit_sbb_r32_imm(DRCTOP, REG_EDX, ((INT32)SIMMVAL >> 31)); // sbb edx,[rtreg].lo - emit_jcc(DRCTOP, COND_L, mips3.drcdata->generate_trap_exception); // jl generate_trap_exception - } - else - { - if (0 < SIMMVAL) - emit_jmp(DRCTOP, mips3.drcdata->generate_trap_exception); // jmp generate_trap_exception - } - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x0b: /* TLTIU */ - if (RSREG != 0) - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), ((INT32)SIMMVAL >> 31)); - // cmp [rsreg].hi,upper - emit_jcc(DRCTOP, COND_B, mips3.drcdata->generate_trap_exception); // jb generate_trap_exception - emit_jcc_short_link(DRCTOP, COND_A, &link1); // ja skip - emit_cmp_m32_imm(DRCTOP, REGADDRLO(RSREG), SIMMVAL); // cmp [rsreg].lo,lower - emit_jcc(DRCTOP, COND_B, mips3.drcdata->generate_trap_exception); // jb generate_trap_exception - resolve_link(DRCTOP, &link1); // skip: - } - else - { - emit_mov_m32_imm(DRCTOP, REGADDRLO(RTREG), (0 < SIMMVAL)); // mov [rtreg].lo,const - emit_mov_m32_imm(DRCTOP, REGADDRHI(RTREG), 0); // mov [rtreg].hi,sign-extend(const) - } - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x0c: /* TEQI */ - if (RSREG != 0) - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), ((INT32)SIMMVAL >> 31)); - // cmp [rsreg].hi,upper - emit_jcc_short_link(DRCTOP, COND_NE, &link1); // jne skip - emit_cmp_m32_imm(DRCTOP, REGADDRLO(RSREG), SIMMVAL); // cmp [rsreg].lo,lower - emit_jcc(DRCTOP, COND_E, mips3.drcdata->generate_trap_exception); // je generate_trap_exception - resolve_link(DRCTOP, &link1); // skip: - } - else - { - if (0 == SIMMVAL) - emit_jmp(DRCTOP, mips3.drcdata->generate_trap_exception); // jmp generate_trap_exception - } - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x0e: /* TNEI */ - if (RSREG != 0) - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), ((INT32)SIMMVAL >> 31)); - // cmp [rsreg].hi,upper - emit_jcc_short_link(DRCTOP, COND_E, &link1); // je skip - emit_cmp_m32_imm(DRCTOP, REGADDRLO(RSREG), SIMMVAL); // cmp [rsreg].lo,lower - emit_jcc(DRCTOP, COND_NE, mips3.drcdata->generate_trap_exception); // jne generate_trap_exception - resolve_link(DRCTOP, &link1); // skip: - } - else - { - if (0 != SIMMVAL) - emit_jmp(DRCTOP, mips3.drcdata->generate_trap_exception); // jmp generate_trap_exception - } - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_MAY_CAUSE_EXCEPTION; - - case 0x10: /* BLTZAL */ - if (RSREG == 0) - return RECOMPILE_SUCCESSFUL_CP(1,4); - else - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), 0); // cmp [rsreg].hi,0 - emit_jcc_near_link(DRCTOP, COND_GE, &link1); // jge skip - } - emit_mov_m64_imm32(DRCTOP, MABS(&mips3.core->r[31]), pc + 8); // mov [31],pc + 8 - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x11: /* BGEZAL */ - if (RSREG == 0) - { - emit_mov_m64_imm32(DRCTOP, MABS(&mips3.core->r[31]), pc + 8); // mov [31],pc + 8 - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - return RECOMPILE_SUCCESSFUL_CP(0,0) | RECOMPILE_END_OF_STRING; - } - else - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), 0); // cmp [rsreg].hi,0 - emit_jcc_near_link(DRCTOP, COND_L, &link1); // jl skip - } - - emit_mov_m64_imm32(DRCTOP, MABS(&mips3.core->r[31]), pc + 8); // mov [31],pc + 8 - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x12: /* BLTZALL */ - if (RSREG == 0) - return RECOMPILE_SUCCESSFUL_CP(1,4); - else - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), 0); // cmp [rsreg].hi,0 - emit_jcc_near_link(DRCTOP, COND_GE, &link1); // jge skip - } - - emit_mov_m64_imm32(DRCTOP, MABS(&mips3.core->r[31]), pc + 8); // mov [31],pc + 8 - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,8); - - case 0x13: /* BGEZALL */ - if (RSREG == 0) - { - emit_mov_m64_imm32(DRCTOP, MABS(&mips3.core->r[31]), pc + 8); // mov [31],pc + 8 - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - return RECOMPILE_SUCCESSFUL_CP(0,0) | RECOMPILE_END_OF_STRING; - } - else - { - emit_cmp_m32_imm(DRCTOP, REGADDRHI(RSREG), 0); // cmp [rsreg].hi,0 - emit_jcc_near_link(DRCTOP, COND_L, &link1); // jl skip - } - - emit_mov_m64_imm32(DRCTOP, MABS(&mips3.core->r[31]), pc + 8); // mov [31],pc + 8 - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,8); - } - - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_END_OF_STRING; -} - - - -/*************************************************************************** - COP0 RECOMPILATION -***************************************************************************/ - -/*------------------------------------------------------------------ - recompile_set_cop0_reg -------------------------------------------------------------------*/ - -static UINT32 recompile_set_cop0_reg(drc_core *drc, UINT8 reg) -{ - emit_link link1; - - switch (reg) - { - case COP0_Cause: - emit_mov_r32_m32(DRCTOP, REG_EBX, CPR0ADDR(COP0_Cause)); // mov ebx,[COP0_Cause] - emit_and_r32_imm(DRCTOP, REG_EAX, ~0xfc00); // and eax,~0xfc00 - emit_and_r32_imm(DRCTOP, REG_EBX, 0xfc00); // and ebx,0xfc00 - emit_or_r32_r32(DRCTOP, REG_EAX, REG_EBX); // or eax,ebx - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Cause), REG_EAX); // mov [COP0_Cause],eax - emit_and_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(COP0_Status)); // and eax,[COP0_Status] - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_CHECK_SW_INTERRUPTS; - - case COP0_Status: - emit_mov_r32_m32(DRCTOP, REG_EBX, CPR0ADDR(COP0_Status)); // mov ebx,[COP0_Status] - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Status), REG_EAX); // mov [COP0_Status],eax - emit_xor_r32_r32(DRCTOP, REG_EBX, REG_EAX); // xor ebx,eax -/* -#ifdef ENABLE_DEBUGGER - _test_r32_imm(REG_EAX, 0xe0); // test eax,0xe0 - _jcc_short_link(COND_Z, &link1); // jz skip - _sub_r32_imm(REG_ESP, 4); // sub esp,4 - _push_r32(REG_EAX); // push eax - _push_imm("System set 64-bit addressing mode, SR=%08X"); // push <string> - _call(mame_printf_debug); // call mame_printf_debug - _call(mame_debug_break); // call mame_debug_break - _add_r32_imm(REG_ESP, 12); // add esp,12 - _resolve_link(&link1); // skip: -#endif -*/ - emit_test_r32_imm(DRCTOP, REG_EBX, 0x8000); // test ebx,0x8000 - emit_jcc_short_link(DRCTOP, COND_Z, &link1); // jz skip - append_update_cycle_counting(drc); // update cycle counting - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,4) | RECOMPILE_CHECK_INTERRUPTS | RECOMPILE_END_OF_STRING | RECOMPILE_ADD_DISPATCH; - - case COP0_Count: - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Count), REG_EAX); // mov [COP0_Count],eax - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - emit_push_r32(DRCTOP, REG_EAX); // push eax - emit_call(DRCTOP, (x86code *)activecpu_gettotalcycles); // call activecpu_gettotalcycles - emit_pop_r32(DRCTOP, REG_EBX); // pop ebx - emit_add_r32_imm(DRCTOP, REG_ESP, 8); // add esp,8 - emit_sub_r32_r32(DRCTOP, REG_EAX, REG_EBX); // sub eax,ebx - emit_sbb_r32_imm(DRCTOP, REG_EDX, 0); // sbb edx,0 - emit_sub_r32_r32(DRCTOP, REG_EAX, REG_EBX); // sub eax,ebx - emit_sbb_r32_imm(DRCTOP, REG_EDX, 0); // sbb edx,0 - emit_mov_m64_r64(DRCTOP, MABS(&mips3.core->count_zero_time), REG_EDX, REG_EAX); // mov [mips3.core->count_zero_time],edx:eax - append_update_cycle_counting(drc); // update cycle counting - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case COP0_Compare: - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Compare), REG_EAX); // mov [COP0_Compare],eax - emit_and_m32_imm(DRCTOP, CPR0ADDR(COP0_Cause), ~0x8000); // and [COP0_Cause],~0x8000 - append_update_cycle_counting(drc); // update cycle counting - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case COP0_PRId: - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case COP0_Config: - emit_mov_r32_m32(DRCTOP, REG_EBX, CPR0ADDR(COP0_Config)); // mov ebx,[COP0_Cause] - emit_and_r32_imm(DRCTOP, REG_EAX, 0x0007); // and eax,0x0007 - emit_and_r32_imm(DRCTOP, REG_EBX, ~0x0007); // and ebx,~0x0007 - emit_or_r32_r32(DRCTOP, REG_EAX, REG_EBX); // or eax,ebx - emit_mov_m32_r32(DRCTOP, CPR0ADDR(COP0_Config), REG_EAX); // mov [COP0_Cause],eax - return RECOMPILE_SUCCESSFUL_CP(1,4); - - default: - emit_mov_m32_r32(DRCTOP, CPR0ADDR(reg), REG_EAX); // mov [reg],eax - return RECOMPILE_SUCCESSFUL_CP(1,4); - } - return RECOMPILE_UNIMPLEMENTED; -} - - -/*------------------------------------------------------------------ - recompile_get_cop0_reg -------------------------------------------------------------------*/ - -static UINT32 recompile_get_cop0_reg(drc_core *drc, UINT8 reg) -{ - emit_link link1; - - switch (reg) - { - case COP0_Count: - emit_sub_r32_imm(DRCTOP, REG_EBP, 250); // sub ebp,24 - emit_jcc_short_link(DRCTOP, COND_NS, &link1); // jns notneg - emit_xor_r32_r32(DRCTOP, REG_EBP, REG_EBP); // xor ebp,ebp - resolve_link(DRCTOP, &link1); // notneg: - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_sub_r32_imm(DRCTOP, REG_ESP, 12); // sub esp,12 - emit_call(DRCTOP, (x86code *)activecpu_gettotalcycles); // call activecpu_gettotalcycles - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_sub_r32_m32(DRCTOP, REG_EAX, MABS(LO(&mips3.core->count_zero_time))); // sub eax,[mips3.core->count_zero_time+0] - emit_sbb_r32_m32(DRCTOP, REG_EDX, MABS(HI(&mips3.core->count_zero_time))); // sbb edx,[mips3.core->count_zero_time+4] - emit_shrd_r32_r32_imm(DRCTOP, REG_EAX, REG_EDX, 1); // shrd eax,edx,1 - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case COP0_Cause: - emit_sub_r32_imm(DRCTOP, REG_EBP, 250); // sub ebp,24 - emit_jcc_short_link(DRCTOP, COND_NS, &link1); // jns notneg - emit_xor_r32_r32(DRCTOP, REG_EBP, REG_EBP); // xor ebp,ebp - resolve_link(DRCTOP, &link1); // notneg: - emit_mov_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(reg)); // mov eax,[reg] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - default: - emit_mov_r32_m32(DRCTOP, REG_EAX, CPR0ADDR(reg)); // mov eax,[reg] - return RECOMPILE_SUCCESSFUL_CP(1,4); - } - return RECOMPILE_UNIMPLEMENTED; -} - - -/*------------------------------------------------------------------ - recompile_cop0 -------------------------------------------------------------------*/ - -static UINT32 recompile_cop0(drc_core *drc, UINT32 pc, UINT32 op) -{ - emit_link checklink; - UINT32 result; - - if (mips3.drcoptions & MIPS3DRC_STRICT_COP0) - { - emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_KSU_MASK); // test [COP0_Status],SR_KSU_MASK - emit_jcc_short_link(DRCTOP, COND_Z, &checklink); // jz okay - emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_COP0); // test [COP0_Status],SR_COP0 - emit_jcc(DRCTOP, COND_Z, mips3.drcdata->generate_cop_exception); // jz generate_cop_exception - resolve_link(DRCTOP, &checklink); // okay: - } - - switch (RSREG) - { - case 0x00: /* MFCz */ - result = RECOMPILE_SUCCESSFUL_CP(1,4); - if (RTREG != 0) - { - result = recompile_get_cop0_reg(drc, RDREG); // read cop0 reg - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - return result; - - case 0x01: /* DMFCz */ - result = RECOMPILE_SUCCESSFUL_CP(1,4); - if (RTREG != 0) - { - result = recompile_get_cop0_reg(drc, RDREG); // read cop0 reg - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - return result; - - case 0x02: /* CFCz */ - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, CCR0ADDR(RDREG)); // mov eax,[mips3.core->ccr[0][rdreg]] - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [rtreg],edx:eax - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x04: /* MTCz */ - if (RTREG != 0) - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[mips3.core->r[RTREG]] - else - emit_xor_r32_r32(DRCTOP, REG_EAX, REG_EAX); // xor eax,eax - result = recompile_set_cop0_reg(drc, RDREG); // write cop0 reg - return result; - - case 0x05: /* DMTCz */ - if (RTREG != 0) - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[mips3.core->r[RTREG]] - else - emit_xor_r32_r32(DRCTOP, REG_EAX, REG_EAX); // xor eax,eax - result = recompile_set_cop0_reg(drc, RDREG); // write cop0 reg - return result; - - case 0x06: /* CTCz */ - if (RTREG != 0) - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[mips3.core->r[RTREG]] - else - emit_xor_r32_r32(DRCTOP, REG_EAX, REG_EAX); // xor eax,eax - emit_mov_m32_r32(DRCTOP, CCR0ADDR(RDREG), REG_EAX); // mov [mips3.core->ccr[0][RDREG]],eax - return RECOMPILE_SUCCESSFUL_CP(1,4); - -// case 0x08: /* BC */ -// switch (RTREG) -// { -// case 0x00: /* BCzF */ if (!mips3.core->cf[0][0]) ADDPC(SIMMVAL); break; -// case 0x01: /* BCzF */ if (mips3.core->cf[0][0]) ADDPC(SIMMVAL); break; -// case 0x02: /* BCzFL */ invalid_instruction(op); break; -// case 0x03: /* BCzTL */ invalid_instruction(op); break; -// default: invalid_instruction(op); break; -// } -// break; - - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: /* COP */ - switch (op & 0x01ffffff) - { - case 0x01: /* TLBR */ - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - emit_push_imm(DRCTOP, (FPTR)mips3.core); // push mips3.core - drc_append_save_call_restore(drc, (x86code *)mips3com_tlbr, 12); // call tlbr - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x02: /* TLBWI */ - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - emit_push_imm(DRCTOP, (FPTR)mips3.core); // push mips3.core - drc_append_save_call_restore(drc, (x86code *)mips3com_tlbwi, 12); // call tlbwi - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x06: /* TLBWR */ - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - emit_push_imm(DRCTOP, (FPTR)mips3.core); // push mips3.core - drc_append_save_call_restore(drc, (x86code *)mips3com_tlbwr, 12); // call tlbwr - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x08: /* TLBP */ - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - emit_push_imm(DRCTOP, (FPTR)mips3.core); // push mips3.core - drc_append_save_call_restore(drc, (x86code *)mips3com_tlbp, 12); // call tlbp - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x10: /* RFE */ - emit_jmp(DRCTOP, mips3.drcdata->generate_invalidop_exception); // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_END_OF_STRING; - - case 0x18: /* ERET */ - emit_mov_r32_m32(DRCTOP, REG_EDI, CPR0ADDR(COP0_EPC)); // mov edi,[COP0_EPC] - emit_and_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), ~SR_EXL); // and [COP0_Status],~SR_EXL - return RECOMPILE_SUCCESSFUL_CP(1,0) | RECOMPILE_CHECK_INTERRUPTS | RECOMPILE_END_OF_STRING | RECOMPILE_ADD_DISPATCH; - - default: - emit_jmp(DRCTOP, mips3.drcdata->generate_invalidop_exception); // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_END_OF_STRING; - } - break; - -// default: -// _jmp(mips3.drcdata->generate_invalidop_exception); // jmp generate_invalidop_exception -// return RECOMPILE_SUCCESSFUL | RECOMPILE_END_OF_STRING; - } - return RECOMPILE_UNIMPLEMENTED; -} - - - -/*************************************************************************** - COP1 RECOMPILATION -***************************************************************************/ - -/*------------------------------------------------------------------ - recompile_cop1 -------------------------------------------------------------------*/ - -static UINT32 recompile_cop1(drc_core *drc, UINT32 pc, UINT32 op) -{ - emit_link link1; - int cycles, i; - - if (mips3.drcoptions & MIPS3DRC_STRICT_COP1) - { - emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_COP1); // test [COP0_Status],SR_COP1 - emit_jcc(DRCTOP, COND_Z, mips3.drcdata->generate_cop_exception); // jz generate_cop_exception - } - - switch (RSREG) - { - case 0x00: /* MFCz */ - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, FPR32ADDR(RDREG)); // mov eax,[RDREG] - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [RTREG],edx:eax - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x01: /* DMFCz */ - if (RTREG != 0) - emit_mov_m64_m64(DRCTOP, REGADDR(RTREG), FPR64ADDR(RDREG)); - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x02: /* CFCz */ - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, CCR1ADDR(RDREG)); // mov eax,[mips3.core->ccr[1][RDREG]] - if (RDREG == 31) - { - emit_and_r32_imm(DRCTOP, REG_EAX, ~0xfe800000); // and eax,~0xfe800000 - emit_xor_r32_r32(DRCTOP, REG_EBX, REG_EBX); // xor ebx,ebx - emit_cmp_m8_imm(DRCTOP, MABS(&mips3.core->cf[1][0]), 0); // cmp [cf[0]],0 - emit_setcc_r8(DRCTOP, COND_NZ, REG_BL); // setnz bl - emit_shl_r32_imm(DRCTOP, REG_EBX, 23); // shl ebx,23 - emit_or_r32_r32(DRCTOP, REG_EAX, REG_EBX); // or eax,ebx - if (mips3.core->flavor >= MIPS3_TYPE_MIPS_IV) - for (i = 1; i <= 7; i++) - { - emit_cmp_m8_imm(DRCTOP, MABS(&mips3.core->cf[1][i]), 0); // cmp [cf[i]],0 - emit_setcc_r8(DRCTOP, COND_NZ, REG_BL); // setnz bl - emit_shl_r32_imm(DRCTOP, REG_EBX, 24+i); // shl ebx,24+i - emit_or_r32_r32(DRCTOP, REG_EAX, REG_EBX); // or eax,ebx - } - } - emit_cdq(DRCTOP); // cdq - emit_mov_m64_r64(DRCTOP, REGADDR(RTREG), REG_EDX, REG_EAX); // mov [mips3.core->r[RTREG]],edx:eax - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x04: /* MTCz */ - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[mips3.core->r[RTREG]] - emit_mov_m32_r32(DRCTOP, FPR32ADDR(RDREG), REG_EAX); // mov [mips3.core->cpr[1][RDREG]],eax - } - else - emit_mov_m32_imm(DRCTOP, FPR32ADDR(RDREG), 0); // mov [mips3.core->cpr[1][RDREG]],0 - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x05: /* DMTCz */ - if (RTREG != 0) - emit_mov_m64_m64(DRCTOP, FPR64ADDR(RDREG), REGADDR(RTREG)); - else - emit_zero_m64(DRCTOP, FPR64ADDR(RDREG)); // mov [mips3.core->cpr[1][RDREG]],0 - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x06: /* CTCz */ - if (RTREG != 0) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[mips3.core->r[RTREG]] - emit_mov_m32_r32(DRCTOP, CCR1ADDR(RDREG), REG_EAX); // mov [mips3.core->ccr[1][RDREG]],eax - } - else - emit_mov_m32_imm(DRCTOP, CCR1ADDR(RDREG), 0); // mov [mips3.core->ccr[1][RDREG]],0 - if (RDREG == 31) - { - emit_mov_r32_m32(DRCTOP, REG_EAX, MABS(LO(&mips3.core->ccr[1][RDREG]))); // mov eax,[mips3.core->ccr[1][RDREG]] - emit_test_r32_imm(DRCTOP, REG_EAX, 1 << 23); // test eax,1<<23 - emit_setcc_m8(DRCTOP, COND_NZ, MABS(&mips3.core->cf[1][0])); // setnz [cf[0]] - if (mips3.core->flavor >= MIPS3_TYPE_MIPS_IV) - for (i = 1; i <= 7; i++) - { - emit_test_r32_imm(DRCTOP, REG_EAX, 1 << (24+i)); // test eax,1<<(24+i) - emit_setcc_m8(DRCTOP, COND_NZ, MABS(&mips3.core->cf[1][i])); // setnz [cf[i]] - } - emit_and_r32_imm(DRCTOP, REG_EAX, 3); // and eax,3 - emit_test_r32_imm(DRCTOP, REG_EAX, 1); // test eax,1 - emit_jcc_near_link(DRCTOP, COND_Z, &link1); // jz skip - emit_xor_r32_imm(DRCTOP, REG_EAX, 2); // xor eax,2 - resolve_link(DRCTOP, &link1); // skip: - drc_append_set_fp_rounding(drc, REG_EAX); // set_rounding(EAX) - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x08: /* BC */ - switch ((op >> 16) & 3) - { - case 0x00: /* BCzF */ - emit_cmp_m8_imm(DRCTOP, CF1ADDR((op >> 18) & 7), 0); // cmp [cf[x]],0 - emit_jcc_near_link(DRCTOP, COND_NZ, &link1); // jnz link1 - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x01: /* BCzT */ - emit_cmp_m8_imm(DRCTOP, CF1ADDR((op >> 18) & 7), 0); // cmp [cf[x]],0 - emit_jcc_near_link(DRCTOP, COND_Z, &link1); // jz link1 - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x02: /* BCzFL */ - emit_cmp_m8_imm(DRCTOP, CF1ADDR((op >> 18) & 7), 0); // cmp [cf[x]],0 - emit_jcc_near_link(DRCTOP, COND_NZ, &link1); // jnz link1 - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,8); - - case 0x03: /* BCzTL */ - emit_cmp_m8_imm(DRCTOP, CF1ADDR((op >> 18) & 7), 0); // cmp [cf[x]],0 - emit_jcc_near_link(DRCTOP, COND_Z, &link1); // jz link1 - cycles = recompile_delay_slot(drc, pc + 4); // <next instruction> - append_branch_or_dispatch(drc, pc + 4 + (SIMMVAL << 2), 1+cycles); // <branch or dispatch> - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,8); - } - break; - - default: - switch (op & 0x3f) - { - case 0x00: - if (IS_SINGLE(op)) /* ADD.S */ - { - if (USE_SSE) - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_addss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // addss xmm0,[ftreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else - { - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FTREG)); // fld [ftreg] - emit_faddp(DRCTOP); // faddp - emit_fstp_m32(DRCTOP, FPR32ADDR(FDREG)); // fstp [fdreg] - } - } - else /* ADD.D */ - { - if (USE_SSE) - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_addsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // addsd xmm0,[ftreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - else - { - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FTREG)); // fld [ftreg] - emit_faddp(DRCTOP); // faddp - emit_fstp_m64(DRCTOP, FPR64ADDR(FDREG)); // fstp [fdreg] - } - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x01: - if (IS_SINGLE(op)) /* SUB.S */ - { - if (USE_SSE) - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_subss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // subss xmm0,[ftreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else - { - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FTREG)); // fld [ftreg] - emit_fsubp(DRCTOP); // fsubp - emit_fstp_m32(DRCTOP, FPR32ADDR(FDREG)); // fstp [fdreg] - } - } - else /* SUB.D */ - { - if (USE_SSE) - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_subsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // subsd xmm0,[ftreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - else - { - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FTREG)); // fld [ftreg] - emit_fsubp(DRCTOP); // fsubp - emit_fstp_m64(DRCTOP, FPR64ADDR(FDREG)); // fstp [fdreg] - } - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x02: - if (IS_SINGLE(op)) /* MUL.S */ - { - if (USE_SSE) - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_mulss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // mulss xmm0,[ftreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else - { - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FTREG)); // fld [ftreg] - emit_fmulp(DRCTOP); // fmulp - emit_fstp_m32(DRCTOP, FPR32ADDR(FDREG)); // fstp [fdreg] - } - } - else /* MUL.D */ - { - if (USE_SSE) - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_mulsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // mulsd xmm0,[ftreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - else - { - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FTREG)); // fld [ftreg] - emit_fmulp(DRCTOP); // fmulp - emit_fstp_m64(DRCTOP, FPR64ADDR(FDREG)); // fstp [fdreg] - } - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x03: - if (IS_SINGLE(op)) /* DIV.S */ - { - if (USE_SSE) - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_divss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // divss xmm0,[ftreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else - { - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FTREG)); // fld [ftreg] - emit_fdivp(DRCTOP); // fdivp - emit_fstp_m32(DRCTOP, FPR32ADDR(FDREG)); // fstp [fdreg] - } - } - else /* DIV.D */ - { - if (USE_SSE) - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_divsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // divsd xmm0,[ftreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - else - { - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FTREG)); // fld [ftreg] - emit_fdivp(DRCTOP); // fdivp - emit_fstp_m64(DRCTOP, FPR64ADDR(FDREG)); // fstp [fdreg] - } - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x04: - if (IS_SINGLE(op)) /* SQRT.S */ - { - if (USE_SSE) - { - emit_sqrtss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // sqrtss xmm0,[fsreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else - { - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - emit_fsqrt(DRCTOP); // fsqrt - emit_fstp_m32(DRCTOP, FPR32ADDR(FDREG)); // fstp [fdreg] - } - } - else /* SQRT.D */ - { - if (USE_SSE) - { - emit_sqrtsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // sqrtsd xmm0,[fsreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - else - { - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fsqrt(DRCTOP); // fsqrt - emit_fstp_m64(DRCTOP, FPR64ADDR(FDREG)); // fstp [fdreg] - } - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x05: - if (IS_SINGLE(op)) /* ABS.S */ - { - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - emit_fabs(DRCTOP); // fabs - emit_fstp_m32(DRCTOP, FPR32ADDR(FDREG)); // fstp [fdreg] - } - else /* ABS.D */ - { - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fabs(DRCTOP); // fabs - emit_fstp_m64(DRCTOP, FPR64ADDR(FDREG)); // fstp [fdreg] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x06: - if (IS_SINGLE(op)) /* MOV.S */ - { - emit_mov_r32_m32(DRCTOP, REG_EAX, FPR32ADDR(FSREG)); // mov eax,[fsreg] - emit_mov_m32_r32(DRCTOP, FPR32ADDR(FDREG), REG_EAX); // mov [fdreg],eax - } - else /* MOV.D */ - emit_mov_m64_m64(DRCTOP, FPR64ADDR(FDREG), FPR64ADDR(FSREG)); - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x07: - if (IS_SINGLE(op)) /* NEG.S */ - { - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - emit_fchs(DRCTOP); // fchs - emit_fstp_m32(DRCTOP, FPR32ADDR(FDREG)); // fstp [fdreg] - } - else /* NEG.D */ - { - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fchs(DRCTOP); // fchs - emit_fstp_m64(DRCTOP, FPR64ADDR(FDREG)); // fstp [fdreg] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x08: - drc_append_set_temp_fp_rounding(drc, FPRND_NEAR); - if (IS_SINGLE(op)) /* ROUND.L.S */ - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - else /* ROUND.L.D */ - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fistp_m64(DRCTOP, FPR64ADDR(FDREG)); // fistp [fdreg] - drc_append_restore_fp_rounding(drc); - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x09: - drc_append_set_temp_fp_rounding(drc, FPRND_CHOP); - if (IS_SINGLE(op)) /* TRUNC.L.S */ - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - else /* TRUNC.L.D */ - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fistp_m64(DRCTOP, FPR64ADDR(FDREG)); // fistp [fdreg] - drc_append_restore_fp_rounding(drc); - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x0a: - drc_append_set_temp_fp_rounding(drc, FPRND_UP); - if (IS_SINGLE(op)) /* CEIL.L.S */ - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - else /* CEIL.L.D */ - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fistp_m64(DRCTOP, FPR64ADDR(FDREG)); // fistp [fdreg] - drc_append_restore_fp_rounding(drc); - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x0b: - drc_append_set_temp_fp_rounding(drc, FPRND_DOWN); - if (IS_SINGLE(op)) /* FLOOR.L.S */ - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - else /* FLOOR.L.D */ - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fistp_m64(DRCTOP, FPR64ADDR(FDREG)); // fistp [fdreg] - drc_append_restore_fp_rounding(drc); - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x0c: - drc_append_set_temp_fp_rounding(drc, FPRND_NEAR); - if (IS_SINGLE(op)) /* ROUND.W.S */ - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - else /* ROUND.W.D */ - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fistp_m32(DRCTOP, FPR32ADDR(FDREG)); // fistp [fdreg] - drc_append_restore_fp_rounding(drc); - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x0d: - drc_append_set_temp_fp_rounding(drc, FPRND_CHOP); - if (IS_SINGLE(op)) /* TRUNC.W.S */ - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - else /* TRUNC.W.D */ - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fistp_m32(DRCTOP, FPR32ADDR(FDREG)); // fistp [fdreg] - drc_append_restore_fp_rounding(drc); - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x0e: - drc_append_set_temp_fp_rounding(drc, FPRND_UP); - if (IS_SINGLE(op)) /* CEIL.W.S */ - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - else /* CEIL.W.D */ - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fistp_m32(DRCTOP, FPR32ADDR(FDREG)); // fistp [fdreg] - drc_append_restore_fp_rounding(drc); - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x0f: - drc_append_set_temp_fp_rounding(drc, FPRND_DOWN); - if (IS_SINGLE(op)) /* FLOOR.W.S */ - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - else /* FLOOR.W.D */ - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fistp_m32(DRCTOP, FPR32ADDR(FDREG)); // fistp [fdreg] - drc_append_restore_fp_rounding(drc); - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x11: /* R5000 */ - if (mips3.core->flavor < MIPS3_TYPE_MIPS_IV) - { - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); - // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_END_OF_STRING; - } - emit_cmp_m8_imm(DRCTOP, CF1ADDR((op >> 18) & 7), 0); // cmp [cf[x]],0 - emit_jcc_short_link(DRCTOP, ((op >> 16) & 1) ? COND_Z : COND_NZ, &link1); - // jz/nz skip - if (IS_SINGLE(op)) /* MOVT/F.S */ - { - emit_mov_r32_m32(DRCTOP, REG_EAX, FPR32ADDR(FSREG)); // mov eax,[fsreg] - emit_mov_m32_r32(DRCTOP, FPR32ADDR(FDREG), REG_EAX); // mov [fdreg],eax - } - else /* MOVT/F.D */ - emit_mov_m64_m64(DRCTOP, FPR64ADDR(FDREG), FPR64ADDR(FSREG)); - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x12: /* R5000 */ - if (mips3.core->flavor < MIPS3_TYPE_MIPS_IV) - { - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); - // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_END_OF_STRING; - } - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - emit_or_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // or eax,[rtreg].hi - emit_jcc_short_link(DRCTOP, COND_NZ, &link1); // jnz skip - if (IS_SINGLE(op)) /* MOVZ.S */ - { - emit_mov_r32_m32(DRCTOP, REG_EAX, FPR32ADDR(FSREG)); // mov eax,[fsreg] - emit_mov_m32_r32(DRCTOP, FPR32ADDR(FDREG), REG_EAX); // mov [fdreg],eax - } - else /* MOVZ.D */ - emit_mov_m64_m64(DRCTOP, FPR64ADDR(FDREG), FPR64ADDR(FSREG)); - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x13: /* R5000 */ - if (mips3.core->flavor < MIPS3_TYPE_MIPS_IV) - { - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); - // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_END_OF_STRING; - } - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // mov eax,[rtreg].lo - emit_or_r32_m32(DRCTOP, REG_EAX, REGADDRHI(RTREG)); // or eax,[rtreg].hi - emit_jcc_short_link(DRCTOP, COND_Z, &link1); // jz skip - if (IS_SINGLE(op)) /* MOVN.S */ - { - emit_mov_r32_m32(DRCTOP, REG_EAX, FPR32ADDR(FSREG)); // mov eax,[fsreg] - emit_mov_m32_r32(DRCTOP, FPR32ADDR(FDREG), REG_EAX); // mov [fdreg],eax - } - else /* MOVN.D */ - emit_mov_m64_m64(DRCTOP, FPR64ADDR(FDREG), FPR64ADDR(FSREG)); - resolve_link(DRCTOP, &link1); // skip: - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x15: /* R5000 */ - if (mips3.core->flavor < MIPS3_TYPE_MIPS_IV) - { - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); - // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_END_OF_STRING; - } - emit_fld1(DRCTOP); // fld1 - if (IS_SINGLE(op)) /* RECIP.S */ - { - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - emit_fdivp(DRCTOP); // fdivp - emit_fstp_m32(DRCTOP, FPR32ADDR(FDREG)); // fstp [fdreg] - } - else /* RECIP.D */ - { - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fdivp(DRCTOP); // fdivp - emit_fstp_m64(DRCTOP, FPR64ADDR(FDREG)); // fstp [fdreg] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x16: /* R5000 */ - if (mips3.core->flavor < MIPS3_TYPE_MIPS_IV) - { - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); - // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_END_OF_STRING; - } - emit_fld1(DRCTOP); // fld1 - if (IS_SINGLE(op)) /* RSQRT.S */ - { - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - emit_fsqrt(DRCTOP); // fsqrt - emit_fdivp(DRCTOP); // fdivp - emit_fstp_m32(DRCTOP, FPR32ADDR(FDREG)); // fstp [fdreg] - } - else /* RSQRT.D */ - { - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fsqrt(DRCTOP); // fsqrt - emit_fdivp(DRCTOP); // fdivp - emit_fstp_m64(DRCTOP, FPR64ADDR(FDREG)); // fstp [fdreg] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x20: - if (IS_INTEGRAL(op)) - { - if (IS_SINGLE(op)) /* CVT.S.W */ - emit_fild_m32(DRCTOP, FPR32ADDR(FSREG)); // fild [fsreg] - else /* CVT.S.L */ - emit_fild_m64(DRCTOP, FPR64ADDR(FSREG)); // fild [fsreg] - } - else /* CVT.S.D */ - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fstp_m32(DRCTOP, FPR32ADDR(FDREG)); // fstp [fdreg] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x21: - if (IS_INTEGRAL(op)) - { - if (IS_SINGLE(op)) /* CVT.D.W */ - emit_fild_m32(DRCTOP, FPR32ADDR(FSREG)); // fild [fsreg] - else /* CVT.D.L */ - emit_fild_m64(DRCTOP, FPR64ADDR(FSREG)); // fild [fsreg] - } - else /* CVT.D.S */ - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - emit_fstp_m64(DRCTOP, FPR64ADDR(FDREG)); // fstp [fdreg] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x24: - if (IS_SINGLE(op)) /* CVT.W.S */ - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - else /* CVT.W.D */ - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fistp_m32(DRCTOP, FPR32ADDR(FDREG)); // fistp [fdreg] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x25: - if (IS_SINGLE(op)) /* CVT.L.S */ - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - else /* CVT.L.D */ - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fistp_m64(DRCTOP, FPR64ADDR(FDREG)); // fistp [fdreg] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x30: - case 0x38: - emit_mov_m8_imm(DRCTOP, CF1ADDR((op >> 8) & 7), 0); /* C.F.S/D */ - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x31: - case 0x39: - emit_mov_m8_imm(DRCTOP, CF1ADDR((op >> 8) & 7), 0); /* C.UN.S/D */ - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x32: - case 0x3a: - if (USE_SSE) - { - if (IS_SINGLE(op)) /* C.EQ.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_comiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // comiss xmm0,[ftreg] - } - else - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_comisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // comisd xmm0,[ftreg] - } - emit_setcc_m8(DRCTOP, COND_E, CF1ADDR((op >> 8) & 7)); // sete [cf[x]] - } - else - { - if (IS_SINGLE(op)) /* C.EQ.S */ - { - emit_fld_m32(DRCTOP, FPR32ADDR(FTREG)); // fld [ftreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - } - else - { - emit_fld_m64(DRCTOP, FPR64ADDR(FTREG)); // fld [ftreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - } - emit_fcompp(DRCTOP); // fcompp - emit_fstsw_ax(DRCTOP); // fnstsw ax - emit_sahf(DRCTOP); // sahf - emit_setcc_m8(DRCTOP, COND_E, CF1ADDR((op >> 8) & 7)); // sete [cf[x]] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x33: - case 0x3b: - if (USE_SSE) - { - if (IS_SINGLE(op)) /* C.UEQ.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_ucomiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // ucomiss xmm0,[ftreg] - } - else - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_ucomisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // ucomisd xmm0,[ftreg] - } - emit_setcc_m8(DRCTOP, COND_E, CF1ADDR((op >> 8) & 7)); // sete [cf[x]] - } - else - { - if (IS_SINGLE(op)) /* C.UEQ.S */ - { - emit_fld_m32(DRCTOP, FPR32ADDR(FTREG)); // fld [ftreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - } - else - { - emit_fld_m64(DRCTOP, FPR64ADDR(FTREG)); // fld [ftreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - } - emit_fucompp(DRCTOP); // fucompp - emit_fstsw_ax(DRCTOP); // fnstsw ax - emit_sahf(DRCTOP); // sahf - emit_setcc_m8(DRCTOP, COND_E, CF1ADDR((op >> 8) & 7)); // sete [cf[x]] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x34: - case 0x3c: - if (USE_SSE) - { - if (IS_SINGLE(op)) /* C.OLT.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_comiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // comiss xmm0,[ftreg] - } - else - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_comisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // comisd xmm0,[ftreg] - } - emit_setcc_m8(DRCTOP, COND_B, CF1ADDR((op >> 8) & 7)); // setb [cf[x]] - } - else - { - if (IS_SINGLE(op)) /* C.OLT.S */ - { - emit_fld_m32(DRCTOP, FPR32ADDR(FTREG)); // fld [ftreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - } - else - { - emit_fld_m64(DRCTOP, FPR64ADDR(FTREG)); // fld [ftreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - } - emit_fcompp(DRCTOP); // fcompp - emit_fstsw_ax(DRCTOP); // fnstsw ax - emit_sahf(DRCTOP); // sahf - emit_setcc_m8(DRCTOP, COND_B, CF1ADDR((op >> 8) & 7)); // setb [cf[x]] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x35: - case 0x3d: - if (USE_SSE) - { - if (IS_SINGLE(op)) /* C.ULT.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_ucomiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // ucomiss xmm0,[ftreg] - } - else - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_ucomisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // ucomisd xmm0,[ftreg] - } - emit_setcc_m8(DRCTOP, COND_B, CF1ADDR((op >> 8) & 7)); // setb [cf[x]] - } - else - { - if (IS_SINGLE(op)) /* C.ULT.S */ - { - emit_fld_m32(DRCTOP, FPR32ADDR(FTREG)); // fld [ftreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - } - else - { - emit_fld_m64(DRCTOP, FPR64ADDR(FTREG)); // fld [ftreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - } - emit_fucompp(DRCTOP); // fucompp - emit_fstsw_ax(DRCTOP); // fnstsw ax - emit_sahf(DRCTOP); // sahf - emit_setcc_m8(DRCTOP, COND_B, CF1ADDR((op >> 8) & 7)); // setb [cf[x]] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x36: - case 0x3e: - if (USE_SSE) - { - if (IS_SINGLE(op)) /* C.OLE.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_comiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // comiss xmm0,[ftreg] - } - else - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_comisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // comisd xmm0,[ftreg] - } - emit_setcc_m8(DRCTOP, COND_BE, CF1ADDR((op >> 8) & 7)); // setle [cf[x]] - } - else - { - if (IS_SINGLE(op)) /* C.OLE.S */ - { - emit_fld_m32(DRCTOP, FPR32ADDR(FTREG)); // fld [ftreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - } - else - { - emit_fld_m64(DRCTOP, FPR64ADDR(FTREG)); // fld [ftreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - } - emit_fcompp(DRCTOP); // fcompp - emit_fstsw_ax(DRCTOP); // fnstsw ax - emit_sahf(DRCTOP); // sahf - emit_setcc_m8(DRCTOP, COND_BE, CF1ADDR((op >> 8) & 7)); // setbe [cf[x]] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x37: - case 0x3f: - if (USE_SSE) - { - if (IS_SINGLE(op)) /* C.ULE.S */ - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_ucomiss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // ucomiss xmm0,[ftreg] - } - else - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_ucomisd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // ucomisd xmm0,[ftreg] - } - emit_setcc_m8(DRCTOP, COND_BE, CF1ADDR((op >> 8) & 7)); // setl [cf[x]] - } - else - { - if (IS_SINGLE(op)) /* C.ULE.S */ - { - emit_fld_m32(DRCTOP, FPR32ADDR(FTREG)); // fld [ftreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - } - else - { - emit_fld_m64(DRCTOP, FPR64ADDR(FTREG)); // fld [ftreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - } - emit_fucompp(DRCTOP); // fucompp - emit_fstsw_ax(DRCTOP); // fnstsw ax - emit_sahf(DRCTOP); // sahf - emit_setcc_m8(DRCTOP, COND_BE, CF1ADDR((op >> 8) & 7)); // setbe [cf[x]] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - } - break; - } - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_END_OF_STRING; -} - - - -/*************************************************************************** - COP1X RECOMPILATION -***************************************************************************/ - -/*------------------------------------------------------------------ - recompile_cop1x -------------------------------------------------------------------*/ - -static UINT32 recompile_cop1x(drc_core *drc, UINT32 pc, UINT32 op) -{ - if (mips3.drcoptions & MIPS3DRC_STRICT_COP1) - { - emit_test_m32_imm(DRCTOP, CPR0ADDR(COP0_Status), SR_COP1); // test [COP0_Status],SR_COP1 - emit_jcc(DRCTOP, COND_Z, mips3.drcdata->generate_cop_exception); // jz generate_cop_exception - } - - switch (op & 0x3f) - { - case 0x00: /* LWXC1 */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg] - emit_add_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // add eax,[rtreg] - emit_push_r32(DRCTOP, REG_EAX); // push eax - emit_call(DRCTOP, mips3.drcdata->read_and_translate_word); // call read_and_translate_word - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_m32_r32(DRCTOP, FPR32ADDR(FDREG), REG_EAX); // mov [fdreg],eax - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x01: /* LDXC1 */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // sub esp,8 - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg] - emit_add_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // add eax,[rtreg] - emit_push_r32(DRCTOP, REG_EAX); // push eax - emit_call(DRCTOP, mips3.drcdata->read_and_translate_double); // call read_and_translate_double - emit_mov_m32_r32(DRCTOP, FPR64ADDRLO(FDREG), REG_EAX); // mov [fdreg].lo,eax - emit_mov_m32_r32(DRCTOP, FPR64ADDRHI(FDREG), REG_EDX); // mov [fdreg].hi,edx - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x08: /* SWXC1 */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_sub_r32_imm(DRCTOP, REG_ESP, 4); // sub esp,4 - emit_push_m32(DRCTOP, FPR32ADDR(FSREG)); // push [fsreg] - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg] - emit_add_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // add eax,[rtreg] - emit_push_r32(DRCTOP, REG_EAX); // push eax - emit_call(DRCTOP, mips3.drcdata->write_and_translate_word); // call write_and_translate_word - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,8 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x09: /* SDXC1 */ - emit_mov_m32_r32(DRCTOP, ICOUNTADDR, REG_EBP); // mov [icount],ebp - emit_save_pc_before_call(DRCTOP); // save pc - emit_push_m32(DRCTOP, FPR64ADDRHI(FSREG)); // push [fsreg].hi - emit_push_m32(DRCTOP, FPR64ADDRLO(FSREG)); // push [fsreg].lo - emit_mov_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RSREG)); // mov eax,[rsreg] - emit_add_r32_m32(DRCTOP, REG_EAX, REGADDRLO(RTREG)); // add eax,[rtreg] - emit_push_r32(DRCTOP, REG_EAX); // push eax - emit_call(DRCTOP, mips3.drcdata->write_and_translate_double); // call write_and_translate_double - emit_add_r32_imm(DRCTOP, REG_ESP, 12); // add esp,12 - emit_mov_r32_m32(DRCTOP, REG_EBP, ICOUNTADDR); // mov ebp,[icount] - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x0f: /* PREFX */ - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x20: /* MADD.S */ - if (USE_SSE) - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_mulss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // mulss xmm0,[ftreg] - emit_addss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FRREG)); // addss xmm0,[frreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else - { - emit_fld_m32(DRCTOP, FPR32ADDR(FRREG)); // fld [frreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FTREG)); // fld [ftreg] - emit_fmulp(DRCTOP); // fmulp - emit_faddp(DRCTOP); // faddp - emit_fstp_m32(DRCTOP, FPR32ADDR(FDREG)); // fstp [fdreg] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x21: /* MADD.D */ - if (USE_SSE) - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_mulsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // mulsd xmm0,[ftreg] - emit_addsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FRREG)); // addsd xmm0,[frreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - else - { - emit_fld_m64(DRCTOP, FPR64ADDR(FRREG)); // fld [frreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FTREG)); // fld [ftreg] - emit_fmulp(DRCTOP); // fmulp - emit_faddp(DRCTOP); // faddp - emit_fstp_m64(DRCTOP, FPR64ADDR(FDREG)); // fstp [fdreg] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x28: /* MSUB.S */ - if (USE_SSE) - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_mulss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // mulss xmm0,[ftreg] - emit_subss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FRREG)); // subss xmm0,[frreg] - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM0); // movss [fdreg],xmm0 - } - else - { - emit_fld_m32(DRCTOP, FPR32ADDR(FRREG)); // fld [frreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FTREG)); // fld [ftreg] - emit_fmulp(DRCTOP); // fmulp - emit_fsubrp(DRCTOP); // fsubrp - emit_fstp_m32(DRCTOP, FPR32ADDR(FDREG)); // fstp [fdreg] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x29: /* MSUB.D */ - if (USE_SSE) - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_mulsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // mulsd xmm0,[ftreg] - emit_subsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FRREG)); // subsd xmm0,[frreg] - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM0); // movsd [fdreg],xmm0 - } - else - { - emit_fld_m64(DRCTOP, FPR64ADDR(FRREG)); // fld [frreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FTREG)); // fld [ftreg] - emit_fmulp(DRCTOP); // fmulp - emit_fsubrp(DRCTOP); // fsubrp - emit_fstp_m64(DRCTOP, FPR64ADDR(FDREG)); // fstp [fdreg] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x30: /* NMADD.S */ - if (USE_SSE) - { - emit_xorps_r128_r128(DRCTOP, REG_XMM1, REG_XMM1); // xorps xmm1,xmm1 - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_mulss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // mulss xmm0,[ftreg] - emit_addss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FRREG)); // addss xmm0,[frreg] - emit_subss_r128_r128(DRCTOP, REG_XMM1, REG_XMM0); // subss xmm1,xmm0 - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM1); // movss [fdreg],xmm1 - } - else - { - emit_fld_m32(DRCTOP, FPR32ADDR(FRREG)); // fld [frreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FTREG)); // fld [ftreg] - emit_fmulp(DRCTOP); // fmulp - emit_faddp(DRCTOP); // faddp - emit_fchs(DRCTOP); // fchs - emit_fstp_m32(DRCTOP, FPR32ADDR(FDREG)); // fstp [fdreg] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x31: /* NMADD.D */ - if (USE_SSE) - { - emit_xorps_r128_r128(DRCTOP, REG_XMM1, REG_XMM1); // xorps xmm1,xmm1 - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_mulsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // mulsd xmm0,[ftreg] - emit_addsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FRREG)); // addsd xmm0,[frreg] - emit_subss_r128_r128(DRCTOP, REG_XMM1, REG_XMM0); // subss xmm1,xmm0 - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM1); // movsd [fdreg],xmm1 - } - else - { - emit_fld_m64(DRCTOP, FPR64ADDR(FRREG)); // fld [frreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FTREG)); // fld [ftreg] - emit_fmulp(DRCTOP); // fmulp - emit_faddp(DRCTOP); // faddp - emit_fchs(DRCTOP); // fchs - emit_fstp_m64(DRCTOP, FPR64ADDR(FDREG)); // fstp [fdreg] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x38: /* NMSUB.S */ - if (USE_SSE) - { - emit_movss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FSREG)); // movss xmm0,[fsreg] - emit_mulss_r128_m32(DRCTOP, REG_XMM0, FPR32ADDR(FTREG)); // mulss xmm0,[ftreg] - emit_movss_r128_m32(DRCTOP, REG_XMM1, FPR32ADDR(FRREG)); // movss xmm1,[frreg] - emit_subss_r128_r128(DRCTOP, REG_XMM1, REG_XMM0); // subss xmm1,xmm0 - emit_movss_m32_r128(DRCTOP, FPR32ADDR(FDREG), REG_XMM1); // movss [fdreg],xmm1 - } - else - { - emit_fld_m32(DRCTOP, FPR32ADDR(FRREG)); // fld [frreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FSREG)); // fld [fsreg] - emit_fld_m32(DRCTOP, FPR32ADDR(FTREG)); // fld [ftreg] - emit_fmulp(DRCTOP); // fmulp - emit_fsubp(DRCTOP); // fsubp - emit_fstp_m32(DRCTOP, FPR32ADDR(FDREG)); // fstp [fdreg] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x39: /* NMSUB.D */ - if (USE_SSE) - { - emit_movsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FSREG)); // movsd xmm0,[fsreg] - emit_mulsd_r128_m64(DRCTOP, REG_XMM0, FPR64ADDR(FTREG)); // mulsd xmm0,[ftreg] - emit_movsd_r128_m64(DRCTOP, REG_XMM1, FPR64ADDR(FRREG)); // movsd xmm1,[frreg] - emit_subss_r128_r128(DRCTOP, REG_XMM1, REG_XMM0); // subss xmm1,xmm0 - emit_movsd_m64_r128(DRCTOP, FPR64ADDR(FDREG), REG_XMM1); // movsd [fdreg],xmm1 - } - else - { - emit_fld_m64(DRCTOP, FPR64ADDR(FRREG)); // fld [frreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FSREG)); // fld [fsreg] - emit_fld_m64(DRCTOP, FPR64ADDR(FTREG)); // fld [ftreg] - emit_fmulp(DRCTOP); // fmulp - emit_fsubp(DRCTOP); // fsubrp - emit_fstp_m64(DRCTOP, FPR64ADDR(FDREG)); // fstp [fdreg] - } - return RECOMPILE_SUCCESSFUL_CP(1,4); - - case 0x24: /* MADD.W */ - case 0x25: /* MADD.L */ - case 0x2c: /* MSUB.W */ - case 0x2d: /* MSUB.L */ - case 0x34: /* NMADD.W */ - case 0x35: /* NMADD.L */ - case 0x3c: /* NMSUB.W */ - case 0x3d: /* NMSUB.L */ - default: - fprintf(stderr, "cop1x %X\n", op); - break; - } - emit_jmp(DRCTOP, (void *)mips3.drcdata->generate_invalidop_exception); // jmp generate_invalidop_exception - return RECOMPILE_SUCCESSFUL | RECOMPILE_END_OF_STRING; -} diff --git a/src/emu/cpu/mips/mips3.c b/src/emu/cpu/mips/mips3.c index 66627996f67..7f439c9d64e 100644 --- a/src/emu/cpu/mips/mips3.c +++ b/src/emu/cpu/mips/mips3.c @@ -10,6 +10,7 @@ ***************************************************************************/ #include "debugger.h" +#include "deprecat.h" #include "mips3com.h" @@ -1779,12 +1780,12 @@ int mips3_execute(int cycles) break; case 0x20: /* ADD */ if (ENABLE_OVERFLOWS && RSVAL32 > ~RTVAL32) generate_exception(EXCEPTION_OVERFLOW, 1); - else RDVAL64 = (INT32)(RSVAL32 + RTVAL32); + else if (RDREG) RDVAL64 = (INT32)(RSVAL32 + RTVAL32); break; case 0x21: /* ADDU */ if (RDREG) RDVAL64 = (INT32)(RSVAL32 + RTVAL32); break; case 0x22: /* SUB */ if (ENABLE_OVERFLOWS && RSVAL32 < RTVAL32) generate_exception(EXCEPTION_OVERFLOW, 1); - else RDVAL64 = (INT32)(RSVAL32 - RTVAL32); + else if (RDREG) RDVAL64 = (INT32)(RSVAL32 - RTVAL32); break; case 0x23: /* SUBU */ if (RDREG) RDVAL64 = (INT32)(RSVAL32 - RTVAL32); break; case 0x24: /* AND */ if (RDREG) RDVAL64 = RSVAL64 & RTVAL64; break; @@ -1795,12 +1796,12 @@ int mips3_execute(int cycles) case 0x2b: /* SLTU */ if (RDREG) RDVAL64 = (UINT64)RSVAL64 < (UINT64)RTVAL64; break; case 0x2c: /* DADD */ if (ENABLE_OVERFLOWS && RSVAL64 > ~RTVAL64) generate_exception(EXCEPTION_OVERFLOW, 1); - else RDVAL64 = RSVAL64 + RTVAL64; + else if (RDREG) RDVAL64 = RSVAL64 + RTVAL64; break; case 0x2d: /* DADDU */ if (RDREG) RDVAL64 = RSVAL64 + RTVAL64; break; case 0x2e: /* DSUB */ if (ENABLE_OVERFLOWS && RSVAL64 < RTVAL64) generate_exception(EXCEPTION_OVERFLOW, 1); - else RDVAL64 = RSVAL64 - RTVAL64; + else if (RDREG) RDVAL64 = RSVAL64 - RTVAL64; break; case 0x2f: /* DSUBU */ if (RDREG) RDVAL64 = RSVAL64 - RTVAL64; break; case 0x30: /* TGE */ if ((INT64)RSVAL64 >= (INT64)RTVAL64) generate_exception(EXCEPTION_TRAP, 1); break; diff --git a/src/emu/cpu/mips/mips3.h b/src/emu/cpu/mips/mips3.h index e0c22ab7ac0..3ab8ec82ec8 100644 --- a/src/emu/cpu/mips/mips3.h +++ b/src/emu/cpu/mips/mips3.h @@ -54,37 +54,102 @@ enum MIPS3_HI, MIPS3_LO, MIPS3_FPR0, + MIPS3_FPS0, + MIPS3_FPD0, MIPS3_FPR1, + MIPS3_FPS1, + MIPS3_FPD1, MIPS3_FPR2, + MIPS3_FPS2, + MIPS3_FPD2, MIPS3_FPR3, + MIPS3_FPS3, + MIPS3_FPD3, MIPS3_FPR4, + MIPS3_FPS4, + MIPS3_FPD4, MIPS3_FPR5, + MIPS3_FPS5, + MIPS3_FPD5, MIPS3_FPR6, + MIPS3_FPS6, + MIPS3_FPD6, MIPS3_FPR7, + MIPS3_FPS7, + MIPS3_FPD7, MIPS3_FPR8, + MIPS3_FPS8, + MIPS3_FPD8, MIPS3_FPR9, + MIPS3_FPS9, + MIPS3_FPD9, MIPS3_FPR10, + MIPS3_FPS10, + MIPS3_FPD10, MIPS3_FPR11, + MIPS3_FPS11, + MIPS3_FPD11, MIPS3_FPR12, + MIPS3_FPS12, + MIPS3_FPD12, MIPS3_FPR13, + MIPS3_FPS13, + MIPS3_FPD13, MIPS3_FPR14, + MIPS3_FPS14, + MIPS3_FPD14, MIPS3_FPR15, + MIPS3_FPS15, + MIPS3_FPD15, MIPS3_FPR16, + MIPS3_FPS16, + MIPS3_FPD16, MIPS3_FPR17, + MIPS3_FPS17, + MIPS3_FPD17, MIPS3_FPR18, + MIPS3_FPS18, + MIPS3_FPD18, MIPS3_FPR19, + MIPS3_FPS19, + MIPS3_FPD19, MIPS3_FPR20, + MIPS3_FPS20, + MIPS3_FPD20, MIPS3_FPR21, + MIPS3_FPS21, + MIPS3_FPD21, MIPS3_FPR22, + MIPS3_FPS22, + MIPS3_FPD22, MIPS3_FPR23, + MIPS3_FPS23, + MIPS3_FPD23, MIPS3_FPR24, + MIPS3_FPS24, + MIPS3_FPD24, MIPS3_FPR25, + MIPS3_FPS25, + MIPS3_FPD25, MIPS3_FPR26, + MIPS3_FPS26, + MIPS3_FPD26, MIPS3_FPR27, + MIPS3_FPS27, + MIPS3_FPD27, MIPS3_FPR28, + MIPS3_FPS28, + MIPS3_FPD28, MIPS3_FPR29, + MIPS3_FPS29, + MIPS3_FPD29, MIPS3_FPR30, + MIPS3_FPS30, + MIPS3_FPD30, MIPS3_FPR31, + MIPS3_FPS31, + MIPS3_FPD31, + MIPS3_CCR1_31, MIPS3_SR, MIPS3_EPC, MIPS3_CAUSE, diff --git a/src/emu/cpu/mips/mips3com.c b/src/emu/cpu/mips/mips3com.c index 2e10d38d6a4..dfbc777d3a2 100644 --- a/src/emu/cpu/mips/mips3com.c +++ b/src/emu/cpu/mips/mips3com.c @@ -171,12 +171,14 @@ void mips3com_update_cycle_counting(mips3_state *mips) { UINT32 count = (activecpu_gettotalcycles() - mips->count_zero_time) / 2; UINT32 compare = mips->cpr[0][COP0_Compare]; - UINT32 cyclesleft = compare - count; - attotime newtime = ATTOTIME_IN_CYCLES(((INT64)cyclesleft * 2), cpu_getactivecpu()); - timer_adjust_oneshot(mips->compare_int_timer, newtime, cpu_getactivecpu()); + if (compare > count) + { + attotime newtime = ATTOTIME_IN_CYCLES(((INT64)(compare - count) * 2), cpu_getactivecpu()); + timer_adjust_oneshot(mips->compare_int_timer, newtime, cpu_getactivecpu()); + return; + } } - else - timer_adjust_oneshot(mips->compare_int_timer, attotime_never, cpu_getactivecpu()); + timer_adjust_oneshot(mips->compare_int_timer, attotime_never, cpu_getactivecpu()); } @@ -665,38 +667,104 @@ void mips3com_get_info(mips3_state *mips, UINT32 state, cpuinfo *info) case CPUINFO_STR_REGISTER + MIPS3_HI: sprintf(info->s, "HI: %08X%08X", (UINT32)(mips->r[REG_HI] >> 32), (UINT32)mips->r[REG_HI]); break; case CPUINFO_STR_REGISTER + MIPS3_LO: sprintf(info->s, "LO: %08X%08X", (UINT32)(mips->r[REG_LO] >> 32), (UINT32)mips->r[REG_LO]); break; + case CPUINFO_STR_REGISTER + MIPS3_CCR1_31: sprintf(info->s, "CCR31:%08X", (UINT32)mips->ccr[1][0]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPR0: sprintf(info->s, "FPR0: %08X%08X", (UINT32)(mips->cpr[1][0] >> 32), (UINT32)mips->cpr[1][0]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS0: sprintf(info->s, "FPS0: !%16g", *(float *)&mips->cpr[1][0]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD0: sprintf(info->s, "FPD0: !%16g", *(double *)&mips->cpr[1][0]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR1: sprintf(info->s, "FPR1: %08X%08X", (UINT32)(mips->cpr[1][1] >> 32), (UINT32)mips->cpr[1][1]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS1: sprintf(info->s, "FPS1: !%16g", *(float *)&mips->cpr[1][1]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD1: sprintf(info->s, "FPD1: !%16g", *(double *)&mips->cpr[1][1]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR2: sprintf(info->s, "FPR2: %08X%08X", (UINT32)(mips->cpr[1][2] >> 32), (UINT32)mips->cpr[1][2]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS2: sprintf(info->s, "FPS2: !%16g", *(float *)&mips->cpr[1][2]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD2: sprintf(info->s, "FPD2: !%16g", *(double *)&mips->cpr[1][2]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR3: sprintf(info->s, "FPR3: %08X%08X", (UINT32)(mips->cpr[1][3] >> 32), (UINT32)mips->cpr[1][3]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS3: sprintf(info->s, "FPS3: !%16g", *(float *)&mips->cpr[1][3]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD3: sprintf(info->s, "FPD3: !%16g", *(double *)&mips->cpr[1][3]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR4: sprintf(info->s, "FPR4: %08X%08X", (UINT32)(mips->cpr[1][4] >> 32), (UINT32)mips->cpr[1][4]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS4: sprintf(info->s, "FPS4: !%16g", *(float *)&mips->cpr[1][4]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD4: sprintf(info->s, "FPD4: !%16g", *(double *)&mips->cpr[1][4]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR5: sprintf(info->s, "FPR5: %08X%08X", (UINT32)(mips->cpr[1][5] >> 32), (UINT32)mips->cpr[1][5]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS5: sprintf(info->s, "FPS5: !%16g", *(float *)&mips->cpr[1][5]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD5: sprintf(info->s, "FPD5: !%16g", *(double *)&mips->cpr[1][5]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR6: sprintf(info->s, "FPR6: %08X%08X", (UINT32)(mips->cpr[1][6] >> 32), (UINT32)mips->cpr[1][6]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS6: sprintf(info->s, "FPS6: !%16g", *(float *)&mips->cpr[1][6]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD6: sprintf(info->s, "FPD6: !%16g", *(double *)&mips->cpr[1][6]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR7: sprintf(info->s, "FPR7: %08X%08X", (UINT32)(mips->cpr[1][7] >> 32), (UINT32)mips->cpr[1][7]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS7: sprintf(info->s, "FPS7: !%16g", *(float *)&mips->cpr[1][7]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD7: sprintf(info->s, "FPD7: !%16g", *(double *)&mips->cpr[1][7]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR8: sprintf(info->s, "FPR8: %08X%08X", (UINT32)(mips->cpr[1][8] >> 32), (UINT32)mips->cpr[1][8]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS8: sprintf(info->s, "FPS8: !%16g", *(float *)&mips->cpr[1][8]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD8: sprintf(info->s, "FPD8: !%16g", *(double *)&mips->cpr[1][8]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR9: sprintf(info->s, "FPR9: %08X%08X", (UINT32)(mips->cpr[1][9] >> 32), (UINT32)mips->cpr[1][9]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS9: sprintf(info->s, "FPS9: !%16g", *(float *)&mips->cpr[1][9]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD9: sprintf(info->s, "FPD9: !%16g", *(double *)&mips->cpr[1][9]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR10: sprintf(info->s, "FPR10:%08X%08X", (UINT32)(mips->cpr[1][10] >> 32), (UINT32)mips->cpr[1][10]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS10: sprintf(info->s, "FPS10:!%16g", *(float *)&mips->cpr[1][10]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD10: sprintf(info->s, "FPD10:!%16g", *(double *)&mips->cpr[1][10]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR11: sprintf(info->s, "FPR11:%08X%08X", (UINT32)(mips->cpr[1][11] >> 32), (UINT32)mips->cpr[1][11]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS11: sprintf(info->s, "FPS11:!%16g", *(float *)&mips->cpr[1][11]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD11: sprintf(info->s, "FPD11:!%16g", *(double *)&mips->cpr[1][11]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR12: sprintf(info->s, "FPR12:%08X%08X", (UINT32)(mips->cpr[1][12] >> 32), (UINT32)mips->cpr[1][12]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS12: sprintf(info->s, "FPS12:!%16g", *(float *)&mips->cpr[1][12]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD12: sprintf(info->s, "FPD12:!%16g", *(double *)&mips->cpr[1][12]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR13: sprintf(info->s, "FPR13:%08X%08X", (UINT32)(mips->cpr[1][13] >> 32), (UINT32)mips->cpr[1][13]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS13: sprintf(info->s, "FPS13:!%16g", *(float *)&mips->cpr[1][13]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD13: sprintf(info->s, "FPD13:!%16g", *(double *)&mips->cpr[1][13]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR14: sprintf(info->s, "FPR14:%08X%08X", (UINT32)(mips->cpr[1][14] >> 32), (UINT32)mips->cpr[1][14]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS14: sprintf(info->s, "FPS14:!%16g", *(float *)&mips->cpr[1][14]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD14: sprintf(info->s, "FPD14:!%16g", *(double *)&mips->cpr[1][14]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR15: sprintf(info->s, "FPR15:%08X%08X", (UINT32)(mips->cpr[1][15] >> 32), (UINT32)mips->cpr[1][15]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS15: sprintf(info->s, "FPS15:!%16g", *(float *)&mips->cpr[1][15]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD15: sprintf(info->s, "FPD15:!%16g", *(double *)&mips->cpr[1][15]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR16: sprintf(info->s, "FPR16:%08X%08X", (UINT32)(mips->cpr[1][16] >> 32), (UINT32)mips->cpr[1][16]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS16: sprintf(info->s, "FPS16:!%16g", *(float *)&mips->cpr[1][16]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD16: sprintf(info->s, "FPD16:!%16g", *(double *)&mips->cpr[1][16]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR17: sprintf(info->s, "FPR17:%08X%08X", (UINT32)(mips->cpr[1][17] >> 32), (UINT32)mips->cpr[1][17]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS17: sprintf(info->s, "FPS17:!%16g", *(float *)&mips->cpr[1][17]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD17: sprintf(info->s, "FPD17:!%16g", *(double *)&mips->cpr[1][17]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR18: sprintf(info->s, "FPR18:%08X%08X", (UINT32)(mips->cpr[1][18] >> 32), (UINT32)mips->cpr[1][18]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS18: sprintf(info->s, "FPS18:!%16g", *(float *)&mips->cpr[1][18]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD18: sprintf(info->s, "FPD18:!%16g", *(double *)&mips->cpr[1][18]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR19: sprintf(info->s, "FPR19:%08X%08X", (UINT32)(mips->cpr[1][19] >> 32), (UINT32)mips->cpr[1][19]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS19: sprintf(info->s, "FPS19:!%16g", *(float *)&mips->cpr[1][19]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD19: sprintf(info->s, "FPD19:!%16g", *(double *)&mips->cpr[1][19]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR20: sprintf(info->s, "FPR20:%08X%08X", (UINT32)(mips->cpr[1][20] >> 32), (UINT32)mips->cpr[1][20]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS20: sprintf(info->s, "FPS20:!%16g", *(float *)&mips->cpr[1][20]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD20: sprintf(info->s, "FPD20:!%16g", *(double *)&mips->cpr[1][20]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR21: sprintf(info->s, "FPR21:%08X%08X", (UINT32)(mips->cpr[1][21] >> 32), (UINT32)mips->cpr[1][21]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS21: sprintf(info->s, "FPS21:!%16g", *(float *)&mips->cpr[1][21]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD21: sprintf(info->s, "FPD21:!%16g", *(double *)&mips->cpr[1][21]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR22: sprintf(info->s, "FPR22:%08X%08X", (UINT32)(mips->cpr[1][22] >> 32), (UINT32)mips->cpr[1][22]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS22: sprintf(info->s, "FPS22:!%16g", *(float *)&mips->cpr[1][22]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD22: sprintf(info->s, "FPD22:!%16g", *(double *)&mips->cpr[1][22]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR23: sprintf(info->s, "FPR23:%08X%08X", (UINT32)(mips->cpr[1][23] >> 32), (UINT32)mips->cpr[1][23]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS23: sprintf(info->s, "FPS23:!%16g", *(float *)&mips->cpr[1][23]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD23: sprintf(info->s, "FPD23:!%16g", *(double *)&mips->cpr[1][23]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR24: sprintf(info->s, "FPR24:%08X%08X", (UINT32)(mips->cpr[1][24] >> 32), (UINT32)mips->cpr[1][24]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS24: sprintf(info->s, "FPS24:!%16g", *(float *)&mips->cpr[1][24]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD24: sprintf(info->s, "FPD24:!%16g", *(double *)&mips->cpr[1][24]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR25: sprintf(info->s, "FPR25:%08X%08X", (UINT32)(mips->cpr[1][25] >> 32), (UINT32)mips->cpr[1][25]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS25: sprintf(info->s, "FPS25:!%16g", *(float *)&mips->cpr[1][25]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD25: sprintf(info->s, "FPD25:!%16g", *(double *)&mips->cpr[1][25]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR26: sprintf(info->s, "FPR26:%08X%08X", (UINT32)(mips->cpr[1][26] >> 32), (UINT32)mips->cpr[1][26]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS26: sprintf(info->s, "FPS26:!%16g", *(float *)&mips->cpr[1][26]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD26: sprintf(info->s, "FPD26:!%16g", *(double *)&mips->cpr[1][26]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR27: sprintf(info->s, "FPR27:%08X%08X", (UINT32)(mips->cpr[1][27] >> 32), (UINT32)mips->cpr[1][27]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS27: sprintf(info->s, "FPS27:!%16g", *(float *)&mips->cpr[1][27]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD27: sprintf(info->s, "FPD27:!%16g", *(double *)&mips->cpr[1][27]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR28: sprintf(info->s, "FPR28:%08X%08X", (UINT32)(mips->cpr[1][28] >> 32), (UINT32)mips->cpr[1][28]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS28: sprintf(info->s, "FPS28:!%16g", *(float *)&mips->cpr[1][28]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD28: sprintf(info->s, "FPD28:!%16g", *(double *)&mips->cpr[1][28]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR29: sprintf(info->s, "FPR29:%08X%08X", (UINT32)(mips->cpr[1][29] >> 32), (UINT32)mips->cpr[1][29]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS29: sprintf(info->s, "FPS29:!%16g", *(float *)&mips->cpr[1][29]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD29: sprintf(info->s, "FPD29:!%16g", *(double *)&mips->cpr[1][29]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR30: sprintf(info->s, "FPR30:%08X%08X", (UINT32)(mips->cpr[1][30] >> 32), (UINT32)mips->cpr[1][30]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS30: sprintf(info->s, "FPS30:!%16g", *(float *)&mips->cpr[1][30]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD30: sprintf(info->s, "FPD30:!%16g", *(double *)&mips->cpr[1][30]); break; case CPUINFO_STR_REGISTER + MIPS3_FPR31: sprintf(info->s, "FPR31:%08X%08X", (UINT32)(mips->cpr[1][31] >> 32), (UINT32)mips->cpr[1][31]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPS31: sprintf(info->s, "FPS31:!%16g", *(float *)&mips->cpr[1][31]); break; + case CPUINFO_STR_REGISTER + MIPS3_FPD31: sprintf(info->s, "FPD31:!%16g", *(double *)&mips->cpr[1][31]); break; } } diff --git a/src/emu/cpu/mips/mips3drc.c b/src/emu/cpu/mips/mips3drc.c index 50dadefb448..11666848934 100644 --- a/src/emu/cpu/mips/mips3drc.c +++ b/src/emu/cpu/mips/mips3drc.c @@ -1,13 +1,33 @@ /*************************************************************************** mips3drc.c - x86 Dynamic recompiler for MIPS III/IV emulator. - Written by Aaron Giles - Philosophy: this is intended to be a very basic implementation of a - dynamic compiler in order to keep things simple. There are certainly - more optimizations that could be added but for now, we keep it - strictly to NOP stripping and LUI optimizations. + Universal machine language-based MIPS III/IV emulator. + + Copyright Aaron Giles + Released for general non-commercial use under the MAME license + Visit http://mamedev.org for licensing and usage restrictions. + +**************************************************************************** + + Future improvements/changes: + + * Add DRC option to flush PC before calling memory handlers + + * Map MIPS registers to i5-i7 (or more) + - should rely on backend hints to know how many to assign + - add instrumentation to figure out which ones are most used + - on entry, load registers; on exit, flush them + - also flush before calling debugger + + * Constant tracking? (hasn't bought us much in the past) + + * Customized mapped/unmapped memory handlers + - create 3 sets of handlers: cached, uncached, general + - default to general + - in general case, if cached use RECALL to point to cached code + - (same for uncached) + - in cached/uncached case, fall back to general case ***************************************************************************/ @@ -17,8 +37,9 @@ #include "mips3com.h" #include "mips3fe.h" #include "deprecat.h" -#include "cpu/x86log.h" #include "cpu/drcfe.h" +#include "cpu/drcuml.h" +#include "cpu/drcumlsh.h" extern unsigned dasmmips3(char *buffer, unsigned pc, UINT32 op); @@ -28,21 +49,25 @@ extern unsigned dasmmips3(char *buffer, unsigned pc, UINT32 op); DEBUGGING ***************************************************************************/ -#define LOG_CODE (0) -#define SINGLE_INSTRUCTION_MODE (0) +#define FORCE_C_BACKEND (0) +#define LOG_UML (0) +#define LOG_NATIVE (0) -#ifdef ENABLE_DEBUGGER -#define COMPARE_AGAINST_C (0) -#else -#define COMPARE_AGAINST_C (0) -#endif +#define SINGLE_INSTRUCTION_MODE (0) +#define PRINTF_EXCEPTIONS (0) + +#define PROBE_ADDRESS ~0 /*************************************************************************** - CONFIGURATION + CONSTANTS ***************************************************************************/ +/* map variables */ +#define MAPVAR_PC MVAR(0) +#define MAPVAR_CYCLES MVAR(1) + /* size of the execution code cache */ #define CACHE_SIZE (32 * 1024 * 1024) @@ -52,13 +77,9 @@ extern unsigned dasmmips3(char *buffer, unsigned pc, UINT32 op); #define COMPILE_MAX_INSTRUCTIONS ((COMPILE_BACKWARDS_BYTES/4) + (COMPILE_FORWARDS_BYTES/4)) #define COMPILE_MAX_SEQUENCE 64 -/* hack when running comparison against the C core */ -#if COMPARE_AGAINST_C -#undef MIPS3_COUNT_READ_CYCLES -#undef MIPS3_CAUSE_READ_CYCLES -#define MIPS3_COUNT_READ_CYCLES 0 -#define MIPS3_CAUSE_READ_CYCLES 0 -#endif +/* exit codes */ +#define EXECUTE_OUT_OF_CYCLES 0 +#define EXECUTE_MISSING_CODE 1 @@ -66,9 +87,33 @@ extern unsigned dasmmips3(char *buffer, unsigned pc, UINT32 op); MACROS ***************************************************************************/ -#define SR mips3.core->cpr[0][COP0_Status] -#define IS_FR0 (!(SR & SR_FR)) -#define IS_FR1 (SR & SR_FR) +#ifdef LSB_FIRST +#define LOPTR(x) ((UINT32 *)(x)) +#else +#define LOPTR(x) ((UINT32 *)(x) + 1) +#endif + +#define R32(reg) ((reg == 0) ? DRCUML_PTYPE_IMMEDIATE : DRCUML_PTYPE_MEMORY), ((reg) == 0) ? 0 : (FPTR)LOPTR(&mips3.core->r[reg]) +#define LO32 R32(REG_LO) +#define HI32 R32(REG_HI) +#define CPR032(reg) MEM(LOPTR(&mips3.core->cpr[0][reg])) +#define CCR032(reg) MEM(LOPTR(&mips3.core->ccr[0][reg])) +#define FPR32(reg) MEM((mips3.cstate->mode == 0) ? &((float *)&mips3.core->cpr[1][0])[reg] : (float *)&mips3.core->cpr[1][reg]) +#define CCR132(reg) MEM(LOPTR(&mips3.core->ccr[1][reg])) +#define CPR232(reg) MEM(LOPTR(&mips3.core->cpr[2][reg])) +#define CCR232(reg) MEM(LOPTR(&mips3.core->ccr[2][reg])) + +#define R64(reg) ((reg == 0) ? DRCUML_PTYPE_IMMEDIATE : DRCUML_PTYPE_MEMORY), ((reg) == 0) ? 0 : (FPTR)&mips3.core->r[reg] +#define LO64 R64(REG_LO) +#define HI64 R64(REG_HI) +#define CPR064(reg) MEM(&mips3.core->cpr[0][reg]) +#define CCR064(reg) MEM(&mips3.core->ccr[0][reg]) +#define FPR64(reg) MEM((mips3.cstate->mode == 0) ? (double *)&mips3.core->cpr[1][(reg)/2] : (double *)&mips3.core->cpr[1][reg]) +#define CCR164(reg) MEM(&mips3.core->ccr[1][reg]) +#define CPR264(reg) MEM(&mips3.core->cpr[2][reg]) +#define CCR264(reg) MEM(&mips3.core->ccr[2][reg]) + +#define FCCMASK(which) fcc_mask[(mips3.core->flavor < MIPS3_TYPE_MIPS_IV) ? 0 : ((which) & 7)] @@ -80,10 +125,10 @@ extern unsigned dasmmips3(char *buffer, unsigned pc, UINT32 op); typedef struct _fast_ram_info fast_ram_info; struct _fast_ram_info { - offs_t start; - offs_t end; - UINT8 readonly; - void * base; + offs_t start; /* start of the RAM block */ + offs_t end; /* end of the RAM block */ + UINT8 readonly; /* TRUE if read-only */ + void * base; /* base in memory where the RAM lives */ }; @@ -91,14 +136,43 @@ struct _fast_ram_info typedef struct _hotspot_info hotspot_info; struct _hotspot_info { - offs_t pc; - UINT32 opcode; - UINT32 cycles; + offs_t pc; /* PC to consider */ + UINT32 opcode; /* required opcode at that PC */ + UINT32 cycles; /* number of cycles to eat when hit */ }; -/* opaque drc-specific info */ -typedef struct _mips3drc_data mips3drc_data; +/* internal compiler state */ +typedef struct _compiler_state compiler_state; +struct _compiler_state +{ + UINT32 cycles; /* accumulated cycles */ + UINT8 checkints; /* need to check interrupts before next instruction */ + UINT8 checksoftints; /* need to check software interrupts before next instruction */ + drcuml_codelabel labelnum; /* index for local labels */ +}; + + +/* cache-based state */ +typedef struct _mips3_cache_state mips3_cache_state; +struct _mips3_cache_state +{ + /* parameters for subroutines */ + UINT64 numcycles; /* return value from gettotalcycles */ + UINT32 mode; /* current global mode */ + + /* tables */ + UINT8 fpmode[4]; /* FPU mode table */ + UINT64 slt_table[32]; /* FLAGS table for slt */ + UINT64 sltu_table[32]; /* FLAGS table for sltu */ + UINT32 c_un_table[32]; /* FLAGS table for c_un */ + UINT32 c_eq_table[32]; /* FLAGS table for c_eq */ + UINT32 c_ueq_table[32]; /* FLAGS table for c_ueq */ + UINT32 c_olt_table[32]; /* FLAGS table for c_olt */ + UINT32 c_ult_table[32]; /* FLAGS table for c_ult */ + UINT32 c_ole_table[32]; /* FLAGS table for c_ole */ + UINT32 c_ule_table[32]; /* FLAGS table for c_ule */ +}; /* MIPS3 registers */ @@ -106,37 +180,43 @@ typedef struct _mips3_regs mips3_regs; struct _mips3_regs { /* core state */ - UINT8 * cache; /* base of the cache */ - mips3_state * core; /* pointer to the core MIPS3 state */ - drcfe_state * drcfe; /* pointer to the DRC front-end state */ - drc_core * drc; /* pointer to the DRC core */ - mips3drc_data * drcdata; /* pointer to the DRC-specific data */ - UINT32 drcoptions; /* configurable DRC options */ + drccache * cache; /* pointer to the DRC code cache */ + drcuml_state * drcuml; /* DRC UML generator state */ + drcfe_state * drcfe; /* pointer to the DRC front-end state */ + UINT32 drcoptions; /* configurable DRC options */ + mips3_state * core; /* pointer to the core MIPS3 state */ + mips3_cache_state * cstate; /* state that needs to live in the cache */ /* internal stuff */ - UINT32 nextpc; - UINT8 cache_dirty; - + UINT8 cache_dirty; /* true if we need to flush the cache */ + + /* subroutines */ + drcuml_codehandle * entry; /* entry point */ + drcuml_codehandle * nocode; /* nocode exception handler */ + drcuml_codehandle * out_of_cycles; /* out of cycles exception handler */ + drcuml_codehandle * tlb_mismatch; /* tlb mismatch at current PC */ + drcuml_codehandle * read8; /* read byte */ + drcuml_codehandle * write8; /* write byte */ + drcuml_codehandle * read16; /* read half */ + drcuml_codehandle * write16; /* write half */ + drcuml_codehandle * read32; /* read word */ + drcuml_codehandle * read32mask; /* read word masked */ + drcuml_codehandle * write32; /* write word */ + drcuml_codehandle * write32mask; /* write word masked */ + drcuml_codehandle * read64; /* read double */ + drcuml_codehandle * read64mask; /* read double masked */ + drcuml_codehandle * write64; /* write double */ + drcuml_codehandle * write64mask; /* write double masked */ + drcuml_codehandle * exception[EXCEPTION_COUNT]; /* array of exception handlers */ + drcuml_codehandle * exception_norecover[EXCEPTION_COUNT]; /* array of no-recover exception handlers */ + /* fast RAM */ - UINT32 fastram_select; - fast_ram_info fastram[MIPS3_MAX_FASTRAM]; + UINT32 fastram_select; + fast_ram_info fastram[MIPS3_MAX_FASTRAM]; /* hotspots */ - UINT32 hotspot_select; - hotspot_info hotspot[MIPS3_MAX_HOTSPOTS]; - - /* code logging */ - x86log_context *log; - -#if COMPARE_AGAINST_C - /* memory state */ - UINT8 access_size; - UINT8 access_type; - offs_t access_addr; - UINT64 access_data; - UINT64 access_mask; - memory_handlers orighandler; -#endif + UINT32 hotspot_select; + hotspot_info hotspot[MIPS3_MAX_HOTSPOTS]; }; @@ -145,15 +225,37 @@ struct _mips3_regs FUNCTION PROTOTYPES ***************************************************************************/ -static void mips3drc_init(void); -static void mips3drc_exit(void); +static void code_flush_cache(drcuml_state *drcuml); +static void code_compile_block(drcuml_state *drcuml, UINT8 mode, offs_t pc); -#if COMPARE_AGAINST_C -static void execute_c_version(void); -static void compare_c_version(void); -static void mips3c_init(mips3_flavor flavor, int bigendian, int index, int clock, const struct mips3_config *config, int (*irqcallback)(int)); -static void mips3c_reset(void); -#endif +static void cfunc_printf_exception(void *param); +static void cfunc_get_cycles(void *param); +static void cfunc_printf_probe(void *param); + +static void static_generate_entry_point(drcuml_state *drcuml); +static void static_generate_nocode_handler(drcuml_state *drcuml); +static void static_generate_out_of_cycles(drcuml_state *drcuml); +static void static_generate_exception(drcuml_state *drcuml, UINT8 exception, int recover, const char *name); +static void static_generate_memory_accessor(drcuml_state *drcuml, int size, int iswrite, int ismasked, const char *name, drcuml_codehandle **handleptr); + +static void generate_update_cycles(drcuml_block *block, compiler_state *compiler, UINT32 nextpc, UINT32 *mempc, int allow_exception); +static void generate_checksum_block(drcuml_block *block, compiler_state *compiler, const opcode_desc *seqhead, const opcode_desc *seqlast); +static void generate_sequence_instruction(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc); +static void generate_delay_slot_and_branch(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc, UINT8 linkreg); +static int generate_opcode(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc); +static int generate_special(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc); +static int generate_regimm(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc); +static int generate_idt(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc); +static int generate_set_cop0_reg(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc, UINT8 reg); +static int generate_get_cop0_reg(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc, UINT8 reg); +static int generate_cop0(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc); +static int generate_cop1(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc); +static int generate_cop1x(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc); + +static void log_add_disasm_comment(drcuml_block *block, UINT32 pc, UINT32 op); +static const char *log_desc_flags_to_string(UINT32 flags); +static void log_register_list(drcuml_state *drcuml, const char *string, UINT64 gprmask, UINT64 fprmask); +static void log_opcode_desc(drcuml_state *drcuml, const opcode_desc *desclist, int indent); @@ -162,7 +264,380 @@ static void mips3c_reset(void); ***************************************************************************/ static mips3_regs mips3; -static size_t allocatedsize; + + +/* bit indexes for various FCCs */ +static const UINT32 fcc_mask[8] = { 1 << 23, 1 << 25, 1 << 26, 1 << 27, 1 << 28, 1 << 29, 1 << 30, 1 << 31 }; + +/* lookup table for FP modes */ +static const UINT8 fpmode_source[4] = +{ + DRCUML_FMOD_ROUND, + DRCUML_FMOD_TRUNC, + DRCUML_FMOD_CEIL, + DRCUML_FMOD_FLOOR +}; + +/* flag lookup table for SLT */ +static const UINT64 slt_table_source[32] = +{ + /* ..... */ 0, + /* ....C */ 0, + /* ...V. */ 1, + /* ...VC */ 1, + /* ..Z.. */ 0, + /* ..Z.C */ 0, + /* ..ZV. */ 1, + /* ..ZVC */ 1, + /* .S... */ 1, + /* .S..C */ 1, + /* .S.V. */ 0, + /* .S.VC */ 0, + /* .SZ.. */ 1, + /* .SZ.C */ 1, + /* .SZV. */ 0, + /* .SZVC */ 0, + /* U.... */ 0, + /* U...C */ 0, + /* U..V. */ 1, + /* U..VC */ 1, + /* U.Z.. */ 0, + /* U.Z.C */ 0, + /* U.ZV. */ 1, + /* U.ZVC */ 1, + /* US... */ 1, + /* US..C */ 1, + /* US.V. */ 0, + /* US.VC */ 0, + /* USZ.. */ 1, + /* USZ.C */ 1, + /* USZV. */ 0, + /* USZVC */ 0 +}; + +/* flag lookup table for SLTU */ +static const UINT64 sltu_table_source[32] = +{ + /* ..... */ 0, + /* ....C */ 1, + /* ...V. */ 0, + /* ...VC */ 1, + /* ..Z.. */ 0, + /* ..Z.C */ 1, + /* ..ZV. */ 0, + /* ..ZVC */ 1, + /* .S... */ 0, + /* .S..C */ 1, + /* .S.V. */ 0, + /* .S.VC */ 1, + /* .SZ.. */ 0, + /* .SZ.C */ 1, + /* .SZV. */ 0, + /* .SZVC */ 1, + /* U.... */ 0, + /* U...C */ 1, + /* U..V. */ 0, + /* U..VC */ 1, + /* U.Z.. */ 0, + /* U.Z.C */ 1, + /* U.ZV. */ 0, + /* U.ZVC */ 1, + /* US... */ 0, + /* US..C */ 1, + /* US.V. */ 0, + /* US.VC */ 1, + /* USZ.. */ 0, + /* USZ.C */ 1, + /* USZV. */ 0, + /* USZVC */ 1 +}; + +/* flag lookup table for C.UN */ +static const UINT32 c_un_table_source[32] = +{ + /* ..... */ 0, + /* ....C */ 0, + /* ...V. */ 0, + /* ...VC */ 0, + /* ..Z.. */ 0, + /* ..Z.C */ 0, + /* ..ZV. */ 0, + /* ..ZVC */ 0, + /* .S... */ 0, + /* .S..C */ 0, + /* .S.V. */ 0, + /* .S.VC */ 0, + /* .SZ.. */ 0, + /* .SZ.C */ 0, + /* .SZV. */ 0, + /* .SZVC */ 0, + /* U.... */ ~0, + /* U...C */ ~0, + /* U..V. */ ~0, + /* U..VC */ ~0, + /* U.Z.. */ ~0, + /* U.Z.C */ ~0, + /* U.ZV. */ ~0, + /* U.ZVC */ ~0, + /* US... */ ~0, + /* US..C */ ~0, + /* US.V. */ ~0, + /* US.VC */ ~0, + /* USZ.. */ ~0, + /* USZ.C */ ~0, + /* USZV. */ ~0, + /* USZVC */ ~0 +}; + +/* flag lookup table for C.EQ */ +static const UINT32 c_eq_table_source[32] = +{ + /* ..... */ 0, + /* ....C */ 0, + /* ...V. */ 0, + /* ...VC */ 0, + /* ..Z.. */ ~0, + /* ..Z.C */ ~0, + /* ..ZV. */ ~0, + /* ..ZVC */ ~0, + /* .S... */ 0, + /* .S..C */ 0, + /* .S.V. */ 0, + /* .S.VC */ 0, + /* .SZ.. */ ~0, + /* .SZ.C */ ~0, + /* .SZV. */ ~0, + /* .SZVC */ ~0, + /* U.... */ 0, + /* U...C */ 0, + /* U..V. */ 0, + /* U..VC */ 0, + /* U.Z.. */ 0, + /* U.Z.C */ 0, + /* U.ZV. */ 0, + /* U.ZVC */ 0, + /* US... */ 0, + /* US..C */ 0, + /* US.V. */ 0, + /* US.VC */ 0, + /* USZ.. */ 0, + /* USZ.C */ 0, + /* USZV. */ 0, + /* USZVC */ 0 +}; + +/* flag lookup table for C.UEQ */ +static const UINT32 c_ueq_table_source[32] = +{ + /* ..... */ 0, + /* ....C */ 0, + /* ...V. */ 0, + /* ...VC */ 0, + /* ..Z.. */ ~0, + /* ..Z.C */ ~0, + /* ..ZV. */ ~0, + /* ..ZVC */ ~0, + /* .S... */ 0, + /* .S..C */ 0, + /* .S.V. */ 0, + /* .S.VC */ 0, + /* .SZ.. */ ~0, + /* .SZ.C */ ~0, + /* .SZV. */ ~0, + /* .SZVC */ ~0, + /* U.... */ ~0, + /* U...C */ ~0, + /* U..V. */ ~0, + /* U..VC */ ~0, + /* U.Z.. */ ~0, + /* U.Z.C */ ~0, + /* U.ZV. */ ~0, + /* U.ZVC */ ~0, + /* US... */ ~0, + /* US..C */ ~0, + /* US.V. */ ~0, + /* US.VC */ ~0, + /* USZ.. */ ~0, + /* USZ.C */ ~0, + /* USZV. */ ~0, + /* USZVC */ ~0 +}; + +/* flag lookup table for C.OLT */ +static const UINT32 c_olt_table_source[32] = +{ + /* ..... */ 0, + /* ....C */ ~0, + /* ...V. */ 0, + /* ...VC */ ~0, + /* ..Z.. */ 0, + /* ..Z.C */ ~0, + /* ..ZV. */ 0, + /* ..ZVC */ ~0, + /* .S... */ 0, + /* .S..C */ ~0, + /* .S.V. */ 0, + /* .S.VC */ ~0, + /* .SZ.. */ 0, + /* .SZ.C */ ~0, + /* .SZV. */ 0, + /* .SZVC */ ~0, + /* U.... */ 0, + /* U...C */ 0, + /* U..V. */ 0, + /* U..VC */ 0, + /* U.Z.. */ 0, + /* U.Z.C */ 0, + /* U.ZV. */ 0, + /* U.ZVC */ 0, + /* US... */ 0, + /* US..C */ 0, + /* US.V. */ 0, + /* US.VC */ 0, + /* USZ.. */ 0, + /* USZ.C */ 0, + /* USZV. */ 0, + /* USZVC */ 0 +}; + +/* flag lookup table for C.ULT */ +static const UINT32 c_ult_table_source[32] = +{ + /* ..... */ 0, + /* ....C */ ~0, + /* ...V. */ 0, + /* ...VC */ ~0, + /* ..Z.. */ 0, + /* ..Z.C */ ~0, + /* ..ZV. */ 0, + /* ..ZVC */ ~0, + /* .S... */ 0, + /* .S..C */ ~0, + /* .S.V. */ 0, + /* .S.VC */ ~0, + /* .SZ.. */ 0, + /* .SZ.C */ ~0, + /* .SZV. */ 0, + /* .SZVC */ ~0, + /* U.... */ ~0, + /* U...C */ ~0, + /* U..V. */ ~0, + /* U..VC */ ~0, + /* U.Z.. */ ~0, + /* U.Z.C */ ~0, + /* U.ZV. */ ~0, + /* U.ZVC */ ~0, + /* US... */ ~0, + /* US..C */ ~0, + /* US.V. */ ~0, + /* US.VC */ ~0, + /* USZ.. */ ~0, + /* USZ.C */ ~0, + /* USZV. */ ~0, + /* USZVC */ ~0 +}; + +/* flag lookup table for C.OLE */ +static const UINT32 c_ole_table_source[32] = +{ + /* ..... */ 0, + /* ....C */ ~0, + /* ...V. */ 0, + /* ...VC */ ~0, + /* ..Z.. */ ~0, + /* ..Z.C */ ~0, + /* ..ZV. */ ~0, + /* ..ZVC */ ~0, + /* .S... */ 0, + /* .S..C */ ~0, + /* .S.V. */ 0, + /* .S.VC */ ~0, + /* .SZ.. */ ~0, + /* .SZ.C */ ~0, + /* .SZV. */ ~0, + /* .SZVC */ ~0, + /* U.... */ 0, + /* U...C */ 0, + /* U..V. */ 0, + /* U..VC */ 0, + /* U.Z.. */ 0, + /* U.Z.C */ 0, + /* U.ZV. */ 0, + /* U.ZVC */ 0, + /* US... */ 0, + /* US..C */ 0, + /* US.V. */ 0, + /* US.VC */ 0, + /* USZ.. */ 0, + /* USZ.C */ 0, + /* USZV. */ 0, + /* USZVC */ 0 +}; + +/* flag lookup table for C.ULE */ +static const UINT32 c_ule_table_source[32] = +{ + /* ..... */ 0, + /* ....C */ ~0, + /* ...V. */ 0, + /* ...VC */ ~0, + /* ..Z.. */ ~0, + /* ..Z.C */ ~0, + /* ..ZV. */ ~0, + /* ..ZVC */ ~0, + /* .S... */ 0, + /* .S..C */ ~0, + /* .S.V. */ 0, + /* .S.VC */ ~0, + /* .SZ.. */ ~0, + /* .SZ.C */ ~0, + /* .SZV. */ ~0, + /* .SZVC */ ~0, + /* U.... */ ~0, + /* U...C */ ~0, + /* U..V. */ ~0, + /* U..VC */ ~0, + /* U.Z.. */ ~0, + /* U.Z.C */ ~0, + /* U.ZV. */ ~0, + /* U.ZVC */ ~0, + /* US... */ ~0, + /* US..C */ ~0, + /* US.V. */ ~0, + /* US.VC */ ~0, + /* USZ.. */ ~0, + /* USZ.C */ ~0, + /* USZV. */ ~0, + /* USZVC */ ~0 +}; + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + epc - compute the exception PC from a + descriptor +-------------------------------------------------*/ + +INLINE UINT32 epc(const opcode_desc *desc) +{ + return (desc->flags & OPFLAG_IN_DELAY_SLOT) ? (desc->pc - 3) : desc->pc; +} + + +/*------------------------------------------------- + alloc_handle - allocate a handle if not + already allocated +-------------------------------------------------*/ + +INLINE void alloc_handle(drcuml_state *drcuml, drcuml_codehandle **handleptr, const char *name) +{ + if (*handleptr == NULL) + *handleptr = drcuml_handle_alloc(drcuml, name); +} @@ -183,30 +658,57 @@ static void mips3_init(mips3_flavor flavor, int bigendian, int index, int clock, COMPILE_MAX_SEQUENCE, /* maximum instructions to include in a sequence */ mips3fe_describe /* callback to describe a single instruction */ }; - size_t memsize = mips3com_init(NULL, flavor, bigendian, index, clock, config, irqcallback, NULL); - - /* allocate a cache and memory for the core data in a single block */ - allocatedsize = CACHE_SIZE + sizeof(*mips3.core) + memsize; - mips3.core = osd_alloc_executable(allocatedsize); - if (mips3.core == NULL) - fatalerror("Unable to allocate cache of size %d\n", (UINT32)allocatedsize); - mips3.cache = (UINT8 *)(mips3.core + 1) + memsize; + UINT32 flags = 0; + size_t extrasize; + void *extramem; + + /* determine how much memory beyond the core size we need */ + extrasize = mips3com_init(NULL, flavor, bigendian, index, clock, config, irqcallback, NULL); + + /* allocate enough space for the cache and the core */ + mips3.cache = drccache_alloc(CACHE_SIZE + sizeof(*mips3.core) + extrasize); + if (mips3.cache == NULL) + fatalerror("Unable to allocate cache of size %d", (UINT32)(CACHE_SIZE + sizeof(*mips3.core) + extrasize)); + + /* allocate the core and the extra memory */ + mips3.core = drccache_memory_alloc_near(mips3.cache, sizeof(*mips3.core)); + extramem = drccache_memory_alloc(mips3.cache, extrasize); /* initialize the core */ - mips3com_init(mips3.core, flavor, bigendian, index, clock, config, irqcallback, mips3.core + 1); -#if COMPARE_AGAINST_C - mips3c_init(flavor, bigendian, index, clock, config, irqcallback); -#endif - - /* initialize the DRC to use the cache */ - mips3drc_init(); - if (Machine->debug_mode || SINGLE_INSTRUCTION_MODE) + mips3com_init(mips3.core, flavor, bigendian, index, clock, config, irqcallback, extramem); + + /* allocate memory for cache-local state and initialize it */ + mips3.cstate = drccache_memory_alloc_near(mips3.cache, sizeof(*mips3.cstate)); + memset(mips3.cstate, 0, sizeof(*mips3.cstate)); + memcpy(mips3.cstate->fpmode, fpmode_source, sizeof(fpmode_source)); + memcpy(mips3.cstate->slt_table, slt_table_source, sizeof(slt_table_source)); + memcpy(mips3.cstate->sltu_table, sltu_table_source, sizeof(sltu_table_source)); + memcpy(mips3.cstate->c_un_table, c_un_table_source, sizeof(c_un_table_source)); + memcpy(mips3.cstate->c_eq_table, c_eq_table_source, sizeof(c_eq_table_source)); + memcpy(mips3.cstate->c_ueq_table, c_ueq_table_source, sizeof(c_ueq_table_source)); + memcpy(mips3.cstate->c_olt_table, c_olt_table_source, sizeof(c_olt_table_source)); + memcpy(mips3.cstate->c_ult_table, c_ult_table_source, sizeof(c_ult_table_source)); + memcpy(mips3.cstate->c_ole_table, c_ole_table_source, sizeof(c_ole_table_source)); + memcpy(mips3.cstate->c_ule_table, c_ule_table_source, sizeof(c_ule_table_source)); + + /* initialize the UML generator */ + if (FORCE_C_BACKEND) + flags |= DRCUML_OPTION_USE_C; + if (LOG_UML) + flags |= DRCUML_OPTION_LOG_UML; + if (LOG_NATIVE) + flags |= DRCUML_OPTION_LOG_NATIVE; + mips3.drcuml = drcuml_alloc(mips3.cache, flags, 2, 32, 2); + if (mips3.drcuml == NULL) + fatalerror("Error initializing the UML"); + + /* initialize the front-end helper */ + if (SINGLE_INSTRUCTION_MODE) feconfig.max_sequence = 1; mips3.drcfe = drcfe_init(&feconfig, mips3.core); - - /* start up code logging */ - if (LOG_CODE) - mips3.log = x86log_create_context("mips3drc.asm"); + + /* mark the cache dirty so it is updated on next execute */ + mips3.cache_dirty = TRUE; } @@ -216,13 +718,9 @@ static void mips3_init(mips3_flavor flavor, int bigendian, int index, int clock, static void mips3_reset(void) { - /* reset the common code and flush the cache */ + /* reset the common code and mark the cache dirty */ mips3com_reset(mips3.core); - drc_cache_reset(mips3.drc); - -#if COMPARE_AGAINST_C - mips3c_reset(); -#endif + mips3.cache_dirty = TRUE; } @@ -233,14 +731,28 @@ static void mips3_reset(void) static int mips3_execute(int cycles) { + drcuml_state *drcuml = mips3.drcuml; + int execute_result; + /* reset the cache if dirty */ if (mips3.cache_dirty) - drc_cache_reset(mips3.drc); + code_flush_cache(drcuml); mips3.cache_dirty = FALSE; /* execute */ mips3.core->icount = cycles; - drc_execute(mips3.drc); + do + { + /* run as much as we can */ + execute_result = drcuml_execute(drcuml, mips3.entry); + + /* if we need to recompile, do it */ + if (execute_result == EXECUTE_MISSING_CODE) + code_compile_block(drcuml, mips3.cstate->mode, mips3.core->pc); + + } while (execute_result != EXECUTE_OUT_OF_CYCLES); + + /* return the number of cycles executed */ return cycles - mips3.core->icount; } @@ -251,16 +763,10 @@ static int mips3_execute(int cycles) static void mips3_exit(void) { - /* clean up code logging */ - if (LOG_CODE) - x86log_free_context(mips3.log); - /* clean up the DRC */ drcfe_exit(mips3.drcfe); - mips3drc_exit(); - - /* free the cache */ - osd_free_executable(mips3.core, CACHE_SIZE + sizeof(*mips3.core)); + drcuml_free(mips3.drcuml); + drccache_free(mips3.cache); } @@ -314,46 +820,2549 @@ static offs_t mips3_dasm(char *buffer, offs_t pc, const UINT8 *oprom, const UINT /*************************************************************************** - COMMON UTILITIES + CACHE MANAGEMENT ***************************************************************************/ /*------------------------------------------------- - fastram_ptr - return a pointer to the base of - memory containing the given address, if it - is within a fast RAM range; returns 0 - otherwise + code_flush_cache - flush the cache and + regenerate static code -------------------------------------------------*/ -#ifdef PTR64 -static void *fastram_ptr(offs_t addr, int size, int iswrite) +static void code_flush_cache(drcuml_state *drcuml) { - int ramnum; + /* empty the transient cache contents */ + drcuml_reset(drcuml); + + /* generate the entry point and out-of-cycles handlers */ + static_generate_entry_point(drcuml); + static_generate_nocode_handler(drcuml); + static_generate_out_of_cycles(drcuml); + + /* append exception handlers for various types */ + static_generate_exception(drcuml, EXCEPTION_INTERRUPT, TRUE, "exception_interrupt"); + static_generate_exception(drcuml, EXCEPTION_INTERRUPT, FALSE, "exception_interrupt_norecover"); + static_generate_exception(drcuml, EXCEPTION_TLBMOD, TRUE, "exception_tlbmod"); + static_generate_exception(drcuml, EXCEPTION_TLBLOAD, TRUE, "exception_tlbload"); + static_generate_exception(drcuml, EXCEPTION_TLBSTORE, TRUE, "exception_tlbstore"); + static_generate_exception(drcuml, EXCEPTION_SYSCALL, TRUE, "exception_syscall"); + static_generate_exception(drcuml, EXCEPTION_BREAK, TRUE, "exception_break"); + static_generate_exception(drcuml, EXCEPTION_INVALIDOP, TRUE, "exception_invalidop"); + static_generate_exception(drcuml, EXCEPTION_BADCOP, TRUE, "exception_badcop"); + static_generate_exception(drcuml, EXCEPTION_OVERFLOW, TRUE, "exception_overflow"); + static_generate_exception(drcuml, EXCEPTION_TRAP, TRUE, "exception_trap"); + + /* add subroutines for memory accesses */ + static_generate_memory_accessor(drcuml, 1, FALSE, FALSE, "read8", &mips3.read8); + static_generate_memory_accessor(drcuml, 1, TRUE, FALSE, "write8", &mips3.write8); + static_generate_memory_accessor(drcuml, 2, FALSE, FALSE, "read16", &mips3.read16); + static_generate_memory_accessor(drcuml, 2, TRUE, FALSE, "write16", &mips3.write16); + static_generate_memory_accessor(drcuml, 4, FALSE, FALSE, "read32", &mips3.read32); + static_generate_memory_accessor(drcuml, 4, FALSE, TRUE, "read32mask", &mips3.read32mask); + static_generate_memory_accessor(drcuml, 4, TRUE, FALSE, "write32", &mips3.write32); + static_generate_memory_accessor(drcuml, 4, TRUE, TRUE, "write32mask", &mips3.write32mask); + static_generate_memory_accessor(drcuml, 8, FALSE, FALSE, "read64", &mips3.read64); + static_generate_memory_accessor(drcuml, 8, FALSE, TRUE, "read64mask", &mips3.read64mask); + static_generate_memory_accessor(drcuml, 8, TRUE, FALSE, "write64", &mips3.write64); + static_generate_memory_accessor(drcuml, 8, TRUE, TRUE, "write64mask", &mips3.write64mask); +} - /* only direct maps are supported */ - if (addr < 0x80000000 || addr >= 0xc0000000) - return NULL; - addr &= 0x1fffffff; - /* adjust address for endianness */ - if (mips3.core->bigendian) +/*------------------------------------------------- + code_compile_block - compile a block of the + given mode at the specified pc +-------------------------------------------------*/ + +static void code_compile_block(drcuml_state *drcuml, UINT8 mode, offs_t pc) +{ + compiler_state compiler = { 0 }; + const opcode_desc *seqhead, *seqlast; + const opcode_desc *desclist; + int override = FALSE; + drcuml_block *block; + jmp_buf errorbuf; + + /* get a description of this sequence */ + desclist = drcfe_describe_code(mips3.drcfe, pc); + if (LOG_UML) + log_opcode_desc(drcuml, desclist, 0); + + /* if we get an error back, flush the cache and try again */ + if (setjmp(errorbuf) != 0) + code_flush_cache(drcuml); + + /* start the block */ + block = drcuml_block_begin(drcuml, 4096, &errorbuf); + + /* loop until we get through all instruction sequences */ + for (seqhead = desclist; seqhead != NULL; seqhead = seqlast->next) { - if (size == 1) - addr ^= 3; - else if (size == 2) - addr ^= 2; + const opcode_desc *curdesc; + UINT32 nextpc; + + /* add a code log entry */ + if (LOG_UML) + UML_COMMENT(block, "-------------------------"); // comment + + /* determine the last instruction in this sequence */ + for (seqlast = seqhead; seqlast != NULL; seqlast = seqlast->next) + if (seqlast->flags & OPFLAG_END_SEQUENCE) + break; + assert(seqlast != NULL); + + /* if we don't have a hash for this mode/pc, or if we are overriding all, add one */ + if (override || !drcuml_hash_exists(drcuml, mode, seqhead->pc)) + UML_HASH(block, mode, seqhead->pc); // hash mode,pc + + /* if we already have a hash, and this is the first sequence, assume that we */ + /* are recompiling due to being out of sync and allow future overrides */ + else if (seqhead == desclist) + { + override = TRUE; + UML_HASH(block, mode, seqhead->pc); // hash mode,pc + } + + /* otherwise, redispatch to that fixed PC and skip the rest of the processing */ + else + { + UML_LABEL(block, seqhead->pc | 0x80000000); // label seqhead->pc | 0x80000000 + UML_HASHJMP(block, IMM(mips3.cstate->mode), IMM(seqhead->pc), mips3.nocode); + // hashjmp <mode>,seqhead->pc,nocode + continue; + } + + /* validate this code block if we're not pointing into ROM */ + if (memory_get_write_ptr(cpu_getactivecpu(), ADDRESS_SPACE_PROGRAM, seqhead->physpc) != NULL) + generate_checksum_block(block, &compiler, seqhead, seqlast); + + /* label this instruction, if it may be jumped to locally */ + if (seqhead->flags & OPFLAG_IS_BRANCH_TARGET) + UML_LABEL(block, seqhead->pc | 0x80000000); // label seqhead->pc | 0x80000000 + + /* iterate over instructions in the sequence and compile them */ + for (curdesc = seqhead; curdesc != seqlast->next; curdesc = curdesc->next) + generate_sequence_instruction(block, &compiler, curdesc); + + /* if we need to return to the start, do it */ + if (seqlast->flags & OPFLAG_RETURN_TO_START) + nextpc = pc; + + /* otherwise we just go to the next instruction */ + else + nextpc = seqlast->pc + (seqlast->skipslots + 1) * 4; + + /* count off cycles and go there */ + generate_update_cycles(block, &compiler, nextpc, NULL, TRUE); // <subtract cycles> + + /* if the last instruction can change modes, use a variable mode; otherwise, assume the same mode */ + if (seqlast->flags & OPFLAG_CAN_CHANGE_MODES) + UML_HASHJMP(block, MEM(&mips3.cstate->mode), IMM(nextpc), mips3.nocode);// hashjmp <mode>,nextpc,nocode + else if (seqlast->next == NULL || seqlast->next->pc != nextpc) + UML_HASHJMP(block, IMM(mips3.cstate->mode), IMM(nextpc), mips3.nocode); // hashjmp <mode>,nextpc,nocode } - /* search for fastram */ + /* end the sequence */ + drcuml_block_end(block); +} + + + +/*************************************************************************** + C FUNCTION CALLBACKS +***************************************************************************/ + +/*------------------------------------------------- + cfunc_printf_exception - log any exceptions that + aren't interrupts +-------------------------------------------------*/ + +static void cfunc_printf_exception(void *param) +{ + printf("Exception: EPC=%08X Cause=%08X BadVAddr=%08X Jmp=%08X\n", (UINT32)mips3.core->cpr[0][COP0_EPC], (UINT32)mips3.core->cpr[0][COP0_Cause], (UINT32)mips3.core->cpr[0][COP0_BadVAddr], mips3.core->pc); + cfunc_printf_probe((void *)(FPTR)mips3.core->cpr[0][COP0_EPC]); +} + + +/*------------------------------------------------- + cfunc_get_cycles - compute the total number + of cycles executed so far +-------------------------------------------------*/ + +static void cfunc_get_cycles(void *param) +{ + UINT64 *dest = param; + *dest = activecpu_gettotalcycles(); +} + + +/*------------------------------------------------- + cfunc_printf_probe - print the current CPU + state and return +-------------------------------------------------*/ + +static void cfunc_printf_probe(void *param) +{ + UINT32 pc = (UINT32)(FPTR)param; + + printf(" PC=%08X r1=%08X%08X r2=%08X%08X r3=%08X%08X\n", + pc, + (UINT32)(mips3.core->r[1] >> 32), (UINT32)mips3.core->r[1], + (UINT32)(mips3.core->r[2] >> 32), (UINT32)mips3.core->r[2], + (UINT32)(mips3.core->r[3] >> 32), (UINT32)mips3.core->r[3]); + printf(" r4=%08X%08X r5=%08X%08X r6=%08X%08X r7=%08X%08X\n", + (UINT32)(mips3.core->r[4] >> 32), (UINT32)mips3.core->r[4], + (UINT32)(mips3.core->r[5] >> 32), (UINT32)mips3.core->r[5], + (UINT32)(mips3.core->r[6] >> 32), (UINT32)mips3.core->r[6], + (UINT32)(mips3.core->r[7] >> 32), (UINT32)mips3.core->r[7]); + printf(" r8=%08X%08X r9=%08X%08X r10=%08X%08X r11=%08X%08X\n", + (UINT32)(mips3.core->r[8] >> 32), (UINT32)mips3.core->r[8], + (UINT32)(mips3.core->r[9] >> 32), (UINT32)mips3.core->r[9], + (UINT32)(mips3.core->r[10] >> 32), (UINT32)mips3.core->r[10], + (UINT32)(mips3.core->r[11] >> 32), (UINT32)mips3.core->r[11]); + printf("r12=%08X%08X r13=%08X%08X r14=%08X%08X r15=%08X%08X\n", + (UINT32)(mips3.core->r[12] >> 32), (UINT32)mips3.core->r[12], + (UINT32)(mips3.core->r[13] >> 32), (UINT32)mips3.core->r[13], + (UINT32)(mips3.core->r[14] >> 32), (UINT32)mips3.core->r[14], + (UINT32)(mips3.core->r[15] >> 32), (UINT32)mips3.core->r[15]); + printf("r16=%08X%08X r17=%08X%08X r18=%08X%08X r19=%08X%08X\n", + (UINT32)(mips3.core->r[16] >> 32), (UINT32)mips3.core->r[16], + (UINT32)(mips3.core->r[17] >> 32), (UINT32)mips3.core->r[17], + (UINT32)(mips3.core->r[18] >> 32), (UINT32)mips3.core->r[18], + (UINT32)(mips3.core->r[19] >> 32), (UINT32)mips3.core->r[19]); + printf("r20=%08X%08X r21=%08X%08X r22=%08X%08X r23=%08X%08X\n", + (UINT32)(mips3.core->r[20] >> 32), (UINT32)mips3.core->r[20], + (UINT32)(mips3.core->r[21] >> 32), (UINT32)mips3.core->r[21], + (UINT32)(mips3.core->r[22] >> 32), (UINT32)mips3.core->r[22], + (UINT32)(mips3.core->r[23] >> 32), (UINT32)mips3.core->r[23]); + printf("r24=%08X%08X r25=%08X%08X r26=%08X%08X r27=%08X%08X\n", + (UINT32)(mips3.core->r[24] >> 32), (UINT32)mips3.core->r[24], + (UINT32)(mips3.core->r[25] >> 32), (UINT32)mips3.core->r[25], + (UINT32)(mips3.core->r[26] >> 32), (UINT32)mips3.core->r[26], + (UINT32)(mips3.core->r[27] >> 32), (UINT32)mips3.core->r[27]); + printf("r28=%08X%08X r29=%08X%08X r30=%08X%08X r31=%08X%08X\n", + (UINT32)(mips3.core->r[28] >> 32), (UINT32)mips3.core->r[28], + (UINT32)(mips3.core->r[29] >> 32), (UINT32)mips3.core->r[29], + (UINT32)(mips3.core->r[30] >> 32), (UINT32)mips3.core->r[30], + (UINT32)(mips3.core->r[31] >> 32), (UINT32)mips3.core->r[31]); + printf(" hi=%08X%08X lo=%08X%08X\n", + (UINT32)(mips3.core->r[REG_HI] >> 32), (UINT32)mips3.core->r[REG_HI], + (UINT32)(mips3.core->r[REG_LO] >> 32), (UINT32)mips3.core->r[REG_LO]); +} + + + +/*************************************************************************** + STATIC CODEGEN +***************************************************************************/ + +/*------------------------------------------------- + static_generate_entry_point - generate a + static entry point +-------------------------------------------------*/ + +static void static_generate_entry_point(drcuml_state *drcuml) +{ + drcuml_codelabel skip = 1; + drcuml_block *block; + jmp_buf errorbuf; + + /* if we get an error back, we're screwed */ + if (setjmp(errorbuf) != 0) + fatalerror("Unrecoverable error in static_generate_entry_point"); + + /* begin generating */ + block = drcuml_block_begin(drcuml, 20, &errorbuf); + + /* forward references */ + alloc_handle(drcuml, &mips3.exception_norecover[EXCEPTION_INTERRUPT], "interrupt_norecover"); + alloc_handle(drcuml, &mips3.nocode, "nocode"); + + alloc_handle(drcuml, &mips3.entry, "entry"); + UML_HANDLE(block, mips3.entry); // handle entry + + /* reset the FPU mode */ + UML_AND(block, IREG(0), CCR164(31), IMM(3)); // and i0,ccr1[31],3 + UML_LOAD1U(block, IREG(0), &mips3.cstate->fpmode[0], IREG(0)); // load1u i0,fpmode,i0 + UML_SETFMOD(block, IREG(0)); // setfmod i0 + + /* check for interrupts */ + UML_AND(block, IREG(0), CPR032(COP0_Cause), CPR032(COP0_Status)); // and i0,[Cause],[Status] + UML_ANDf(block, IREG(0), IREG(0), IMM(0xfc00), FLAGS_Z); // and i0,i0,0xfc00,Z + UML_JMPc(block, IF_Z, skip); // jmp skip,Z + UML_TEST(block, CPR032(COP0_Status), IMM(SR_IE)); // test [Status],SR_IE + UML_JMPc(block, IF_Z, skip); // jmp skip,Z + UML_TEST(block, CPR032(COP0_Status), IMM(SR_EXL | SR_ERL)); // test [Status],SR_EXL | SR_ERL + UML_JMPc(block, IF_NZ, skip); // jmp skip,NZ + UML_MOV(block, IREG(0), MEM(&mips3.core->pc)); // mov i0,pc + UML_MOV(block, IREG(1), IMM(0)); // mov i1,0 + UML_CALLH(block, mips3.exception_norecover[EXCEPTION_INTERRUPT]); // callh exception_norecover + UML_LABEL(block, skip); // skip: + + /* generate a hash jump via the current mode and PC */ + UML_HASHJMP(block, MEM(&mips3.cstate->mode), MEM(&mips3.core->pc), mips3.nocode);// hashjmp <mode>,<pc>,nocode + + drcuml_block_end(block); +} + + +/*------------------------------------------------- + static_generate_nocode_handler - generate an + exception handler for "out of code" +-------------------------------------------------*/ + +static void static_generate_nocode_handler(drcuml_state *drcuml) +{ + drcuml_block *block; + jmp_buf errorbuf; + + /* if we get an error back, we're screwed */ + if (setjmp(errorbuf) != 0) + fatalerror("Unrecoverable error in static_generate_nocode_handler"); + + /* begin generating */ + block = drcuml_block_begin(drcuml, 10, &errorbuf); + + /* generate a hash jump via the current mode and PC */ + alloc_handle(drcuml, &mips3.nocode, "nocode"); + UML_HANDLE(block, mips3.nocode); // handle nocode + UML_GETEXP(block, IREG(0)); // getexp i0 + UML_MOV(block, MEM(&mips3.core->pc), IREG(0)); // mov [pc],i0 + UML_EXIT(block, IMM(EXECUTE_MISSING_CODE)); // exit EXECUTE_MISSING_CODE + + drcuml_block_end(block); +} + + +/*------------------------------------------------- + static_generate_out_of_cycles - generate an + out of cycles exception handler +-------------------------------------------------*/ + +static void static_generate_out_of_cycles(drcuml_state *drcuml) +{ + drcuml_block *block; + jmp_buf errorbuf; + + /* if we get an error back, we're screwed */ + if (setjmp(errorbuf) != 0) + fatalerror("Unrecoverable error in static_generate_out_of_cycles"); + + /* begin generating */ + block = drcuml_block_begin(drcuml, 10, &errorbuf); + + /* generate a hash jump via the current mode and PC */ + alloc_handle(drcuml, &mips3.out_of_cycles, "out_of_cycles"); + UML_HANDLE(block, mips3.out_of_cycles); // handle out_of_cycles + UML_GETEXP(block, IREG(0)); // getexp i0 + UML_MOV(block, MEM(&mips3.core->pc), IREG(0)); // mov <pc>,i0 + UML_EXIT(block, IMM(EXECUTE_OUT_OF_CYCLES)); // exit EXECUTE_OUT_OF_CYCLES + + drcuml_block_end(block); +} + + +/*------------------------------------------------- + static_generate_exception - generate a static + exception handler +-------------------------------------------------*/ + +static void static_generate_exception(drcuml_state *drcuml, UINT8 exception, int recover, const char *name) +{ + UINT32 offset = (exception >= EXCEPTION_TLBMOD && exception <= EXCEPTION_TLBSTORE) ? 0x00 : 0x180; + drcuml_codelabel next = 1; + drcuml_block *block; + jmp_buf errorbuf; + + /* if we get an error back, we're screwed */ + if (setjmp(errorbuf) != 0) + fatalerror("Unrecoverable error in static_generate_exception"); + + /* begin generating */ + block = drcuml_block_begin(drcuml, 1024, &errorbuf); + + /* add a global entry for this */ + if (recover) + { + alloc_handle(drcuml, &mips3.exception[exception], name); + UML_HANDLE(block, mips3.exception[exception]); // handle name + } + else + { + alloc_handle(drcuml, &mips3.exception_norecover[exception], name); + UML_HANDLE(block, mips3.exception_norecover[exception]); // handle name + } + + /* exception parameter is expected to be the fault address in this case */ + if (exception == EXCEPTION_TLBLOAD || exception == EXCEPTION_TLBSTORE) + { + /* set BadVAddr to the fault address */ + UML_GETEXP(block, CPR032(COP0_BadVAddr)); // mov [BadVAddr],exp + + /* set the upper bits of EntryHi to the fault page */ + UML_AND(block, IREG(0), IREG(0), IMM(0xffffe000)); // and i0,i0,0xffffe000 + UML_AND(block, IREG(1), CPR032(COP0_EntryHi), IMM(0x000000ff)); // and i1,[EntryHi],0x000000ff + UML_OR(block, CPR032(COP0_EntryHi), IREG(0), IREG(1)); // or [EntryHi],i0,i1 + + /* set the lower bits of Context to the fault page */ + UML_SHR(block, IREG(0), IREG(0), IMM(9)); // shr i0,i0,9 + UML_AND(block, IREG(1), CPR032(COP0_Context), IMM(0xff800000)); // and i1,[Context],0xff800000 + UML_OR(block, CPR032(COP0_Context), IREG(0), IREG(1)); // or [Context],i0,i1 + } + + /* set the EPC and Cause registers */ + if (recover) + { + UML_RECOVER(block, IREG(0), MAPVAR_PC); // recover i0,PC + UML_RECOVER(block, IREG(1), MAPVAR_CYCLES); // recover i1,CYCLES + } + + UML_AND(block, IREG(2), CPR032(COP0_Cause), IMM(~0x800000ff)); // and i2,[Cause],~0x800000ff + UML_TEST(block, IREG(0), IMM(1)); // test i0,1 + UML_JMPc(block, IF_Z, next); // jz <next> + UML_OR(block, IREG(2), IREG(2), IMM(0x80000000)); // or i2,i2,0x80000000 + UML_SUB(block, IREG(0), IREG(0), IMM(1)); // sub i0,i0,1 + UML_LABEL(block, next); // <next>: + UML_MOV(block, CPR032(COP0_EPC), IREG(0)); // mov [EPC],i0 + UML_OR(block, CPR032(COP0_Cause), IREG(2), IMM(exception << 2)); // or [Cause],i2,exception << 2 + + /* set EXL in the SR */ + UML_OR(block, CPR032(COP0_Status), CPR032(COP0_Status), IMM(SR_EXL)); // or [Status],[Status],SR_EXL + + /* optionally print exceptions */ + if (PRINTF_EXCEPTIONS && exception != EXCEPTION_INTERRUPT && exception != EXCEPTION_SYSCALL) + UML_CALLC(block, cfunc_printf_exception, NULL); // callc cfunc_printf_exception,NULL + + /* choose our target PC */ + UML_MOV(block, IREG(0), IMM(0xbfc00200 + offset)); // mov i0,0xbfc00200 + offset + UML_TEST(block, CPR032(COP0_Status), IMM(SR_BEV)); // test [Status],SR_BEV + UML_MOVc(block, IF_Z, IREG(0), IMM(0x80000000 + offset)); // mov i0,0x80000000 + offset,z + + /* adjust cycles */ + UML_SUBf(block, MEM(&mips3.core->icount), MEM(&mips3.core->icount), IREG(1), FLAGS_S); // sub icount,icount,cycles,S + UML_EXHc(block, IF_S, mips3.out_of_cycles, IREG(0)); // exh out_of_cycles,i0 + + UML_HASHJMP(block, MEM(&mips3.cstate->mode), IREG(0), mips3.nocode); // hashjmp <mode>,i0,nocode + + drcuml_block_end(block); +} + + +/*------------------------------------------------------------------ + static_generate_memory_accessor +------------------------------------------------------------------*/ + +static void static_generate_memory_accessor(drcuml_state *drcuml, int size, int iswrite, int ismasked, const char *name, drcuml_codehandle **handleptr) +{ + /* on entry, address is in I0; data for writes is in I1; mask for accesses is in I2 */ + /* on exit, read result is in I0 */ + /* routine trashes I0-I3 */ + drcuml_codehandle *exception_target = mips3.exception[iswrite ? EXCEPTION_TLBSTORE : EXCEPTION_TLBLOAD]; + drcuml_block *block; + jmp_buf errorbuf; + int label = 0; + int ramnum; + + /* if we get an error back, we're screwed */ + if (setjmp(errorbuf) != 0) + fatalerror("Unrecoverable error in static_generate_exception"); + + /* begin generating */ + block = drcuml_block_begin(drcuml, 1024, &errorbuf); + + /* add a global entry for this */ + alloc_handle(drcuml, handleptr, name); + UML_HANDLE(block, *handleptr); // handle *handleptr + + /* general case: assume paging and perform a translation */ + UML_SHR(block, IREG(3), IREG(0), IMM(12)); // shr i3,i0,12 + UML_LOAD4(block, IREG(3), mips3.core->tlb_table, IREG(3)); // load4 i3,[tlb_table],i3 + UML_TEST(block, IREG(3), IMM(iswrite ? 1 : 2)); // test i3,iswrite ? 1 : 2 + UML_EXHc(block, IF_NZ, exception_target, IREG(0)); // exnz exception_target,i0 + UML_AND(block, IREG(0), IREG(0), IMM(0xfff)); // and i0,i0,0xfff + UML_AND(block, IREG(3), IREG(3), IMM(~0xfff)); // and i3,i3,~0xfff + UML_OR(block, IREG(0), IREG(0), IREG(3)); // or i0,i0,i3 + for (ramnum = 0; ramnum < MIPS3_MAX_FASTRAM; ramnum++) - if (mips3.fastram[ramnum].base != NULL && (!iswrite || !mips3.fastram[ramnum].readonly) && - addr >= mips3.fastram[ramnum].start && addr <= mips3.fastram[ramnum].end) + if (!Machine->debug_mode && mips3.fastram[ramnum].base != NULL && (!iswrite || !mips3.fastram[ramnum].readonly)) + { + void *fastbase = (UINT8 *)mips3.fastram[ramnum].base - mips3.fastram[ramnum].start; + UINT32 skip = label++; + if (mips3.fastram[ramnum].end != 0xffffffff) + { + UML_CMP(block, IREG(0), IMM(mips3.fastram[ramnum].end)); // cmp i0,end + UML_JMPc(block, IF_A, skip); // ja skip + } + if (mips3.fastram[ramnum].start != 0x00000000) + { + UML_CMP(block, IREG(0), IMM(mips3.fastram[ramnum].start)); // cmp i0,fastram_start + UML_JMPc(block, IF_B, skip); // jb skip + } + + if (!iswrite) + { + if (size == 1) + { + UML_XOR(block, IREG(0), IREG(0), IMM(mips3.core->bigendian ? BYTE4_XOR_BE(0) : BYTE4_XOR_LE(0))); + // xor i0,i0,bytexor + UML_LOAD1U(block, IREG(0), fastbase, IREG(0)); // load1u i0,fastbase,i0 + } + else if (size == 2) + { + UML_SHR(block, IREG(0), IREG(0), IMM(1)); // shr i0,i0,1 + UML_XOR(block, IREG(0), IREG(0), IMM(mips3.core->bigendian ? BYTE_XOR_BE(0) : BYTE_XOR_LE(0))); + // xor i0,i0,bytexor + UML_LOAD2U(block, IREG(0), fastbase, IREG(0)); // load2u i0,fastbase,i0 + } + else if (size == 4) + { + UML_SHR(block, IREG(0), IREG(0), IMM(2)); // shr i0,i0,2 + UML_LOAD4(block, IREG(0), fastbase, IREG(0)); // load4 i0,fastbase,i0 + } + else if (size == 8) + { + UML_SHR(block, IREG(0), IREG(0), IMM(3)); // shr i0,i0,3 + UML_DLOAD8(block, IREG(0), fastbase, IREG(0)); // dload8 i0,fastbase,i0 + UML_DROR(block, IREG(0), IREG(0), IMM(32 * (mips3.core->bigendian ? BYTE_XOR_BE(0) : BYTE_XOR_LE(0)))); + // dror i0,i0,32*bytexor + } + UML_RET(block); // ret + } + else + { + if (size == 1) + { + UML_XOR(block, IREG(0), IREG(0), IMM(mips3.core->bigendian ? BYTE4_XOR_BE(0) : BYTE4_XOR_LE(0))); + // xor i0,i0,bytexor + UML_STORE1(block, fastbase, IREG(0), IREG(1)); // store1 fastbase,i0,i1 + } + else if (size == 2) + { + UML_SHR(block, IREG(0), IREG(0), IMM(1)); // shr i0,i0,1 + UML_XOR(block, IREG(0), IREG(0), IMM(mips3.core->bigendian ? BYTE_XOR_BE(0) : BYTE_XOR_LE(0))); + // xor i0,i0,bytexor + UML_STORE2(block, fastbase, IREG(0), IREG(1)); // store2 fastbase,i0,i1 + } + else if (size == 4) + { + UML_SHR(block, IREG(0), IREG(0), IMM(2)); // shr i0,i0,2 + if (ismasked) + { + UML_LOAD4(block, IREG(3), fastbase, IREG(0)); // load4 i3,fastbase,i0 + UML_AND(block, IREG(1), IREG(1), IREG(2)); // and i1,i1,i2 + UML_XOR(block, IREG(2), IREG(2), IMM(0xffffffff)); // xor i2,i2,0xfffffffff + UML_AND(block, IREG(3), IREG(3), IREG(2)); // and i3,i3,i2 + UML_OR(block, IREG(1), IREG(1), IREG(3)); // or i1,i1,i3 + } + UML_STORE4(block, fastbase, IREG(0), IREG(1)); // store4 fastbase,i0,i1 + } + else if (size == 8) + { + UML_SHR(block, IREG(0), IREG(0), IMM(3)); // shr i0,i0,3 + UML_DROR(block, IREG(1), IREG(1), IMM(32 * (mips3.core->bigendian ? BYTE_XOR_BE(0) : BYTE_XOR_LE(0)))); + // dror i1,i1,32*bytexor + if (ismasked) + { + UML_DROR(block, IREG(2), IREG(2), IMM(32 * (mips3.core->bigendian ? BYTE_XOR_BE(0) : BYTE_XOR_LE(0)))); + // dror i2,i2,32*bytexor + UML_DLOAD8(block, IREG(3), fastbase, IREG(0)); // dload8 i3,fastbase,i0 + UML_DAND(block, IREG(1), IREG(1), IREG(2)); // dand i1,i1,i2 + UML_DXOR(block, IREG(2), IREG(2), IMM(U64(0xffffffffffffffff))); + // dxor i2,i2,0xfffffffffffffffff + UML_DAND(block, IREG(3), IREG(3), IREG(2)); // dand i3,i3,i2 + UML_DOR(block, IREG(1), IREG(1), IREG(3)); // dor i1,i1,i3 + } + UML_DSTORE8(block, fastbase, IREG(0), IREG(1)); // dstore8 fastbase,i0,i1 + } + UML_RET(block); // ret + } + + UML_LABEL(block, skip); // skip: + } + + switch (size) + { + case 1: + if (iswrite) + UML_WRITE1(block, PROGRAM, IREG(0), IREG(1)); // write1 i0,i1 + else + UML_READ1U(block, IREG(0), PROGRAM, IREG(0)); // read1u i0,i0 + break; + + case 2: + if (iswrite) + UML_WRITE2(block, PROGRAM, IREG(0), IREG(1)); // write2 i0,i1 + else + UML_READ2U(block, IREG(0), PROGRAM, IREG(0)); // read2u i0,i0 + break; + + case 4: + if (iswrite) + { + if (!ismasked) + UML_WRITE4(block, PROGRAM, IREG(0), IREG(1)); // write4 PROGRAM,i0,i1 + else + UML_WRIT4M(block, PROGRAM, IREG(0), IREG(2), IREG(1)); // writ4m PROGRAM,i0,i2,i1 + } + else + { + if (!ismasked) + UML_READ4(block, IREG(0), PROGRAM, IREG(0)); // read4 i0,PROGRAM,i0 + else + UML_READ4M(block, IREG(0), PROGRAM, IREG(0), IREG(2)); // read4m i0,PROGRAM,i0,i2 + } + break; + + case 8: + if (iswrite) + { + if (!ismasked) + UML_DWRITE8(block, PROGRAM, IREG(0), IREG(1)); // dwrite8 PROGRAM,i0,i1 + else + UML_DWRIT8M(block, PROGRAM, IREG(0), IREG(2), IREG(1)); // dwrit8m PROGRAM,i0,i2,i1 + } + else + { + if (!ismasked) + UML_DREAD8(block, IREG(0), PROGRAM, IREG(0)); // dread8 i0,PROGRAM,i0 + else + UML_DREAD8M(block, IREG(0), PROGRAM, IREG(0), IREG(2)); // dread8m i0,PROGRAM,i0,i2 + } + break; + } + UML_RET(block); // ret + + drcuml_block_end(block); +} + + + +/*************************************************************************** + CODE GENERATION +***************************************************************************/ + +/*------------------------------------------------- + generate_update_cycles - generate code to + subtract cycles from the icount and generate + an exception if out +-------------------------------------------------*/ + +static void generate_update_cycles(drcuml_block *block, compiler_state *compiler, UINT32 nextpc, UINT32 *mempc, int allow_exception) +{ + /* check software interrupts if pending */ + if (compiler->checksoftints) + { + drcuml_codelabel skip; + + compiler->checksoftints = FALSE; + UML_AND(block, IREG(0), CPR032(COP0_Cause), CPR032(COP0_Status)); // and i0,[Cause],[Status] + UML_ANDf(block, IREG(0), IREG(0), IMM(0x0300), FLAGS_Z); // and i0,i0,0x0300,Z + UML_JMPc(block, IF_Z, skip = compiler->labelnum++); // jmp skip,Z + if (mempc == NULL) + UML_MOV(block, IREG(0), IMM(nextpc)); // mov i0,nextpc + else + UML_MOV(block, IREG(0), MEM(mempc)); // mov i0,[mempc] + UML_MOV(block, IREG(1), IMM(compiler->cycles)); // mov i1,cycles + UML_CALLH(block, mips3.exception_norecover[EXCEPTION_INTERRUPT]); // callh interrupt_norecover + UML_LABEL(block, skip); // skip: + } + + /* check full interrupts if pending */ + if (compiler->checkints) + { + drcuml_codelabel skip; + + compiler->checkints = FALSE; + UML_AND(block, IREG(0), CPR032(COP0_Cause), CPR032(COP0_Status)); // and i0,[Cause],[Status] + UML_ANDf(block, IREG(0), IREG(0), IMM(0xfc00), FLAGS_Z); // and i0,i0,0xfc00,Z + UML_JMPc(block, IF_Z, skip = compiler->labelnum++); // jmp skip,Z + UML_TEST(block, CPR032(COP0_Status), IMM(SR_IE)); // test [Status],SR_IE + UML_JMPc(block, IF_Z, skip); // jmp skip,Z + UML_TEST(block, CPR032(COP0_Status), IMM(SR_EXL | SR_ERL)); // test [Status],SR_EXL | SR_ERL + UML_JMPc(block, IF_NZ, skip); // jmp skip,NZ + if (mempc == NULL) + UML_MOV(block, IREG(0), IMM(nextpc)); // mov i0,nextpc + else + UML_MOV(block, IREG(0), MEM(mempc)); // mov i0,[mempc] + UML_MOV(block, IREG(1), IMM(compiler->cycles)); // mov i1,cycles + UML_CALLH(block, mips3.exception_norecover[EXCEPTION_INTERRUPT]); // callh interrupt_norecover + UML_LABEL(block, skip); // skip: + } + + /* account for cycles */ + if (compiler->cycles > 0) + { + UML_SUBf(block, MEM(&mips3.core->icount), MEM(&mips3.core->icount), MAPVAR_CYCLES, allow_exception ? FLAGS_S : 0); + // sub icount,icount,cycles,S + UML_MAPVAR(block, MAPVAR_CYCLES, 0); // mapvar cycles,0 + if (allow_exception) { - return (UINT8 *)mips3.fastram[ramnum].base + (addr - mips3.fastram[ramnum].start); + if (mempc == NULL) + UML_EXHc(block, IF_S, mips3.out_of_cycles, IMM(nextpc)); // exh out_of_cycles,nextpc + else + UML_EXHc(block, IF_S, mips3.out_of_cycles, MEM(mempc)); // exh out_of_cycles,<mempc> } + } + compiler->cycles = 0; +} + + +/*------------------------------------------------- + generate_checksum_block - generate code to + validate a sequence of opcodes +-------------------------------------------------*/ + +static void generate_checksum_block(drcuml_block *block, compiler_state *compiler, const opcode_desc *seqhead, const opcode_desc *seqlast) +{ + const opcode_desc *curdesc; + if (LOG_UML) + UML_COMMENT(block, "[Validation for %08X]", seqhead->pc); // comment + + /* loose verify or single instruction: just compare and fail */ + if (!(mips3.drcoptions & MIPS3DRC_STRICT_VERIFY) || seqhead->next == NULL) + { + UML_LOAD4(block, IREG(0), seqhead->opptr.l, IMM(0)); // load4 i0,*opptr + UML_CMP(block, IREG(0), IMM(*seqhead->opptr.l)); // cmp i0,*opptr + UML_EXHc(block, IF_NE, mips3.nocode, IMM(epc(seqhead))); // exne nocode,seqhead->pc + } + + /* full verification; sum up everything */ + else + { + UINT32 sum = 0; + UML_LOAD4(block, IREG(0), seqhead->opptr.l, IMM(0)); // load4 i0,*opptr + sum += *seqhead->opptr.l; + for (curdesc = seqhead->next; curdesc != seqlast->next; curdesc = curdesc->next) + { + UML_LOAD4(block, IREG(1), curdesc->opptr.l, IMM(0)); // load4 i1,*opptr + UML_ADD(block, IREG(0), IREG(0), IREG(1)); // add i0,i0,i1 + sum += *curdesc->opptr.l; + } + UML_CMP(block, IREG(0), IMM(sum)); // cmp i0,sum + UML_EXHc(block, IF_NE, mips3.nocode, IMM(epc(seqhead))); // exne nocode,seqhead->pc + } +} + + +/*------------------------------------------------- + generate_sequence_instruction - generate code + for a single instruction in a sequence +-------------------------------------------------*/ + +static void generate_sequence_instruction(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc) +{ + offs_t expc; + int hotnum; + + /* add an entry for the log */ + if (LOG_UML) + log_add_disasm_comment(block, desc->pc, *desc->opptr.l); + + /* set the PC map variable */ + expc = (desc->flags & OPFLAG_IN_DELAY_SLOT) ? desc->pc - 3 : desc->pc; + UML_MAPVAR(block, MAPVAR_PC, expc); // mapvar PC,expc + + /* accumulate total cycles */ + compiler->cycles += desc->cycles; + + /* is this a hotspot? */ + for (hotnum = 0; hotnum < MIPS3_MAX_HOTSPOTS; hotnum++) + if (desc->pc == mips3.hotspot[hotnum].pc && *desc->opptr.l == mips3.hotspot[hotnum].opcode) + { + compiler->cycles += mips3.hotspot[hotnum].cycles; + break; + } + + /* update the icount map variable */ + UML_MAPVAR(block, MAPVAR_CYCLES, compiler->cycles); // mapvar CYCLES,compiler->cycles + + /* if we want a probe, add it here */ + if (desc->pc == PROBE_ADDRESS) + UML_CALLC(block, cfunc_printf_probe, desc->pc); // callc cfunc_printf_probe,desc->pc + + /* if we are debugging, call the debugger */ + if (Machine->debug_mode) + { + UML_MOV(block, MEM(&mips3.core->pc), IMM(desc->pc)); // mov [pc],desc->pc + UML_DEBUG(block, IMM(desc->pc)); // debug desc->pc + } + + /* validate our TLB entry at this PC; if we fail, we need to recompile */ + if ((desc->flags & OPFLAG_VALIDATE_TLB) && (desc->pc < 0x80000000 || desc->pc >= 0xc0000000)) + { + UML_CMP(block, MEM(&mips3.core->tlb_table[desc->pc >> 12]), + IMM(mips3.core->tlb_table[desc->pc >> 12])); // cmp [tlbentry],*tlbentry + UML_EXHc(block, IF_NE, mips3.tlb_mismatch, IMM(0)); // exh tlb_mismatch,0,NE + } + + /* if this is an invalid opcode, generate the exception now */ + if (desc->flags & OPFLAG_INVALID_OPCODE) + UML_EXH(block, mips3.exception[EXCEPTION_INVALIDOP], IMM(0)); // exh invalidop,0 + + /* otherwise, it's a regular instruction */ + else + { + /* compile the instruction */ + if (!generate_opcode(block, compiler, desc)) + fatalerror("Unimplemented op %08X (%02X,%02X)", *desc->opptr.l, *desc->opptr.l >> 26, *desc->opptr.l & 0x3f); + } +} + + +/*------------------------------------------------------------------ + generate_delay_slot_and_branch +------------------------------------------------------------------*/ + +static void generate_delay_slot_and_branch(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc, UINT8 linkreg) +{ + compiler_state compiler_temp = *compiler; + UINT32 op = *desc->opptr.l; + + /* set the link if needed */ + if (linkreg != 0) + UML_DSEXT4(block, R64(linkreg), IMM((INT32)(desc->pc + 8))); // dsext4 <linkreg>,desc->pc + 8 + + /* compile the delay slot using temporary compiler state */ + assert(desc->delay != NULL); + generate_sequence_instruction(block, &compiler_temp, desc->delay); // <next instruction> + + /* update the cycles and jump through the hash table to the target */ + if (desc->targetpc != BRANCH_TARGET_DYNAMIC) + { + generate_update_cycles(block, &compiler_temp, desc->targetpc, NULL, TRUE); // <subtract cycles> + if (desc->flags & OPFLAG_INTRABLOCK_BRANCH) + UML_JMP(block, desc->targetpc | 0x80000000); // jmp desc->targetpc | 0x80000000 + else + UML_HASHJMP(block, IMM(mips3.cstate->mode), IMM(desc->targetpc), mips3.nocode); + // hashjmp <mode>,desc->targetpc,nocode + } + else + { + generate_update_cycles(block, &compiler_temp, 0, LOPTR(&mips3.core->r[RSREG]), TRUE); + // <subtract cycles> + UML_HASHJMP(block, IMM(mips3.cstate->mode), R32(RSREG), mips3.nocode); // hashjmp <mode>,<rsreg>,nocode + } + + /* update the label */ + compiler->labelnum = compiler_temp.labelnum; + + /* reset the mapvar to the current cycles */ + UML_MAPVAR(block, MAPVAR_CYCLES, compiler->cycles); // mapvar CYCLES,compiler->cycles +} - return NULL; + +/*------------------------------------------------- + generate_opcode - generate code for a specific + opcode +-------------------------------------------------*/ + +static int generate_opcode(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc) +{ + int in_delay_slot = ((desc->flags & OPFLAG_IN_DELAY_SLOT) != 0); + UINT32 op = *desc->opptr.l; + UINT8 opswitch = op >> 26; + drcuml_codelabel skip; + + switch (opswitch) + { + /* ----- sub-groups ----- */ + + case 0x00: /* SPECIAL - MIPS I */ + return generate_special(block, compiler, desc); + + case 0x01: /* REGIMM - MIPS I */ + return generate_regimm(block, compiler, desc); + + case 0x1c: /* IDT-specific */ + return generate_idt(block, compiler, desc); + + + /* ----- jumps and branches ----- */ + + case 0x02: /* J - MIPS I */ + generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp> + return TRUE; + + case 0x03: /* JAL - MIPS I */ + generate_delay_slot_and_branch(block, compiler, desc, 31); // <next instruction + hashjmp> + return TRUE; + + case 0x04: /* BEQ - MIPS I */ + case 0x14: /* BEQL - MIPS II */ + UML_DCMP(block, R64(RSREG), R64(RTREG)); // dcmp <rsreg>,<rtreg> + UML_JMPc(block, IF_NE, skip = compiler->labelnum++); // jmp skip,NE + generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp> + UML_LABEL(block, skip); // skip: + return TRUE; + + case 0x05: /* BNE - MIPS I */ + case 0x15: /* BNEL - MIPS II */ + UML_DCMP(block, R64(RSREG), R64(RTREG)); // dcmp <rsreg>,<rtreg> + UML_JMPc(block, IF_E, skip = compiler->labelnum++); // jmp skip,E + generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp> + UML_LABEL(block, skip); // skip: + return TRUE; + + case 0x06: /* BLEZ - MIPS I */ + case 0x16: /* BLEZL - MIPS II */ + if (RSREG != 0) + { + UML_DCMP(block, R64(RSREG), IMM(0)); // dcmp <rsreg>,0 + UML_JMPc(block, IF_G, skip = compiler->labelnum++); // jmp skip,G + generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp> + UML_LABEL(block, skip); // skip: + } + else + generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp> + return TRUE; + + case 0x07: /* BGTZ - MIPS I */ + case 0x17: /* BGTZL - MIPS II */ + UML_DCMP(block, R64(RSREG), IMM(0)); // dcmp <rsreg>,0 + UML_JMPc(block, IF_LE, skip = compiler->labelnum++); // jmp skip,LE + generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp> + UML_LABEL(block, skip); // skip: + return TRUE; + + + /* ----- immediate arithmetic ----- */ + + case 0x0f: /* LUI - MIPS I */ + if (RTREG != 0) + UML_DSEXT4(block, R64(RTREG), IMM(SIMMVAL << 16)); // dsext4 <rtreg>,SIMMVAL << 16 + return TRUE; + + case 0x08: /* ADDI - MIPS I */ + if (mips3.drcoptions & MIPS3DRC_CHECK_OVERFLOWS) + { + UML_ADDf(block, IREG(0), R32(RSREG), IMM(SIMMVAL), FLAGS_V); // add i0,<rsreg>,SIMMVAL,V + UML_EXHc(block, IF_V, mips3.exception[EXCEPTION_OVERFLOW], IMM(0)); // exh overflow,0 + } + else + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + if (RTREG != 0) + UML_DSEXT4(block, R64(RTREG), IREG(0)); // dsext4 <rtreg>,i0 + return TRUE; + + case 0x09: /* ADDIU - MIPS I */ + if (RTREG != 0) + { + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL,V + UML_DSEXT4(block, R64(RTREG), IREG(0)); // dsext4 <rtreg>,i0 + } + return TRUE; + + case 0x18: /* DADDI - MIPS III */ + if (mips3.drcoptions & MIPS3DRC_CHECK_OVERFLOWS) + { + UML_DADDf(block, IREG(0), R64(RSREG), IMM(SIMMVAL), FLAGS_V); // dadd i0,<rsreg>,SIMMVAL,V + UML_EXHc(block, IF_V, mips3.exception[EXCEPTION_OVERFLOW], IMM(0)); // exh overflow,0 + } + else + UML_DADD(block, IREG(0), R64(RSREG), IMM(SIMMVAL)); // dadd i0,<rsreg>,SIMMVAL + if (RTREG != 0) + UML_DMOV(block, R64(RTREG), IREG(0)); // dmov <rtreg>,i0 + return TRUE; + + case 0x19: /* DADDIU - MIPS III */ + if (RTREG != 0) + UML_DADD(block, R64(RTREG), R64(RSREG), IMM(SIMMVAL)); // dadd <rtreg>,<rsreg>,SIMMVAL + return TRUE; + + case 0x0c: /* ANDI - MIPS I */ + if (RTREG != 0) + UML_DAND(block, R64(RTREG), R64(RSREG), IMM(UIMMVAL)); // dand <rtreg>,<rsreg>,UIMMVAL + return TRUE; + + case 0x0d: /* ORI - MIPS I */ + if (RTREG != 0) + UML_DOR(block, R64(RTREG), R64(RSREG), IMM(UIMMVAL)); // dor <rtreg>,<rsreg>,UIMMVAL + return TRUE; + + case 0x0e: /* XORI - MIPS I */ + if (RTREG != 0) + UML_DXOR(block, R64(RTREG), R64(RSREG), IMM(UIMMVAL)); // dxor <rtreg>,<rsreg>,UIMMVAL + return TRUE; + + case 0x0a: /* SLTI - MIPS I */ + if (RTREG != 0) + { + UML_DCMP(block, R64(RSREG), IMM(SIMMVAL)); // dcmp <rsreg>,SIMMVAL + UML_DFLAGS(block, R64(RTREG), (UINT64)~0, mips3.cstate->slt_table); // dflags <rtreg>,~0,slt_table + } + return TRUE; + + case 0x0b: /* SLTIU - MIPS I */ + if (RTREG != 0) + { + UML_DCMP(block, R64(RSREG), IMM(SIMMVAL)); // dcmp <rsreg>,SIMMVAL + UML_DFLAGS(block, R64(RTREG), (UINT64)~0, mips3.cstate->sltu_table);// dflags <rtreg>,~0,sltu_table + } + return TRUE; + + + /* ----- memory load operations ----- */ + + case 0x20: /* LB - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_CALLH(block, mips3.read8); // callh read8 + if (RTREG != 0) + UML_DSEXT1(block, R64(RTREG), IREG(0)); // dsext1 <rtreg>,i0 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x21: /* LH - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_CALLH(block, mips3.read16); // callh read16 + if (RTREG != 0) + UML_DSEXT2(block, R64(RTREG), IREG(0)); // dsext2 <rtreg>,i0 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x23: /* LW - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_CALLH(block, mips3.read32); // callh read32 + if (RTREG != 0) + UML_DSEXT4(block, R64(RTREG), IREG(0)); // dsext4 <rtreg>,i0 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x24: /* LBU - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_CALLH(block, mips3.read8); // callh read8 + if (RTREG != 0) + UML_DZEXT1(block, R64(RTREG), IREG(0)); // dzext1 <rtreg>,i0 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x25: /* LHU - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_CALLH(block, mips3.read16); // callh read16 + if (RTREG != 0) + UML_DZEXT2(block, R64(RTREG), IREG(0)); // dzext2 <rtreg>,i0 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x27: /* LWU - MIPS III */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_CALLH(block, mips3.read32); // callh read32 + if (RTREG != 0) + UML_DZEXT4(block, R64(RTREG), IREG(0)); // dzext4 <rtreg>,i0 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x37: /* LD - MIPS III */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_CALLH(block, mips3.read64); // callh read64 + if (RTREG != 0) + UML_DMOV(block, R64(RTREG), IREG(0)); // dmov <rtreg>,i0 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x22: /* LWL - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_SHL(block, IREG(4), IREG(0), IMM(3)); // shl i4,i0,3 + if (!mips3.core->bigendian) + UML_XOR(block, IREG(4), IREG(4), IMM(0x18)); // xor i4,i4,0x18 + UML_SHR(block, IREG(2), IMM(~0), IREG(4)); // shr i2,~0,i4 + UML_CALLH(block, mips3.read32mask); // callh read32mask + if (RTREG != 0) + { + UML_SHL(block, IREG(2), IMM(~0), IREG(4)); // shl i2,~0,i4 + UML_XOR(block, IREG(2), IREG(2), IMM(~0)); // xor i2,i2,~0 + UML_SHL(block, IREG(0), IREG(0), IREG(4)); // shl i0,i0,i4 + UML_AND(block, IREG(2), IREG(2), R32(RTREG)); // and i2,i2,<rtreg> + UML_OR(block, IREG(0), IREG(0), IREG(2)); // or i0,i0,i2 + UML_DSEXT4(block, R64(RTREG), IREG(0)); // dsext4 <rtreg>,i0 + } + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x26: /* LWR - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_SHL(block, IREG(4), IREG(0), IMM(3)); // shl i4,i0,3 + if (mips3.core->bigendian) + UML_XOR(block, IREG(4), IREG(4), IMM(0x18)); // xor i4,i4,0x18 + UML_SHL(block, IREG(2), IMM(~0), IREG(4)); // shl i2,~0,i4 + UML_CALLH(block, mips3.read32mask); // callh read32mask + if (RTREG != 0) + { + UML_SHR(block, IREG(2), IMM(~0), IREG(4)); // shr i2,~0,i4 + UML_XOR(block, IREG(2), IREG(2), IMM(~0)); // xor i2,i2,~0 + UML_SHR(block, IREG(0), IREG(0), IREG(4)); // shr i0,i0,i4 + UML_AND(block, IREG(2), IREG(2), R32(RTREG)); // and i2,i2,<rtreg> + UML_OR(block, IREG(0), IREG(0), IREG(2)); // or i0,i0,i2 + UML_DSEXT4(block, R64(RTREG), IREG(0)); // dsext4 <rtreg>,i0 + } + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x1a: /* LDL - MIPS III */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_SHL(block, IREG(4), IREG(0), IMM(3)); // shl i4,i0,3 + if (!mips3.core->bigendian) + UML_XOR(block, IREG(4), IREG(4), IMM(0x38)); // xor i4,i4,0x38 + UML_DSHR(block, IREG(2), IMM((UINT64)~0), IREG(4)); // dshr i2,~0,i4 + UML_CALLH(block, mips3.read64mask); // callh read64mask + if (RTREG != 0) + { + UML_DSHL(block, IREG(2), IMM((UINT64)~0), IREG(4)); // dshl i2,~0,i4 + UML_DXOR(block, IREG(2), IREG(2), IMM((UINT64)~0)); // dxor i2,i2,~0 + UML_DSHL(block, IREG(0), IREG(0), IREG(4)); // dshl i0,i0,i4 + UML_DAND(block, IREG(2), IREG(2), R64(RTREG)); // dand i2,i2,<rtreg> + UML_DOR(block, R64(RTREG), IREG(0), IREG(2)); // dor <rtreg>,i0,i2 + } + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x1b: /* LDR - MIPS III */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_SHL(block, IREG(4), IREG(0), IMM(3)); // shl i4,i0,3 + if (mips3.core->bigendian) + UML_XOR(block, IREG(4), IREG(4), IMM(0x38)); // xor i4,i4,0x38 + UML_DSHL(block, IREG(2), IMM((UINT64)~0), IREG(4)); // dshl i2,~0,i4 + UML_CALLH(block, mips3.read64mask); // callh read64mask + if (RTREG != 0) + { + UML_DSHR(block, IREG(2), IMM((UINT64)~0), IREG(4)); // dshr i2,~0,i4 + UML_DXOR(block, IREG(2), IREG(2), IMM((UINT64)~0)); // dxor i2,i2,~0 + UML_DSHR(block, IREG(0), IREG(0), IREG(4)); // dshr i0,i0,i4 + UML_DAND(block, IREG(2), IREG(2), R64(RTREG)); // dand i2,i2,<rtreg> + UML_DOR(block, R64(RTREG), IREG(0), IREG(2)); // dor <rtreg>,i0,i2 + } + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x31: /* LWC1 - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_CALLH(block, mips3.read32); // callh read32 + UML_MOV(block, FPR32(RTREG), IREG(0)); // mov <cpr1_rt>,i0 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x35: /* LDC1 - MIPS III */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_CALLH(block, mips3.read64); // callh read64 + UML_DMOV(block, FPR64(RTREG), IREG(0)); // dmov <cpr1_rt>,i0 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x32: /* LWC2 - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_CALLH(block, mips3.read32); // callh read32 + UML_DZEXT4(block, CPR264(RTREG), IREG(0)); // dzext4 <cpr2_rt>,i0 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x36: /* LDC2 - MIPS II */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_CALLH(block, mips3.read64); // callh read64 + UML_DMOV(block, CPR264(RTREG), IREG(0)); // dmov <cpr2_rt>,i0 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + + /* ----- memory store operations ----- */ + + case 0x28: /* SB - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_MOV(block, IREG(1), R32(RTREG)); // mov i1,<rtreg> + UML_CALLH(block, mips3.write8); // callh write8 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x29: /* SH - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_MOV(block, IREG(1), R32(RTREG)); // mov i1,<rtreg> + UML_CALLH(block, mips3.write16); // callh write16 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x2b: /* SW - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_MOV(block, IREG(1), R32(RTREG)); // mov i1,<rtreg> + UML_CALLH(block, mips3.write32); // callh write32 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x3f: /* SD - MIPS III */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_DMOV(block, IREG(1), R64(RTREG)); // dmov i1,<rtreg> + UML_CALLH(block, mips3.write64); // callh write64 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x2a: /* SWL - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_SHL(block, IREG(4), IREG(0), IMM(3)); // shl i4,i0,3 + UML_MOV(block, IREG(1), R32(RTREG)); // mov i1,<rtreg> + if (!mips3.core->bigendian) + UML_XOR(block, IREG(4), IREG(4), IMM(0x18)); // xor i4,i4,0x18 + UML_SHR(block, IREG(2), IMM(~0), IREG(4)); // shr i2,~0,i4 + UML_SHR(block, IREG(1), IREG(1), IREG(4)); // shr i1,i1,i4 + UML_CALLH(block, mips3.write32mask); // callh write32mask + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x2e: /* SWR - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_SHL(block, IREG(4), IREG(0), IMM(3)); // shl i4,i0,3 + UML_MOV(block, IREG(1), R32(RTREG)); // mov i1,<rtreg> + if (mips3.core->bigendian) + UML_XOR(block, IREG(4), IREG(4), IMM(0x18)); // xor i4,i4,0x18 + UML_SHL(block, IREG(2), IMM(~0), IREG(4)); // shl i2,~0,i4 + UML_SHL(block, IREG(1), IREG(1), IREG(4)); // shl i1,i1,i4 + UML_CALLH(block, mips3.write32mask); // callh write32mask + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x2c: /* SDL - MIPS III */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_SHL(block, IREG(4), IREG(0), IMM(3)); // shl i4,i0,3 + UML_DMOV(block, IREG(1), R64(RTREG)); // dmov i1,<rtreg> + if (!mips3.core->bigendian) + UML_XOR(block, IREG(4), IREG(4), IMM(0x38)); // xor i4,i4,0x38 + UML_DSHR(block, IREG(2), IMM((UINT64)~0), IREG(4)); // dshr i2,~0,i4 + UML_DSHR(block, IREG(1), IREG(1), IREG(4)); // dshr i1,i1,i4 + UML_CALLH(block, mips3.write64mask); // callh write64mask + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x2d: /* SDR - MIPS III */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_SHL(block, IREG(4), IREG(0), IMM(3)); // shl i4,i0,3 + UML_DMOV(block, IREG(1), R64(RTREG)); // dmov i1,<rtreg> + if (mips3.core->bigendian) + UML_XOR(block, IREG(4), IREG(4), IMM(0x38)); // xor i4,i4,0x38 + UML_DSHL(block, IREG(2), IMM((UINT64)~0), IREG(4)); // dshl i2,~0,i4 + UML_DSHL(block, IREG(1), IREG(1), IREG(4)); // dshl i1,i1,i4 + UML_CALLH(block, mips3.write64mask); // callh write64mask + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x39: /* SWC1 - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_MOV(block, IREG(1), FPR32(RTREG)); // mov i1,<cpr1_rt> + UML_CALLH(block, mips3.write32); // callh write32 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x3d: /* SDC1 - MIPS III */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_DMOV(block, IREG(1), FPR64(RTREG)); // dmov i1,<cpr1_rt> + UML_CALLH(block, mips3.write64); // callh write64 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x3a: /* SWC2 - MIPS I */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_MOV(block, IREG(1), CPR232(RTREG)); // mov i1,<cpr2_rt> + UML_CALLH(block, mips3.write32); // callh write32 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x3e: /* SDC2 - MIPS II */ + UML_ADD(block, IREG(0), R32(RSREG), IMM(SIMMVAL)); // add i0,<rsreg>,SIMMVAL + UML_DMOV(block, IREG(1), CPR264(RTREG)); // dmov i1,<cpr2_rt> + UML_CALLH(block, mips3.write64); // callh write64 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + + /* ----- effective no-ops ----- */ + + case 0x2f: /* CACHE - MIPS II */ + case 0x33: /* PREF - MIPS IV */ + return TRUE; + + + /* ----- coprocessor instructions ----- */ + + case 0x10: /* COP0 - MIPS I */ + return generate_cop0(block, compiler, desc); + + case 0x11: /* COP1 - MIPS I */ + return generate_cop1(block, compiler, desc); + + case 0x13: /* COP1X - MIPS IV */ + return generate_cop1x(block, compiler, desc); + + case 0x12: /* COP2 - MIPS I */ + UML_EXH(block, mips3.exception[EXCEPTION_INVALIDOP], IMM(0)); // exh invalidop,0 + return TRUE; + + + /* ----- unimplemented/illegal instructions ----- */ + +// case 0x30: /* LL - MIPS II */ logerror("mips3 Unhandled op: LL\n"); break; +// case 0x34: /* LLD - MIPS III */ logerror("mips3 Unhandled op: LLD\n"); break; +// case 0x38: /* SC - MIPS II */ logerror("mips3 Unhandled op: SC\n"); break; +// case 0x3c: /* SCD - MIPS III */ logerror("mips3 Unhandled op: SCD\n"); break; +// default: /* ??? */ invalid_instruction(op); break; + } + + return FALSE; } + + +/*------------------------------------------------- + generate_special - compile opcodes in the + 'SPECIAL' group +-------------------------------------------------*/ + +static int generate_special(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc) +{ + UINT32 op = *desc->opptr.l; + UINT8 opswitch = op & 63; + + switch (opswitch) + { + /* ----- shift instructions ----- */ + + case 0x00: /* SLL - MIPS I */ + if (RDREG != 0) + { + UML_SHL(block, IREG(0), R32(RTREG), IMM(SHIFT)); // shl i0,<rtreg>,<shift> + UML_DSEXT4(block, R64(RDREG), IREG(0)); // dsext4 <rdreg>,i0 + } + return TRUE; + + case 0x02: /* SRL - MIPS I */ + if (RDREG != 0) + { + UML_SHR(block, IREG(0), R32(RTREG), IMM(SHIFT)); // shr i0,<rtreg>,<shift> + UML_DSEXT4(block, R64(RDREG), IREG(0)); // dsext4 <rdreg>,i0 + } + return TRUE; + + case 0x03: /* SRA - MIPS I */ + if (RDREG != 0) + { + UML_SAR(block, IREG(0), R32(RTREG), IMM(SHIFT)); // sar i0,<rtreg>,<shift> + UML_DSEXT4(block, R64(RDREG), IREG(0)); // dsext4 <rdreg>,i0 + } + return TRUE; + + case 0x04: /* SLLV - MIPS I */ + if (RDREG != 0) + { + UML_SHL(block, IREG(0), R32(RTREG), R32(RSREG)); // shl i0,<rtreg>,<rsreg> + UML_DSEXT4(block, R64(RDREG), IREG(0)); // dsext4 <rdreg>,i0 + } + return TRUE; + + case 0x06: /* SRLV - MIPS I */ + if (RDREG != 0) + { + UML_SHR(block, IREG(0), R32(RTREG), R32(RSREG)); // shr i0,<rtreg>,<rsreg> + UML_DSEXT4(block, R64(RDREG), IREG(0)); // dsext4 <rdreg>,i0 + } + return TRUE; + + case 0x07: /* SRAV - MIPS I */ + if (RDREG != 0) + { + UML_SAR(block, IREG(0), R32(RTREG), R32(RSREG)); // sar i0,<rtreg>,<rsreg> + UML_DSEXT4(block, R64(RDREG), IREG(0)); // dsext4 <rdreg>,i0 + } + return TRUE; + + case 0x38: /* DSLL - MIPS III */ + if (RDREG != 0) + UML_DSHL(block, R64(RDREG), R64(RTREG), IMM(SHIFT)); // dshl <rdreg>,<rtreg>,<shift> + return TRUE; + + case 0x3a: /* DSRL - MIPS III */ + if (RDREG != 0) + UML_DSHR(block, R64(RDREG), R64(RTREG), IMM(SHIFT)); // dshr <rdreg>,<rtreg>,<shift> + return TRUE; + + case 0x3b: /* DSRA - MIPS III */ + if (RDREG != 0) + UML_DSAR(block, R64(RDREG), R64(RTREG), IMM(SHIFT)); // dsar <rdreg>,<rtreg>,<shift> + return TRUE; + + case 0x3c: /* DSLL32 - MIPS III */ + if (RDREG != 0) + UML_DSHL(block, R64(RDREG), R64(RTREG), IMM(SHIFT + 32)); // dshl <rdreg>,<rtreg>,<shift>+32 + return TRUE; + + case 0x3e: /* DSRL32 - MIPS III */ + if (RDREG != 0) + UML_DSHR(block, R64(RDREG), R64(RTREG), IMM(SHIFT + 32)); // dshr <rdreg>,<rtreg>,<shift>+32 + return TRUE; + + case 0x3f: /* DSRA32 - MIPS III */ + if (RDREG != 0) + UML_DSAR(block, R64(RDREG), R64(RTREG), IMM(SHIFT + 32)); // dsar <rdreg>,<rtreg>,<shift>+32 + return TRUE; + + case 0x14: /* DSLLV - MIPS III */ + if (RDREG != 0) + UML_DSHL(block, R64(RDREG), R64(RTREG), R64(RSREG)); // dshl <rdreg>,<rtreg>,<rsreg> + return TRUE; + + case 0x16: /* DSRLV - MIPS III */ + if (RDREG != 0) + UML_DSHR(block, R64(RDREG), R64(RTREG), R64(RSREG)); // dshr <rdreg>,<rtreg>,<rsreg> + return TRUE; + + case 0x17: /* DSRAV - MIPS III */ + if (RDREG != 0) + UML_DSAR(block, R64(RDREG), R64(RTREG), R64(RSREG)); // dsar <rdreg>,<rtreg>,<rsreg> + return TRUE; + + + /* ----- basic arithmetic ----- */ + + case 0x20: /* ADD - MIPS I */ + if (mips3.drcoptions & MIPS3DRC_CHECK_OVERFLOWS) + { + UML_ADDf(block, IREG(0), R32(RSREG), R32(RTREG), FLAGS_V); // add i0,<rsreg>,<rtreg>,V + UML_EXHc(block, IF_V, mips3.exception[EXCEPTION_OVERFLOW], IMM(0)); // exh overflow,0,V + if (RDREG != 0) + UML_DSEXT4(block, R64(RDREG), IREG(0)); // dsext4 <rdreg>,i0 + } + else if (RDREG != 0) + { + UML_ADD(block, IREG(0), R32(RSREG), R32(RTREG)); // add i0,<rsreg>,<rtreg> + UML_DSEXT4(block, R64(RDREG), IREG(0)); // dsext4 <rdreg>,i0 + } + return TRUE; + + case 0x21: /* ADDU - MIPS I */ + if (RDREG != 0) + { + UML_ADD(block, IREG(0), R32(RSREG), R32(RTREG)); // add i0,<rsreg>,<rtreg> + UML_DSEXT4(block, R64(RDREG), IREG(0)); // dsext4 <rdreg>,i0 + } + return TRUE; + + case 0x2c: /* DADD - MIPS III */ + if (mips3.drcoptions & MIPS3DRC_CHECK_OVERFLOWS) + { + UML_DADDf(block, IREG(0), R64(RSREG), R64(RTREG), FLAGS_V); // dadd i0,<rsreg>,<rtreg>,V + UML_EXHc(block, IF_V, mips3.exception[EXCEPTION_OVERFLOW], IMM(0)); // exh overflow,0,V + if (RDREG != 0) + UML_DMOV(block, R64(RDREG), IREG(0)); // dmov <rdreg>,i0 + } + else if (RDREG != 0) + UML_DADD(block, R64(RDREG), R64(RSREG), R64(RTREG)); // dadd <rdreg>,<rsreg>,<rtreg> + return TRUE; + + case 0x2d: /* DADDU - MIPS III */ + if (RDREG != 0) + UML_DADD(block, R64(RDREG), R64(RSREG), R64(RTREG)); // dadd <rdreg>,<rsreg>,<rtreg> + return TRUE; + + case 0x22: /* SUB - MIPS I */ + if (mips3.drcoptions & MIPS3DRC_CHECK_OVERFLOWS) + { + UML_SUBf(block, IREG(0), R32(RSREG), R32(RTREG), FLAGS_V); // sub i0,<rsreg>,<rtreg>,V + UML_EXHc(block, IF_V, mips3.exception[EXCEPTION_OVERFLOW], IMM(0)); // exh overflow,0,V + if (RDREG != 0) + UML_DSEXT4(block, R64(RDREG), IREG(0)); // dsext4 <rdreg>,i0 + } + else if (RDREG != 0) + { + UML_SUB(block, IREG(0), R32(RSREG), R32(RTREG)); // sub i0,<rsreg>,<rtreg> + UML_DSEXT4(block, R64(RDREG), IREG(0)); // dsext4 <rdreg>,i0 + } + return TRUE; + + case 0x23: /* SUBU - MIPS I */ + if (RDREG != 0) + { + UML_SUB(block, IREG(0), R32(RSREG), R32(RTREG)); // sub i0,<rsreg>,<rtreg> + UML_DSEXT4(block, R64(RDREG), IREG(0)); // dsext4 <rdreg>,i0 + } + return TRUE; + + case 0x2e: /* DSUB - MIPS III */ + if (mips3.drcoptions & MIPS3DRC_CHECK_OVERFLOWS) + { + UML_DSUBf(block, IREG(0), R64(RSREG), R64(RTREG), FLAGS_V); // dsub i0,<rsreg>,<rtreg>,V + UML_EXHc(block, IF_V, mips3.exception[EXCEPTION_OVERFLOW], IMM(0)); // exh overflow,0,V + if (RDREG != 0) + UML_DMOV(block, R64(RDREG), IREG(0)); // dmov <rdreg>,i0 + } + else if (RDREG != 0) + UML_DSUB(block, R64(RDREG), R64(RSREG), R64(RTREG)); // dsub <rdreg>,<rsreg>,<rtreg> + return TRUE; + + case 0x2f: /* DSUBU - MIPS III */ + if (RDREG != 0) + UML_DSUB(block, R64(RDREG), R64(RSREG), R64(RTREG)); // dsub <rdreg>,<rsreg>,<rtreg> + return TRUE; + + case 0x18: /* MULT - MIPS I */ + UML_MULS(block, IREG(0), IREG(1), R32(RSREG), R32(RTREG)); // muls i0,i1,<rsreg>,<rtreg> + UML_DSEXT4(block, LO64, IREG(0)); // dsext4 lo,i0 + UML_DSEXT4(block, HI64, IREG(1)); // dsext4 hi,i1 + return TRUE; + + case 0x19: /* MULTU - MIPS I */ + UML_MULU(block, IREG(0), IREG(1), R32(RSREG), R32(RTREG)); // mulu i0,i1,<rsreg>,<rtreg> + UML_DSEXT4(block, LO64, IREG(0)); // dsext4 lo,i0 + UML_DSEXT4(block, HI64, IREG(1)); // dsext4 hi,i1 + return TRUE; + + case 0x1c: /* DMULT - MIPS III */ + UML_DMULS(block, LO64, HI64, R64(RSREG), R64(RTREG)); // dmuls lo,hi,<rsreg>,<rtreg> + return TRUE; + + case 0x1d: /* DMULTU - MIPS III */ + UML_DMULU(block, LO64, HI64, R64(RSREG), R64(RTREG)); // dmulu lo,hi,<rsreg>,<rtreg> + return TRUE; + + case 0x1a: /* DIV - MIPS I */ + UML_DIVS(block, IREG(0), IREG(1), R32(RSREG), R32(RTREG)); // divs i0,i1,<rsreg>,<rtreg> + UML_DSEXT4(block, LO64, IREG(0)); // dsext4 lo,i0 + UML_DSEXT4(block, HI64, IREG(1)); // dsext4 hi,i1 + return TRUE; + + case 0x1b: /* DIVU - MIPS I */ + UML_DIVU(block, IREG(0), IREG(1), R32(RSREG), R32(RTREG)); // divu i0,i1,<rsreg>,<rtreg> + UML_DSEXT4(block, LO64, IREG(0)); // dsext4 lo,i0 + UML_DSEXT4(block, HI64, IREG(1)); // dsext4 hi,i1 + return TRUE; + + case 0x1e: /* DDIV - MIPS III */ + UML_DDIVS(block, LO64, HI64, R64(RSREG), R64(RTREG)); // ddivs lo,hi,<rsreg>,<rtreg> + return TRUE; + + case 0x1f: /* DDIVU - MIPS III */ + UML_DDIVU(block, LO64, HI64, R64(RSREG), R64(RTREG)); // ddivu lo,hi,<rsreg>,<rtreg> + return TRUE; + + + /* ----- basic logical ops ----- */ + + case 0x24: /* AND - MIPS I */ + if (RDREG != 0) + UML_DAND(block, R64(RDREG), R64(RSREG), R64(RTREG)); // dand <rdreg>,<rsreg>,<rtreg> + return TRUE; + + case 0x25: /* OR - MIPS I */ + if (RDREG != 0) + UML_DOR(block, R64(RDREG), R64(RSREG), R64(RTREG)); // dor <rdreg>,<rsreg>,<rtreg> + return TRUE; + + case 0x26: /* XOR - MIPS I */ + if (RDREG != 0) + UML_DXOR(block, R64(RDREG), R64(RSREG), R64(RTREG)); // dxor <rdreg>,<rsreg>,<rtreg> + return TRUE; + + case 0x27: /* NOR - MIPS I */ + if (RDREG != 0) + { + UML_DOR(block, IREG(0), R64(RSREG), R64(RTREG)); // dor i0,<rsreg>,<rtreg> + UML_DXOR(block, R64(RDREG), IREG(0), IMM((UINT64)~0)); // dxor <rdreg>,i0,~0 + } + return TRUE; + + + /* ----- basic comparisons ----- */ + + case 0x2a: /* SLT - MIPS I */ + if (RDREG != 0) + { + UML_DCMP(block, R64(RSREG), R64(RTREG)); // dcmp <rsreg>,<rtreg> + UML_DFLAGS(block, R64(RDREG), (UINT64)~0, mips3.cstate->slt_table); // dflags <rdreg>,~0,slt_table + } + return TRUE; + + case 0x2b: /* SLTU - MIPS I */ + if (RDREG != 0) + { + UML_DCMP(block, R64(RSREG), R64(RTREG)); // dcmp <rsreg>,<rtreg> + UML_DFLAGS(block, R64(RDREG), (UINT64)~0, mips3.cstate->sltu_table);// dflags <rdreg>,~0,sltu_table + } + return TRUE; + + + /* ----- conditional traps ----- */ + + case 0x30: /* TGE - MIPS II */ + UML_DCMP(block, R64(RSREG), R64(RTREG)); // dcmp <rsreg>,<rtreg> + UML_EXHc(block, IF_GE, mips3.exception[EXCEPTION_TRAP], IMM(0)); // exh trap,0,GE + return TRUE; + + case 0x31: /* TGEU - MIPS II */ + UML_DCMP(block, R64(RSREG), R64(RTREG)); // dcmp <rsreg>,<rtreg> + UML_EXHc(block, IF_AE, mips3.exception[EXCEPTION_TRAP], IMM(0)); // exh trap,0,AE + return TRUE; + + case 0x32: /* TLT - MIPS II */ + UML_DCMP(block, R64(RSREG), R64(RTREG)); // dcmp <rsreg>,<rtreg> + UML_EXHc(block, IF_L, mips3.exception[EXCEPTION_TRAP], IMM(0)); // exh trap,0,LT + return TRUE; + + case 0x33: /* TLTU - MIPS II */ + UML_DCMP(block, R64(RSREG), R64(RTREG)); // dcmp <rsreg>,<rtreg> + UML_EXHc(block, IF_B, mips3.exception[EXCEPTION_TRAP], IMM(0)); // exh trap,0,B + return TRUE; + + case 0x34: /* TEQ - MIPS II */ + UML_DCMP(block, R64(RSREG), R64(RTREG)); // dcmp <rsreg>,<rtreg> + UML_EXHc(block, IF_E, mips3.exception[EXCEPTION_TRAP], IMM(0)); // exh trap,0,E + return TRUE; + + case 0x36: /* TNE - MIPS II */ + UML_DCMP(block, R64(RSREG), R64(RTREG)); // dcmp <rsreg>,<rtreg> + UML_EXHc(block, IF_NE, mips3.exception[EXCEPTION_TRAP], IMM(0)); // exh trap,0,NE + return TRUE; + + + /* ----- conditional moves ----- */ + + case 0x0a: /* MOVZ - MIPS IV */ + if (RDREG != 0) + { + UML_DCMP(block, R64(RTREG), IMM(0)); // dcmp <rtreg>,0 + UML_DMOVc(block, IF_Z, R64(RDREG), R64(RSREG)); // mov <rdreg>,<rsreg>,Z + } + return TRUE; + + case 0x0b: /* MOVN - MIPS IV */ + if (RDREG != 0) + { + UML_DCMP(block, R64(RTREG), IMM(0)); // dcmp <rtreg>,0 + UML_DMOVc(block, IF_NZ, R64(RDREG), R64(RSREG)); // mov <rdreg>,<rsreg>,NZ + } + return TRUE; + + case 0x01: /* MOVF/MOVT - MIPS IV */ + if (RDREG != 0) + { + UML_TEST(block, CCR132(31), IMM(fcc_mask[(op >> 18) & 7])); // test ccr31,fcc_mask[x] + UML_DMOVc(block, ((op >> 16) & 1) ? IF_NZ : IF_Z, R64(RDREG), R64(RSREG)); + // mov <rdreg>,<rsreg>,NZ/Z + } + return TRUE; + + + /* ----- jumps and branches ----- */ + + case 0x08: /* JR - MIPS I */ + generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp> + return TRUE; + + case 0x09: /* JALR - MIPS I */ + generate_delay_slot_and_branch(block, compiler, desc, 31); // <next instruction + hashjmp> + return TRUE; + + + /* ----- system calls ----- */ + + case 0x0c: /* SYSCALL - MIPS I */ + UML_EXH(block, mips3.exception[EXCEPTION_SYSCALL], IMM(0)); // exh syscall,0 + return TRUE; + + case 0x0d: /* BREAK - MIPS I */ + UML_EXH(block, mips3.exception[EXCEPTION_BREAK], IMM(0)); // exh break,0 + return TRUE; + + + /* ----- effective no-ops ----- */ + + case 0x0f: /* SYNC - MIPS II */ + return TRUE; + + + /* ----- hi/lo register access ----- */ + + case 0x10: /* MFHI - MIPS I */ + if (RDREG != 0) + UML_DMOV(block, R64(RDREG), HI64); // dmov <rdreg>,hi + return TRUE; + + case 0x11: /* MTHI - MIPS I */ + UML_DMOV(block, HI64, R64(RSREG)); // dmov hi,<rsreg> + return TRUE; + + case 0x12: /* MFLO - MIPS I */ + if (RDREG != 0) + UML_DMOV(block, R64(RDREG), LO64); // dmov <rdreg>,lo + return TRUE; + + case 0x13: /* MTLO - MIPS I */ + UML_DMOV(block, LO64, R64(RSREG)); // dmov lo,<rsreg> + return TRUE; + } + return FALSE; +} + + +/*------------------------------------------------- + generate_regimm - compile opcodes in the + 'REGIMM' group +-------------------------------------------------*/ + +static int generate_regimm(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc) +{ + UINT32 op = *desc->opptr.l; + UINT8 opswitch = RTREG; + drcuml_codelabel skip; + + switch (opswitch) + { + case 0x00: /* BLTZ */ + case 0x02: /* BLTZL */ + case 0x10: /* BLTZAL */ + case 0x12: /* BLTZALL */ + if (RSREG != 0) + { + UML_DCMP(block, R64(RSREG), IMM(0)); // dcmp <rsreg>,0 + UML_JMPc(block, IF_GE, skip = compiler->labelnum++); // jmp skip,GE + generate_delay_slot_and_branch(block, compiler, desc, (opswitch & 0x10) ? 31 : 0); + // <next instruction + hashjmp> + UML_LABEL(block, skip); // skip: + } + return TRUE; + + case 0x01: /* BGEZ */ + case 0x03: /* BGEZL */ + case 0x11: /* BGEZAL */ + case 0x13: /* BGEZALL */ + if (RSREG != 0) + { + UML_DCMP(block, R64(RSREG), IMM(0)); // dcmp <rsreg>,0 + UML_JMPc(block, IF_L, skip = compiler->labelnum++); // jmp skip,L + generate_delay_slot_and_branch(block, compiler, desc, (opswitch & 0x10) ? 31 : 0); + // <next instruction + hashjmp> + UML_LABEL(block, skip); // skip: + } + else + generate_delay_slot_and_branch(block, compiler, desc, (opswitch & 0x10) ? 31 : 0); + // <next instruction + hashjmp> + return TRUE; + + case 0x08: /* TGEI */ + UML_DCMP(block, R64(RSREG), IMM(SIMMVAL)); // dcmp <rsreg>,SIMMVAL + UML_EXHc(block, IF_GE, mips3.exception[EXCEPTION_TRAP], IMM(0)); // exh trap,0,GE + return TRUE; + + case 0x09: /* TGEIU */ + UML_DCMP(block, R64(RSREG), IMM(SIMMVAL)); // dcmp <rsreg>,SIMMVAL + UML_EXHc(block, IF_AE, mips3.exception[EXCEPTION_TRAP], IMM(0)); // exh trap,0,AE + return TRUE; + + case 0x0a: /* TLTI */ + UML_DCMP(block, R64(RSREG), IMM(SIMMVAL)); // dcmp <rsreg>,SIMMVAL + UML_EXHc(block, IF_L, mips3.exception[EXCEPTION_TRAP], IMM(0)); // exh trap,0,L + return TRUE; + + case 0x0b: /* TLTIU */ + UML_DCMP(block, R64(RSREG), IMM(SIMMVAL)); // dcmp <rsreg>,SIMMVAL + UML_EXHc(block, IF_B, mips3.exception[EXCEPTION_TRAP], IMM(0)); // exh trap,0,B + return TRUE; + + case 0x0c: /* TEQI */ + UML_DCMP(block, R64(RSREG), IMM(SIMMVAL)); // dcmp <rsreg>,SIMMVAL + UML_EXHc(block, IF_E, mips3.exception[EXCEPTION_TRAP], IMM(0)); // exh trap,0,E + return TRUE; + + case 0x0e: /* TNEI */ + UML_DCMP(block, R64(RSREG), IMM(SIMMVAL)); // dcmp <rsreg>,SIMMVAL + UML_EXHc(block, IF_NE, mips3.exception[EXCEPTION_TRAP], IMM(0)); // exh trap,0,NE + return TRUE; + } + return FALSE; +} + + +/*------------------------------------------------- + generate_idt - compile opcodes in the IDT- + specific group +-------------------------------------------------*/ + +static int generate_idt(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc) +{ + fatalerror("Unimplemented IDT instructions"); +#if 0 + UINT32 op = *desc->opptr.l; + UINT8 opswitch = op & 0x1f; + + switch (opswitch) + { + case 0: /* MAD */ + if (RSREG != 0 && RTREG != 0) + { + emit_mov_r32_m32(REG_EAX, REGADDR(RSREG)); // mov eax,[rsreg] + emit_mov_r32_m32(REG_EDX, REGADDR(RTREG)); // mov edx,[rtreg] + emit_imul_r32(DRCTOP, REG_EDX); // imul edx + emit_add_r32_m32(DRCTOP, LOADDR); // add eax,[lo] + emit_adc_r32_m32(DRCTOP, HIADDR); // adc edx,[hi] + emit_mov_r32_r32(DRCTOP, REG_EBX, REG_EDX); // mov ebx,edx + emit_cdq(DRCTOP); // cdq + emit_mov_m64_r64(DRCTOP, LOADDR, REG_EAX); // mov [lo],edx:eax + emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EBX); // mov eax,ebx + emit_cdq(DRCTOP); // cdq + emit_mov_m64_r64(DRCTOP, HIADDR, REG_EAX); // mov [hi],edx:eax + } + return compile_SUCCESSFUL_CP(3,4); + + case 1: /* MADU */ + if (RSREG != 0 && RTREG != 0) + { + emit_mov_r32_m32(REG_EAX, REGADDR(RSREG)); // mov eax,[rsreg] + emit_mov_r32_m32(REG_EDX, REGADDR(RTREG)); // mov edx,[rtreg] + emit_mul_r32(DRCTOP, REG_EDX); // mul edx + emit_add_r32_m32(DRCTOP, LOADDR); // add eax,[lo] + emit_adc_r32_m32(DRCTOP, HIADDR); // adc edx,[hi] + emit_mov_r32_r32(DRCTOP, REG_EBX, REG_EDX); // mov ebx,edx + emit_cdq(DRCTOP); // cdq + emit_mov_m64_r64(DRCTOP, LOADDR, REG_EDX, REG_EAX); // mov [lo],edx:eax + emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EBX); // mov eax,ebx + emit_cdq(DRCTOP); // cdq + emit_mov_m64_r64(DRCTOP, HIADDR, REG_EDX, REG_EAX); // mov [hi],edx:eax + } + return compile_SUCCESSFUL_CP(3,4); + + case 2: /* MUL */ + if (RDREG != 0) + { + if (RSREG != 0 && RTREG != 0) + { + emit_mov_r32_m32(REG_EAX, REGADDR(RSREG)); // mov eax,[rsreg] + emit_imul_r32_m32(REG_EAX, REGADDR(RTREG)); // imul eax,[rtreg] + emit_cdq(DRCTOP); // cdq + emit_mov_m64_r64(REG_ESI, REGADDR(RDREG), REG_EDX, REG_EAX); // mov [rd],edx:eax + } + else + emit_zero_m64(REG_ESI, REGADDR(RDREG)); + } + return compile_SUCCESSFUL_CP(3,4); + } #endif + return FALSE; +} + + +/*------------------------------------------------- + generate_set_cop0_reg - generate code to + handle special COP0 registers +-------------------------------------------------*/ + +static int generate_set_cop0_reg(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc, UINT8 reg) +{ + int in_delay_slot = ((desc->flags & OPFLAG_IN_DELAY_SLOT) != 0); + drcuml_codelabel link; + + switch (reg) + { + case COP0_Cause: + UML_AND(block, IREG(0), IREG(0), IMM(~0xfc00)); // and i0,i0,~0xfc00 + UML_AND(block, IREG(1), CPR032(COP0_Cause), IMM(0xfc00)); // and i1,[Cause],0xfc00 + UML_OR(block, CPR032(COP0_Cause), IREG(0), IREG(1)); // or [Cause],i0,i1 + compiler->checksoftints = TRUE; + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, NULL, TRUE); + return TRUE; + + case COP0_Status: + generate_update_cycles(block, compiler, desc->pc, NULL, !in_delay_slot);// <subtract cycles> + UML_MOV(block, IREG(1), CPR032(COP0_Status)); // mov i1,[Status] + UML_MOV(block, CPR032(COP0_Status), IREG(0)); // mov [Status],i0 + UML_SHR(block, IREG(2), IREG(1), IMM(26)); // shr i2,i1,26 + UML_AND(block, MEM(&mips3.cstate->mode), IREG(2), IMM(1)); // and [mode],i2,1 + UML_XOR(block, IREG(0), IREG(0), IREG(1)); // xor i0,i0,i1 + UML_TEST(block, IREG(0), IMM(0x8000)); // test i0,0x8000 + UML_CALLCc(block, IF_NZ, mips3com_update_cycle_counting, mips3.core); // callc mips3com_update_cycle_counting,mips.core,NZ + compiler->checkints = TRUE; + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, NULL, TRUE); + return TRUE; + + case COP0_Count: + generate_update_cycles(block, compiler, desc->pc, NULL, !in_delay_slot);// <subtract cycles> + UML_MOV(block, CPR032(COP0_Count), IREG(0)); // mov [Count],i0 + UML_CALLC(block, cfunc_get_cycles, &mips3.cstate->numcycles); // callc cfunc_get_cycles,&mips3.numcycles + UML_DZEXT4(block, IREG(0), IREG(0)); // dzext4 i0,i0 + UML_DADD(block, IREG(0), IREG(0), IREG(0)); // dadd i0,i0,i0 + UML_DSUB(block, MEM(&mips3.core->count_zero_time), MEM(&mips3.cstate->numcycles), IREG(0)); + // dsub [count_zero_time],[mips3.numcycles],i0 + UML_CALLC(block, mips3com_update_cycle_counting, mips3.core); // callc mips3com_update_cycle_counting,mips.core + return TRUE; + + case COP0_Compare: + generate_update_cycles(block, compiler, desc->pc, NULL, !in_delay_slot);// <subtract cycles> + UML_MOV(block, CPR032(COP0_Compare), IREG(0)); // mov [Compare],i0 + UML_AND(block, CPR032(COP0_Cause), CPR032(COP0_Cause), IMM(~0x8000)); // and [Cause],[Cause],~0x8000 + UML_CALLC(block, mips3com_update_cycle_counting, mips3.core); // callc mips3com_update_cycle_counting,mips.core + return TRUE; + + case COP0_PRId: + return TRUE; + + case COP0_Config: + UML_AND(block, IREG(0), IREG(0), IMM(0x0007)); // and i0,i0,0x0007 + UML_AND(block, IREG(1), CPR032(COP0_Config), IMM(~0x0007)); // and i1,[Config],~0x0007 + UML_OR(block, CPR032(COP0_Config), IREG(0), IREG(1)); // or [Config],i0,i1 + return TRUE; + + case COP0_EntryHi: + UML_XOR(block, IREG(1), IREG(0), CPR032(reg)); // xor i1,i0,cpr0[reg] + UML_MOV(block, CPR032(reg), IREG(0)); // mov cpr0[reg],i0 + UML_TEST(block, IREG(1), IMM(0xff)); // test i1,0xff + UML_JMPc(block, IF_Z, link = compiler->labelnum++); // jmp link,z + UML_CALLC(block, mips3com_unmap_tlb_entries, mips3.core); // callc mips3com_unmap_tlb_entries + UML_CALLC(block, mips3com_map_tlb_entries, mips3.core); // callc mips3com_map_tlb_entries + UML_LABEL(block, link); // link: + return TRUE; + + default: + UML_MOV(block, CPR032(reg), IREG(0)); // mov cpr0[reg],i0 + return TRUE; + } +} + + +/*------------------------------------------------- + generate_get_cop0_reg - generate code to + read special COP0 registers +-------------------------------------------------*/ + +static int generate_get_cop0_reg(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc, UINT8 reg) +{ + drcuml_codelabel link1, link2; + + switch (reg) + { + case COP0_Count: + generate_update_cycles(block, compiler, desc->pc, NULL, FALSE); // <subtract cycles> + UML_CALLC(block, cfunc_get_cycles, &mips3.cstate->numcycles); // callc cfunc_get_cycles,&mips3.numcycles + UML_DSUB(block, IREG(0), MEM(&mips3.cstate->numcycles), MEM(&mips3.core->count_zero_time)); + // dsub i0,[numcycles],[count_zero_time] + UML_DSHR(block, IREG(0), IREG(0), IMM(1)); // dshr i0,i0,1 + UML_DSEXT4(block, IREG(0), IREG(0)); // dsext4 i0,i0 + return TRUE; + + case COP0_Random: + generate_update_cycles(block, compiler, desc->pc, NULL, FALSE); // <subtract cycles> + UML_CALLC(block, cfunc_get_cycles, &mips3.cstate->numcycles); // callc cfunc_get_cycles,&mips3.numcycles + UML_DSUB(block, IREG(0), MEM(&mips3.cstate->numcycles), MEM(&mips3.core->count_zero_time)); + // dsub i0,[numcycles],[count_zero_time] + UML_AND(block, IREG(1), CPR032(COP0_Wired), IMM(0x3f)); // and i1,[Wired],0x3f + UML_SUBf(block, IREG(2), IMM(48), IREG(1), FLAGS_BE); // sub i2,48,i1,ALL + UML_JMPc(block, IF_BE, link1 = compiler->labelnum++); // jmp link1,BE + UML_DZEXT4(block, IREG(2), IREG(2)); // dzext4 i2,i2 + UML_DDIVU(block, IREG(0), IREG(2), IREG(0), IREG(2)); // ddivu i0,i2,i0,i2 + UML_ADD(block, IREG(0), IREG(2), IREG(1)); // add i0,i2,i1 + UML_DAND(block, IREG(0), IREG(0), IMM(0x3f)); // dand i0,i0,0x3f + UML_JMP(block, link2 = compiler->labelnum++); // jmp link2 + UML_LABEL(block, link1); // link1: + UML_DSEXT4(block, IREG(0), IMM(47)); // dsext4 i0,47 + UML_LABEL(block, link2); // link2: + return TRUE; + + default: + UML_DSEXT4(block, IREG(0), CPR032(reg)); // dsext4 i0,cpr0[reg] + return TRUE; + } +} + + +/*------------------------------------------------- + generate_cop0 - compile COP0 opcodes +-------------------------------------------------*/ + +static int generate_cop0(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc) +{ + UINT32 op = *desc->opptr.l; + UINT8 opswitch = RSREG; + + /* generate an exception if COP0 is disabled or we are not in kernel mode */ + if (mips3.drcoptions & MIPS3DRC_STRICT_COP0) + { + drcuml_codelabel okay; + + UML_TEST(block, CPR032(COP0_Status), IMM(SR_KSU_MASK)); // test [Status],SR_KSU_MASK + UML_JMPc(block, IF_Z, okay = compiler->labelnum++); // jmp okay,Z + UML_TEST(block, CPR032(COP0_Status), IMM(SR_COP0)); // test [Status],SR_COP0 + UML_EXHc(block, IF_Z, mips3.exception[EXCEPTION_BADCOP], IMM(0)); // exh cop,0,Z + UML_LABEL(block, okay); // okay: + } + + switch (opswitch) + { + case 0x00: /* MFCz */ + if (RTREG != 0) + { + generate_get_cop0_reg(block, compiler, desc, RDREG); // <get cop0 reg> + UML_DSEXT4(block, R64(RTREG), IREG(0)); // dsext4 <rtreg>,i0 + } + return TRUE; + + case 0x01: /* DMFCz */ + if (RTREG != 0) + { + generate_get_cop0_reg(block, compiler, desc, RDREG); // <get cop0 reg> + UML_DMOV(block, R64(RTREG), IREG(0)); // dmov <rtreg>,i0 + } + return TRUE; + + case 0x02: /* CFCz */ + if (RTREG != 0) + UML_DSEXT4(block, R32(RTREG), CCR032(RDREG)); // dsext4 <rtreg>,ccr0[rdreg] + return TRUE; + + case 0x04: /* MTCz */ + UML_DSEXT4(block, IREG(0), R32(RTREG)); // dsext4 i0,<rtreg> + generate_set_cop0_reg(block, compiler, desc, RDREG); // <set cop0 reg> + return TRUE; + + case 0x05: /* DMTCz */ + UML_DMOV(block, IREG(0), R64(RTREG)); // dmov i0,<rtreg> + generate_set_cop0_reg(block, compiler, desc, RDREG); // <set cop0 reg> + return TRUE; + + case 0x06: /* CTCz */ + UML_DSEXT4(block, CCR064(RDREG), R32(RTREG)); // dsext4 ccr0[rdreg],<rtreg> + return TRUE; + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x1f: /* COP */ + switch (op & 0x01ffffff) + { + case 0x01: /* TLBR */ + UML_CALLC(block, mips3com_tlbr, mips3.core); // callc mips3com_tlbr,mips3.core + return TRUE; + + case 0x02: /* TLBWI */ + UML_CALLC(block, mips3com_tlbwi, mips3.core); // callc mips3com_tlbwi,mips3.core + return TRUE; + + case 0x06: /* TLBWR */ + UML_CALLC(block, mips3com_tlbwr, mips3.core); // callc mips3com_tlbwr,mips3.core + return TRUE; + + case 0x08: /* TLBP */ + UML_CALLC(block, mips3com_tlbp, mips3.core); // callc mips3com_tlbp,mips3.core + return TRUE; + + case 0x18: /* ERET */ + UML_AND(block, CPR032(COP0_Status), CPR032(COP0_Status), IMM(~SR_EXL)); + // and [Status],[Status],~SR_EXL + compiler->checkints = TRUE; + generate_update_cycles(block, compiler, 0, LOPTR(&mips3.core->cpr[0][COP0_EPC]), TRUE); + // <subtract cycles> + UML_HASHJMP(block, IMM(mips3.cstate->mode), CPR032(COP0_EPC), mips3.nocode); + // hashjmp <mode>,[EPC],nocode + return TRUE; + + case 0x20: /* WAIT */ + return TRUE; + } + break; + } + + return FALSE; +} + + + +/*************************************************************************** + COP1 RECOMPILATION +***************************************************************************/ + +/*------------------------------------------------- + generate_cop1 - compile COP1 opcodes +-------------------------------------------------*/ + +static int generate_cop1(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc) +{ + UINT32 op = *desc->opptr.l; + drcuml_codelabel skip; + int condition; + + /* generate an exception if COP1 is disabled */ + if (mips3.drcoptions & MIPS3DRC_STRICT_COP1) + { + UML_TEST(block, CPR032(COP0_Status), IMM(SR_COP1)); // test [Status],SR_COP1 + UML_EXHc(block, IF_Z, mips3.exception[EXCEPTION_BADCOP], IMM(0)); // exh cop,0,Z + } + + switch (RSREG) + { + case 0x00: /* MFCz - MIPS I */ + if (RTREG != 0) + UML_DSEXT4(block, R64(RTREG), FPR32(RDREG)); // dsext4 <rtreg>,fpr[rdreg] + return TRUE; + + case 0x01: /* DMFCz - MIPS III */ + if (RTREG != 0) + UML_DMOV(block, R64(RTREG), FPR64(RDREG)); // dmov <rtreg>,fpr[rdreg] + return TRUE; + + case 0x02: /* CFCz - MIPS I */ + if (RTREG != 0) + UML_DSEXT4(block, R64(RTREG), CCR132(RDREG)); // dsext4 <rtreg>,ccr132[rdreg] + return TRUE; + + case 0x04: /* MTCz - MIPS I */ + UML_MOV(block, FPR32(RDREG), R32(RTREG)); // mov fpr[rdreg],<rtreg> + return TRUE; + + case 0x05: /* DMTCz - MIPS III */ + UML_DMOV(block, FPR64(RDREG), R64(RTREG)); // dmov fpr[rdreg],<rtreg> + return TRUE; + + case 0x06: /* CTCz - MIPS I */ + if (RDREG != 31) + UML_DSEXT4(block, CCR164(RDREG), R32(RTREG)); // dsext4 ccr1[rdreg],<rtreg> + else + { + UML_XOR(block, IREG(0), CCR132(31), R32(RTREG)); // xor i0,ccr1[31],<rtreg> + UML_DSEXT4(block, CCR164(31), R32(RTREG)); // dsext4 ccr1[31],<rtreg> + UML_TEST(block, IREG(0), IMM(3)); // test i0,3 + UML_JMPc(block, IF_Z, skip = compiler->labelnum++); // jmp skip,Z + UML_AND(block, IREG(0), CCR132(31), IMM(3)); // and i0,ccr1[31],3 + UML_LOAD1U(block, IREG(0), &mips3.cstate->fpmode[0], IREG(0)); // load1u i0,fpmode,i0 + UML_SETFMOD(block, IREG(0)); // setfmod i0 + UML_LABEL(block, skip); // skip: + } + return TRUE; + + case 0x08: /* BC */ + switch ((op >> 16) & 3) + { + case 0x00: /* BCzF - MIPS I */ + case 0x02: /* BCzFL - MIPS II */ + UML_TEST(block, CCR132(31), IMM(FCCMASK(op >> 18))); // test ccr1[31],fccmask[which] + UML_JMPc(block, IF_NZ, skip = compiler->labelnum++); // jmp skip,NZ + generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp> + UML_LABEL(block, skip); // skip: + return TRUE; + + case 0x01: /* BCzT - MIPS I */ + case 0x03: /* BCzTL - MIPS II */ + UML_TEST(block, CCR132(31), IMM(FCCMASK(op >> 18))); // test ccr1[31],fccmask[which] + UML_JMPc(block, IF_Z, skip = compiler->labelnum++); // jmp skip,Z + generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp> + UML_LABEL(block, skip); // skip: + return TRUE; + } + break; + + default: + switch (op & 0x3f) + { + case 0x00: + if (IS_SINGLE(op)) /* ADD.S - MIPS I */ + UML_FSADD(block, FPR32(FDREG), FPR32(FSREG), FPR32(FTREG)); // fsadd <fdreg>,<fsreg>,<ftreg> + else /* ADD.D - MIPS I */ + UML_FDADD(block, FPR64(FDREG), FPR64(FSREG), FPR64(FTREG)); // fdadd <fdreg>,<fsreg>,<ftreg> + return TRUE; + + case 0x01: + if (IS_SINGLE(op)) /* SUB.S - MIPS I */ + UML_FSSUB(block, FPR32(FDREG), FPR32(FSREG), FPR32(FTREG)); // fssub <fdreg>,<fsreg>,<ftreg> + else /* SUB.D - MIPS I */ + UML_FDSUB(block, FPR64(FDREG), FPR64(FSREG), FPR64(FTREG)); // fdsub <fdreg>,<fsreg>,<ftreg> + return TRUE; + + case 0x02: + if (IS_SINGLE(op)) /* MUL.S - MIPS I */ + UML_FSMUL(block, FPR32(FDREG), FPR32(FSREG), FPR32(FTREG)); // fsmul <fdreg>,<fsreg>,<ftreg> + else /* MUL.D - MIPS I */ + UML_FDMUL(block, FPR64(FDREG), FPR64(FSREG), FPR64(FTREG)); // fdmul <fdreg>,<fsreg>,<ftreg> + return TRUE; + + case 0x03: + if (IS_SINGLE(op)) /* DIV.S - MIPS I */ + UML_FSDIV(block, FPR32(FDREG), FPR32(FSREG), FPR32(FTREG)); // fsdiv <fdreg>,<fsreg>,<ftreg> + else /* DIV.D - MIPS I */ + UML_FDDIV(block, FPR64(FDREG), FPR64(FSREG), FPR64(FTREG)); // fddiv <fdreg>,<fsreg>,<ftreg> + return TRUE; + + case 0x04: + if (IS_SINGLE(op)) /* SQRT.S - MIPS II */ + UML_FSSQRT(block, FPR32(FDREG), FPR32(FSREG)); // fssqrt <fdreg>,<fsreg> + else /* SQRT.D - MIPS II */ + UML_FDSQRT(block, FPR64(FDREG), FPR64(FSREG)); // fdsqrt <fdreg>,<fsreg> + return TRUE; + + case 0x05: + if (IS_SINGLE(op)) /* ABS.S - MIPS I */ + UML_FSABS(block, FPR32(FDREG), FPR32(FSREG)); // fsabs <fdreg>,<fsreg> + else /* ABS.D - MIPS I */ + UML_FDABS(block, FPR64(FDREG), FPR64(FSREG)); // fdabs <fdreg>,<fsreg> + return TRUE; + + case 0x06: + if (IS_SINGLE(op)) /* MOV.S - MIPS I */ + UML_FSMOV(block, FPR32(FDREG), FPR32(FSREG)); // fsmov <fdreg>,<fsreg> + else /* MOV.D - MIPS I */ + UML_FDMOV(block, FPR64(FDREG), FPR64(FSREG)); // fdmov <fdreg>,<fsreg> + return TRUE; + + case 0x07: + if (IS_SINGLE(op)) /* NEG.S - MIPS I */ + UML_FSNEG(block, FPR32(FDREG), FPR32(FSREG)); // fsneg <fdreg>,<fsreg> + else /* NEG.D - MIPS I */ + UML_FDNEG(block, FPR64(FDREG), FPR64(FSREG)); // fdneg <fdreg>,<fsreg> + return TRUE; + + case 0x08: + if (IS_SINGLE(op)) /* ROUND.L.S - MIPS III */ + UML_FSTOI8R(block, FPR64(FDREG), FPR32(FSREG)); // fstoi8r <fdreg>,<fsreg> + else /* ROUND.L.D - MIPS III */ + UML_FDTOI8R(block, FPR64(FDREG), FPR64(FSREG)); // fdtoi8r <fdreg>,<fsreg> + return TRUE; + + case 0x09: + if (IS_SINGLE(op)) /* TRUNC.L.S - MIPS III */ + UML_FSTOI8T(block, FPR64(FDREG), FPR32(FSREG)); // fstoi8t <fdreg>,<fsreg> + else /* TRUNC.L.D - MIPS III */ + UML_FDTOI8T(block, FPR64(FDREG), FPR64(FSREG)); // fdtoi8t <fdreg>,<fsreg> + return TRUE; + + case 0x0a: + if (IS_SINGLE(op)) /* CEIL.L.S - MIPS III */ + UML_FSTOI8C(block, FPR64(FDREG), FPR32(FSREG)); // fstoi8c <fdreg>,<fsreg> + else /* CEIL.L.D - MIPS III */ + UML_FDTOI8C(block, FPR64(FDREG), FPR64(FSREG)); // fdtoi8c <fdreg>,<fsreg> + return TRUE; + + case 0x0b: + if (IS_SINGLE(op)) /* FLOOR.L.S - MIPS III */ + UML_FSTOI8F(block, FPR64(FDREG), FPR32(FSREG)); // fstoi8f <fdreg>,<fsreg> + else /* FLOOR.L.D - MIPS III */ + UML_FDTOI8F(block, FPR64(FDREG), FPR64(FSREG)); // fdtoi8f <fdreg>,<fsreg> + return TRUE; + + case 0x0c: + if (IS_SINGLE(op)) /* ROUND.W.S - MIPS II */ + UML_FSTOI4R(block, FPR32(FDREG), FPR32(FSREG)); // fstoi4r <fdreg>,<fsreg> + else /* ROUND.W.D - MIPS II */ + UML_FDTOI4R(block, FPR32(FDREG), FPR64(FSREG)); // fdtoi4r <fdreg>,<fsreg> + return TRUE; + + case 0x0d: + if (IS_SINGLE(op)) /* TRUNC.W.S - MIPS II */ + UML_FSTOI4T(block, FPR32(FDREG), FPR32(FSREG)); // fstoi4t <fdreg>,<fsreg> + else /* TRUNC.W.D - MIPS II */ + UML_FDTOI4T(block, FPR32(FDREG), FPR64(FSREG)); // fdtoi4t <fdreg>,<fsreg> + return TRUE; + + case 0x0e: + if (IS_SINGLE(op)) /* CEIL.W.S - MIPS II */ + UML_FSTOI4C(block, FPR32(FDREG), FPR32(FSREG)); // fstoi4c <fdreg>,<fsreg> + else /* CEIL.W.D - MIPS II */ + UML_FDTOI4C(block, FPR32(FDREG), FPR64(FSREG)); // fdtoi4c <fdreg>,<fsreg> + return TRUE; + + case 0x0f: + if (IS_SINGLE(op)) /* FLOOR.W.S - MIPS II */ + UML_FSTOI4F(block, FPR32(FDREG), FPR32(FSREG)); // fstoi4f <fdreg>,<fsreg> + else /* FLOOR.W.D - MIPS II */ + UML_FDTOI4F(block, FPR32(FDREG), FPR64(FSREG)); // fdtoi4f <fdreg>,<fsreg> + return TRUE; + + case 0x11: + condition = ((op >> 16) & 1) ? IF_NZ : IF_Z; + UML_TEST(block, CCR132(31), IMM(FCCMASK(op >> 18))); // test ccr31,fccmask[op] + if (IS_SINGLE(op)) /* MOVT/F.S - MIPS IV */ + UML_FSMOVc(block, condition, FPR32(FDREG), FPR32(FSREG)); // fsmov <fdreg>,<fsreg>,condition + else /* MOVT/F.D - MIPS IV */ + UML_FDMOVc(block, condition, FPR64(FDREG), FPR64(FSREG)); // fdmov <fdreg>,<fsreg>,condition + return TRUE; + + case 0x12: + UML_DCMP(block, R64(RTREG), IMM(0)); // dcmp <rtreg>,0 + if (IS_SINGLE(op)) /* MOVZ.S - MIPS IV */ + UML_FSMOVc(block, IF_Z, FPR32(FDREG), FPR32(FSREG)); // fsmov <fdreg>,<fsreg>,Z + else /* MOVZ.D - MIPS IV */ + UML_FDMOVc(block, IF_Z, FPR64(FDREG), FPR64(FSREG)); // fdmov <fdreg>,<fsreg>,Z + return TRUE; + + case 0x13: + UML_DCMP(block, R64(RTREG), IMM(0)); // dcmp <rtreg>,0 + if (IS_SINGLE(op)) /* MOVN.S - MIPS IV */ + UML_FSMOVc(block, IF_NZ, FPR32(FDREG), FPR32(FSREG)); // fsmov <fdreg>,<fsreg>,NZ + else /* MOVN.D - MIPS IV */ + UML_FDMOVc(block, IF_NZ, FPR64(FDREG), FPR64(FSREG)); // fdmov <fdreg>,<fsreg>,NZ + return TRUE; + + case 0x15: + if (IS_SINGLE(op)) /* RECIP.S - MIPS IV */ + UML_FSRECIP(block, FPR32(FDREG), FPR32(FSREG)); // fsrecip <fdreg>,<fsreg> + else /* RECIP.D - MIPS IV */ + UML_FDRECIP(block, FPR64(FDREG), FPR64(FSREG)); // fdrecip <fdreg>,<fsreg> + return TRUE; + + case 0x16: + if (IS_SINGLE(op)) /* RSQRT.S - MIPS IV */ + UML_FSRSQRT(block, FPR32(FDREG), FPR32(FSREG)); // fsrsqrt <fdreg>,<fsreg> + else /* RSQRT.D - MIPS IV */ + UML_FDRSQRT(block, FPR64(FDREG), FPR64(FSREG)); // fdrsqrt <fdreg>,<fsreg> + return TRUE; + + case 0x20: + if (IS_INTEGRAL(op)) + { + if (IS_SINGLE(op)) /* CVT.S.W - MIPS I */ + UML_FSFRI4(block, FPR32(FDREG), FPR32(FSREG)); // fsfri4 <fdreg>,<fsreg> + else /* CVT.S.L - MIPS I */ + UML_FSFRI8(block, FPR32(FDREG), FPR64(FSREG)); // fsfri8 <fdreg>,<fsreg> + } + else /* CVT.S.D - MIPS I */ + UML_FSFRFD(block, FPR32(FDREG), FPR64(FSREG)); // fsfrfd <fdreg>,<fsreg> + return TRUE; + + case 0x21: + if (IS_INTEGRAL(op)) + { + if (IS_SINGLE(op)) /* CVT.D.W - MIPS I */ + UML_FDFRI4(block, FPR64(FDREG), FPR32(FSREG)); // fdfri4 <fdreg>,<fsreg> + else /* CVT.D.L - MIPS I */ + UML_FDFRI8(block, FPR64(FDREG), FPR64(FSREG)); // fdfri8 <fdreg>,<fsreg> + } + else /* CVT.D.S - MIPS I */ + UML_FDFRFS(block, FPR64(FDREG), FPR32(FSREG)); // fdfrfs <fdreg>,<fsreg> + return TRUE; + + case 0x24: + if (IS_SINGLE(op)) /* CVT.W.S - MIPS I */ + UML_FSTOI4(block, FPR32(FDREG), FPR32(FSREG)); // fstoi4 <fdreg>,<fsreg> + else /* CVT.W.D - MIPS I */ + UML_FDTOI4(block, FPR32(FDREG), FPR64(FSREG)); // fdtoi4 <fdreg>,<fsreg> + return TRUE; + + case 0x25: + if (IS_SINGLE(op)) /* CVT.L.S - MIPS I */ + UML_FSTOI8(block, FPR64(FDREG), FPR32(FSREG)); // fstoi8 <fdreg>,<fsreg> + else /* CVT.L.D - MIPS I */ + UML_FDTOI8(block, FPR64(FDREG), FPR64(FSREG)); // fdtoi8 <fdreg>,<fsreg> + return TRUE; + + case 0x30: + case 0x38: /* C.F.S/D - MIPS I */ + UML_AND(block, CCR132(31), CCR132(31), IMM(~FCCMASK(op >> 8))); // and ccr31,ccr31,~fccmask[op] + return TRUE; + + case 0x31: + case 0x39: + if (IS_SINGLE(op)) /* C.UN.S - MIPS I */ + UML_FSCMP(block, FPR32(FSREG), FPR32(FTREG)); // fscmp <fsreg>,<ftreg> + else /* C.UN.D - MIPS I */ + UML_FDCMP(block, FPR64(FSREG), FPR64(FTREG)); // fdcmp <fsreg>,<ftreg> + UML_FLAGS(block, CCR132(31), FCCMASK(op >> 8), mips3.cstate->c_un_table); + // flags ccr31,fccmask[op],c_eq_table + return TRUE; + + case 0x32: + case 0x3a: + if (IS_SINGLE(op)) /* C.EQ.S - MIPS I */ + UML_FSCMP(block, FPR32(FSREG), FPR32(FTREG)); // fscmp <fsreg>,<ftreg> + else /* C.EQ.D - MIPS I */ + UML_FDCMP(block, FPR64(FSREG), FPR64(FTREG)); // fdcmp <fsreg>,<ftreg> + UML_FLAGS(block, CCR132(31), FCCMASK(op >> 8), mips3.cstate->c_eq_table); + // flags ccr31,fccmask[op],c_eq_table + return TRUE; + + case 0x33: + case 0x3b: + if (IS_SINGLE(op)) /* C.UEQ.S - MIPS I */ + UML_FSCMP(block, FPR32(FSREG), FPR32(FTREG)); // fscmp <fsreg>,<ftreg> + else /* C.UEQ.D - MIPS I */ + UML_FDCMP(block, FPR64(FSREG), FPR64(FTREG)); // fdcmp <fsreg>,<ftreg> + UML_FLAGS(block, CCR132(31), FCCMASK(op >> 8), mips3.cstate->c_ueq_table); + // flags ccr31,fccmask[op],c_eq_table + return TRUE; + + case 0x34: + case 0x3c: + if (IS_SINGLE(op)) /* C.OLT.S - MIPS I */ + UML_FSCMP(block, FPR32(FSREG), FPR32(FTREG)); // fscmp <fsreg>,<ftreg> + else /* C.OLT.D - MIPS I */ + UML_FDCMP(block, FPR64(FSREG), FPR64(FTREG)); // fdcmp <fsreg>,<ftreg> + UML_FLAGS(block, CCR132(31), FCCMASK(op >> 8), mips3.cstate->c_olt_table); + // flags ccr31,fccmask[op],c_lt_table + return TRUE; + + case 0x35: + case 0x3d: + if (IS_SINGLE(op)) /* C.ULT.S - MIPS I */ + UML_FSCMP(block, FPR32(FSREG), FPR32(FTREG)); // fscmp <fsreg>,<ftreg> + else /* C.ULT.D - MIPS I */ + UML_FDCMP(block, FPR64(FSREG), FPR64(FTREG)); // fdcmp <fsreg>,<ftreg> + UML_FLAGS(block, CCR132(31), FCCMASK(op >> 8), mips3.cstate->c_ult_table); + // flags ccr31,fccmask[op],c_lt_table + return TRUE; + + case 0x36: + case 0x3e: + if (IS_SINGLE(op)) /* C.OLE.S - MIPS I */ + UML_FSCMP(block, FPR32(FSREG), FPR32(FTREG)); // fscmp <fsreg>,<ftreg> + else /* C.OLE.D - MIPS I */ + UML_FDCMP(block, FPR64(FSREG), FPR64(FTREG)); // fdcmp <fsreg>,<ftreg> + UML_FLAGS(block, CCR132(31), FCCMASK(op >> 8), mips3.cstate->c_ole_table); + // flags ccr31,fccmask[op],c_le_table + return TRUE; + + case 0x37: + case 0x3f: + if (IS_SINGLE(op)) /* C.ULE.S - MIPS I */ + UML_FSCMP(block, FPR32(FSREG), FPR32(FTREG)); // fscmp <fsreg>,<ftreg> + else /* C.ULE.D - MIPS I */ + UML_FDCMP(block, FPR64(FSREG), FPR64(FTREG)); // fdcmp <fsreg>,<ftreg> + UML_FLAGS(block, CCR132(31), FCCMASK(op >> 8), mips3.cstate->c_ule_table); + // flags ccr31,fccmask[op],c_le_table + return TRUE; + } + break; + } + return FALSE; +} + + + +/*************************************************************************** + COP1X RECOMPILATION +***************************************************************************/ + +/*------------------------------------------------- + generate_cop1x - compile COP1X opcodes +-------------------------------------------------*/ + +static int generate_cop1x(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc) +{ + int in_delay_slot = ((desc->flags & OPFLAG_IN_DELAY_SLOT) != 0); + UINT32 op = *desc->opptr.l; + + if (mips3.drcoptions & MIPS3DRC_STRICT_COP1) + { + UML_TEST(block, CPR032(COP0_Status), IMM(SR_COP1)); // test [Status],SR_COP1 + UML_EXHc(block, IF_Z, mips3.exception[EXCEPTION_BADCOP], IMM(0)); // exh cop,0,Z + } + + switch (op & 0x3f) + { + case 0x00: /* LWXC1 - MIPS IV */ + UML_ADD(block, IREG(0), R32(RSREG), R32(RTREG)); // add i0,<rsreg>,<rtreg> + UML_CALLH(block, mips3.read32); // callh read32 + UML_MOV(block, FPR32(RDREG), IREG(0)); // mov <cpr1_rd>,i0 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x01: /* LDXC1 - MIPS IV */ + UML_ADD(block, IREG(0), R32(RSREG), R32(RTREG)); // add i0,<rsreg>,<rtreg> + UML_CALLH(block, mips3.read64); // callh read64 + UML_DMOV(block, FPR64(RDREG), IREG(0)); // dmov <cpr1_rd>,i0 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x08: /* SWXC1 - MIPS IV */ + UML_ADD(block, IREG(0), R32(RSREG), R32(RTREG)); // add i0,<rsreg>,<rtreg> + UML_MOV(block, IREG(1), FPR32(RTREG)); // mov i1,<cpr1_rt> + UML_CALLH(block, mips3.write32); // callh write32 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x09: /* SDXC1 - MIPS IV */ + UML_ADD(block, IREG(0), R32(RSREG), R32(RTREG)); // add i0,<rsreg>,<rtreg> + UML_DMOV(block, IREG(1), FPR64(RTREG)); // dmov i1,<cpr1_rt> + UML_CALLH(block, mips3.write64); // callh write64 + if (!in_delay_slot) + generate_update_cycles(block, compiler, desc->pc + 4, 0, TRUE); + return TRUE; + + case 0x0f: /* PREFX */ + return TRUE; + + case 0x20: /* MADD.S - MIPS IV */ + UML_FSMUL(block, FREG(0), FPR32(FSREG), FPR32(FTREG)); // fsmul f0,<fsreg>,<ftreg> + UML_FSADD(block, FPR32(FDREG), FREG(0), FPR32(FRREG)); // fsadd <fdreg>,f0,<frreg> + return TRUE; + + case 0x21: /* MADD.D - MIPS IV */ + UML_FDMUL(block, FREG(0), FPR64(FSREG), FPR64(FTREG)); // fdmul f0,<fsreg>,<ftreg> + UML_FDADD(block, FPR64(FDREG), FREG(0), FPR64(FRREG)); // fdadd <fdreg>,f0,<frreg> + return TRUE; + + case 0x28: /* MSUB.S - MIPS IV */ + UML_FSMUL(block, FREG(0), FPR32(FSREG), FPR32(FTREG)); // fsmul f0,<fsreg>,<ftreg> + UML_FSSUB(block, FPR32(FDREG), FREG(0), FPR32(FRREG)); // fssub <fdreg>,f0,<frreg> + return TRUE; + + case 0x29: /* MSUB.D - MIPS IV */ + UML_FDMUL(block, FREG(0), FPR64(FSREG), FPR64(FTREG)); // fdmul f0,<fsreg>,<ftreg> + UML_FDSUB(block, FPR64(FDREG), FREG(0), FPR64(FRREG)); // fdsub <fdreg>,f0,<frreg> + return TRUE; + + case 0x30: /* NMADD.S - MIPS IV */ + UML_FSMUL(block, FREG(0), FPR32(FSREG), FPR32(FTREG)); // fsmul f0,<fsreg>,<ftreg> + UML_FSADD(block, FREG(0), FREG(0), FPR32(FRREG)); // fsadd f0,f0,<frreg> + UML_FSNEG(block, FPR32(FDREG), FREG(0)); // fsneg <fdreg>,f0 + return TRUE; + + case 0x31: /* NMADD.D - MIPS IV */ + UML_FDMUL(block, FREG(0), FPR64(FSREG), FPR64(FTREG)); // fdmul f0,<fsreg>,<ftreg> + UML_FDADD(block, FREG(0), FREG(0), FPR64(FRREG)); // fdadd f0,f0,<frreg> + UML_FDNEG(block, FPR64(FDREG), FREG(0)); // fdneg <fdreg>,f0 + return TRUE; + + case 0x38: /* NMSUB.S - MIPS IV */ + UML_FSMUL(block, FREG(0), FPR32(FSREG), FPR32(FTREG)); // fsmul f0,<fsreg>,<ftreg> + UML_FSSUB(block, FPR32(FDREG), FPR32(FRREG), FREG(0)); // fssub <fdreg>,<frreg>,f0 + return TRUE; + + case 0x39: /* NMSUB.D - MIPS IV */ + UML_FDMUL(block, FREG(0), FPR64(FSREG), FPR64(FTREG)); // fdmul f0,<fsreg>,<ftreg> + UML_FDSUB(block, FPR64(FDREG), FPR64(FRREG), FREG(0)); // fdsub <fdreg>,<frreg>,f0 + return TRUE; + + default: + fprintf(stderr, "cop1x %X\n", op); + break; + } + return FALSE; +} @@ -366,27 +3375,23 @@ static void *fastram_ptr(offs_t addr, int size, int iswrite) including disassembly of a MIPS instruction -------------------------------------------------*/ -static void log_add_disasm_comment(x86log_context *log, x86code *base, UINT32 pc, UINT32 op) +static void log_add_disasm_comment(drcuml_block *block, UINT32 pc, UINT32 op) { -#if (LOG_CODE) - { - char buffer[100]; - dasmmips3(buffer, pc, op); - x86log_add_comment(log, base, "%08X: %s", pc, buffer); - } +#if (LOG_UML) + char buffer[100]; + dasmmips3(buffer, pc, op); + UML_COMMENT(block, "%08X: %s", pc, buffer); // comment #endif } -#ifdef PTR64 - /*------------------------------------------------- - desc_flags_to_string - generate a string + log_desc_flags_to_string - generate a string representing the instruction description flags -------------------------------------------------*/ -static const char *desc_flags_to_string(UINT32 flags) +static const char *log_desc_flags_to_string(UINT32 flags) { static char tempbuf[30]; char *dest = tempbuf; @@ -440,7 +3445,7 @@ static const char *desc_flags_to_string(UINT32 flags) log_register_list - log a list of GPR registers -------------------------------------------------*/ -static void log_register_list(x86log_context *log, const char *string, UINT64 gprmask, UINT64 fprmask) +static void log_register_list(drcuml_state *drcuml, const char *string, UINT64 gprmask, UINT64 fprmask) { int count = 0; int regnum; @@ -449,22 +3454,22 @@ static void log_register_list(x86log_context *log, const char *string, UINT64 gp if ((gprmask & ~1) == 0 && fprmask == 0) return; - x86log_printf(log, "[%s:", string); + drcuml_log_printf(drcuml, "[%s:", string); for (regnum = 1; regnum < 32; regnum++) if (gprmask & ((UINT64)1 << regnum)) - x86log_printf(log, "%sr%d", (count++ == 0) ? "" : ",", regnum); + drcuml_log_printf(drcuml, "%sr%d", (count++ == 0) ? "" : ",", regnum); if (gprmask & ((UINT64)1 << REG_LO)) - x86log_printf(log, "%slo", (count++ == 0) ? "" : ","); + drcuml_log_printf(drcuml, "%slo", (count++ == 0) ? "" : ","); if (gprmask & ((UINT64)1 << REG_HI)) - x86log_printf(log, "%shi", (count++ == 0) ? "" : ","); + drcuml_log_printf(drcuml, "%shi", (count++ == 0) ? "" : ","); for (regnum = 0; regnum < 32; regnum++) if (fprmask & ((UINT64)1 << regnum)) - x86log_printf(log, "%sfpr%d", (count++ == 0) ? "" : ",", regnum); + drcuml_log_printf(drcuml, "%sfpr%d", (count++ == 0) ? "" : ",", regnum); if (fprmask & REGFLAG_FCC) - x86log_printf(log, "%sfcc", (count++ == 0) ? "" : ","); - x86log_printf(log, "] "); + drcuml_log_printf(drcuml, "%sfcc", (count++ == 0) ? "" : ","); + drcuml_log_printf(drcuml, "] "); } @@ -472,11 +3477,11 @@ static void log_register_list(x86log_context *log, const char *string, UINT64 gp log_opcode_desc - log a list of descriptions -------------------------------------------------*/ -static void log_opcode_desc(x86log_context *log, const opcode_desc *desclist, int indent) +static void log_opcode_desc(drcuml_state *drcuml, const opcode_desc *desclist, int indent) { /* open the file, creating it if necessary */ if (indent == 0) - x86log_printf(log, "\nDescriptor list @ %08X\n", desclist->pc); + drcuml_log_printf(drcuml, "\nDescriptor list @ %08X\n", desclist->pc); /* output each descriptor */ for ( ; desclist != NULL; desclist = desclist->next) @@ -484,42 +3489,29 @@ static void log_opcode_desc(x86log_context *log, const opcode_desc *desclist, in char buffer[100]; /* disassemle the current instruction and output it to the log */ -#if LOG_CODE +#if LOG_UML dasmmips3(buffer, desclist->pc, *desclist->opptr.l); #else strcpy(buffer, "???"); #endif - x86log_printf(log, "%08X [%08X] t:%08X f:%s: %-30s", desclist->pc, desclist->physpc, desclist->targetpc, desc_flags_to_string(desclist->flags), buffer); + drcuml_log_printf(drcuml, "%08X [%08X] t:%08X f:%s: %-30s", desclist->pc, desclist->physpc, desclist->targetpc, log_desc_flags_to_string(desclist->flags), buffer); /* output register states */ - log_register_list(log, "use", desclist->gpr.used, desclist->fpr.used); - log_register_list(log, "mod", desclist->gpr.modified, desclist->fpr.modified); - log_register_list(log, "lrd", desclist->gpr.liveread, desclist->fpr.liveread); - log_register_list(log, "lwr", desclist->gpr.livewrite, desclist->fpr.livewrite); - x86log_printf(log, "\n"); + log_register_list(drcuml, "use", desclist->gpr.used, desclist->fpr.used); + log_register_list(drcuml, "mod", desclist->gpr.modified, desclist->fpr.modified); + log_register_list(drcuml, "lrd", desclist->gpr.liveread, desclist->fpr.liveread); + log_register_list(drcuml, "lwr", desclist->gpr.livewrite, desclist->fpr.livewrite); + drcuml_log_printf(drcuml, "\n"); /* if we have a delay slot, output it recursively */ if (desclist->delay != NULL) - log_opcode_desc(log, desclist->delay, indent + 1); + log_opcode_desc(drcuml, desclist->delay, indent + 1); /* at the end of a sequence add a dividing line */ if (desclist->flags & OPFLAG_END_SEQUENCE) - x86log_printf(log, "-----\n"); + drcuml_log_printf(drcuml, "-----\n"); } } -#endif - - - -/*************************************************************************** - RECOMPILER CORE -***************************************************************************/ - -#ifdef PTR64 -#include "mdrc64.c" -#else -#include "mdrcold.c" -#endif @@ -725,7 +3717,6 @@ void r4700be_get_info(UINT32 state, cpuinfo *info) } } - void r4700le_get_info(UINT32 state, cpuinfo *info) { switch (state) @@ -913,390 +3904,7 @@ void rm7000le_get_info(UINT32 state, cpuinfo *info) DISASSEMBLERS ***************************************************************************/ -#if !defined(ENABLE_DEBUGGER) && (LOG_CODE) +#if !defined(MAME_DEBUG) && (LOG_UML) #include "mips3dsm.c" #endif - - -/*************************************************************************** - C CORE FOR COMPARISON -***************************************************************************/ - -#if COMPARE_AGAINST_C - -static UINT8 dummy_read_byte_32le(offs_t offs) -{ - if (mips3.access_type == 0) - { - mips3.access_type = 1; - mips3.access_size = 1; - mips3.access_addr = offs; - mips3.access_mask = 0; - mips3.access_data = program_read_byte_32le(offs); - } - else - { - assert(mips3.access_type == 1); - assert(mips3.access_size == 1); - assert(mips3.access_addr == offs); - assert(mips3.access_mask == 0); - mips3.access_type = 0; - } - return mips3.access_data; -} - -static UINT16 dummy_read_word_32le(offs_t offs) -{ - if (mips3.access_type == 0) - { - mips3.access_type = 1; - mips3.access_size = 2; - mips3.access_addr = offs; - mips3.access_mask = 0; - mips3.access_data = program_read_word_32le(offs); - } - else - { - assert(mips3.access_type == 1); - assert(mips3.access_size == 2); - assert(mips3.access_addr == offs); - assert(mips3.access_mask == 0); - mips3.access_type = 0; - } - return mips3.access_data; -} - -static UINT32 dummy_read_dword_32le(offs_t offs) -{ - if (mips3.access_type == 0) - { - mips3.access_type = 1; - mips3.access_size = 4; - mips3.access_addr = offs; - mips3.access_mask = 0; - mips3.access_data = program_read_dword_32le(offs); - } - else - { - assert(mips3.access_type == 1); - assert(mips3.access_size == 4); - assert(mips3.access_addr == offs); - assert(mips3.access_mask == 0); - mips3.access_type = 0; - } - return mips3.access_data; -} - -static UINT32 dummy_read_masked_32le(offs_t offs, UINT32 mask) -{ - if (mips3.access_type == 0) - { - mips3.access_type = 1; - mips3.access_size = 4; - mips3.access_addr = offs; - mips3.access_mask = mask; - mips3.access_data = program_read_masked_32le(offs, mask); - } - else - { - assert(mips3.access_type == 1); - assert(mips3.access_size == 4); - assert(mips3.access_addr == offs); - assert(mips3.access_mask == mask); - mips3.access_type = 0; - } - return mips3.access_data; -} - -static UINT64 dummy_read_qword_32le(offs_t offs) -{ - if (mips3.access_type == 0) - { - mips3.access_type = 1; - mips3.access_size = 8; - mips3.access_addr = offs; - mips3.access_mask = 0; - mips3.access_data = (*mips3.orighandler.readdouble)(offs); - } - else - { - assert(mips3.access_type == 1); - assert(mips3.access_size == 8); - assert(mips3.access_addr == offs); - assert(mips3.access_mask == 0); - mips3.access_type = 0; - } - return mips3.access_data; -} - -static UINT64 dummy_read_qword_masked_32le(offs_t offs, UINT64 mask) -{ - if (mips3.access_type == 0) - { - mips3.access_type = 1; - mips3.access_size = 8; - mips3.access_addr = offs; - mips3.access_mask = mask; - mips3.access_data = (*mips3.orighandler.readdouble_masked)(offs, mask); - } - else - { - assert(mips3.access_type == 1); - assert(mips3.access_size == 8); - assert(mips3.access_addr == offs); - assert(mips3.access_mask == mask); - mips3.access_type = 0; - } - return mips3.access_data; -} - -static void dummy_write_byte_32le(offs_t offs, UINT8 data) -{ - if (mips3.access_type == 0) - { - mips3.access_type = 2; - mips3.access_size = 1; - mips3.access_addr = offs; - mips3.access_data = data; - mips3.access_mask = 0; - } - else - { - assert(mips3.access_type == 2); - assert(mips3.access_size == 1); - assert(mips3.access_addr == offs); - assert(mips3.access_data == data); - assert(mips3.access_mask == 0); - mips3.access_type = 0; - program_write_byte_32le(offs, data); - } -} - -static void dummy_write_word_32le(offs_t offs, UINT16 data) -{ - if (mips3.access_type == 0) - { - mips3.access_type = 2; - mips3.access_size = 2; - mips3.access_addr = offs; - mips3.access_data = data; - mips3.access_mask = 0; - } - else - { - assert(mips3.access_type == 2); - assert(mips3.access_size == 2); - assert(mips3.access_addr == offs); - assert(mips3.access_data == data); - assert(mips3.access_mask == 0); - mips3.access_type = 0; - program_write_word_32le(offs, data); - } -} - -static void dummy_write_dword_32le(offs_t offs, UINT32 data) -{ - if (mips3.access_type == 0) - { - mips3.access_type = 2; - mips3.access_size = 4; - mips3.access_addr = offs; - mips3.access_data = data; - mips3.access_mask = 0; - } - else - { - assert(mips3.access_type == 2); - assert(mips3.access_size == 4); - assert(mips3.access_addr == offs); - assert(mips3.access_data == data); - assert(mips3.access_mask == 0); - mips3.access_type = 0; - program_write_dword_32le(offs, data); - } -} - -static void dummy_write_masked_32le(offs_t offs, UINT32 data, UINT32 mask) -{ - if (mips3.access_type == 0) - { - mips3.access_type = 2; - mips3.access_size = 4; - mips3.access_addr = offs; - mips3.access_data = data; - mips3.access_mask = mask; - } - else - { - assert(mips3.access_type == 2); - assert(mips3.access_size == 4); - assert(mips3.access_addr == offs); - assert(mips3.access_data == data); - assert(mips3.access_mask == mask); - mips3.access_type = 0; - program_write_masked_32le(offs, data, mask); - } -} - -static void dummy_write_qword_32le(offs_t offs, UINT64 data) -{ - if (mips3.access_type == 0) - { - mips3.access_type = 2; - mips3.access_size = 8; - mips3.access_addr = offs; - mips3.access_data = data; - mips3.access_mask = 0; - } - else - { - assert(mips3.access_type == 2); - assert(mips3.access_size == 8); - assert(mips3.access_addr == offs); - assert(mips3.access_data == data); - assert(mips3.access_mask == 0); - mips3.access_type = 0; - (*mips3.orighandler.writedouble)(offs, data); - } -} - -static void dummy_write_qword_masked_32le(offs_t offs, UINT64 data, UINT64 mask) -{ - if (mips3.access_type == 0) - { - mips3.access_type = 2; - mips3.access_size = 8; - mips3.access_addr = offs; - mips3.access_data = data; - mips3.access_mask = mask; - } - else - { - assert(mips3.access_type == 2); - assert(mips3.access_size == 8); - assert(mips3.access_addr == offs); - assert(mips3.access_data == data); - assert(mips3.access_mask == mask); - mips3.access_type = 0; - (*mips3.orighandler.writedouble_masked)(offs, data, mask); - } -} - -static const memory_handlers override_memory = -{ - dummy_read_byte_32le, - dummy_read_word_32le, - dummy_read_dword_32le, - dummy_read_masked_32le, - dummy_read_qword_32le, - dummy_read_qword_masked_32le, - - dummy_write_byte_32le, - dummy_write_word_32le, - dummy_write_dword_32le, - dummy_write_masked_32le, - dummy_write_qword_32le, - dummy_write_qword_masked_32le -}; - -#undef SR - -#define mips3 c_mips3 -#define mips3_regs c_mips3_regs - -#define mips3_get_context c_mips3_get_context -#define mips3_set_context c_mips3_set_context -#define mips3_reset c_mips3_reset -#define mips3_translate c_mips3_translate -#define mips3_dasm c_mips3_dasm -#define mips3_execute c_mips3_execute -#define mips3_set_info c_mips3_set_info -#define mips3_get_info c_mips3_get_info - -#define r4600be_init c_r4600be_init -#define r4600le_init c_r4600le_init -#define r4600be_get_info c_r4600be_get_info -#define r4600le_get_info c_r4600le_get_info - -#define r4650be_init c_r4650be_init -#define r4650le_init c_r4650le_init -#define r4650be_get_info c_r4650be_get_info -#define r4650le_get_info c_r4650le_get_info - -#define r4700be_init c_r4700be_init -#define r4700le_init c_r4700le_init -#define r4700be_get_info c_r4700be_get_info -#define r4700le_get_info c_r4700le_get_info - -#define r5000be_init c_r5000be_init -#define r5000le_init c_r5000le_init -#define r5000be_get_info c_r5000be_get_info -#define r5000le_get_info c_r5000le_get_info - -#define qed5271be_init c_qed5271be_init -#define qed5271le_init c_qed5271le_init -#define qed5271be_get_info c_qed5271be_get_info -#define qed5271le_get_info c_qed5271le_get_info - -#define rm7000be_init c_rm7000be_init -#define rm7000le_init c_rm7000le_init -#define rm7000be_get_info c_rm7000be_get_info -#define rm7000le_get_info c_rm7000le_get_info - -#include "mips3.c" - -#undef mips3 - -static void mips3c_init(mips3_flavor flavor, int bigendian, int index, int clock, const struct mips3_config *config, int (*irqcallback)(int)) -{ - size_t memsize = mips3com_init(&c_mips3.core, flavor, bigendian, index, clock, config, irqcallback, NULL); - void *memory = auto_malloc(memsize); - mips3com_init(&c_mips3.core, flavor, bigendian, index, clock, config, irqcallback, memory); - mips3.orighandler = mips3.core->memory; - mips3.core->memory = override_memory; - c_mips3.core.memory = override_memory; -} - -static void mips3c_reset(void) -{ - c_mips3_reset(); -} - -static void execute_c_version(void) -{ - int save_debug_mode = Machine->debug_mode; - int i; - - assert(mips3.access_type == 0); - - c_mips3.core.cpr[0][COP0_Cause] = (c_mips3.core.cpr[0][COP0_Cause] & ~0xfc00) | (mips3.core->cpr[0][COP0_Cause] & 0xfc00); - check_irqs(); - - assert(c_mips3.core.pc == mips3.core->pc); - assert(c_mips3.core.r[REG_HI] == mips3.core->r[REG_HI]); - assert(c_mips3.core.r[REG_LO] == mips3.core->r[REG_LO]); - for (i = 0; i < 32; i++) - { - assert(c_mips3.core.r[i] == mips3.core->r[i]); - assert(c_mips3.core.cpr[0][i] == mips3.core->cpr[0][i]); - assert(c_mips3.core.ccr[0][i] == mips3.core->ccr[0][i]); - assert(c_mips3.core.cpr[1][i] == mips3.core->cpr[1][i]); - assert(c_mips3.core.ccr[1][i] == mips3.core->ccr[1][i]); - } - for (i = 0; i < 8; i++) - assert(c_mips3.core.cf[1][i] == mips3.core->cf[1][i]); - assert(c_mips3.core.count_zero_time == mips3.core->count_zero_time); - - mips3.access_type = 0; - - Machine->debug_mode = 0; - c_mips3_execute(0); - Machine->debug_mode = save_debug_mode; -} - -static void compare_c_version(void) -{ -} - -#endif diff --git a/src/emu/cpu/mips/mips3dsm.c b/src/emu/cpu/mips/mips3dsm.c index a3f4cc6aeb2..197c5775437 100644 --- a/src/emu/cpu/mips/mips3dsm.c +++ b/src/emu/cpu/mips/mips3dsm.c @@ -353,7 +353,7 @@ unsigned dasmmips3(char *buffer, unsigned pc, UINT32 op) sprintf(buffer, "nop"); else sprintf(buffer, "sll %s,%s,%d", reg[rd], reg[rt], shift); break; - case 0x01: sprintf(buffer, "mov%c %s,%s,%d", ((op >> 16) & 1) ? 't' : 'f', reg[rd], reg[rt], (op >> 18) & 7); break; + case 0x01: sprintf(buffer, "mov%c %s,%s,%d", ((op >> 16) & 1) ? 't' : 'f', reg[rd], reg[rs], (op >> 18) & 7); break; case 0x02: sprintf(buffer, "srl %s,%s,%d", reg[rd], reg[rt], shift); break; case 0x03: sprintf(buffer, "sra %s,%s,%d", reg[rd], reg[rt], shift); break; case 0x04: sprintf(buffer, "sllv %s,%s,%s", reg[rd], reg[rt], reg[rs]); break; diff --git a/src/emu/cpu/mips/mips3fe.c b/src/emu/cpu/mips/mips3fe.c index 5bef908d7ac..eadf1b1e5e4 100644 --- a/src/emu/cpu/mips/mips3fe.c +++ b/src/emu/cpu/mips/mips3fe.c @@ -5,7 +5,7 @@ Front-end for MIPS3 recompiler Copyright Aaron Giles - Released for general use under the MAME license + Released for general non-commercial use under the MAME license Visit http://mamedev.org for licensing and usage restrictions. ***************************************************************************/ @@ -465,6 +465,13 @@ static int describe_instruction_cop0(mips3_state *mips, UINT32 op, opcode_desc * { case 0x00: /* MFCz */ case 0x01: /* DMFCz */ + if (RDREG == COP0_Count) + desc->cycles += MIPS3_COUNT_READ_CYCLES; + if (RDREG == COP0_Cause) + desc->cycles += MIPS3_CAUSE_READ_CYCLES; + desc->gpr.modified |= REGFLAG_R(RTREG); + return TRUE; + case 0x02: /* CFCz */ desc->gpr.modified |= REGFLAG_R(RTREG); return TRUE; diff --git a/src/emu/cpu/mips/mips3fe.h b/src/emu/cpu/mips/mips3fe.h index 1f6661657dd..ebb02429cab 100644 --- a/src/emu/cpu/mips/mips3fe.h +++ b/src/emu/cpu/mips/mips3fe.h @@ -5,7 +5,7 @@ Front-end for MIPS3 recompiler Copyright Aaron Giles - Released for general use under the MAME license + Released for general non-commercial use under the MAME license Visit http://mamedev.org for licensing and usage restrictions. ***************************************************************************/ diff --git a/src/emu/cpu/mips/r3000.c b/src/emu/cpu/mips/r3000.c index 57f795e974e..6449c5b3a87 100644 --- a/src/emu/cpu/mips/r3000.c +++ b/src/emu/cpu/mips/r3000.c @@ -7,6 +7,7 @@ ***************************************************************************/ #include "debugger.h" +#include "deprecat.h" #include "r3000.h" diff --git a/src/emu/cpu/x86emit.h b/src/emu/cpu/x86emit.h index fc05d632b30..fb7ff742e48 100644 --- a/src/emu/cpu/x86emit.h +++ b/src/emu/cpu/x86emit.h @@ -1608,6 +1608,25 @@ INLINE void emit_jecxz(x86code **emitptr, x86code *target) resolve_link(&target, &link); } +#ifdef PTR64 + +INLINE void emit_jrcxz_link(x86code **emitptr, emit_link *linkinfo) +{ + emit_op_simple(emitptr, OP_JrCXZ_Jb, OP_64BIT); + emit_byte(emitptr, 0); + linkinfo->target = *emitptr; + linkinfo->size = 1; +} + +INLINE void emit_jrcxz(x86code **emitptr, x86code *target) +{ + emit_link link; + emit_jrcxz_link(emitptr, &link); + resolve_link(&target, &link); +} + +#endif + /*------------------------------------------------- emit_call/jmp_* @@ -2754,6 +2773,26 @@ INLINE void emit_stmxcsr_m32(x86code **emitptr, DECLARE_MEMPARAMS) { emit_op_mod /*************************************************************************** + MISC SSE EMITTERS +***************************************************************************/ + +INLINE void emit_movd_r128_r32(x86code **emitptr, UINT8 dreg, UINT8 sreg) { emit_op_modrm_reg(emitptr, OP_MOVD_Vd_Ed, OP_32BIT, dreg, sreg); } +INLINE void emit_movd_r128_m32(x86code **emitptr, UINT8 dreg, DECLARE_MEMPARAMS) { emit_op_modrm_mem(emitptr, OP_MOVD_Vd_Ed, OP_32BIT, dreg, MEMPARAMS); } +INLINE void emit_movd_r32_r128(x86code **emitptr, UINT8 dreg, UINT8 sreg) { emit_op_modrm_reg(emitptr, OP_MOVD_Ed_Vd, OP_32BIT, sreg, dreg); } +INLINE void emit_movd_m32_r128(x86code **emitptr, DECLARE_MEMPARAMS, UINT8 sreg) { emit_op_modrm_mem(emitptr, OP_MOVD_Ed_Vd, OP_32BIT, sreg, MEMPARAMS); } + +#ifdef PTR64 + +INLINE void emit_movq_r128_r64(x86code **emitptr, UINT8 dreg, UINT8 sreg) { emit_op_modrm_reg(emitptr, OP_MOVD_Vd_Ed, OP_64BIT, dreg, sreg); } +INLINE void emit_movq_r128_m64(x86code **emitptr, UINT8 dreg, DECLARE_MEMPARAMS) { emit_op_modrm_mem(emitptr, OP_MOVD_Vd_Ed, OP_64BIT, dreg, MEMPARAMS); } +INLINE void emit_movq_r64_r128(x86code **emitptr, UINT8 dreg, UINT8 sreg) { emit_op_modrm_reg(emitptr, OP_MOVD_Ed_Vd, OP_64BIT, sreg, dreg); } +INLINE void emit_movq_m64_r128(x86code **emitptr, DECLARE_MEMPARAMS, UINT8 sreg) { emit_op_modrm_mem(emitptr, OP_MOVD_Ed_Vd, OP_64BIT, sreg, MEMPARAMS); } + +#endif + + + +/*************************************************************************** SSE SCALAR SINGLE EMITTERS ***************************************************************************/ diff --git a/src/emu/video/voodoo.c b/src/emu/video/voodoo.c index 005aa55b0a1..d1026e708a1 100644 --- a/src/emu/video/voodoo.c +++ b/src/emu/video/voodoo.c @@ -3686,7 +3686,7 @@ static void voodoo_w(voodoo_state *v, offs_t offset, UINT32 data, UINT32 mem_mas } /* modify the offset based on the mem_mask */ - if (mem_mask) + if (mem_mask != 0xffffffff) { if (!ACCESSING_BITS_16_31) offset |= 0x80000000; @@ -3860,6 +3860,9 @@ static UINT32 register_r(voodoo_state *v, offs_t offset) /* return the current scanline for now */ case vRetrace: + + /* eat some cycles since people like polling here */ + activecpu_eat_cycles(10); result = video_screen_get_vpos(v->screen); break; |