diff options
Diffstat (limited to 'src/devices/cpu/uml.cpp')
-rw-r--r-- | src/devices/cpu/uml.cpp | 1352 |
1 files changed, 941 insertions, 411 deletions
diff --git a/src/devices/cpu/uml.cpp b/src/devices/cpu/uml.cpp index 84cdfc9a1b6..7663ee90208 100644 --- a/src/devices/cpu/uml.cpp +++ b/src/devices/cpu/uml.cpp @@ -33,8 +33,12 @@ #include "emu.h" #include "drcuml.h" + #include "drcumlsh.h" +#include <type_traits> + + using namespace uml; @@ -43,7 +47,6 @@ using namespace uml; // DEBUGGING //************************************************************************** -#define VALIDATE_BACKEND (0) #define LOG_SIMPLIFICATIONS (0) @@ -53,19 +56,19 @@ using namespace uml; //************************************************************************** // opcode validation condition/flag valid bitmasks -#define OPFLAGS_NONE 0x00 -#define OPFLAGS_C FLAG_C -#define OPFLAGS_Z FLAG_Z -#define OPFLAGS_SZ (FLAG_S | FLAG_Z) -#define OPFLAGS_SZC (FLAG_S | FLAG_Z | FLAG_C) -#define OPFLAGS_SZV (FLAG_S | FLAG_Z | FLAG_V) -#define OPFLAGS_SZVC (FLAG_S | FLAG_Z | FLAG_V | FLAG_C) -#define OPFLAGS_UZC (FLAG_U | FLAG_Z | FLAG_C) -#define OPFLAGS_ALL 0x1f -#define OPFLAGS_P1 0x81 -#define OPFLAGS_P2 0x82 -#define OPFLAGS_P3 0x83 -#define OPFLAGS_P4 0x84 +constexpr u8 OPFLAGS_NONE = FLAGS_NONE; +constexpr u8 OPFLAGS_C = FLAG_C; +constexpr u8 OPFLAGS_Z = FLAG_Z; +constexpr u8 OPFLAGS_SZ = FLAG_S | FLAG_Z; +constexpr u8 OPFLAGS_SZC = FLAG_S | FLAG_Z | FLAG_C; +constexpr u8 OPFLAGS_SZV = FLAG_S | FLAG_Z | FLAG_V; +constexpr u8 OPFLAGS_SZVC = FLAG_S | FLAG_Z | FLAG_V | FLAG_C; +constexpr u8 OPFLAGS_UZC = FLAG_U | FLAG_Z | FLAG_C; +constexpr u8 OPFLAGS_ALL = FLAGS_ALL; +constexpr u8 OPFLAGS_P1 = 0x81; +constexpr u8 OPFLAGS_P2 = 0x82; +constexpr u8 OPFLAGS_P3 = 0x83; +constexpr u8 OPFLAGS_P4 = 0x84; // parameter input/output states #define PIO_IN 0x01 @@ -86,7 +89,6 @@ using namespace uml; #define PTYPES_IMM (1 << parameter::PTYPE_IMMEDIATE) #define PTYPES_IREG (1 << parameter::PTYPE_INT_REGISTER) #define PTYPES_FREG (1 << parameter::PTYPE_FLOAT_REGISTER) -#define PTYPES_VREG (1 << parameter::PTYPE_VECTOR_REGISTER) #define PTYPES_MVAR (1 << parameter::PTYPE_MAPVAR) #define PTYPES_MEM (1 << parameter::PTYPE_MEMORY) #define PTYPES_SIZE (1 << parameter::PTYPE_SIZE) @@ -137,45 +139,49 @@ opcode_info const instruction::s_opcode_info_table[OP_MAX] = // Control Flow Operations OPINFO0(NOP, "nop", 4, false, NONE, NONE, NONE) - OPINFO1(DEBUG, "debug", 4, false, NONE, NONE, ALL, PINFO(IN, OP, IANY)) + OPINFO1(DEBUG, "debug", 4, false, NONE, NONE, ALL, PINFO(IN, OP, IANY)) // MAME debugger breakpoint + OPINFO0(BREAK, "break", 4, false, NONE, NONE, NONE) // (for debugging) Issues a breakpoint exception to allow for debugging the generated assembly OPINFO1(EXIT, "exit", 4, true, NONE, NONE, ALL, PINFO(IN, OP, IANY)) OPINFO3(HASHJMP, "hashjmp", 4, false, NONE, NONE, ALL, PINFO(IN, OP, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, HANDLE)) OPINFO1(JMP, "jmp", 4, true, NONE, NONE, NONE, PINFO(IN, OP, LABEL)) - OPINFO2(EXH, "exh", 4, true, NONE, NONE, ALL, PINFO(IN, OP, HANDLE), PINFO(IN, OP, IANY)) - OPINFO1(CALLH, "callh", 4, true, NONE, NONE, ALL, PINFO(IN, OP, HANDLE)) + OPINFO2(EXH, "exh", 4, true, NONE, NONE, ALL, PINFO(IN, OP, HANDLE), PINFO(IN, OP, IANY)) // Call exception handler + OPINFO1(CALLH, "callh", 4, true, NONE, NONE, ALL, PINFO(IN, OP, HANDLE)) // Call handle OPINFO0(RET, "ret", 4, true, NONE, NONE, ALL) - OPINFO2(CALLC, "callc", 4, true, NONE, NONE, ALL, PINFO(IN, OP, CFUNC), PINFO(IN, OP, PTR)) - OPINFO2(RECOVER, "recover", 4, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, MVAR)) + OPINFO2(CALLC, "callc", 4, true, NONE, NONE, ALL, PINFO(IN, OP, CFUNC), PINFO(IN, OP, PTR)) // Call C function + OPINFO2(RECOVER, "recover", 4, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, MVAR)) // Get value from mapvar // Internal Register Operations - OPINFO1(SETFMOD, "setfmod", 4, false, NONE, NONE, ALL, PINFO(IN, OP, IANY)) - OPINFO1(GETFMOD, "getfmod", 4, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM)) - OPINFO1(GETEXP, "getexp", 4, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM)) - OPINFO2(GETFLGS, "getflgs", 4, false, P2, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, IMV)) - OPINFO1(SAVE, "save", 4, false, ALL, NONE, ALL, PINFO(OUT, OP, STATE)) - OPINFO1(RESTORE, "restore", 4, false, NONE, ALL, ALL, PINFO(IN, OP, STATE)) + OPINFO1(SETFMOD, "setfmod", 4, false, NONE, NONE, ALL, PINFO(IN, OP, IANY)) // Set floating point control mode + OPINFO1(GETFMOD, "getfmod", 4, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM)) // Get floating point control mode + OPINFO1(GETEXP, "getexp", 4, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM)) // Get exception parameter value + OPINFO2(GETFLGS, "getflgs", 4, false, P2, NONE, NONE, PINFO(OUT, OP, IRM), PINFO(IN, OP, IMV)) // Get status register flags + OPINFO1(SETFLGS, "setflgs", 4, false, NONE, ALL, ALL, PINFO(IN, OP, IANY)) // (for debugging) Set status register flags + OPINFO1(SAVE, "save", 4, false, ALL, NONE, ALL, PINFO(OUT, OP, STATE)) // Save current state to drcuml_machine_state + OPINFO1(RESTORE, "restore", 4, false, NONE, ALL, ALL, PINFO(IN, OP, STATE)) // Load saved state from drcuml_machine_state // Integer Operations - OPINFO4(LOAD, "!load", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, PTR), PINFO(IN, 4, IANY), PINFO(IN, OP, SCSIZE)) - OPINFO4(LOADS, "!loads", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, PTR), PINFO(IN, 4, IANY), PINFO(IN, OP, SCSIZE)) - OPINFO4(STORE, "!store", 4|8, false, NONE, NONE, ALL, PINFO(IN, OP, PTR), PINFO(IN, 4, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, SCSIZE)) - OPINFO3(READ, "!read", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, 4, IANY), PINFO(IN, OP, SPSIZE)) - OPINFO4(READM, "!readm", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, 4, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, SPSIZE)) - OPINFO3(WRITE, "!write", 4|8, false, NONE, NONE, ALL, PINFO(IN, 4, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, SPSIZE)) - OPINFO4(WRITEM, "!writem", 4|8, false, NONE, NONE, ALL, PINFO(IN, 4, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, SPSIZE)) - OPINFO2(CARRY, "!carry", 4|8, false, NONE, C, ALL, PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) - OPINFO1(SET, "!set", 4|8, true, NONE, NONE, ALL, PINFO(OUT, OP, IRM)) + OPINFO4(LOAD, "!load", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, PTR), PINFO(IN, 4, IANY), PINFO(IN, OP, SCSIZE)) // Load unsigned value from specified memory location + OPINFO4(LOADS, "!loads", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, PTR), PINFO(IN, 4, IANY), PINFO(IN, OP, SCSIZE)) // Load signed value from specified memory location + OPINFO4(STORE, "!store", 4|8, false, NONE, NONE, ALL, PINFO(IN, OP, PTR), PINFO(IN, 4, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, SCSIZE)) // Store value to specified memory location + OPINFO3(READ, "!read", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, 4, IANY), PINFO(IN, OP, SPSIZE)) // Read memory from emulated machine using memory space reader + OPINFO4(READM, "!readm", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, IRM), PINFO(IN, 4, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, SPSIZE)) // Read memory from emulated machine using memory space reader (masked) + OPINFO3(WRITE, "!write", 4|8, false, NONE, NONE, ALL, PINFO(IN, 4, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, SPSIZE)) // Write to emulated machine's memory using memory space writer + OPINFO4(WRITEM, "!writem", 4|8, false, NONE, NONE, ALL, PINFO(IN, 4, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, SPSIZE)) // Write to emulated machine's memory using memory space writer (masked) + OPINFO2(CARRY, "!carry", 4|8, false, NONE, C, ALL, PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) // Set carry status flag on CPU + OPINFO1(SET, "!set", 4|8, true, NONE, NONE, ALL, PINFO(OUT, OP, IRM)) // Get the state of the specified condition (e.g. calling UML_SET with COND_NZ will return 0 if the condition is not met and 1 if the condition is met) OPINFO2(MOV, "!mov", 4|8, true, NONE, NONE, NONE, PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY)) OPINFO3(SEXT, "!sext", 4|8, false, NONE, SZ, ALL, PINFO(OUT, OP, IRM), PINFO(IN, P3, IANY), PINFO(IN, OP, SIZE)) - OPINFO4(ROLAND, "!roland", 4|8, false, NONE, SZ, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) - OPINFO4(ROLINS, "!rolins", 4|8, false, NONE, SZ, ALL, PINFO(INOUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) + OPINFO4(ROLAND, "!roland", 4|8, false, NONE, SZ, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) // Rotate left + AND (see drcbec.cpp for implementation) + OPINFO4(ROLINS, "!rolins", 4|8, false, NONE, SZ, ALL, PINFO(INOUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) // Rotate left + OR (see drcbec.cpp for implementation) OPINFO3(ADD, "!add", 4|8, false, NONE, SZVC, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) OPINFO3(ADDC, "!addc", 4|8, false, C, SZVC, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) OPINFO3(SUB, "!sub", 4|8, false, NONE, SZVC, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) OPINFO3(SUBB, "!subb", 4|8, false, C, SZVC, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) OPINFO2(CMP, "!cmp", 4|8, false, NONE, SZVC, ALL, PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) - OPINFO4(MULU, "!mulu", 4|8, false, NONE, SZV, ALL, PINFO(OUT, OP, IRM), PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) - OPINFO4(MULS, "!muls", 4|8, false, NONE, SZV, ALL, PINFO(OUT, OP, IRM), PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) + OPINFO4(MULU, "!mulu", 4|8, false, NONE, SZV, ALL, PINFO(OUT, OP, IRM), PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) // Unsigned 32x32=64 and 64x64=128 multiplication + OPINFO3(MULULW, "!mululw", 4|8, false, NONE, SZV, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) // Unsigned 32x32=32 and 64x64=64 multiplication (overflow set based on 32x32=64 calculation but zero and sign based on 32-bit result) + OPINFO4(MULS, "!muls", 4|8, false, NONE, SZV, ALL, PINFO(OUT, OP, IRM), PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) // Signed 32x32=64 and 64x64=128 multiplication + OPINFO3(MULSLW, "!mulslw", 4|8, false, NONE, SZV, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) // Signed 32x32=32 and 64x64=64 multiplication (overflow set based on 32x32=64 calculation but zero and sign based on 32-bit result) OPINFO4(DIVU, "!divu", 4|8, false, NONE, SZV, ALL, PINFO(OUT, OP, IRM), PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) OPINFO4(DIVS, "!divs", 4|8, false, NONE, SZV, ALL, PINFO(OUT, OP, IRM), PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) OPINFO3(AND, "!and", 4|8, false, NONE, SZ, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) @@ -194,18 +200,18 @@ opcode_info const instruction::s_opcode_info_table[OP_MAX] = OPINFO3(RORC, "!rorc", 4|8, false, C, SZC, ALL, PINFO(OUT, OP, IRM), PINFO(IN, OP, IANY), PINFO(IN, OP, IANY)) // Floating Point Operations - OPINFO3(FLOAD, "f#load", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, OP, PTR), PINFO(IN, 4, IANY)) - OPINFO3(FSTORE, "f#store", 4|8, false, NONE, NONE, ALL, PINFO(IN, OP, PTR), PINFO(IN, 4, IANY), PINFO(IN, OP, FRM)) - OPINFO3(FREAD, "f#read", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, 4, IANY), PINFO(IN, OP, SPSIZE)) - OPINFO3(FWRITE, "f#write", 4|8, false, NONE, NONE, ALL, PINFO(IN, 4, IANY), PINFO(IN, OP, FANY), PINFO(IN, OP, SPSIZE)) + OPINFO3(FLOAD, "f#load", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, OP, PTR), PINFO(IN, 4, IANY)) // Load float/double value from specified memory location + OPINFO3(FSTORE, "f#store", 4|8, false, NONE, NONE, ALL, PINFO(IN, OP, PTR), PINFO(IN, 4, IANY), PINFO(IN, OP, FRM)) // Save float/double value to specified memory location + OPINFO3(FREAD, "f#read", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, 4, IANY), PINFO(IN, OP, SPSIZE)) // Read float/double value from emulated machine using memory space reader + OPINFO3(FWRITE, "f#write", 4|8, false, NONE, NONE, ALL, PINFO(IN, 4, IANY), PINFO(IN, OP, FANY), PINFO(IN, OP, SPSIZE)) // Write float/double value to emulated machine using memory space writer OPINFO2(FMOV, "f#mov", 4|8, true, NONE, NONE, NONE, PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY)) - OPINFO4(FTOINT, "f#toint", 4|8, false, NONE, NONE, ALL, PINFO(OUT, P3, IRM), PINFO(IN, OP, FANY), PINFO(IN, OP, SIZE), PINFO(IN, OP, ROUND)) - OPINFO3(FFRINT, "f#frint", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, P3, IANY), PINFO(IN, OP, SIZE)) - OPINFO3(FFRFLT, "f#frflt", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, P3, FANY), PINFO(IN, OP, SIZE)) - OPINFO2(FRNDS, "f#rnds", 8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, P3, FANY)) + OPINFO4(FTOINT, "f#toint", 4|8, false, NONE, NONE, ALL, PINFO(OUT, P3, IRM), PINFO(IN, OP, FANY), PINFO(IN, OP, SIZE), PINFO(IN, OP, ROUND)) // Float/double to integer + OPINFO3(FFRINT, "f#frint", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, P3, IANY), PINFO(IN, OP, SIZE)) // Float/double from integer + OPINFO3(FFRFLT, "f#frflt", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, P3, FANY), PINFO(IN, OP, SIZE)) // Convert float to double or double to float + OPINFO2(FRNDS, "f#rnds", 8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, P3, FANY)) // Convert double to float and then back to double, or float to double and back to float OPINFO3(FADD, "f#add", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY), PINFO(IN, OP, FANY)) OPINFO3(FSUB, "f#sub", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY), PINFO(IN, OP, FANY)) - OPINFO2(FCMP, "f#cmp", 4|8, false, NONE, UZC, ALL, PINFO(IN, OP, FANY), PINFO(IN, OP, FANY)) + OPINFO2(FCMP, "f#cmp", 4|8, false, NONE, UZC, ALL, PINFO(IN, OP, FANY), PINFO(IN, OP, FANY)) // Note: status flags except FLAG_U are undefined when comparing with NaN OPINFO3(FMUL, "f#mul", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY), PINFO(IN, OP, FANY)) OPINFO3(FDIV, "f#div", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY), PINFO(IN, OP, FANY)) OPINFO2(FNEG, "f#neg", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY)) @@ -213,40 +219,13 @@ opcode_info const instruction::s_opcode_info_table[OP_MAX] = OPINFO2(FSQRT, "f#sqrt", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY)) OPINFO2(FRECIP, "f#recip", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY)) OPINFO2(FRSQRT, "f#rsqrt", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY)) - OPINFO2(FCOPYI, "f#copyi", 4|8, false, NONE, NONE, NONE, PINFO(OUT, OP, FRM), PINFO(IN, OP, IRM)) - OPINFO2(ICOPYF, "icopyf#", 4|8, false, NONE, NONE, NONE, PINFO(OUT, OP, IRM), PINFO(IN, OP, FRM)) + OPINFO2(FCOPYI, "f#copyi", 4|8, false, NONE, NONE, NONE, PINFO(OUT, OP, FRM), PINFO(IN, OP, IRM)) // Load float/double value from integer representation (e.g. 0x3f800000 -> 1.0f) + OPINFO2(ICOPYF, "icopyf#", 4|8, false, NONE, NONE, NONE, PINFO(OUT, OP, IRM), PINFO(IN, OP, FRM)) // Store float/double value as integer representation (e.g. 1.0f -> 0x3f800000) }; //************************************************************************** -// INLINE FUNCTIONS -//************************************************************************** - -//------------------------------------------------- -// rol32 - perform a 32-bit left rotate -//------------------------------------------------- - -inline u32 rol32(u32 source, u8 count) -{ - count &= 31; - return (source << count) | (source >> (32 - count)); -} - - -//------------------------------------------------- -// rol64 - perform a 64-bit left rotate -//------------------------------------------------- - -inline u64 rol64(u64 source, u8 count) -{ - count &= 63; - return (source << count) | (source >> (64 - count)); -} - - - -//************************************************************************** // UML CODE HANDLE //************************************************************************** @@ -283,6 +262,830 @@ void uml::code_handle::set_codeptr(drccodeptr code) // UML INSTRUCTION //************************************************************************** +struct uml::instruction::simplify_op +{ +private: + static inline constexpr u64 paramsizemask[] = { 0xffU, 0xffffU, 0xffffffffU, 0xffffffff'ffffffffU }; + + static u64 size_mask(instruction const &inst) + { + return (inst.size() == 4) ? 0xffffffffU : 0xffffffff'ffffffffU; + } + + static void truncate_immediate(instruction &inst, int pnum, u64 mask) + { + if (inst.param(pnum).is_immediate()) + inst.m_param[pnum] = inst.param(pnum).immediate() & mask; + } + + static void normalise_commutative(instruction &inst) + { + const u64 mask = size_mask(inst); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, mask); + + // if a source is the destination put it first, and put a single immediate last + if ((inst.param(0) == inst.param(2)) || (inst.param(1).is_immediate() && !inst.param(2).is_immediate())) + { + using std::swap; + swap(inst.m_param[1], inst.m_param[2]); + } + } + + static void convert_to_mov_immediate(instruction &inst, u64 immediate) + { + u64 const mask = size_mask(inst); + immediate &= mask; + if (!inst.flags()) + { + inst.m_opcode = OP_MOV; + inst.m_numparams = 2; + inst.m_param[1] = immediate; + } + else if (immediate == mask) + { + inst.m_opcode = OP_OR; + inst.m_numparams = 3; + inst.m_param[1] = inst.param(0); + inst.m_param[2] = mask; + } + else + { + inst.m_opcode = OP_AND; + inst.m_numparams = 3; + if (immediate) + { + inst.m_param[1] = immediate; + inst.m_param[2] = mask; + } + else + { + inst.m_param[1] = inst.param(0); + inst.m_param[2] = 0; + } + } + } + +public: + static void truncate_imm(instruction &inst) + { + u64 const mask = size_mask(inst); + + for (int i = 0; inst.numparams() > i; ++i) + truncate_immediate(inst, i, mask); + } + + static void read(instruction &inst) + { + // truncate immediate address to size + truncate_immediate(inst, 1, 0xffffffff); + } + + static void readm(instruction &inst) + { + auto const size = inst.param(3).size(); + u64 const mask = paramsizemask[size]; + + // truncate immediate address and mask to size + truncate_immediate(inst, 1, 0xffffffff); + truncate_immediate(inst, 2, mask); + + // convert to READ if the mask is all ones + if (inst.param(2).is_immediate_value(mask)) + { + inst.m_opcode = OP_READ; + inst.m_param[2] = inst.param(3); + inst.m_numparams = 3; + } + } + + static void write(instruction &inst) + { + auto const size = inst.param(2).size(); + + // truncate immediate address and data to size + truncate_immediate(inst, 0, 0xffffffff); + truncate_immediate(inst, 1, paramsizemask[size]); + } + + static void writem(instruction &inst) + { + auto const size = inst.param(3).size(); + u64 const mask = paramsizemask[size]; + + // truncate immediate address, data and mask to size + truncate_immediate(inst, 0, 0xffffffff); + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, mask); + + // convert to WRITE if the mask is all ones + if (inst.param(2).is_immediate_value(mask)) + { + inst.m_opcode = OP_WRITE; + inst.m_param[2] = inst.param(3); + inst.m_numparams = 3; + } + } + + static void set(instruction &inst) + { + // convert to MOV if the condition is constant + if (inst.condition() == COND_ALWAYS) + convert_to_mov_immediate(inst, 1); + } + + static void mov(instruction &inst) + { + // convert move-to-self to NOP if the destination isn't larger than the size + if (inst.param(0) == inst.param(1)) + { + if (!inst.param(0).is_int_register() || (inst.size() == 8)) + { + inst.nop(); + return; + } + } + + // truncate immediate source to instruction size + truncate_immediate(inst, 1, size_mask(inst)); + } + + static void sext(instruction &inst) + { + // convert immediate source to MOV or a logic operation if flags are requested + if (inst.param(1).is_immediate()) + { + u64 val = inst.param(1).immediate(); + switch (inst.param(2).size()) + { + case SIZE_BYTE: val = u64(s64(s8(u8(val)))); break; + case SIZE_WORD: val = u64(s64(s16(u16(val)))); break; + case SIZE_DWORD: val = u64(s64(s32(u32(val)))); break; + case SIZE_QWORD: break; + } + convert_to_mov_immediate(inst, val); + } + } + + static void roland(instruction &inst) + { + auto const size = inst.size(); + auto const bits = size << 3; + u64 const mask = size_mask(inst); + assert((size == 4) || (size == 8)); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, bits - 1); + truncate_immediate(inst, 3, mask); + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate() && inst.param(3).is_immediate()) + { + // constant result, convert to MOV or a logic operation + if (size == 4) + convert_to_mov_immediate(inst, rotl_32(inst.param(1).immediate(), inst.param(2).immediate()) & inst.param(3).immediate()); + else + convert_to_mov_immediate(inst, rotl_64(inst.param(1).immediate(), inst.param(2).immediate()) & inst.param(3).immediate()); + } + else if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // only mask is variable, convert to AND + inst.m_opcode = OP_AND; + if (size == 4) + inst.m_param[1] = parameter(rotl_32(inst.param(1).immediate(), inst.param(2).immediate())); + else + inst.m_param[1] = parameter(rotl_64(inst.param(1).immediate(), inst.param(2).immediate())); + inst.m_param[2] = inst.param(3); + inst.m_numparams = 3; + } + else if (inst.param(2).is_immediate_value(0) || inst.param(3).is_immediate_value(0)) + { + // no shift or zero mask, convert to AND (may be subsequently converted to MOV) + inst.m_opcode = OP_AND; + inst.m_param[2] = inst.param(3); + inst.m_numparams = 3; + } + else if (inst.param(3).is_immediate_value(mask)) + { + // all mask bits set, convert to ROL + inst.m_opcode = OP_ROL; + inst.m_numparams = 3; + } + else if (inst.param(2).is_immediate() && inst.param(3).is_immediate_value((mask << inst.param(2).immediate()) & mask)) + { + // equivalent to shift left + inst.m_opcode = OP_SHL; + inst.m_numparams = 3; + } + else if (inst.param(2).is_immediate() && inst.param(3).is_immediate_value(mask >> (bits - inst.param(2).immediate()))) + { + // equivalent to shift right + inst.m_opcode = OP_SHR; + inst.m_numparams = 3; + inst.m_param[2] = bits - inst.param(2).immediate(); + } + } + + static void rolins(instruction &inst) + { + auto const size = inst.size(); + auto const bits = size << 3; + u64 const mask = size_mask(inst); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, bits - 1); + truncate_immediate(inst, 3, mask); + + if (inst.param(3).is_immediate_value(mask)) + { + // all mask bits set, convert to ROL + inst.m_opcode = OP_ROL; + inst.m_numparams = 3; + } + else if (inst.param(3).is_immediate_value(0)) + { + // no mask bits set, convert to AND + inst.m_opcode = OP_AND; + inst.m_numparams = 3; + inst.m_param[1] = inst.param(0); + inst.m_param[2] = mask; + } + } + + static void add(instruction &inst) + { + // clean up operands + normalise_commutative(inst); + + // can't optimise carry or overflow flag generation + if (inst.flags() & (FLAG_C | FLAG_V)) + return; + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant value, convert to MOV or a logic operation + convert_to_mov_immediate(inst, inst.param(1).immediate() + inst.param(2).immediate()); + } + else if (inst.param(2).is_immediate_value(0)) + { + // add zero, convert to MOV or AND + if (inst.flags()) + { + inst.m_opcode = OP_AND; + inst.m_param[2] = size_mask(inst); + } + else + { + inst.m_opcode = OP_MOV; + inst.m_numparams = 2; + } + } + } + + static void sub(instruction &inst) + { + u64 const mask = size_mask(inst); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, mask); + + // can't optimise carry or overflow flag generation + if (inst.flags() & (FLAG_C | FLAG_V)) + return; + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant value, convert to MOV or a logic operation + convert_to_mov_immediate(inst, inst.param(1).immediate() - inst.param(2).immediate()); + } + else if (inst.param(2).is_immediate_value(0)) + { + // subtract zero, convert to MOV or AND + if (inst.flags()) + { + inst.m_opcode = OP_AND; + inst.m_param[2] = mask; + } + else + { + inst.m_opcode = OP_MOV; + inst.m_numparams = 2; + } + } + } + + static void cmp(instruction &inst) + { + u64 const mask = size_mask(inst); + + // completely elide if flags are not used + if (!inst.flags()) + { + inst.nop(); + return; + } + + // truncate immediates to instruction size + truncate_immediate(inst, 0, mask); + truncate_immediate(inst, 1, mask); + } + + template <opcode_t LoWordOp, typename Short, typename Long> + static void mul(instruction &inst) + { + bool const low_only = inst.param(0) == inst.param(1); + + // if the two destination operands are identical and the S and Z flags aren't required, convert to MUL.LW + if (low_only && !(inst.flags() & (FLAG_Z | FLAG_S))) + { + inst.m_opcode = LoWordOp; + inst.m_param[1] = inst.param(2); + inst.m_param[2] = inst.param(3); + inst.m_numparams = 3; + return; + } + + const u64 mask = size_mask(inst); + + // truncate immediates to instruction size + truncate_immediate(inst, 2, mask); + truncate_immediate(inst, 3, mask); + + // put a single immediate last + if (inst.param(2).is_immediate() && !inst.param(3).is_immediate()) + { + using std::swap; + swap(inst.m_param[2], inst.m_param[3]); + } + + // can only simplify the low-only form, can't optimise overflow flag generation + if (!low_only || (inst.flags() & FLAG_V)) + return; + + if (inst.param(2).is_immediate_value(0) || inst.param(3).is_immediate_value(0)) + { + // multiplying anything by zero yields zero + convert_to_mov_immediate(inst, 0); + } + else if (inst.param(2).is_immediate() && inst.param(3).is_immediate()) + { + // convert constant result to MOV or a logic op + auto const size = inst.size(); + auto const bits = size << 3; + assert((size == 4) || (size == 8)); + + if (size == 4) + { + Short const param2 = Short(u32(inst.param(2).immediate())); + Short const param3 = Short(u32(inst.param(3).immediate())); + Short const val = param2 * param3; + bool const no_overflow = (val / param2) == param3; + bool const z_ok = !(inst.flags() & FLAG_Z) || no_overflow; + bool const s_ok = !(inst.flags() & FLAG_S) || (std::is_signed_v<Short> ? !BIT(param2 ^ param3 ^ val, bits - 1) : (no_overflow && !BIT(val, bits - 1))); + + if (z_ok && s_ok) + convert_to_mov_immediate(inst, u32(val)); + } + else + { + Long const param2 = Long(inst.param(2).immediate()); + Long const param3 = Long(inst.param(3).immediate()); + Long const val = param2 * param3; + bool const no_overflow = (val / param2) == param3; + bool const z_ok = !(inst.flags() & FLAG_Z) || no_overflow; + bool const s_ok = !(inst.flags() & FLAG_S) || (std::is_signed_v<Long> ? !BIT(param2 ^ param3 ^ val, bits - 1) : (no_overflow && !BIT(val, bits - 1))); + + if (z_ok && s_ok) + convert_to_mov_immediate(inst, u64(val)); + } + } + } + + template <typename Short, typename Long> + static void mullw(instruction &inst) + { + // clean up operands + normalise_commutative(inst); + + // can't optimise overflow flag generation + if (inst.flags() & FLAG_V) + return; + + if (inst.param(1).is_immediate_value(0) || inst.param(2).is_immediate_value(0)) + { + // multiplying anything by zero yields zero + convert_to_mov_immediate(inst, 0); + } + else if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // convert constant result to MOV or a logic op + auto const size = inst.size(); + assert((size == 4) || (size == 8)); + + if (size == 4) + { + Short const param1 = Short(u32(inst.param(1).immediate())); + Short const param2 = Short(u32(inst.param(2).immediate())); + Short const val = param1 * param2; + convert_to_mov_immediate(inst, u32(val)); + } + else + { + Long const param1 = Long(inst.param(1).immediate()); + Long const param2 = Long(inst.param(2).immediate()); + Long const val = param1 * param2; + convert_to_mov_immediate(inst, u64(val)); + } + } + } + + template <typename Short, typename Long> + static void div(instruction &inst) + { + auto const size = inst.size(); + u64 const mask = size_mask(inst); + + // truncate immediates to instruction size + truncate_immediate(inst, 2, mask); + truncate_immediate(inst, 3, mask); + + // can't optimise overflow flag generation + if (inst.flags() & FLAG_V) + return; + + // optimise the quotient-only form with two immediate inputs if not dividing by zero + if ((inst.param(0) == inst.param(1)) && inst.param(2).is_immediate() && inst.param(3).is_immediate() && !inst.param(3).is_immediate_value(0)) + { + if (inst.param(2).is_immediate_value(0)) + { + // dividing zero by anything yields zero + convert_to_mov_immediate(inst, 0); + } + else + { + // convert constant result to MOV or a logic op + assert((size == 4) || (size == 8)); + + if (size == 4) + convert_to_mov_immediate(inst, u32(Short(u32(inst.param(2).immediate())) / Short(u32(inst.param(3).immediate())))); + else + convert_to_mov_immediate(inst, u64(Long(inst.param(2).immediate()) / Long(inst.param(3).immediate()))); + } + } + } + + static void _and(instruction &inst) + { + // clean up operands + normalise_commutative(inst); + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant value, convert to MOV or a logic operation + convert_to_mov_immediate(inst, inst.param(1).immediate() & inst.param(2).immediate()); + } + else if (inst.param(2).is_immediate_value(0)) + { + // any immediate zero always yields zero + convert_to_mov_immediate(inst, 0); + } + else if (inst.param(2).is_immediate_value(size_mask(inst)) || (inst.param(1) == inst.param(2))) + { + if (!inst.flags()) + { + // convert to MOV if the value will be unaffected and flags aren't updated + inst.m_opcode = OP_MOV; + inst.m_numparams = 2; + } + else if ((inst.param(0) == inst.param(1)) && (!inst.param(0).is_int_register() || (inst.size() == 8))) + { + // convert to TEST if the value will be unaffected and the destination is no larger than the operand size + inst.m_opcode = OP_TEST; + inst.m_numparams = 2; + } + } + } + + static void test(instruction &inst) + { + u64 const mask = size_mask(inst); + + // completely elide if flags are not used + if (!inst.flags()) + { + inst.nop(); + return; + } + + // truncate immediates to instruction size + truncate_immediate(inst, 0, mask); + truncate_immediate(inst, 1, mask); + + // put a single immediate second + if (inst.param(0).is_immediate() && !inst.param(1).is_immediate()) + { + using std::swap; + swap(inst.m_param[0], inst.m_param[1]); + } + + if (inst.param(0).is_immediate() && inst.param(1).is_immediate()) + { + // two immediates, combine values and set second operand to all 0 or all 1 + u64 const val = inst.param(0).immediate() & inst.param(1).immediate(); + inst.m_param[0] = val; + inst.m_param[1] = val ? mask : 0; + } + else if (inst.param(0) == inst.param(1)) + { + // testing a value against itself, turn the second operand into an immediate + inst.m_param[1] = mask; + } + } + + static void _or(instruction &inst) + { + u64 const mask = size_mask(inst); + + // clean up operands + normalise_commutative(inst); + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant value, convert to MOV or a logic operation + convert_to_mov_immediate(inst, inst.param(1).immediate() | inst.param(2).immediate()); + } + else if (inst.param(2).is_immediate_value(mask)) + { + // an immediate with all bits set is unaffected by the other value + convert_to_mov_immediate(inst, mask); + } + else if (inst.param(2).is_immediate_value(0) || (inst.param(1) == inst.param(2))) + { + if (!inst.flags()) + { + // convert to MOV if the value will be unaffected and flags aren't updated + inst.m_opcode = OP_MOV; + inst.m_numparams = 2; + } + else if ((inst.param(0) == inst.param(1)) && (!inst.param(0).is_int_register() || (inst.size() == 8))) + { + // convert to TEST if the value will be unaffected and the destination is no larger than the operand size + inst.m_opcode = OP_TEST; + inst.m_numparams = 2; + } + else + { + // convert to AND to simplify code generation + inst.m_opcode = OP_AND; + inst.m_param[2] = mask; + } + } + } + + static void _xor(instruction &inst) + { + // clean up operands + normalise_commutative(inst); + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant value, convert to MOV or a logic operation + convert_to_mov_immediate(inst, inst.param(1).immediate() ^ inst.param(2).immediate()); + } + else if (inst.param(1) == inst.param(2)) + { + // equivalent values cancel out + convert_to_mov_immediate(inst, 0); + } + else if (inst.param(2).is_immediate_value(0)) + { + if (!inst.flags()) + { + // convert to MOV if the value will be unaffected and flags aren't updated + inst.m_opcode = OP_MOV; + inst.m_numparams = 2; + } + else if ((inst.param(0) == inst.param(1)) && (!inst.param(0).is_int_register() || (inst.size() == 8))) + { + // convert to TEST if the value will be unaffected and the destination is no larger than the operand size + inst.m_opcode = OP_TEST; + inst.m_numparams = 2; + } + else + { + // convert to AND to simplify code generation + inst.m_opcode = OP_AND; + inst.m_param[2] = size_mask(inst); + } + } + } + + static void lzcnt(instruction &inst) + { + // convert immediate source to MOV or a logic operation if flags are requested + if (inst.param(1).is_immediate()) + { + auto const size = inst.size(); + assert((size == 4) || (size == 8)); + + u64 const val = inst.param(1).immediate(); + convert_to_mov_immediate(inst, (size == 4) ? count_leading_zeros_32(u32(val)) : count_leading_zeros_64(val)); + } + } + + static void bswap(instruction &inst) + { + // convert immediate source to MOV or a logic operation if flags are requested + if (inst.param(1).is_immediate()) + { + auto const size = inst.size(); + assert((size == 4) || (size == 8)); + + u64 const val = inst.param(1).immediate(); + convert_to_mov_immediate(inst, (size == 4) ? swapendian_int32(u32(val)) : swapendian_int64(val)); + } + } + + static void shl(instruction &inst) + { + auto const size = inst.size(); + auto const bits = size << 3; + u64 const mask = size_mask(inst); + assert((size == 4) || (size == 8)); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, bits - 1); + + // can't optimise carry flag generation + if (inst.flags() & FLAG_C) + return; + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant result, convert to MOV or a logic operation + if (size == 4) + convert_to_mov_immediate(inst, u32(inst.param(1).immediate()) << inst.param(2).immediate()); + else + convert_to_mov_immediate(inst, inst.param(1).immediate() << inst.param(2).immediate()); + } + else if (inst.param(2).is_immediate_value(0)) + { + // no shift, convert to AND (may be subsequently converted to MOV) + inst.m_opcode = OP_AND; + inst.m_param[2] = mask; + } + } + + template <typename Short, typename Long> + static void shr(instruction &inst) + { + auto const size = inst.size(); + auto const bits = size << 3; + u64 const mask = size_mask(inst); + assert((size == 4) || (size == 8)); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, bits - 1); + + // can't optimise carry flag generation + if (inst.flags() & FLAG_C) + return; + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant result, convert to MOV or a logic operation + if (size == 4) + convert_to_mov_immediate(inst, u32(Short(u32(inst.param(1).immediate())) >> inst.param(2).immediate())); + else + convert_to_mov_immediate(inst, u64(Long(inst.param(1).immediate()) >> inst.param(2).immediate())); + } + else if (inst.param(2).is_immediate_value(0)) + { + // no shift, convert to AND (may be subsequently converted to MOV) + inst.m_opcode = OP_AND; + inst.m_param[2] = mask; + } + } + + static void rol(instruction &inst) + { + auto const size = inst.size(); + auto const bits = size << 3; + u64 const mask = size_mask(inst); + assert((size == 4) || (size == 8)); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, bits - 1); + + // can't optimise carry flag generation + if (inst.flags() & FLAG_C) + return; + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant result, convert to MOV or a logic operation + if (size == 4) + convert_to_mov_immediate(inst, rotl_32(inst.param(1).immediate(), inst.param(2).immediate())); + else + convert_to_mov_immediate(inst, rotl_64(inst.param(1).immediate(), inst.param(2).immediate())); + } + else if (inst.param(2).is_immediate_value(0)) + { + // no shift, convert to AND (may be subsequently converted to MOV) + inst.m_opcode = OP_AND; + inst.m_param[2] = mask; + } + } + + static void ror(instruction &inst) + { + auto const size = inst.size(); + auto const bits = size << 3; + u64 const mask = size_mask(inst); + assert((size == 4) || (size == 8)); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, bits - 1); + + // can't optimise carry flag generation + if (inst.flags() & FLAG_C) + return; + + if (inst.param(1).is_immediate() && inst.param(2).is_immediate()) + { + // constant result, convert to MOV or a logic operation + if (size == 4) + convert_to_mov_immediate(inst, rotr_32(inst.param(1).immediate(), inst.param(2).immediate())); + else + convert_to_mov_immediate(inst, rotr_64(inst.param(1).immediate(), inst.param(2).immediate())); + } + else if (inst.param(2).is_immediate_value(0)) + { + // no shift, convert to AND (may be subsequently converted to MOV) + inst.m_opcode = OP_AND; + inst.m_param[2] = mask; + } + } + + static void rolrc(instruction &inst) + { + auto const size = inst.size(); + auto const bits = size << 3; + u64 const mask = size_mask(inst); + assert((size == 4) || (size == 8)); + + // truncate immediates to instruction size + truncate_immediate(inst, 1, mask); + truncate_immediate(inst, 2, bits - 1); + + // can't optimise zero or sign flag generation + if (inst.flags() & (FLAG_Z | FLAG_S)) + return; + + // convert to NOP or MOV if there's no rotation + if (inst.param(2).is_immediate_value(0)) + { + if ((inst.param(0) == inst.param(1)) && (!inst.param(0).is_int_register() || (inst.size() == 8))) + { + inst.nop(); + } + else + { + inst.m_opcode = OP_MOV; + inst.m_numparams = 2; + } + } + } + + static void fmov(instruction &inst) + { + // convert move-to-self to NOP + if (inst.param(0) == inst.param(1)) + inst.nop(); + } + + static void fread(instruction &inst) + { + // truncate immediate address to size + truncate_immediate(inst, 1, 0xffffffff); + } + + static void fwrite(instruction &inst) + { + // truncate immediate address to size + truncate_immediate(inst, 0, 0xffffffff); + } +}; + + //------------------------------------------------- // configure - configure an opcode with no // parameters @@ -396,344 +1199,69 @@ void uml::instruction::configure(opcode_t op, u8 size, parameter p0, parameter p void uml::instruction::simplify() { - // can't simplify if flags are in play - if (m_flags != 0) - return; - - static constexpr u64 instsizemask[] = { 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0xffffffffffffffffU }; - static constexpr u64 paramsizemask[] = { 0xff, 0xffff, 0xffffffff, 0xffffffffffffffffU }; - // loop until we've simplified all we can opcode_t origop; do { +#if LOG_SIMPLIFICATIONS + uml::instruction const orig = *this; +#endif + // switch off the opcode origop = m_opcode; switch (m_opcode) { - // READM: convert to READ if the mask is wide open - case OP_READM: - if (m_param[2].is_immediate_value(paramsizemask[m_param[3].size()])) - { - m_opcode = OP_READ; - m_numparams = 2; - m_param[2] = m_param[3]; - } - break; - - // WRITEM: convert to WRITE if the mask is wide open - case OP_WRITEM: - if (m_param[2].is_immediate_value(paramsizemask[m_param[3].size()])) - { - m_opcode = OP_WRITE; - m_numparams = 2; - m_param[2] = m_param[3]; - } - break; - - // SET: convert to MOV if constant condition - case OP_SET: - if (m_condition == COND_ALWAYS) - convert_to_mov_immediate(1); - break; - - // MOV: convert to NOP if move-to-self - case OP_MOV: - if (m_param[0] == m_param[1]) - nop(); - break; - - // SEXT: convert immediates to MOV - case OP_SEXT: - if (m_param[1].is_immediate()) - switch (m_param[2].size()) - { - case SIZE_BYTE: convert_to_mov_immediate(s8(m_param[1].immediate())); break; - case SIZE_WORD: convert_to_mov_immediate(s16(m_param[1].immediate())); break; - case SIZE_DWORD: convert_to_mov_immediate(s32(m_param[1].immediate())); break; - case SIZE_QWORD: convert_to_mov_immediate(s64(m_param[1].immediate())); break; - case SIZE_DQWORD: fatalerror("Invalid SEXT target size\n"); - } - break; - - // ROLAND: convert to MOV if all immediate, or to ROL or AND if one is not needed, or to SHL/SHR if the mask is right - case OP_ROLAND: - if (m_param[1].is_immediate() && m_param[2].is_immediate() && m_param[3].is_immediate()) - { - assert(m_size == 4 || m_size == 8); - if (m_size == 4) - convert_to_mov_immediate(rol32(m_param[1].immediate(), m_param[2].immediate()) & m_param[3].immediate()); - else - convert_to_mov_immediate(rol64(m_param[1].immediate(), m_param[2].immediate()) & m_param[3].immediate()); - } - else if (m_param[2].is_immediate_value(0)) - { - m_opcode = OP_AND; - m_numparams = 3; - m_param[2] = m_param[3]; - } - else if (m_param[3].is_immediate_value(instsizemask[m_size])) - { - m_opcode = OP_ROL; - m_numparams = 3; - } - else if (m_param[2].is_immediate() && m_param[3].is_immediate_value((0xffffffffffffffffU << m_param[2].immediate()) & instsizemask[m_size])) - { - m_opcode = OP_SHL; - m_numparams = 3; - } - else if (m_param[2].is_immediate() && m_param[3].is_immediate_value(instsizemask[m_size] >> (8 * m_size - m_param[2].immediate()))) - { - m_opcode = OP_SHR; - m_numparams = 3; - m_param[2] = 8 * m_size - m_param[2].immediate(); - } - break; - - // ROLINS: convert to ROLAND if the mask is full - case OP_ROLINS: - if (m_param[3].is_immediate_value(instsizemask[m_size])) - m_opcode = OP_ROLAND; - break; - - // ADD: convert to MOV if immediate, or if adding 0 - case OP_ADD: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - convert_to_mov_immediate(m_param[1].immediate() + m_param[2].immediate()); - else if (m_param[1].is_immediate_value(0)) - convert_to_mov_param(2); - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // SUB: convert to MOV if immediate, or if subtracting 0 - case OP_SUB: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - convert_to_mov_immediate(m_param[1].immediate() - m_param[2].immediate()); - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // CMP: no-op if no flags needed, compare i0 to i0 if the parameters are equal - case OP_CMP: - if (m_flags == 0) - nop(); - else if (m_param[0] == m_param[1]) - cmp(I0, I0); - break; - - // MULU: convert simple form to MOV if immediate, or if multiplying by 0 - case OP_MULU: - if (m_param[0] == m_param[1]) - { - if (m_param[2].is_immediate_value(0) || m_param[3].is_immediate_value(0)) - convert_to_mov_immediate(0); - else if (m_param[2].is_immediate() && m_param[3].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(u32(u32(m_param[1].immediate()) * u32(m_param[2].immediate()))); - else if (m_size == 8) - convert_to_mov_immediate(u64(u64(m_param[1].immediate()) * u64(m_param[2].immediate()))); - } - } - break; - - // MULS: convert simple form to MOV if immediate, or if multiplying by 0 - case OP_MULS: - if (m_param[0] == m_param[1]) - { - if (m_param[2].is_immediate_value(0) || m_param[3].is_immediate_value(0)) - convert_to_mov_immediate(0); - else if (m_param[2].is_immediate() && m_param[3].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(s32(s32(m_param[1].immediate()) * s32(m_param[2].immediate()))); - else if (m_size == 8) - convert_to_mov_immediate(s64(s64(m_param[1].immediate()) * s64(m_param[2].immediate()))); - } - } - break; - - // DIVU: convert simple form to MOV if immediate, or if dividing with 0 - case OP_DIVU: - if (m_param[0] == m_param[1] && !m_param[3].is_immediate_value(0)) - { - if (m_param[2].is_immediate_value(0)) - convert_to_mov_immediate(0); - else if (m_param[2].is_immediate() && m_param[3].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(u32(u32(m_param[1].immediate()) / u32(m_param[2].immediate()))); - else if (m_size == 8) - convert_to_mov_immediate(u64(u64(m_param[1].immediate()) / u64(m_param[2].immediate()))); - } - } - break; - - // DIVS: convert simple form to MOV if immediate, or if dividing with 0 - case OP_DIVS: - if (m_param[0] == m_param[1] && !m_param[3].is_immediate_value(0)) - { - if (m_param[2].is_immediate_value(0)) - convert_to_mov_immediate(0); - else if (m_param[2].is_immediate() && m_param[3].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(s32(s32(m_param[1].immediate()) / s32(m_param[2].immediate()))); - else if (m_size == 8) - convert_to_mov_immediate(s64(s64(m_param[1].immediate()) / s64(m_param[2].immediate()))); - } - } - break; - - // AND: convert to MOV if immediate, or if anding against 0 or 0xffffffff - case OP_AND: - if (m_param[1].is_immediate_value(0) || m_param[2].is_immediate_value(0)) - convert_to_mov_immediate(0); - else if (m_param[1].is_immediate() && m_param[2].is_immediate()) - convert_to_mov_immediate(m_param[1].immediate() & m_param[2].immediate()); - else if (m_param[1].is_immediate_value(instsizemask[m_size])) - convert_to_mov_param(2); - else if (m_param[2].is_immediate_value(instsizemask[m_size])) - convert_to_mov_param(1); - break; - - // TEST: no-op if no flags needed - case OP_TEST: - if (m_flags == 0) - nop(); - break; - - // OR: convert to MOV if immediate, or if oring against 0 or 0xffffffff - case OP_OR: - if (m_param[1].is_immediate_value(instsizemask[m_size]) || m_param[2].is_immediate_value(instsizemask[m_size])) - convert_to_mov_immediate(instsizemask[m_size]); - else if (m_param[1].is_immediate() && m_param[2].is_immediate()) - convert_to_mov_immediate(m_param[1].immediate() | m_param[2].immediate()); - else if (m_param[1].is_immediate_value(0)) - convert_to_mov_param(2); - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // XOR: convert to MOV if immediate, or if xoring against 0 - case OP_XOR: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - convert_to_mov_immediate(m_param[1].immediate() ^ m_param[2].immediate()); - else if (m_param[1].is_immediate_value(0)) - convert_to_mov_param(2); - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // LZCNT: convert to MOV if immediate - case OP_LZCNT: - if (m_param[1].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(count_leading_zeros(m_param[1].immediate())); - else if (m_size == 8) - { - if ((m_param[1].immediate() >> 32) == 0) - convert_to_mov_immediate(32 + count_leading_zeros(m_param[1].immediate())); - else - convert_to_mov_immediate(count_leading_zeros(m_param[1].immediate() >> 32)); - } - } - break; - - // BSWAP: convert to MOV if immediate - case OP_BSWAP: - if (m_param[1].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(swapendian_int32(m_param[1].immediate())); - else if (m_size == 8) - convert_to_mov_immediate(swapendian_int64(m_param[1].immediate())); - } - break; - - // SHL: convert to MOV if immediate or shifting by 0 - case OP_SHL: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - convert_to_mov_immediate(m_param[1].immediate() << m_param[2].immediate()); - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // SHR: convert to MOV if immediate or shifting by 0 - case OP_SHR: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(u32(m_param[1].immediate()) >> m_param[2].immediate()); - else if (m_size == 8) - convert_to_mov_immediate(u64(m_param[1].immediate()) >> m_param[2].immediate()); - } - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // SAR: convert to MOV if immediate or shifting by 0 - case OP_SAR: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(s32(m_param[1].immediate()) >> m_param[2].immediate()); - else if (m_size == 8) - convert_to_mov_immediate(s64(m_param[1].immediate()) >> m_param[2].immediate()); - } - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // ROL: convert to NOP if immediate or rotating by 0 - case OP_ROL: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(rol32(m_param[1].immediate(), m_param[2].immediate())); - else if (m_size == 8) - convert_to_mov_immediate(rol64(m_param[1].immediate(), m_param[2].immediate())); - } - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // ROR: convert to NOP if immediate or rotating by 0 - case OP_ROR: - if (m_param[1].is_immediate() && m_param[2].is_immediate()) - { - if (m_size == 4) - convert_to_mov_immediate(rol32(m_param[1].immediate(), 32 - m_param[2].immediate())); - else if (m_size == 8) - convert_to_mov_immediate(rol64(m_param[1].immediate(), 64 - m_param[2].immediate())); - } - else if (m_param[2].is_immediate_value(0)) - convert_to_mov_param(1); - break; - - // FMOV: convert to NOP if move-to-self - case OP_FMOV: - if (m_param[0] == m_param[1]) - nop(); - break; - - default: - break; + case OP_DEBUG: simplify_op::truncate_imm(*this); break; + case OP_EXIT: simplify_op::truncate_imm(*this); break; + case OP_EXH: simplify_op::truncate_imm(*this); break; + case OP_READ: simplify_op::read(*this); break; + case OP_READM: simplify_op::readm(*this); break; + case OP_WRITE: simplify_op::write(*this); break; + case OP_WRITEM: simplify_op::writem(*this); break; + case OP_SET: simplify_op::set(*this); break; + case OP_MOV: simplify_op::mov(*this); break; + case OP_SEXT: simplify_op::sext(*this); break; + case OP_ROLAND: simplify_op::roland(*this); break; + case OP_ROLINS: simplify_op::rolins(*this); break; + case OP_ADD: simplify_op::add(*this); break; + case OP_ADDC: simplify_op::truncate_imm(*this); break; + case OP_SUB: simplify_op::sub(*this); break; + case OP_SUBB: simplify_op::truncate_imm(*this); break; + case OP_CMP: simplify_op::cmp(*this); break; + case OP_MULU: simplify_op::mul<OP_MULULW, u32, u64>(*this); break; + case OP_MULULW: simplify_op::mullw<u32, u64>(*this); break; + case OP_MULS: simplify_op::mul<OP_MULSLW, s32, s64>(*this); break; + case OP_MULSLW: simplify_op::mullw<s32, s64>(*this); break; + case OP_DIVU: simplify_op::div<u32, u64>(*this); break; + case OP_DIVS: simplify_op::div<s32, s64>(*this); break; + case OP_AND: simplify_op::_and(*this); break; + case OP_TEST: simplify_op::test(*this); break; + case OP_OR: simplify_op::_or(*this); break; + case OP_XOR: simplify_op::_xor(*this); break; + case OP_LZCNT: simplify_op::lzcnt(*this); break; + case OP_TZCNT: simplify_op::truncate_imm(*this); break; + case OP_BSWAP: simplify_op::bswap(*this); break; + case OP_SHL: simplify_op::shl(*this); break; + case OP_SHR: simplify_op::shr<u32, u64>(*this); break; + case OP_SAR: simplify_op::shr<s32, s64>(*this); break; + case OP_ROL: simplify_op::rol(*this); break; + case OP_ROLC: simplify_op::rolrc(*this); break; + case OP_ROR: simplify_op::ror(*this); break; + case OP_RORC: simplify_op::rolrc(*this); break; + case OP_FREAD: simplify_op::fread(*this); break; + case OP_FWRITE: simplify_op::fwrite(*this); break; + + default: break; } - /* - if (LOG_SIMPLIFICATIONS && memcmp(&orig, inst, sizeof(orig)) != 0) - { - std::string disasm1 = orig.disasm(block->drcuml); - std::string disasm2 = inst->disasm(block->drcuml); - osd_printf_debug("Simplified: %-50.50s -> %s\n", disasm1.c_str(), disasm2.c_str()); - } - */ +#if LOG_SIMPLIFICATIONS + if (orig != *this) + osd_printf_debug("Simplified: %-50.50s -> %s\n", orig.disasm(), disasm()); +#endif // loop until we stop changing opcodes - } while (m_opcode != origop); + } + while (m_opcode != origop); } @@ -766,7 +1294,7 @@ void uml::instruction::validate() } // make sure we aren't missing any parameters - if (m_numparams < ARRAY_LENGTH(opinfo.param)) + if (m_numparams < std::size(opinfo.param)) assert(opinfo.param[m_numparams].typemask == 0); #endif // MAME_DEBUG } @@ -954,22 +1482,24 @@ std::string uml::instruction::disasm(drcuml_state *drcuml) const const char *symbol; u32 symoffset; - // symbol - if (drcuml != nullptr && (symbol = drcuml->symbol_find(param.memory(), &symoffset)) != nullptr) + if (drcuml && (symbol = drcuml->symbol_find(param.memory(), &symoffset)) != nullptr) { + // symbol if (symoffset == 0) util::stream_format(buffer, "[%s]", symbol); else util::stream_format(buffer, "[%s+$%X]", symbol, symoffset); } - - // cache memory else if (drcuml != nullptr && drcuml->cache().contains_pointer(param.memory())) + { + // cache memory util::stream_format(buffer, "[+$%X]", u32(uintptr_t(drccodeptr(param.memory()) - drcuml->cache().near()))); - - // general memory + } else + { + // general memory util::stream_format(buffer, "[[$%p]]", param.memory()); + } } break; @@ -1013,5 +1543,5 @@ std::string uml::instruction::disasm(drcuml_state *drcuml) const if (m_flags & FLAG_C) buffer.put('C'); } - return buffer.str(); + return std::move(buffer).str(); } |