iop: fix division handling based on the EE div operator

Division will now handle properly division by 0 and signed overflow
This commit is contained in:
Gregory Hainaut 2016-01-27 19:13:31 +01:00
parent 11f59ada16
commit b21ce8c9fb
1 changed files with 58 additions and 105 deletions

View File

@ -506,114 +506,71 @@ void rpsxDIV_const()
*/
// Of course x86 cpu does overflow !
if (g_psxConstRegs[_Rs_] == 0x80000000u && g_psxConstRegs[_Rt_] == 0xFFFFFFFFu) {
// FIXME depends if div/divu
xMOV(ptr32[&psxRegs.GPR.n.hi], 0);
xMOV(ptr32[&psxRegs.GPR.n.lo], 0x80000000);
return;
}
if (g_psxConstRegs[_Rt_] == 0) {
// FIXME
// hi must be rs
// lo must be 0xFFFF_FFFFF is rs >= 0, 0x1 otherwise if rs < 0 and sign
}
if (g_psxConstRegs[_Rt_] != 0) {
lo = *(int*)&g_psxConstRegs[_Rs_] / *(int*)&g_psxConstRegs[_Rt_];
hi = *(int*)&g_psxConstRegs[_Rs_] % *(int*)&g_psxConstRegs[_Rt_];
xMOV(ptr32[&psxRegs.GPR.n.hi], hi);
xMOV(ptr32[&psxRegs.GPR.n.lo], lo);
}
}
void rpsxDIVsuperconsts(int info, int sign)
{
u32 imm = g_psxConstRegs[_Rs_];
if (imm == 0x80000000u) {
// FIXME if RT is 0xFFFFFFFFu
// hi must be 0
// lo must be 0x80000000
// FIXME depends if div/divu
//
// Otherwise standard division
}
if( imm ) {
// Lo/Hi = Rs / Rt (signed)
xMOV(ecx, ptr[&psxRegs.GPR.r[_Rt_]]);
xCMP(ecx, 0);
j8Ptr[0] = JE8(0);
xMOV(eax, imm);
if( sign ) {
xCDQ();
xDIV(ecx);
}
else {
xXOR(edx, edx);
xUDIV(ecx);
}
xMOV(ptr[&psxRegs.GPR.n.lo], eax);
xMOV(ptr[&psxRegs.GPR.n.hi], edx);
x86SetJ8(j8Ptr[0]);
}
else {
xXOR(eax, eax);
xMOV(ptr[&psxRegs.GPR.n.hi], eax);
xMOV(ptr[&psxRegs.GPR.n.lo], eax);
// FIXME lo must be 0xFFFF_FFFFF if rt is 0
}
}
void rpsxDIVsuperconstt(int info, int sign)
{
u32 imm = g_psxConstRegs[_Rt_];
if (imm == 0xFFFFFFFFu) {
// FIXME if RS is 0x80000000
// hi must be 0
// lo must be 0x80000000
// FIXME depends if div/divu
//
// Otherwise standard division
}
if( imm ) {
xMOV(eax, ptr[&psxRegs.GPR.r[_Rs_]]);
xMOV(ecx, imm);
//xCDQ();
if( sign ) {
xCDQ();
xDIV(ecx);
}
else {
xXOR(edx, edx);
xUDIV(ecx);
}
xMOV(ptr[&psxRegs.GPR.n.lo], eax);
xMOV(ptr[&psxRegs.GPR.n.hi], edx);
} else {
// FIXME
// hi must be rs
// lo must be 0xFFFF_FFFFF is rs >= 0, 0x1 otherwise if rs < 0 and sign
xMOV(ptr32[&psxRegs.GPR.n.hi], g_psxConstRegs[_Rs_]);
if (g_psxConstRegs[_Rs_] & 0x80000000u) {
xMOV(ptr32[&psxRegs.GPR.n.lo], 0x1);
} else {
xMOV(ptr32[&psxRegs.GPR.n.lo], 0xFFFFFFFFu);
}
}
}
void rpsxDIVsuper(int info, int sign)
void rpsxDIVsuper(int info, int sign, int process = 0)
{
// Lo/Hi = Rs / Rt (signed)
xMOV(ecx, ptr[&psxRegs.GPR.r[_Rt_]]);
xCMP(ecx, 0);
j8Ptr[0] = JE8(0);
xMOV(eax, ptr[&psxRegs.GPR.r[_Rs_]]);
if( process & PROCESS_CONSTT )
xMOV(ecx, g_psxConstRegs[_Rt_]);
else
xMOV(ecx, ptr[&psxRegs.GPR.r[_Rt_]]);
if( process & PROCESS_CONSTS )
xMOV(eax, g_psxConstRegs[_Rs_]);
else
xMOV(eax, ptr[&psxRegs.GPR.r[_Rs_]]);
u8 *end1;
if (sign) //test for overflow (x86 will just throw an exception)
{
xCMP(eax, 0x80000000 );
u8 *cont1 = JNE8(0);
xCMP(ecx, 0xffffffff );
u8 *cont2 = JNE8(0);
//overflow case:
xXOR(edx, edx); //EAX remains 0x80000000
end1 = JMP8(0);
x86SetJ8(cont1);
x86SetJ8(cont2);
}
xCMP(ecx, 0 );
u8 *cont3 = JNE8(0);
//divide by zero
xMOV(edx, eax);
if (sign) //set EAX to (EAX < 0)?1:-1
{
xSAR(eax, 31 ); //(EAX < 0)?-1:0
xSHL(eax, 1 ); //(EAX < 0)?-2:0
xNOT(eax); //(EAX < 0)?1:-1
}
else
xMOV(eax, 0xffffffff );
u8 *end2 = JMP8(0);
// Normal division
x86SetJ8(cont3);
if( sign ) {
xCDQ();
xDIV(ecx);
@ -623,22 +580,15 @@ void rpsxDIVsuper(int info, int sign)
xUDIV(ecx);
}
if (sign) x86SetJ8( end1 );
x86SetJ8( end2 );
xMOV(ptr[&psxRegs.GPR.n.lo], eax);
xMOV(ptr[&psxRegs.GPR.n.hi], edx);
x86SetJ8(j8Ptr[0]);
// FIXME if RS is 0x80000000 and RT is 0xFFFF_FFFFF
// hi must be 0
// lo must be 0x80000000
// FIXME depends if div/divu
// FIXME
// hi must be rs
// lo must be 0xFFFF_FFFFF is rs >= 0, 0x1 otherwise
}
void rpsxDIV_consts(int info) { rpsxDIVsuperconsts(info, 1); }
void rpsxDIV_constt(int info) { rpsxDIVsuperconstt(info, 1); }
void rpsxDIV_consts(int info) { rpsxDIVsuper(info, 1, PROCESS_CONSTS); }
void rpsxDIV_constt(int info) { rpsxDIVsuper(info, 1, PROCESS_CONSTT); }
void rpsxDIV_(int info) { rpsxDIVsuper(info, 1); }
PSXRECOMPILE_CONSTCODE3_PENALTY(DIV, 1, psxInstCycles_Div);
@ -653,11 +603,14 @@ void rpsxDIVU_const()
hi = g_psxConstRegs[_Rs_] % g_psxConstRegs[_Rt_];
xMOV(ptr32[&psxRegs.GPR.n.hi], hi);
xMOV(ptr32[&psxRegs.GPR.n.lo], lo);
} else {
xMOV(ptr32[&psxRegs.GPR.n.hi], g_psxConstRegs[_Rs_]);
xMOV(ptr32[&psxRegs.GPR.n.lo], 0xFFFFFFFFu);
}
}
void rpsxDIVU_consts(int info) { rpsxDIVsuperconsts(info, 0); }
void rpsxDIVU_constt(int info) { rpsxDIVsuperconstt(info, 0); }
void rpsxDIVU_consts(int info) { rpsxDIVsuper(info, 0, PROCESS_CONSTS); }
void rpsxDIVU_constt(int info) { rpsxDIVsuper(info, 0, PROCESS_CONSTT); }
void rpsxDIVU_(int info) { rpsxDIVsuper(info, 0); }
PSXRECOMPILE_CONSTCODE3_PENALTY(DIVU, 1, psxInstCycles_Div);