From 88aa249ce18887f347c1011c4d35e833941cd9bb Mon Sep 17 00:00:00 2001 From: joevt Date: Tue, 9 Apr 2024 01:25:23 -0700 Subject: [PATCH] poweropcodes: Fix div. dividend is supposed to be a twos compliment number. Fix test for dividend = -0x80000000 and divisor = -1. Previously, the test was assuming dividend was a 32-bit value from rA. Fix OV calculation. Previously, it was using power_setsoov which I think is only for add and subtract operations. Fix CR calculation. It depends on the remainder, not the quotient. --- cpu/ppc/poweropcodes.cpp | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/cpu/ppc/poweropcodes.cpp b/cpu/ppc/poweropcodes.cpp index 679a733..fb099b5 100644 --- a/cpu/ppc/poweropcodes.cpp +++ b/cpu/ppc/poweropcodes.cpp @@ -98,23 +98,39 @@ void dppc_interpreter::power_div() { uint32_t ppc_result_d; ppc_grab_regsdab(ppc_cur_instruction); - uint64_t dividend = ((uint64_t)ppc_result_a << 32) | ppc_state.spr[SPR::MQ]; - int32_t divisor = ppc_result_b; + int64_t dividend = (uint64_t(ppc_result_a) << 32) | ppc_state.spr[SPR::MQ]; + int32_t divisor = ppc_result_b; + int64_t quotient; + int32_t remainder; - if ((ppc_result_a == 0x80000000UL && divisor == -1) || !divisor) { - ppc_state.spr[SPR::MQ] = 0; - ppc_result_d = 0x80000000UL; // -2^31 aka INT32_MIN + if (dividend == -0x80000000 && divisor == -1) { + remainder = 0; + ppc_result_d = 0x80000000U; // -2^31 aka INT32_MIN + if (ov) + ppc_state.spr[SPR::XER] |= XER::SO | XER::OV; + } else if (!divisor) { + remainder = 0; + ppc_result_d = 0x80000000U; // -2^31 aka INT32_MIN + if (ov) + ppc_state.spr[SPR::XER] |= XER::SO | XER::OV; } else { - ppc_result_d = uint32_t(dividend / divisor); - ppc_state.spr[SPR::MQ] = dividend % divisor; + quotient = dividend / divisor; + remainder = dividend % divisor; + ppc_result_d = uint32_t(quotient); + if (ov) { + if (((quotient >> 31) + 1) & ~1) { + ppc_state.spr[SPR::XER] |= XER::SO | XER::OV; + } else { + ppc_state.spr[SPR::XER] &= ~XER::OV; + } + } } - if (ov) - power_setsoov(ppc_result_b, ppc_result_a, ppc_result_d); if (rec) - ppc_changecrf0(ppc_result_d); + ppc_changecrf0(remainder); ppc_store_iresult_reg(reg_d, ppc_result_d); + ppc_state.spr[SPR::MQ] = remainder; } template void dppc_interpreter::power_div();