summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2025-04-10 01:58:08 +1000
committer Vas Crabb <vas@vastheman.com>2025-04-10 01:58:08 +1000
commit51c9a3a0b1ef73611120e449e622ada315bc470f (patch)
tree840d54793f49622d9b8a2069bc539d191ac5f540
parentcff4924beb77418816a72b2dfe7e833eeff93040 (diff)
Still more recompiler fixes:
* cpu/uml.cpp: Fixed some cases where simplifying multiplication instructons incorrectly changed output flags. * cpu/drcbex64.cpp: Fixed some cases where a ROLC/RORC with zero shift count could incorrectly clear the carry flag. * cpu/drcbex86.cpp: Made 64-bit ROLC/RORC with zero bit count preserve the carry flag. * cpu/drcbec.cpp: Fixed FSMOV/FDMOV turning things that look like signalling NaNs into quiet NaNs on i686. * cpu/drcbex64.cpp: Greatly reduced copy/paste in the code generation for multiply instructions.
-rw-r--r--src/devices/cpu/drcbec.cpp4
-rw-r--r--src/devices/cpu/drcbex64.cpp332
-rw-r--r--src/devices/cpu/drcbex86.cpp39
-rw-r--r--src/devices/cpu/uml.cpp146
4 files changed, 199 insertions, 322 deletions
diff --git a/src/devices/cpu/drcbec.cpp b/src/devices/cpu/drcbec.cpp
index cc4ab25f965..757ba9b28e9 100644
--- a/src/devices/cpu/drcbec.cpp
+++ b/src/devices/cpu/drcbec.cpp
@@ -2093,7 +2093,7 @@ int drcbe_c::execute(code_handle &entry)
[[fallthrough]];
case MAKE_OPCODE_SHORT(OP_FMOV, 4, 0):
- FSPARAM0 = FSPARAM1;
+ PARAM0 = PARAM1;
break;
case MAKE_OPCODE_SHORT(OP_FTOI4T, 4, 0): // FSTOI4T dst,src1
@@ -2236,7 +2236,7 @@ int drcbe_c::execute(code_handle &entry)
[[fallthrough]];
case MAKE_OPCODE_SHORT(OP_FMOV, 8, 0):
- FDPARAM0 = FDPARAM1;
+ DPARAM0 = DPARAM1;
break;
case MAKE_OPCODE_SHORT(OP_FTOI4T, 8, 0): // FDTOI4T dst,src1
diff --git a/src/devices/cpu/drcbex64.cpp b/src/devices/cpu/drcbex64.cpp
index c07d07d158d..cc1fe4fc952 100644
--- a/src/devices/cpu/drcbex64.cpp
+++ b/src/devices/cpu/drcbex64.cpp
@@ -547,10 +547,8 @@ private:
void op_sub(Assembler &a, const uml::instruction &inst);
void op_subc(Assembler &a, const uml::instruction &inst);
void op_cmp(Assembler &a, const uml::instruction &inst);
- void op_mulu(Assembler &a, const uml::instruction &inst);
- void op_mululw(Assembler &a, const uml::instruction &inst);
- void op_muls(Assembler &a, const uml::instruction &inst);
- void op_mulslw(Assembler &a, const uml::instruction &inst);
+ template <Inst::Id Opcode> void op_mul(Assembler &a, const uml::instruction &inst);
+ template <Inst::Id Opcode> void op_mullw(Assembler &a, const uml::instruction &inst);
void op_divu(Assembler &a, const uml::instruction &inst);
void op_divs(Assembler &a, const uml::instruction &inst);
void op_and(Assembler &a, const uml::instruction &inst);
@@ -674,44 +672,44 @@ inline void drcbe_x64::generate_one(Assembler &a, const uml::instruction &inst)
case uml::OP_RESTORE: op_restore(a, inst); break; // RESTORE dst
// Integer Operations
- case uml::OP_LOAD: op_load(a, inst); break; // LOAD dst,base,index,size
- case uml::OP_LOADS: op_loads(a, inst); break; // LOADS dst,base,index,size
- case uml::OP_STORE: op_store(a, inst); break; // STORE base,index,src,size
- case uml::OP_READ: op_read(a, inst); break; // READ dst,src1,spacesize
- case uml::OP_READM: op_readm(a, inst); break; // READM dst,src1,mask,spacesize
- case uml::OP_WRITE: op_write(a, inst); break; // WRITE dst,src1,spacesize
- case uml::OP_WRITEM: op_writem(a, inst); break; // WRITEM dst,src1,spacesize
- case uml::OP_CARRY: op_carry(a, inst); break; // CARRY src,bitnum
- case uml::OP_SET: op_set(a, inst); break; // SET dst,c
- case uml::OP_MOV: op_mov(a, inst); break; // MOV dst,src[,c]
- case uml::OP_SEXT: op_sext(a, inst); break; // SEXT dst,src
- case uml::OP_ROLAND: op_roland(a, inst); break; // ROLAND dst,src1,src2,src3
- case uml::OP_ROLINS: op_rolins(a, inst); break; // ROLINS dst,src1,src2,src3
- case uml::OP_ADD: op_add(a, inst); break; // ADD dst,src1,src2[,f]
- case uml::OP_ADDC: op_addc(a, inst); break; // ADDC dst,src1,src2[,f]
- case uml::OP_SUB: op_sub(a, inst); break; // SUB dst,src1,src2[,f]
- case uml::OP_SUBB: op_subc(a, inst); break; // SUBB dst,src1,src2[,f]
- case uml::OP_CMP: op_cmp(a, inst); break; // CMP src1,src2[,f]
- case uml::OP_MULU: op_mulu(a, inst); break; // MULU dst,edst,src1,src2[,f]
- case uml::OP_MULULW: op_mululw(a, inst); break; // MULULW dst,src1,src2[,f]
- case uml::OP_MULS: op_muls(a, inst); break; // MULS dst,edst,src1,src2[,f]
- case uml::OP_MULSLW: op_mulslw(a, inst); break; // MULSLW dst,src1,src2[,f]
- case uml::OP_DIVU: op_divu(a, inst); break; // DIVU dst,edst,src1,src2[,f]
- case uml::OP_DIVS: op_divs(a, inst); break; // DIVS dst,edst,src1,src2[,f]
- case uml::OP_AND: op_and(a, inst); break; // AND dst,src1,src2[,f]
- case uml::OP_TEST: op_test(a, inst); break; // TEST src1,src2[,f]
- case uml::OP_OR: op_or(a, inst); break; // OR dst,src1,src2[,f]
- case uml::OP_XOR: op_xor(a, inst); break; // XOR dst,src1,src2[,f]
- case uml::OP_LZCNT: op_lzcnt(a, inst); break; // LZCNT dst,src[,f]
- case uml::OP_TZCNT: op_tzcnt(a, inst); break; // TZCNT dst,src[,f]
- case uml::OP_BSWAP: op_bswap(a, inst); break; // BSWAP dst,src
- case uml::OP_SHL: op_shift<Inst::kIdShl>(a, inst); break; // SHL dst,src,count[,f]
- case uml::OP_SHR: op_shift<Inst::kIdShr>(a, inst); break; // SHR dst,src,count[,f]
- case uml::OP_SAR: op_shift<Inst::kIdSar>(a, inst); break; // SAR dst,src,count[,f]
- case uml::OP_ROL: op_shift<Inst::kIdRol>(a, inst); break; // ROL dst,src,count[,f]
- case uml::OP_ROLC: op_shift<Inst::kIdRcl>(a, inst); break; // ROLC dst,src,count[,f]
- case uml::OP_ROR: op_shift<Inst::kIdRor>(a, inst); break; // ROR dst,src,count[,f]
- case uml::OP_RORC: op_shift<Inst::kIdRcr>(a, inst); break; // RORC dst,src,count[,f]
+ case uml::OP_LOAD: op_load(a, inst); break; // LOAD dst,base,index,size
+ case uml::OP_LOADS: op_loads(a, inst); break; // LOADS dst,base,index,size
+ case uml::OP_STORE: op_store(a, inst); break; // STORE base,index,src,size
+ case uml::OP_READ: op_read(a, inst); break; // READ dst,src1,spacesize
+ case uml::OP_READM: op_readm(a, inst); break; // READM dst,src1,mask,spacesize
+ case uml::OP_WRITE: op_write(a, inst); break; // WRITE dst,src1,spacesize
+ case uml::OP_WRITEM: op_writem(a, inst); break; // WRITEM dst,src1,spacesize
+ case uml::OP_CARRY: op_carry(a, inst); break; // CARRY src,bitnum
+ case uml::OP_SET: op_set(a, inst); break; // SET dst,c
+ case uml::OP_MOV: op_mov(a, inst); break; // MOV dst,src[,c]
+ case uml::OP_SEXT: op_sext(a, inst); break; // SEXT dst,src
+ case uml::OP_ROLAND: op_roland(a, inst); break; // ROLAND dst,src1,src2,src3
+ case uml::OP_ROLINS: op_rolins(a, inst); break; // ROLINS dst,src1,src2,src3
+ case uml::OP_ADD: op_add(a, inst); break; // ADD dst,src1,src2[,f]
+ case uml::OP_ADDC: op_addc(a, inst); break; // ADDC dst,src1,src2[,f]
+ case uml::OP_SUB: op_sub(a, inst); break; // SUB dst,src1,src2[,f]
+ case uml::OP_SUBB: op_subc(a, inst); break; // SUBB dst,src1,src2[,f]
+ case uml::OP_CMP: op_cmp(a, inst); break; // CMP src1,src2[,f]
+ case uml::OP_MULU: op_mul<Inst::kIdMul>(a, inst); break; // MULU dst,edst,src1,src2[,f]
+ case uml::OP_MULULW: op_mullw<Inst::kIdMul>(a, inst); break; // MULULW dst,src1,src2[,f]
+ case uml::OP_MULS: op_mul<Inst::kIdImul>(a, inst); break; // MULS dst,edst,src1,src2[,f]
+ case uml::OP_MULSLW: op_mullw<Inst::kIdImul>(a, inst); break; // MULSLW dst,src1,src2[,f]
+ case uml::OP_DIVU: op_divu(a, inst); break; // DIVU dst,edst,src1,src2[,f]
+ case uml::OP_DIVS: op_divs(a, inst); break; // DIVS dst,edst,src1,src2[,f]
+ case uml::OP_AND: op_and(a, inst); break; // AND dst,src1,src2[,f]
+ case uml::OP_TEST: op_test(a, inst); break; // TEST src1,src2[,f]
+ case uml::OP_OR: op_or(a, inst); break; // OR dst,src1,src2[,f]
+ case uml::OP_XOR: op_xor(a, inst); break; // XOR dst,src1,src2[,f]
+ case uml::OP_LZCNT: op_lzcnt(a, inst); break; // LZCNT dst,src[,f]
+ case uml::OP_TZCNT: op_tzcnt(a, inst); break; // TZCNT dst,src[,f]
+ case uml::OP_BSWAP: op_bswap(a, inst); break; // BSWAP dst,src
+ case uml::OP_SHL: op_shift<Inst::kIdShl>(a, inst); break; // SHL dst,src,count[,f]
+ case uml::OP_SHR: op_shift<Inst::kIdShr>(a, inst); break; // SHR dst,src,count[,f]
+ case uml::OP_SAR: op_shift<Inst::kIdSar>(a, inst); break; // SAR dst,src,count[,f]
+ case uml::OP_ROL: op_shift<Inst::kIdRol>(a, inst); break; // ROL dst,src,count[,f]
+ case uml::OP_ROLC: op_shift<Inst::kIdRcl>(a, inst); break; // ROLC dst,src,count[,f]
+ case uml::OP_ROR: op_shift<Inst::kIdRor>(a, inst); break; // ROR dst,src,count[,f]
+ case uml::OP_RORC: op_shift<Inst::kIdRcr>(a, inst); break; // RORC dst,src,count[,f]
// Floating Point Operations
case uml::OP_FLOAD: op_fload(a, inst); break; // FLOAD dst,base,index
@@ -1585,7 +1583,7 @@ void drcbe_x64::shift_op_param(Assembler &a, Inst::Id const opcode, size_t opsiz
mov_reg_param(a, shift, param);
- a.and_(shift, opsize * 8 - 1);
+ a.and_(shift, (opsize * 8) - 1);
if ((update_flags & FLAG_C) || carryin)
{
@@ -1595,9 +1593,14 @@ void drcbe_x64::shift_op_param(Assembler &a, Inst::Id const opcode, size_t opsiz
a.short_().jnz(calc);
if (carryin)
+ {
+ a.sahf(); // restore flags to keep carry in for rolc/rorc
a.mov(rax, r10);
+ }
else if (update_flags & FLAG_C)
+ {
a.clc(); // throw away carry since it'll never be used
+ }
a.short_().jmp(end);
@@ -4397,10 +4400,11 @@ void drcbe_x64::op_cmp(Assembler &a, const instruction &inst)
//-------------------------------------------------
-// op_mulu - process a MULU opcode
+// op_mul - process a MULU or MULS opcode
//-------------------------------------------------
-void drcbe_x64::op_mulu(Assembler &a, const instruction &inst)
+template <Inst::Id Opcode>
+void drcbe_x64::op_mul(Assembler &a, const instruction &inst)
{
// validate instruction
assert(inst.size() == 4 || inst.size() == 8);
@@ -4413,179 +4417,29 @@ void drcbe_x64::op_mulu(Assembler &a, const instruction &inst)
be_parameter src1p(*this, inst.param(2), PTYPE_MRI);
be_parameter src2p(*this, inst.param(3), PTYPE_MRI);
normalize_commutative(src1p, src2p);
- bool compute_hi = (dstp != edstp);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- Gp dstreg = eax;
- Gp edstreg = edx;
-
- // general case
- mov_reg_param(a, dstreg, src1p);
- if (src2p.is_memory())
- a.mul(MABS(src2p.memory(), 4));
- else if (src2p.is_int_register())
- a.mul(Gpd(src2p.ireg()));
- else if (src2p.is_immediate())
- {
- a.mov(edstreg, src2p.immediate());
- a.mul(edstreg);
- }
- mov_param_reg(a, dstp, dstreg);
- if (compute_hi)
- mov_param_reg(a, edstp, edstreg);
- }
+ const bool compute_hi = (dstp != edstp);
- // 64-bit form
- else if (inst.size() == 8)
- {
- Gp dstreg = rax;
- Gp edstreg = rdx;
+ const Gp dstreg = (inst.size() == 4) ? Gp(eax) : Gp(rax);
+ const Gp edstreg = (inst.size() == 4) ? Gp(edx) : Gp(rdx);
- // general case
- mov_reg_param(a, dstreg, src1p);
- if (src2p.is_memory())
- a.mul(MABS(src2p.memory(), 8));
- else if (src2p.is_int_register())
- a.mul(Gpq(src2p.ireg()));
- else if (src2p.is_immediate())
- {
- mov_r64_imm(a, edstreg, src2p.immediate());
- a.mul(edstreg);
- }
- mov_param_reg(a, dstp, dstreg);
- if (compute_hi)
- mov_param_reg(a, edstp, edstreg);
- }
-
- if (inst.flags())
- calculate_status_flags_mul(a, inst.size(), rax, rdx);
-}
-
-
-//-------------------------------------------------
-// op_mululw - process a MULULW (32x32=32) opcode
-//-------------------------------------------------
-
-void drcbe_x64::op_mululw(Assembler &a, const instruction &inst)
-{
- // validate instruction
- assert(inst.size() == 4 || inst.size() == 8);
- assert_no_condition(inst);
- assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S);
-
- // normalize parameters
- be_parameter dstp(*this, inst.param(0), PTYPE_MR);
- be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
- be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
- normalize_commutative(src1p, src2p);
-
- // 32-bit form
- if (inst.size() == 4)
- {
- Gp dstreg = eax;
- Gp hireg = edx;
-
- // general case
- mov_reg_param(a, dstreg, src1p);
- if (src2p.is_memory())
- a.mul(MABS(src2p.memory(), 4));
- else if (src2p.is_int_register())
- a.mul(Gpd(src2p.ireg()));
- else if (src2p.is_immediate())
- {
- a.mov(hireg, src2p.immediate());
- a.mul(hireg);
- }
- mov_param_reg(a, dstp, dstreg);
- }
-
- // 64-bit form
- else if (inst.size() == 8)
+ // general case
+ mov_reg_param(a, dstreg, src1p);
+ if (src2p.is_memory())
{
- Gp dstreg = rax;
- Gp hireg = rdx;
-
- // general case
- mov_reg_param(a, dstreg, src1p);
- if (src2p.is_memory())
- a.mul(MABS(src2p.memory(), 8));
- else if (src2p.is_int_register())
- a.mul(Gpq(src2p.ireg()));
- else if (src2p.is_immediate())
- {
- mov_r64_imm(a, hireg, src2p.immediate());
- a.mul(hireg);
- }
- mov_param_reg(a, dstp, dstreg);
+ a.emit(Opcode, MABS(src2p.memory(), inst.size()));
}
-
- if (inst.flags())
- calculate_status_flags_mul_low(a, inst.size(), rax);
-}
-
-
-//-------------------------------------------------
-// op_muls - process a MULS opcode
-//-------------------------------------------------
-
-void drcbe_x64::op_muls(Assembler &a, const instruction &inst)
-{
- // validate instruction
- assert(inst.size() == 4 || inst.size() == 8);
- assert_no_condition(inst);
- assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S);
-
- // normalize parameters
- be_parameter dstp(*this, inst.param(0), PTYPE_MR);
- be_parameter edstp(*this, inst.param(1), PTYPE_MR);
- be_parameter src1p(*this, inst.param(2), PTYPE_MRI);
- be_parameter src2p(*this, inst.param(3), PTYPE_MRI);
- normalize_commutative(src1p, src2p);
- bool compute_hi = (dstp != edstp);
-
- // 32-bit form
- if (inst.size() == 4)
+ else if (src2p.is_int_register())
{
- Gp dstreg = eax;
- Gp edstreg = edx;
-
- mov_reg_param(a, dstreg, src1p);
- if (src2p.is_memory())
- a.imul(MABS(src2p.memory(), 4));
- else if (src2p.is_int_register())
- a.imul(Gpd(src2p.ireg()));
- else if (src2p.is_immediate())
- {
- a.mov(edstreg, src2p.immediate());
- a.imul(edstreg);
- }
- mov_param_reg(a, dstp, dstreg);
- if (compute_hi)
- mov_param_reg(a, edstp, edstreg);
+ a.emit(Opcode, Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, src2p.ireg()));
}
-
- // 64-bit form
- else if (inst.size() == 8)
+ else if (src2p.is_immediate())
{
- Gp dstreg = rax;
- Gp edstreg = rdx;
-
- mov_reg_param(a, dstreg, src1p);
- if (src2p.is_memory())
- a.imul(MABS(src2p.memory(), 8));
- else if (src2p.is_int_register())
- a.imul(Gpq(src2p.ireg()));
- else if (src2p.is_immediate())
- {
- mov_r64_imm(a, edstreg, src2p.immediate());
- a.imul(edstreg);
- }
- mov_param_reg(a, dstp, dstreg);
- if (compute_hi)
- mov_param_reg(a, edstp, edstreg);
+ a.mov(edstreg, src2p.immediate());
+ a.emit(Opcode, edstreg);
}
+ mov_param_reg(a, dstp, dstreg);
+ if (compute_hi)
+ mov_param_reg(a, edstp, edstreg);
if (inst.flags())
calculate_status_flags_mul(a, inst.size(), rax, rdx);
@@ -4593,10 +4447,12 @@ void drcbe_x64::op_muls(Assembler &a, const instruction &inst)
//-------------------------------------------------
-// op_mulslw - process a MULSLW (32x32=32) opcode
+// op_mullw - process a MULULW or MULSLW
+// (32x32=32) opcode
//-------------------------------------------------
-void drcbe_x64::op_mulslw(Assembler &a, const instruction &inst)
+template <Inst::Id Opcode>
+void drcbe_x64::op_mullw(Assembler &a, const instruction &inst)
{
// validate instruction
assert(inst.size() == 4 || inst.size() == 8);
@@ -4609,43 +4465,25 @@ void drcbe_x64::op_mulslw(Assembler &a, const instruction &inst)
be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
normalize_commutative(src1p, src2p);
- // 32-bit form
- if (inst.size() == 4)
- {
- Gp dstreg = eax;
- Gp hireg = edx;
+ const Gp dstreg = (inst.size() == 4) ? Gp(eax) : Gp(rax);
+ const Gp hireg = (inst.size() == 4) ? Gp(edx) : Gp(rdx);
- mov_reg_param(a, dstreg, src1p);
- if (src2p.is_memory())
- a.imul(MABS(src2p.memory(), 4));
- else if (src2p.is_int_register())
- a.imul(Gpd(src2p.ireg()));
- else if (src2p.is_immediate())
- {
- a.mov(hireg, src2p.immediate());
- a.imul(hireg);
- }
- mov_param_reg(a, dstp, dstreg);
+ // general case
+ mov_reg_param(a, dstreg, src1p);
+ if (src2p.is_memory())
+ {
+ a.emit(Opcode, MABS(src2p.memory(), inst.size()));
}
-
- // 64-bit form
- else if (inst.size() == 8)
+ else if (src2p.is_int_register())
{
- Gp dstreg = rax;
- Gp hireg = rdx;
-
- mov_reg_param(a, dstreg, src1p);
- if (src2p.is_memory())
- a.imul(MABS(src2p.memory(), 8));
- else if (src2p.is_int_register())
- a.imul(Gpq(src2p.ireg()));
- else if (src2p.is_immediate())
- {
- mov_r64_imm(a, hireg, src2p.immediate());
- a.imul(hireg);
- }
- mov_param_reg(a, dstp, dstreg);
+ a.emit(Opcode, Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, src2p.ireg()));
}
+ else if (src2p.is_immediate())
+ {
+ a.mov(hireg, src2p.immediate());
+ a.emit(Opcode, hireg);
+ }
+ mov_param_reg(a, dstp, dstreg);
if (inst.flags())
calculate_status_flags_mul_low(a, inst.size(), rax);
@@ -5189,7 +5027,7 @@ void drcbe_x64::op_shift(Assembler &a, const uml::instruction &inst)
be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
- const bool carry = (Opcode == Inst::kIdRcl) || (Opcode == Inst::kIdRcr);
+ const bool carryin = (Opcode == Inst::kIdRcl) || (Opcode == Inst::kIdRcr);
if (dstp.is_memory() && ((inst.size() == 8) || !dstp.is_cold_register()) && (dstp == src1p))
{
@@ -5203,7 +5041,7 @@ void drcbe_x64::op_shift(Assembler &a, const uml::instruction &inst)
// pick a target register
const Gp dstreg = dstp.select_register((inst.size() == 4) ? Gp(eax) : Gp(rax), src2p);
- if (carry)
+ if (carryin)
mov_reg_param(a, dstreg, src1p, true);
else
mov_reg_param(a, dstreg, src1p);
diff --git a/src/devices/cpu/drcbex86.cpp b/src/devices/cpu/drcbex86.cpp
index 5109bab556b..5813108c2bc 100644
--- a/src/devices/cpu/drcbex86.cpp
+++ b/src/devices/cpu/drcbex86.cpp
@@ -2633,10 +2633,9 @@ void drcbe_x86::emit_rcl_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi,
Label loop = a.newLabel();
Label skipall = a.newLabel();
Label skiploop = a.newLabel();
- Label end = a.newLabel();
a.pushfd(); // keep carry flag after and
- emit_mov_r32_p32_keepflags(a, ecx, param);
+ emit_mov_r32_p32(a, ecx, param);
a.and_(ecx, 63);
a.popfd();
@@ -2656,27 +2655,18 @@ void drcbe_x86::emit_rcl_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi,
a.rcl(reglo, 1);
a.rcl(reghi, 1);
- if (inst.flags())
- {
- calculate_status_flags(a, reglo, FLAG_Z);
- a.pushfd();
- calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
- emit_combine_z_flags(a);
-
- a.short_().jmp(end);
- }
-
a.bind(skipall);
-
if (inst.flags())
{
- a.test(reglo, reglo);
+ if (inst.flags() & FLAG_C)
+ calculate_status_flags(a, reglo, FLAG_Z);
+ else
+ a.test(reglo, reglo);
a.pushfd();
calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
emit_combine_z_flags(a);
}
- a.bind(end);
reset_last_upper_lower_reg();
}
@@ -2691,10 +2681,9 @@ void drcbe_x86::emit_rcr_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi,
Label loop = a.newLabel();
Label skipall = a.newLabel();
Label skiploop = a.newLabel();
- Label end = a.newLabel();
a.pushfd(); // keep carry flag after and
- emit_mov_r32_p32_keepflags(a, ecx, param);
+ emit_mov_r32_p32(a, ecx, param);
a.and_(ecx, 63);
a.popfd();
@@ -2714,26 +2703,18 @@ void drcbe_x86::emit_rcr_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi,
a.rcr(reghi, 1);
a.rcr(reglo, 1);
- if (inst.flags())
- {
- calculate_status_flags(a, reglo, FLAG_Z);
- a.pushfd();
- calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
- emit_combine_z_flags(a);
-
- a.short_().jmp(end);
- }
-
a.bind(skipall);
if (inst.flags())
{
- a.test(reglo, reglo);
+ if (inst.flags() & FLAG_C)
+ calculate_status_flags(a, reglo, FLAG_Z);
+ else
+ a.test(reglo, reglo);
a.pushfd();
calculate_status_flags(a, reghi, FLAG_S | FLAG_Z);
emit_combine_z_flags(a);
}
- a.bind(end);
reset_last_upper_lower_reg();
}
diff --git a/src/devices/cpu/uml.cpp b/src/devices/cpu/uml.cpp
index d88b30ddcfe..46bf06f3306 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;
@@ -591,11 +595,13 @@ public:
truncate_immediate(inst, 1, mask);
}
- template <opcode_t LoWordOp>
+ template <opcode_t LoWordOp, typename Short, typename Long>
static void mul(instruction &inst)
{
- // if the two destination operands are identical, convert to MUL.LW
- if (inst.param(0) == inst.param(1))
+ 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);
@@ -616,6 +622,48 @@ public:
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>
@@ -640,9 +688,19 @@ public:
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()))));
+ {
+ 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
- convert_to_mov_immediate(inst, u64(Long(inst.param(1).immediate()) * Long(inst.param(2).immediate())));
+ {
+ 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));
+ }
}
}
@@ -660,15 +718,15 @@ public:
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))
+ // 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 if (inst.param(2).is_immediate() && inst.param(3).is_immediate())
+ else
{
// convert constant result to MOV or a logic op
assert((size == 4) || (size == 8));
@@ -1138,42 +1196,42 @@ void uml::instruction::simplify()
origop = m_opcode;
switch (m_opcode)
{
- 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;
+ 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;
+
+ default: break;
}
#if 0