summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--docs/source/techspecs/uml_instructions.rst117
-rw-r--r--src/devices/cpu/drcbex64.cpp62
-rw-r--r--src/devices/cpu/drcbex86.cpp19
-rw-r--r--src/devices/cpu/uml.cpp18
4 files changed, 176 insertions, 40 deletions
diff --git a/docs/source/techspecs/uml_instructions.rst b/docs/source/techspecs/uml_instructions.rst
index ef3ec557ad5..d3ab45b5ab2 100644
--- a/docs/source/techspecs/uml_instructions.rst
+++ b/docs/source/techspecs/uml_instructions.rst
@@ -14,16 +14,16 @@ Introduction
------------
UML is the instruction set used by MAME’s recompiler framework.
-Front-ends translate code running on the guest CPUs to UML instructions,
+Code running on emulated guest CPUs is translated to UML instructions,
and back-ends convert the UML instructions to a form that can be
executed or interpreted on the host system.
-Many UML instruction have multiple instruction sizes. Integer instructions
-default to 32-bit size. Adding a ``D`` or ``d`` prefix to the mnemonic changes
-to 64-bit size (double word). Floating point instructions use the mnemonic
-prefix/suffix ``FS`` or ``fs`` for IEEE 754 32-bit format (single precision) or
-or the prefix/suffix ``FD`` or ``fd`` for IEEE 754 64-bit format (double
-precision).
+Many UML instruction have multiple instruction sizes. Integer
+instructions default to 32-bit size. Adding a ``D`` or ``d`` prefix to
+the mnemonic changes to 64-bit size (double word). Floating point
+instructions use the mnemonic prefix/suffix ``FS`` or ``fs`` for
+IEEE 754 32-bit format (single precision) or or the prefix/suffix ``FD``
+or ``fd`` for IEEE 754 64-bit format (double precision).
.. _umlinst-special:
@@ -708,6 +708,11 @@ sign (S)
unordered (U)
Undefined.
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+No simplifications are applied to this instruction.
+
.. _umlinst-control:
@@ -754,6 +759,11 @@ sign (S)
unordered (U)
Unchanged.
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+No simplifications are applied to this instruction.
+
.. _umlinst-carry:
CARRY
@@ -842,6 +852,11 @@ sign (S)
unordered (U)
Undefined.
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+No simplifications are applied to this instruction.
+
.. _umlinst-getfmod:
GETFMOD
@@ -887,6 +902,11 @@ sign (S)
unordered (U)
Undefined.
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+No simplifications are applied to this instruction.
+
.. _umlinst-datamove:
@@ -3103,7 +3123,7 @@ Simplification rules
.. _umlinst-rolc:
ROLC
-^^^^
+~~~~
Rotate an integer value concatenated with the carry flag to the left
(toward the most significant bit position). For each step, the carry
@@ -3174,7 +3194,7 @@ Simplification rules
.. _umlinst-rorc:
RORC
-^^^^
+~~~~
Rotate an integer value concatenated with the carry flag to the right
(toward the least significant bit position). For each step, the carry
@@ -3244,6 +3264,85 @@ Simplification rules
* Immediate values for the ``count`` operand are truncated to five or
six bits for 32-bit or 64-bit operands, respectively.
+.. _umlinst-roland:
+
+ROLAND
+~~~~~~
+
+Rotate and mask an integer value. The value is rotated to the left
+(toward the most significant bit position). Bits shifted out of the
+most significant bit position are shifted into the least significant bit
+position. The logical conjunction of the rotated value and the mask is
+then calculated.
+
++--------------------------------+------------------------------------------------+
+| Disassembly | Usage |
++================================+================================================+
+| .. code-block:: | .. code-block:: C++ |
+| | |
+| roland dst,src,count,mask | UML_ROLAND(block, dst, src, count, mask); |
+| droland dst,src,count,mask | UML_DROLAND(block, dst, src, count, mask); |
++--------------------------------+------------------------------------------------+
+
+Sets ``dst`` to the logical conjunction of the value of ``src`` rotated
+left by ``count`` bit positions and the value of ``mask``.
+
+Back-ends may be able to optimise some forms of this instruction, for
+example when the ``count`` and ``mask`` operands are both immediate
+values.
+
+Operands
+^^^^^^^^
+
+dst (32-bit or 64-bit – memory, integer register)
+ The destination where the rotated and masked value will be stored.
+src (32-bit or 64-bit – memory, integer register, immediate, map variable)
+ The value to be rotated and masked.
+count (32-bit or 64-bit – memory, integer register, immediate, map variable)
+ The number of bit positions to rotate by. Only the least
+ significant five bits or six bits of this operand are used,
+ depending on the instruction size.
+mask (32-bit or 64-bit – memory, integer register, immediate, map variable)
+ The mask to calculate the logical conjunction with.
+
+Flags
+^^^^^
+
+carry (C)
+ Undefined.
+overflow (V)
+ Undefined.
+zero (Z)
+ Set if the result is zero, or cleared otherwise.
+sign (S)
+ Set to the value of the most significant bit of the result (set if
+ the result is a negative signed integer value, or cleared
+ otherwise).
+unordered (U)
+ Undefined.
+
+Simplification rules
+^^^^^^^^^^^^^^^^^^^^
+
+* Converted to :ref:`MOV <umlinst-mov>`, :ref:`AND <umlinst-and>` or
+ :ref:`OR <umlinst-or>` if the ``src`` and ``count`` operands are both
+ immediate values, or if either the ``count`` or ``mask`` operand is
+ the immediate value zero.
+* Converted to :ref:`ROL <umlinst-rol>` if the ``mask`` operand is an
+ immediate value with all bits set.
+* Converted to :ref:`SHL <umlinst-shl>` if the ``count`` operand is an
+ immediate value and the ``mask`` operand is an immediate value
+ containing a single contiguous left-aligned sequence of set bits of
+ the appropriate length for the value of the ``count`` operand.
+* Converted to :ref:`SHR <umlinst-shr>` if the ``count`` operand is an
+ immediate value and the ``mask`` operand is an immediate value
+ containing a single contiguous right-aligned sequence of set bits of
+ the appropriate length for the value of the ``count`` operand.
+* Immediate values for the ``src`` and ``mask`` operands are truncated
+ to the instruction size.
+* Immediate values for the ``count`` operand are truncated to five or
+ six bits for 32-bit or 64-bit operands, respectively.
+
.. _umlinst-fparith:
diff --git a/src/devices/cpu/drcbex64.cpp b/src/devices/cpu/drcbex64.cpp
index 0a011736b5d..be61b59883e 100644
--- a/src/devices/cpu/drcbex64.cpp
+++ b/src/devices/cpu/drcbex64.cpp
@@ -1545,29 +1545,40 @@ void drcbe_x64::shift_op_param(Assembler &a, Inst::Id const opcode, size_t opsiz
// caller must place non-immediate shift in ECX
// FIXME: upper bits may not be cleared for 32-bit form when shift count is zero
const bool carryin = (opcode == Inst::kIdRcl) || (opcode == Inst::kIdRcr);
+ const bool rotate = carryin || (opcode == Inst::kIdRol) || (opcode == Inst::kIdRor);
if (param.is_immediate())
{
const uint32_t bitshift = param.immediate() & (opsize * 8 - 1);
if (bitshift)
+ {
a.emit(opcode, dst, imm(param.immediate()));
- else if (!carryin && (update_flags & FLAG_C))
- a.clc(); // throw away carry since it'll never be used
- if (update_flags & (FLAG_S | FLAG_Z))
+ if (rotate && (update_flags & (FLAG_S | FLAG_Z)))
+ {
+ if (update_flags & FLAG_C)
+ calculate_status_flags(a, opsize, dst, FLAG_S | FLAG_Z);
+ else if (dst.isMem())
+ a.test(dst.as<Mem>(), util::make_bitmask<uint64_t>(opsize * 8));
+ else
+ a.test(dst.as<Gp>(), dst.as<Gp>());
+ }
+ }
+ else if (update_flags)
{
- if (update_flags & FLAG_C)
- calculate_status_flags(a, opsize, dst, FLAG_S | FLAG_Z);
- else if (dst.isMem())
+ if (!carryin && !(update_flags & (FLAG_S | FLAG_Z)))
+ a.clc(); // throw away carry since it'll never be used
+ else if ((!carryin || !(update_flags & FLAG_C)) && dst.isMem())
a.test(dst.as<Mem>(), util::make_bitmask<uint64_t>(opsize * 8));
- else
+ else if (!carryin || !(update_flags & FLAG_C))
a.test(dst.as<Gp>(), dst.as<Gp>());
+ else if (update_flags & (FLAG_S | FLAG_Z))
+ calculate_status_flags(a, opsize, dst, FLAG_S | FLAG_Z);
}
}
else if (update_flags || carryin)
{
- // TODO: flag update could be optimised
Label end;
const Gp shift = ecx;
@@ -1586,8 +1597,12 @@ void drcbe_x64::shift_op_param(Assembler &a, Inst::Id const opcode, size_t opsiz
if (carryin)
a.shr(r10b, 1); // restore carry for rolc/rorc
- else if (update_flags & FLAG_C)
+ else if (!(update_flags & ~FLAG_C))
a.clc(); // throw away carry since it'll never be used
+ else if (dst.isMem())
+ a.test(dst.as<Mem>(), util::make_bitmask<uint64_t>(opsize * 8));
+ else
+ a.test(dst.as<Gp>(), dst.as<Gp>());
a.short_().jmp(end);
@@ -1599,10 +1614,10 @@ void drcbe_x64::shift_op_param(Assembler &a, Inst::Id const opcode, size_t opsiz
a.emit(opcode, dst, cl);
- if ((update_flags & FLAG_C) || carryin)
+ if (carryin)
a.bind(end);
- if (update_flags & (FLAG_S | FLAG_Z))
+ if ((rotate || !(update_flags & FLAG_C)) && (update_flags & (FLAG_S | FLAG_Z)))
{
if (update_flags & FLAG_C)
calculate_status_flags(a, opsize, dst, FLAG_S | FLAG_Z);
@@ -1611,6 +1626,9 @@ void drcbe_x64::shift_op_param(Assembler &a, Inst::Id const opcode, size_t opsiz
else
a.test(dst.as<Gp>(), dst.as<Gp>());
}
+
+ if ((update_flags & FLAG_C) && !carryin)
+ a.bind(end);
}
else
{
@@ -5586,21 +5604,31 @@ void drcbe_x64::op_fcmp(Assembler &a, const instruction &inst)
if (inst.size() == 4)
{
// 32-bit form
- movss_r128_p32(a, src1reg, src1p); // movss src1reg,src1p
+ movss_r128_p32(a, src1reg, src1p);
if (src2p.is_memory())
- a.comiss(src1reg, MABS(src2p.memory())); // comiss src1reg,[src2p]
+ a.comiss(src1reg, MABS(src2p.memory()));
else if (src2p.is_float_register())
- a.comiss(src1reg, Xmm(src2p.freg())); // comiss src1reg,src2p
+ a.comiss(src1reg, Xmm(src2p.freg()));
}
else if (inst.size() == 8)
{
// 64-bit form
- movsd_r128_p64(a, src1reg, src1p); // movsd src1reg,src1p
+ movsd_r128_p64(a, src1reg, src1p);
if (src2p.is_memory())
- a.comisd(src1reg, MABS(src2p.memory())); // comisd src1reg,[src2p]
+ a.comisd(src1reg, MABS(src2p.memory()));
else if (src2p.is_float_register())
- a.comisd(src1reg, Xmm(src2p.freg())); // comisd src1reg,src2p
+ a.comisd(src1reg, Xmm(src2p.freg()));
}
+
+ // clear Z and C if unordered
+ Label ordered = a.newLabel();
+
+ a.short_().jnp(ordered);
+ a.lahf();
+ a.and_(eax, 0x00003e00);
+ a.sahf();
+
+ a.bind(ordered);
}
diff --git a/src/devices/cpu/drcbex86.cpp b/src/devices/cpu/drcbex86.cpp
index 1eaca2c6788..ea192477c12 100644
--- a/src/devices/cpu/drcbex86.cpp
+++ b/src/devices/cpu/drcbex86.cpp
@@ -6986,11 +6986,20 @@ void drcbe_x86::op_fcmp(Assembler &a, const instruction &inst)
be_parameter src2p(*this, inst.param(1), PTYPE_MF);
// general case
- emit_fld_p(a, inst.size(), src2p); // fld src2p
- emit_fld_p(a, inst.size(), src1p); // fld src1p
- a.fcompp(); // fcompp
- a.fnstsw(ax); // fnstsw ax
- a.sahf(); // sahf
+ emit_fld_p(a, inst.size(), src2p);
+ emit_fld_p(a, inst.size(), src1p);
+ a.fcomip(st1);
+ a.fstp(st0);
+
+ // clear Z and C if unordered
+ Label ordered = a.newLabel();
+
+ a.short_().jnp(ordered);
+ a.lahf();
+ a.and_(eax, 0x00003e00);
+ a.sahf();
+
+ a.bind(ordered);
}
diff --git a/src/devices/cpu/uml.cpp b/src/devices/cpu/uml.cpp
index f598a8869d1..23775118d93 100644
--- a/src/devices/cpu/uml.cpp
+++ b/src/devices/cpu/uml.cpp
@@ -140,13 +140,13 @@ 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)) // MAME debugger breakpoint
- OPINFO0(BREAK, "break", 4, false, NONE, NONE, NONE) // (for debugging) Issues a breakpoint exception to allow for debugging the generated assembly
+ OPINFO0(BREAK, "break", 4, false, NONE, NONE, ALL) // (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)) // 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)
+ OPINFO0(RET, "ret", 4, true, NONE, NONE, NONE)
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
@@ -160,15 +160,15 @@ opcode_info const instruction::s_opcode_info_table[OP_MAX] =
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)) // 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
+ OPINFO4(LOAD, "!load", 4|8, false, NONE, NONE, NONE, 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, NONE, 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, NONE, 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)
+ OPINFO1(SET, "!set", 4|8, true, NONE, NONE, NONE, 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)) // Rotate left + AND (see drcbec.cpp for implementation)
@@ -200,8 +200,8 @@ 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)) // 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(FLOAD, "f#load", 4|8, false, NONE, NONE, NONE, 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, NONE, 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))
@@ -211,7 +211,7 @@ opcode_info const instruction::s_opcode_info_table[OP_MAX] =
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)) // Note: status flags except FLAG_U are undefined when comparing with NaN
+ OPINFO2(FCMP, "f#cmp", 4|8, false, NONE, UZC, ALL, PINFO(IN, OP, FANY), PINFO(IN, OP, FANY))
OPINFO3(FMUL, "f#mul", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY), PINFO(IN, OP, FANY))
OPINFO3(FDIV, "f#div", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY), PINFO(IN, OP, FANY))
OPINFO2(FNEG, "f#neg", 4|8, false, NONE, NONE, ALL, PINFO(OUT, OP, FRM), PINFO(IN, OP, FANY))