summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2025-04-08 06:42:46 +1000
committer Vas Crabb <vas@vastheman.com>2025-04-08 06:42:46 +1000
commit63be095e58583a5029536e030ebed297ea9fcbf6 (patch)
treef36f3b341ae188e3fac5bffa1be9a37d089f9ddd /src/devices/cpu
parent113d351625d042ae8bde7dd4b8e40e1e5263d093 (diff)
-cpu/uml.cpp: Rewrote the UML simplification code.
* Fixed many cases that could cause the upper bits of a register not to cleared following a 32-bit operation. * Added more simplifications. * Allow many simplifications when flag updates are requested. * Fixed various bits of unreachable code. * cpu/drcbearm64.cpp, cpu/drcbex64.cpp: Removed code for special-casing some situations the simplifier can now take care of consistently. -cpu/drcbex64.cpp: Fixed a bug causing some shifts to be treated as a no-op when the destination and source are not the same. -cpu/drcbearm64.cpp, cpu/drcbex64.cpp: Added a special case for comparing something to itself. -cpu/e132xs: Use the CARRY instruction rather than a right shift to set up carry in.
Diffstat (limited to 'src/devices/cpu')
-rw-r--r--src/devices/cpu/drcbearm64.cpp51
-rw-r--r--src/devices/cpu/drcbex64.cpp371
-rw-r--r--src/devices/cpu/e132xs/e132xsdrc_ops.hxx4
-rw-r--r--src/devices/cpu/uml.cpp1141
-rw-r--r--src/devices/cpu/uml.h5
5 files changed, 957 insertions, 615 deletions
diff --git a/src/devices/cpu/drcbearm64.cpp b/src/devices/cpu/drcbearm64.cpp
index 635df218248..882f70324a7 100644
--- a/src/devices/cpu/drcbearm64.cpp
+++ b/src/devices/cpu/drcbearm64.cpp
@@ -3718,23 +3718,32 @@ void drcbe_arm64::op_cmp(a64::Assembler &a, const uml::instruction &inst)
be_parameter src1p(*this, inst.param(0), PTYPE_MRI);
be_parameter src2p(*this, inst.param(1), PTYPE_MRI);
- const a64::Gp src1 = src1p.select_register(TEMP_REG1, inst.size());
-
- mov_reg_param(a, inst.size(), src1, src1p);
-
- if (src2p.is_immediate() && is_valid_immediate_addsub(src2p.immediate()))
+ if (src1p == src2p)
{
- if (src2p.is_immediate_value(0))
- a.cmp(src1, select_register(a64::xzr, inst.size()));
- else
- a.cmp(src1, src2p.immediate());
+ const a64::Gp zero = select_register(a64::xzr, inst.size());
+
+ a.cmp(zero, zero);
}
else
{
- const a64::Gp src2 = src2p.select_register(TEMP_REG2, inst.size());
+ const a64::Gp src1 = src1p.select_register(TEMP_REG1, inst.size());
- mov_reg_param(a, inst.size(), src2, src2p);
- a.cmp(src1, src2);
+ mov_reg_param(a, inst.size(), src1, src1p);
+
+ if (src2p.is_immediate() && is_valid_immediate_addsub(src2p.immediate()))
+ {
+ if (src2p.is_immediate_value(0))
+ a.cmp(src1, select_register(a64::xzr, inst.size()));
+ else
+ a.cmp(src1, src2p.immediate());
+ }
+ else
+ {
+ const a64::Gp src2 = src2p.select_register(TEMP_REG2, inst.size());
+
+ mov_reg_param(a, inst.size(), src2, src2p);
+ a.cmp(src1, src2);
+ }
}
if (inst.flags() & FLAG_C)
@@ -4087,12 +4096,6 @@ void drcbe_arm64::op_and(a64::Assembler &a, const uml::instruction &inst)
be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
- if (src1p.is_immediate() || (dstp.is_int_register() && (dstp == src2p)))
- {
- using std::swap;
- swap(src1p, src2p);
- }
-
const a64::Gp dst = dstp.select_register(TEMP_REG3, inst.size());
const a64::Gp src1 = src1p.select_register(dst, inst.size());
@@ -4210,12 +4213,6 @@ void drcbe_arm64::op_or(a64::Assembler &a, const uml::instruction &inst)
be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
- if (src1p.is_immediate() || (dstp.is_int_register() && (dstp == src2p)))
- {
- using std::swap;
- swap(src1p, src2p);
- }
-
const a64::Gp dst = dstp.select_register(TEMP_REG3, inst.size());
const a64::Gp src1 = src1p.select_register(dst, inst.size());
@@ -4274,12 +4271,6 @@ void drcbe_arm64::op_xor(a64::Assembler &a, const uml::instruction &inst)
be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
- if (src1p.is_immediate() || (dstp.is_int_register() && (dstp == src2p)))
- {
- using std::swap;
- swap(src1p, src2p);
- }
-
const a64::Gp dst = dstp.select_register(TEMP_REG3, inst.size());
const a64::Gp src1 = src1p.select_register(dst, inst.size());
diff --git a/src/devices/cpu/drcbex64.cpp b/src/devices/cpu/drcbex64.cpp
index c5bf514f14e..b3eb216a7ac 100644
--- a/src/devices/cpu/drcbex64.cpp
+++ b/src/devices/cpu/drcbex64.cpp
@@ -1395,11 +1395,13 @@ void drcbe_x64::alu_op_param(Assembler &a, Inst::Id const opcode, Operand const
if (is64 && !short_immediate(param.immediate()))
{
// use scratch register for 64-bit immediate
- a.mov(r11, param.immediate()); // mov r11,param
- a.emit(opcode, dst, r11); // op dst,r11
+ a.mov(r11, param.immediate());
+ a.emit(opcode, dst, r11);
}
else
- a.emit(opcode, dst, param.immediate()); // op dst,param
+ {
+ a.emit(opcode, dst, param.immediate());
+ }
}
}
else if (param.is_memory())
@@ -1407,7 +1409,7 @@ void drcbe_x64::alu_op_param(Assembler &a, Inst::Id const opcode, Operand const
if (dst.isMem())
{
// use temporary register for memory,memory
- Gp const tmp = is64 ? param.select_register(rax) : param.select_register(eax);
+ Gp const tmp = param.select_register(is64 ? Gp(rax) : Gp(eax));
a.mov(tmp, MABS(param.memory()));
a.emit(opcode, dst, tmp);
@@ -1555,7 +1557,7 @@ void drcbe_x64::shift_op_param(Assembler &a, Inst::Id const opcode, size_t opsiz
if (bitshift)
a.emit(opcode, dst, imm(param.immediate()));
- else if (update_flags & FLAG_C)
+ 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))
@@ -1594,8 +1596,7 @@ void drcbe_x64::shift_op_param(Assembler &a, Inst::Id const opcode, size_t opsiz
if (carryin)
a.mov(rax, r10);
-
- if (update_flags & FLAG_C)
+ else if (update_flags & FLAG_C)
a.clc(); // throw away carry since it'll never be used
a.short_().jmp(end);
@@ -3832,11 +3833,7 @@ void drcbe_x64::op_roland(Assembler &a, const instruction &inst)
// pick a target register
Gp dstreg = dstp.select_register((inst.size() == 4) ? Gp(eax) : Gp(rax), maskp);
- if (maskp.is_immediate_value(0))
- {
- a.xor_(dstreg, dstreg);
- }
- else if (shiftp.is_immediate() && (srcp.is_immediate() || maskp.is_immediate()))
+ if (shiftp.is_immediate() && (srcp.is_immediate() || maskp.is_immediate()))
{
const unsigned shift = shiftp.immediate() & (bits - 1);
const uint64_t sizemask = util::make_bitmask<uint64_t>(bits);
@@ -3875,55 +3872,33 @@ void drcbe_x64::op_roland(Assembler &a, const instruction &inst)
{
const uint64_t mask = maskp.immediate() & sizemask;
mov_reg_param(a, dstreg, srcp);
- if (mask == sizemask)
+ a.rol(dstreg, shift);
+ if (!inst.flags() && (mask == 0x000000ff))
{
- if (shift)
- a.rol(dstreg, shift);
- if (inst.flags())
- a.test(dstreg, dstreg);
+ a.movzx(dstreg, dstreg.r8());
}
- else if (mask == (util::make_bitmask<uint64_t>(shift) & sizemask))
+ else if (!inst.flags() && (mask == 0x0000ffff))
{
- a.shr(dstreg, bits - shift);
- if (inst.flags())
- a.test(dstreg, dstreg);
+ a.movzx(dstreg, dstreg.r16());
}
- else if (mask == (~util::make_bitmask<uint64_t>(shift) & sizemask))
+ else if (!inst.flags() && (mask == 0xffffffff))
{
- a.shl(dstreg, shift);
+ a.mov(dstreg.r32(), dstreg.r32());
+ }
+ else if ((bits == 32) || (util::sext(mask, 32) == mask))
+ {
+ a.and_(dstreg, mask);
+ }
+ else if (uint32_t(mask) == mask)
+ {
+ a.and_(dstreg, mask); // asmjit converts this to a DWORD-size operation
if (inst.flags())
a.test(dstreg, dstreg);
}
else
{
- a.rol(dstreg, shift);
- if (!inst.flags() && (mask == 0x000000ff))
- {
- a.movzx(dstreg, dstreg.r8());
- }
- else if (!inst.flags() && (mask == 0x0000ffff))
- {
- a.movzx(dstreg, dstreg.r16());
- }
- else if (!inst.flags() && (mask == 0xffffffff))
- {
- a.mov(dstreg.r32(), dstreg.r32());
- }
- else if ((bits == 32) || (util::sext(mask, 32) == mask))
- {
- a.and_(dstreg, mask);
- }
- else if (uint32_t(mask) == mask)
- {
- a.and_(dstreg, mask);
- if (inst.flags())
- a.test(dstreg, dstreg);
- }
- else
- {
- a.mov(rdx, mask);
- a.and_(dstreg, rdx);
- }
+ a.mov(rdx, mask);
+ a.and_(dstreg, rdx);
}
}
}
@@ -3940,19 +3915,7 @@ void drcbe_x64::op_roland(Assembler &a, const instruction &inst)
mov_reg_param(a, dstreg, srcp);
a.rol(dstreg, cl);
}
- alu_op_param(a, Inst::kIdAnd, dstreg, maskp,
- [inst] (Assembler &a, Operand const &dst, be_parameter const &src)
- {
- // all-one cases
- if (ones(src.immediate(), inst.size()))
- {
- if (inst.flags())
- a.test(dst.as<Gp>(), dst.as<Gp>());
- return true;
- }
-
- return false;
- });
+ alu_op_param(a, Inst::kIdAnd, dstreg, maskp);
}
mov_param_reg(a, dstp, dstreg);
@@ -3984,15 +3947,7 @@ void drcbe_x64::op_rolins(Assembler &a, const instruction &inst)
const unsigned bits = inst.size() * 8;
const uint64_t sizemask = util::make_bitmask<uint64_t>(bits);
- if (maskp.is_immediate_value(0))
- {
- if (inst.flags())
- {
- mov_reg_param(a, dstreg, dstp);
- a.test(dstreg, dstreg);
- }
- }
- else if (shiftp.is_immediate() && (srcp.is_immediate() || maskp.is_immediate()))
+ if (shiftp.is_immediate() && (srcp.is_immediate() || maskp.is_immediate()))
{
const unsigned shift = shiftp.immediate() & (bits - 1);
if (srcp.is_immediate())
@@ -4006,68 +3961,53 @@ void drcbe_x64::op_rolins(Assembler &a, const instruction &inst)
{
const uint64_t mask = maskp.immediate() & sizemask;
src &= mask;
- if (mask == sizemask)
+
+ bool flags = false;
+ mov_reg_param(a, dstreg, dstp);
+ if (mask == 0xffffffff'00000000)
{
- if (src)
- {
- a.mov(dstreg, src);
- if (inst.flags())
- a.test(dstreg, dstreg);
- }
- else
- {
- a.xor_(dstreg, dstreg);
- }
+ a.mov(dstreg.r32(), dstreg.r32());
+ }
+ else if (mask == (0xffffffff'ffff0000 & sizemask))
+ {
+ a.movzx(dstreg, dstreg.r16());
+ }
+ else if (mask == (0xffffffff'ffffff00 & sizemask))
+ {
+ a.movzx(dstreg, dstreg.r8());
+ }
+ else if ((bits == 32) || (util::sext(~mask, 32) == ~mask))
+ {
+ a.and_(dstreg, ~mask);
+ flags = true;
+ }
+ else if (uint32_t(~mask) == ~mask)
+ {
+ a.and_(dstreg, ~mask);
}
else
{
- bool flags = false;
- mov_reg_param(a, dstreg, dstp);
- if (mask == 0xffffffff'00000000)
- {
- a.mov(dstreg.r32(), dstreg.r32());
- }
- else if (mask == (0xffffffff'ffff0000 & sizemask))
- {
- a.movzx(dstreg, dstreg.r16());
- }
- else if (mask == (0xffffffff'ffffff00 & sizemask))
- {
- a.movzx(dstreg, dstreg.r8());
- }
- else if ((bits == 32) || (util::sext(~mask, 32) == ~mask))
- {
- a.and_(dstreg, ~mask);
- flags = true;
- }
- else if (uint32_t(~mask) == ~mask)
- {
- a.and_(dstreg, ~mask);
- }
- else
- {
- a.mov(maskreg, ~mask);
- a.and_(dstreg, maskreg);
- flags = true;
- }
+ a.mov(maskreg, ~mask);
+ a.and_(dstreg, maskreg);
+ flags = true;
+ }
- if (src)
+ if (src)
+ {
+ if ((bits == 32) || (util::sext(src, 32) == src))
{
- if ((bits == 32) || (util::sext(src, 32) == src))
- {
- a.or_(dstreg, src);
- }
- else
- {
- a.mov(srcreg, src);
- a.or_(dstreg, srcreg);
- }
+ a.or_(dstreg, src);
}
- else if (!flags && inst.flags())
+ else
{
- a.test(dstreg, dstreg);
+ a.mov(srcreg, src);
+ a.or_(dstreg, srcreg);
}
}
+ else if (!flags && inst.flags())
+ {
+ a.test(dstreg, dstreg);
+ }
}
else
{
@@ -4101,110 +4041,100 @@ void drcbe_x64::op_rolins(Assembler &a, const instruction &inst)
{
// variables source, immediate mask
const uint64_t mask = maskp.immediate() & sizemask;
- if (mask == sizemask)
- {
- mov_reg_param(a, dstreg, srcp);
- if (shift)
- a.rol(dstreg, shift);
- if (inst.flags())
- a.test(dstreg, dstreg);
- }
- else
- {
- mov_reg_param(a, dstreg, dstp);
- bool maskloaded = false;
- if (!shift)
+ mov_reg_param(a, dstreg, dstp);
+
+ bool maskloaded = false;
+ if (!shift)
+ {
+ if (mask == 0x00000000'000000ff)
{
- if (mask == 0x00000000'000000ff)
- {
- if (srcp.is_int_register())
- a.movzx(srcreg, GpbLo(srcp.ireg()));
- else if (srcp.is_memory())
- a.movzx(srcreg, MABS(srcp.memory(), 1));
- }
- else if (mask == 0x00000000'0000ffff)
- {
- if (srcp.is_int_register())
- a.movzx(srcreg, Gpw(srcp.ireg()));
- else if (srcp.is_memory())
- a.movzx(srcreg, MABS(srcp.memory(), 2));
- }
- else if (mask == 0x00000000'ffffffff)
- {
- mov_reg_param(a, srcreg.r32(), srcp);
- }
- else
- {
- mov_reg_param(a, srcreg, srcp);
- a.and_(srcreg, mask);
- }
+ if (srcp.is_int_register())
+ a.movzx(srcreg, GpbLo(srcp.ireg()));
+ else if (srcp.is_memory())
+ a.movzx(srcreg, MABS(srcp.memory(), 1));
}
- else if (mask == (util::make_bitmask<uint64_t>(shift) & sizemask))
+ else if (mask == 0x00000000'0000ffff)
{
- mov_reg_param(a, srcreg, srcp);
- a.shr(srcreg, bits - shift);
+ if (srcp.is_int_register())
+ a.movzx(srcreg, Gpw(srcp.ireg()));
+ else if (srcp.is_memory())
+ a.movzx(srcreg, MABS(srcp.memory(), 2));
}
- else if (mask == (~util::make_bitmask<uint64_t>(shift) & sizemask))
+ else if (mask == 0x00000000'ffffffff)
{
- mov_reg_param(a, srcreg, srcp);
- a.shl(srcreg, shift);
+ mov_reg_param(a, srcreg.r32(), srcp);
}
else
{
mov_reg_param(a, srcreg, srcp);
- a.rol(srcreg, shift);
- if (mask == 0x00000000'000000ff)
- {
- a.movzx(srcreg, srcreg.r8());
- }
- else if (mask == 0x00000000'0000ffff)
- {
- a.movzx(srcreg, srcreg.r16());
- }
- else if (mask == 0x00000000'ffffffff)
- {
- a.mov(srcreg.r32(), srcreg.r32());
- }
- else if ((bits == 32) || (util::sext(mask, 32) == mask) || (uint32_t(mask) == mask))
- {
- a.and_(srcreg, mask);
- }
- else
- {
- a.mov(maskreg, mask);
- a.and_(srcreg, maskreg);
- maskloaded = true;
- }
+ a.and_(srcreg, mask);
}
-
- if (mask == 0xffffffff'00000000)
+ }
+ else if (mask == (util::make_bitmask<uint64_t>(shift) & sizemask))
+ {
+ mov_reg_param(a, srcreg, srcp);
+ a.shr(srcreg, bits - shift);
+ }
+ else if (mask == (~util::make_bitmask<uint64_t>(shift) & sizemask))
+ {
+ mov_reg_param(a, srcreg, srcp);
+ a.shl(srcreg, shift);
+ }
+ else
+ {
+ mov_reg_param(a, srcreg, srcp);
+ a.rol(srcreg, shift);
+ if (mask == 0x00000000'000000ff)
{
- a.mov(dstreg.r32(), dstreg.r32());
+ a.movzx(srcreg, srcreg.r8());
}
- else if (mask == (0xffffffff'ffff0000 & sizemask))
+ else if (mask == 0x00000000'0000ffff)
{
- a.movzx(dstreg, dstreg.r16());
+ a.movzx(srcreg, srcreg.r16());
}
- else if (mask == (0xffffffff'ffffff00 & sizemask))
+ else if (mask == 0x00000000'ffffffff)
{
- a.movzx(dstreg, dstreg.r8());
+ a.mov(srcreg.r32(), srcreg.r32());
}
- else if ((bits == 32) || (util::sext(~mask, 32) == ~mask) || (uint32_t(~mask) == ~mask))
+ else if ((bits == 32) || (util::sext(mask, 32) == mask) || (uint32_t(mask) == mask))
{
- a.and_(dstreg, ~mask & sizemask);
+ a.and_(srcreg, mask);
}
else
{
- if (maskloaded)
- a.not_(maskreg);
- else
- a.mov(maskreg, ~mask);
- a.and_(dstreg, maskreg);
+ a.mov(maskreg, mask);
+ a.and_(srcreg, maskreg);
+ maskloaded = true;
}
+ }
- a.or_(dstreg, srcreg);
+ if (mask == 0xffffffff'00000000)
+ {
+ a.mov(dstreg.r32(), dstreg.r32());
+ }
+ else if (mask == (0xffffffff'ffff0000 & sizemask))
+ {
+ a.movzx(dstreg, dstreg.r16());
+ }
+ else if (mask == (0xffffffff'ffffff00 & sizemask))
+ {
+ a.movzx(dstreg, dstreg.r8());
}
+ else if ((bits == 32) || (util::sext(~mask, 32) == ~mask) || (uint32_t(~mask) == ~mask))
+ {
+ a.and_(dstreg, ~mask & sizemask);
+ }
+ else
+ {
+ if (maskloaded)
+ a.not_(maskreg);
+ else
+ a.mov(maskreg, ~mask);
+ a.and_(dstreg, maskreg);
+ }
+
+ a.or_(dstreg, srcreg);
}
mov_param_reg(a, dstp, dstreg);
@@ -4469,24 +4399,33 @@ void drcbe_x64::op_cmp(Assembler &a, const instruction &inst)
be_parameter src1p(*this, inst.param(0), PTYPE_MRI);
be_parameter src2p(*this, inst.param(1), PTYPE_MRI);
- // memory versus anything
- if (src1p.is_memory())
- alu_op_param(a, Inst::kIdCmp, MABS(src1p.memory(), inst.size()), src2p); // cmp [dstp],src2p
+ if (src1p == src2p)
+ {
+ // doesn't matter what we compare if it's always equal
+ const Gp srcreg = (inst.size() == 4) ? Gp(eax) : Gp(rax); // TODO: consider false dependencies
- // general case
+ a.cmp(srcreg, srcreg);
+ }
+ else if (src1p.is_memory())
+ {
+ // memory versus anything
+ alu_op_param(a, Inst::kIdCmp, MABS(src1p.memory(), inst.size()), src2p);
+ }
else
{
+ // general case
+
// pick a target register for the general case
- Gp src1reg = (inst.size() == 4) ? src1p.select_register(eax) : src1p.select_register(rax);
+ const Gp src1reg = src1p.select_register((inst.size() == 4) ? Gp(eax) : Gp(rax));
if (src1p.is_immediate())
{
if (inst.size() == 4)
- a.mov(src1reg, src1p.immediate()); // mov src1reg,imm
+ a.mov(src1reg, src1p.immediate());
else
- mov_r64_imm(a, src1reg, src1p.immediate()); // mov src1reg,imm
+ mov_r64_imm(a, src1reg, src1p.immediate());
}
- alu_op_param(a, Inst::kIdCmp, src1reg, src2p); // cmp src1reg,src2p
+ alu_op_param(a, Inst::kIdCmp, src1reg, src2p);
}
}
@@ -5361,10 +5300,6 @@ void drcbe_x64::op_shift(Assembler &a, const uml::instruction &inst)
const bool carry = (Opcode == Inst::kIdRcl) || (Opcode == Inst::kIdRcr);
- // optimize immediate zero case
- if (!carry && !inst.flags() && src2p.is_immediate() && !(src2p.immediate() & (inst.size() * 8 - 1)))
- return; // FIXME: needs to clear upper bits for 32-bit form
-
if (dstp.is_memory() && ((inst.size() == 8) || !dstp.is_cold_register()) && (dstp == src1p))
{
// dstp == src1p in memory
@@ -5375,7 +5310,7 @@ void drcbe_x64::op_shift(Assembler &a, const uml::instruction &inst)
// general case
// pick a target register
- Gp dstreg = (inst.size() == 4) ? dstp.select_register(eax, src2p) : dstp.select_register(rax, src2p);
+ const Gp dstreg = dstp.select_register((inst.size() == 4) ? Gp(eax) : Gp(rax), src2p);
if (carry)
mov_reg_param(a, dstreg, src1p, true);
diff --git a/src/devices/cpu/e132xs/e132xsdrc_ops.hxx b/src/devices/cpu/e132xs/e132xsdrc_ops.hxx
index 1f17b598819..072c695f3be 100644
--- a/src/devices/cpu/e132xs/e132xsdrc_ops.hxx
+++ b/src/devices/cpu/e132xs/e132xsdrc_ops.hxx
@@ -1582,7 +1582,7 @@ void hyperstone_device::generate_subc(drcuml_block &block, compiler_state &compi
if (!SrcGlobal || (src_code != SR_REGISTER))
{
- UML_SHR(block, I4, I2, C_SHIFT + 1); // set up carry in, result unused
+ UML_CARRY(block, I2, C_SHIFT);
UML_SUBB(block, I0, I0, I1);
}
else
@@ -1680,7 +1680,7 @@ void hyperstone_device::generate_addc(drcuml_block &block, compiler_state &compi
if (!SrcGlobal || (src_code != SR_REGISTER))
{
- UML_SHR(block, I4, I2, C_SHIFT + 1); // set up carry in, result unused
+ UML_CARRY(block, I2, C_SHIFT);
UML_ADDC(block, I0, I0, I1);
}
else
diff --git a/src/devices/cpu/uml.cpp b/src/devices/cpu/uml.cpp
index 7133cfb6980..6670725a18b 100644
--- a/src/devices/cpu/uml.cpp
+++ b/src/devices/cpu/uml.cpp
@@ -259,6 +259,739 @@ 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_numparams = 3;
+ inst.m_param[2] = inst.param(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_numparams = 3;
+ inst.m_param[2] = inst.param(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;
+ inst.m_numparams = 3;
+ 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);
+ }
+ 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_numparams = 3;
+ inst.m_param[2] = inst.param(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>
+ static void mul(instruction &inst)
+ {
+ // if the two destination operands are identical, convert to MUL.LW
+ if (inst.param(0) == inst.param(1))
+ {
+ inst.m_opcode = LoWordOp;
+ inst.m_numparams = 3;
+ inst.m_param[1] = inst.param(2);
+ inst.m_param[2] = inst.param(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]);
+ }
+ }
+
+ 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)
+ convert_to_mov_immediate(inst, u32(Short(u32(inst.param(1).immediate())) * Short(u32(inst.param(2).immediate()))));
+ else
+ convert_to_mov_immediate(inst, u64(Long(inst.param(1).immediate()) * Long(inst.param(2).immediate())));
+ }
+ }
+
+ 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 if not dividing by zero
+ if ((inst.param(0) == inst.param(1)) && !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 if (inst.param(2).is_immediate() && inst.param(3).is_immediate())
+ {
+ // 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)))
+ {
+ 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]);
+ }
+ }
+
+ 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))
+ {
+ 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 _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;
+ }
+ }
+ }
+
+ 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();
+ }
+};
+
+
//-------------------------------------------------
// configure - configure an opcode with no
// parameters
@@ -372,13 +1105,6 @@ 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
@@ -387,365 +1113,56 @@ void uml::instruction::simplify()
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 = 3;
- 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 = 3;
- 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;
- }
- 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(rotl_32(m_param[1].immediate(), m_param[2].immediate()) & m_param[3].immediate());
- else
- convert_to_mov_immediate(rotl_64(m_param[1].immediate(), m_param[2].immediate()) & m_param[3].immediate());
- }
- else if (m_param[1].is_immediate() && m_param[2].is_immediate())
- {
- assert(m_size == 4 || m_size == 8);
- m_opcode = OP_AND;
- m_numparams = 3;
- if (m_size == 4)
- m_param[1] = parameter(rotl_32(m_param[1].immediate(), m_param[2].immediate()));
- else
- m_param[1] = parameter(rotl_64(m_param[1].immediate(), m_param[2].immediate()));
- m_param[2] = m_param[3];
- }
- else if (m_param[2].is_immediate_value(0) || m_param[3].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[2].immediate()) * u32(m_param[3].immediate())));
- else if (m_size == 8)
- convert_to_mov_immediate(u64(u64(m_param[2].immediate()) * u64(m_param[3].immediate())));
- }
- }
- break;
-
- // MULULW: convert simple form to MOV if immediate, or if multiplying by 0
- case OP_MULULW:
- 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())
- {
- 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[2].immediate()) * s32(m_param[3].immediate())));
- else if (m_size == 8)
- convert_to_mov_immediate(s64(s64(m_param[2].immediate()) * s64(m_param[3].immediate())));
- }
- }
- break;
-
- // MULSLW: convert simple form to MOV if immediate, or if multiplying by 0
- case OP_MULSLW:
- 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())
- {
- 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[2].immediate()) / u32(m_param[3].immediate())));
- else if (m_size == 8)
- convert_to_mov_immediate(u64(u64(m_param[2].immediate()) / u64(m_param[3].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[2].immediate()) / s32(m_param[3].immediate())));
- else if (m_size == 8)
- convert_to_mov_immediate(s64(s64(m_param[2].immediate()) / s64(m_param[3].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_32(m_param[1].immediate()));
- else if (m_size == 8)
- convert_to_mov_immediate(count_leading_zeros_64(m_param[1].immediate()));
- }
- 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())
- {
- if (m_size == 4)
- convert_to_mov_immediate(u32(m_param[1].immediate()) << (m_param[2].immediate() & 31));
- else if (m_size == 8)
- convert_to_mov_immediate(u64(m_param[1].immediate()) << (m_param[2].immediate() & 63));
- }
- 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() & 31));
- else if (m_size == 8)
- convert_to_mov_immediate(u64(m_param[1].immediate()) >> (m_param[2].immediate() & 63));
- }
- 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() & 31));
- else if (m_size == 8)
- convert_to_mov_immediate(s64(m_param[1].immediate()) >> (m_param[2].immediate() & 63));
- }
- 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(rotl_32(m_param[1].immediate(), m_param[2].immediate() & 31));
- else if (m_size == 8)
- convert_to_mov_immediate(rotl_64(m_param[1].immediate(), m_param[2].immediate() & 63));
- }
- 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(rotr_32(m_param[1].immediate(), m_param[2].immediate() & 31));
- else if (m_size == 8)
- convert_to_mov_immediate(rotr_64(m_param[1].immediate(), m_param[2].immediate() & 63));
- }
- 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_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>(*this); break;
+ case OP_MULULW: simplify_op::mullw<u32, u64>(*this); break;
+ case OP_MULS: simplify_op::mul<OP_MULSLW>(*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;
+
+ 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, disasm2);
- }
- */
+#if 0
+ 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, disasm2);
+ }
+#endif
// loop until we stop changing opcodes
- } while (m_opcode != origop);
+ }
+ while (m_opcode != origop);
}
diff --git a/src/devices/cpu/uml.h b/src/devices/cpu/uml.h
index 9577b393fcd..5aefea60e6c 100644
--- a/src/devices/cpu/uml.h
+++ b/src/devices/cpu/uml.h
@@ -222,7 +222,7 @@ namespace uml
};
// C function callback definition
- typedef void (*c_function)(void *ptr);
+ using c_function = void (*)(void *ptr);
// class describing a global code handle
class code_handle
@@ -585,8 +585,6 @@ namespace uml
// opcode validation and simplification
void validate();
- void convert_to_mov_immediate(u64 immediate) { m_opcode = OP_MOV; m_numparams = 2; m_param[1] = immediate; }
- void convert_to_mov_param(int pnum) { m_opcode = OP_MOV; m_numparams = 2; m_param[1] = m_param[pnum]; }
// internal state
opcode_t m_opcode = OP_INVALID; // opcode
@@ -597,6 +595,7 @@ namespace uml
parameter m_param[MAX_PARAMS]; // up to 4 parameters
static opcode_info const s_opcode_info_table[OP_MAX];
+ struct simplify_op;
};
// structure describing rules for parameter encoding