pcsx2/pcsx2/x86/ix86-32/iR5900Arit.cpp

676 lines
15 KiB
C++

/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "Common.h"
#include "R5900OpcodeTables.h"
#include "iR5900.h"
using namespace x86Emitter;
namespace R5900 {
namespace Dynarec {
namespace OpcodeImpl
{
/*********************************************************
* Register arithmetic *
* Format: OP rd, rs, rt *
*********************************************************/
// TODO: overflow checks
#ifndef ARITHMETIC_RECOMPILE
namespace Interp = R5900::Interpreter::OpcodeImpl;
REC_FUNC_DEL(ADD, _Rd_);
REC_FUNC_DEL(ADDU, _Rd_);
REC_FUNC_DEL(DADD, _Rd_);
REC_FUNC_DEL(DADDU, _Rd_);
REC_FUNC_DEL(SUB, _Rd_);
REC_FUNC_DEL(SUBU, _Rd_);
REC_FUNC_DEL(DSUB, _Rd_);
REC_FUNC_DEL(DSUBU, _Rd_);
REC_FUNC_DEL(AND, _Rd_);
REC_FUNC_DEL(OR, _Rd_);
REC_FUNC_DEL(XOR, _Rd_);
REC_FUNC_DEL(NOR, _Rd_);
REC_FUNC_DEL(SLT, _Rd_);
REC_FUNC_DEL(SLTU, _Rd_);
#else
//// ADD
void recADD_const()
{
g_cpuConstRegs[_Rd_].SD[0] = g_cpuConstRegs[_Rs_].SL[0] + g_cpuConstRegs[_Rt_].SL[0];
}
void recADD_constv(int info, int creg, u32 vreg)
{
pxAssert( !(info&PROCESS_EE_XMM) );
s32 cval = g_cpuConstRegs[creg].SL[0];
xMOV(eax, ptr32[&cpuRegs.GPR.r[vreg].SL[0]]);
if (cval)
xADD(eax, cval);
eeSignExtendTo(_Rd_, _Rd_ == vreg && !cval);
}
// s is constant
void recADD_consts(int info)
{
recADD_constv(info, _Rs_, _Rt_);
}
// t is constant
void recADD_constt(int info)
{
recADD_constv(info, _Rt_, _Rs_);
}
// nothing is constant
void recADD_(int info)
{
pxAssert( !(info&PROCESS_EE_XMM) );
xMOV(eax, ptr32[&cpuRegs.GPR.r[_Rs_].SL[0]]);
if (_Rs_ == _Rt_)
xADD(eax, eax);
else
xADD(eax, ptr32[&cpuRegs.GPR.r[_Rt_].SL[0]]);
eeSignExtendTo(_Rd_);
}
EERECOMPILE_CODE0(ADD, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT);
//// ADDU
void recADDU(void)
{
recADD();
}
//// DADD
void recDADD_const(void)
{
g_cpuConstRegs[_Rd_].SD[0] = g_cpuConstRegs[_Rs_].SD[0] + g_cpuConstRegs[_Rt_].SD[0];
}
void recDADD_constv(int info, int creg, u32 vreg)
{
pxAssert( !(info&PROCESS_EE_XMM) );
GPR_reg64 cval = g_cpuConstRegs[creg];
if (_Rd_ == vreg) {
if (!cval.SD[0])
return; // no-op
xADD(ptr32[&cpuRegs.GPR.r[_Rd_].SL[0]], cval.SL[0]);
xADC(ptr32[&cpuRegs.GPR.r[_Rd_].SL[1]], cval.SL[1]);
} else {
xMOV(eax, ptr32[&cpuRegs.GPR.r[vreg].SL[0]]);
xMOV(edx, ptr32[&cpuRegs.GPR.r[vreg].SL[1]]);
if (cval.SD[0]) {
xADD(eax, cval.SL[0]);
xADC(edx, cval.SL[1]);
}
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].SL[0]], eax);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].SL[1]], edx);
}
}
void recDADD_consts(int info)
{
recDADD_constv(info, _Rs_, _Rt_);
}
void recDADD_constt(int info)
{
recDADD_constv(info, _Rt_, _Rs_);
}
void recDADD_(int info)
{
pxAssert( !(info&PROCESS_EE_XMM) );
u32 rs = _Rs_, rt = _Rt_;
if (_Rd_ == _Rt_)
rs = _Rt_, rt = _Rs_;
xMOV(eax, ptr32[&cpuRegs.GPR.r[rt].SL[0]]);
if (_Rd_ == _Rs_ && _Rs_ == _Rt_) {
xSHLD(ptr32[&cpuRegs.GPR.r[_Rd_].SL[1]], eax, 1);
xSHL(ptr32[&cpuRegs.GPR.r[_Rd_].SL[0]], 1);
return;
}
xMOV(edx, ptr32[&cpuRegs.GPR.r[rt].SL[1]]);
if (_Rd_ == rs) {
xADD(ptr32[&cpuRegs.GPR.r[_Rd_].SL[0]], eax);
xADC(ptr32[&cpuRegs.GPR.r[_Rd_].SL[1]], edx);
return;
} else if (rs == rt) {
xADD(eax, eax);
xADC(edx, edx);
} else {
xADD(eax, ptr32[&cpuRegs.GPR.r[rs].SL[0]]);
xADC(edx, ptr32[&cpuRegs.GPR.r[rs].SL[1]]);
}
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].SL[0]], eax);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].SL[1]], edx);
}
EERECOMPILE_CODE0(DADD, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT);
//// DADDU
void recDADDU(void)
{
recDADD();
}
//// SUB
void recSUB_const()
{
g_cpuConstRegs[_Rd_].SD[0] = g_cpuConstRegs[_Rs_].SL[0] - g_cpuConstRegs[_Rt_].SL[0];
}
void recSUB_consts(int info)
{
pxAssert( !(info&PROCESS_EE_XMM) );
s32 sval = g_cpuConstRegs[_Rs_].SL[0];
xMOV(eax, sval);
xSUB(eax, ptr32[&cpuRegs.GPR.r[_Rt_].SL[0]]);
eeSignExtendTo(_Rd_);
}
void recSUB_constt(int info)
{
pxAssert( !(info&PROCESS_EE_XMM) );
s32 tval = g_cpuConstRegs[_Rt_].SL[0];
xMOV(eax, ptr32[&cpuRegs.GPR.r[_Rs_].SL[0]]);
if (tval)
xSUB(eax, tval);
eeSignExtendTo(_Rd_, _Rd_ == _Rs_ && !tval);
}
void recSUB_(int info)
{
pxAssert( !(info&PROCESS_EE_XMM) );
if (_Rs_ == _Rt_) {
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].SL[0]], 0);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].SL[1]], 0);
return;
}
xMOV(eax, ptr32[&cpuRegs.GPR.r[_Rs_].SL[0]]);
xSUB(eax, ptr32[&cpuRegs.GPR.r[_Rt_].SL[0]]);
eeSignExtendTo(_Rd_);
}
EERECOMPILE_CODE0(SUB, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED);
//// SUBU
void recSUBU(void)
{
recSUB();
}
//// DSUB
void recDSUB_const()
{
g_cpuConstRegs[_Rd_].SD[0] = g_cpuConstRegs[_Rs_].SD[0] - g_cpuConstRegs[_Rt_].SD[0];
}
void recDSUB_consts(int info)
{
pxAssert( !(info&PROCESS_EE_XMM) );
GPR_reg64 sval = g_cpuConstRegs[_Rs_];
if (!sval.SD[0] && _Rd_ == _Rt_) {
/* To understand this 64-bit negate, consider that a negate in 2's complement
* is a NOT then an ADD 1. The upper word should only have the NOT stage unless
* the ADD overflows. The ADD only overflows if the lower word is 0.
* Incrementing before a NEG is the same as a NOT and the carry flag is set for
* a non-zero lower word.
*/
xNEG(ptr32[&cpuRegs.GPR.r[_Rd_].SL[0]]);
xADC(ptr32[&cpuRegs.GPR.r[_Rd_].SL[1]], 0);
xNEG(ptr32[&cpuRegs.GPR.r[_Rd_].SL[1]]);
return;
} else {
xMOV(eax, sval.SL[0]);
xMOV(edx, sval.SL[1]);
}
xSUB(eax, ptr32[&cpuRegs.GPR.r[_Rt_].SL[0]]);
xSBB(edx, ptr32[&cpuRegs.GPR.r[_Rt_].SL[1]]);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].SL[0]], eax);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].SL[1]], edx);
}
void recDSUB_constt(int info)
{
pxAssert( !(info&PROCESS_EE_XMM) );
GPR_reg64 tval = g_cpuConstRegs[_Rt_];
if (_Rd_ == _Rs_) {
xSUB(ptr32[&cpuRegs.GPR.r[_Rd_].SL[0]], tval.SL[0]);
xSBB(ptr32[&cpuRegs.GPR.r[_Rd_].SL[1]], tval.SL[1]);
} else {
xMOV(eax, ptr32[&cpuRegs.GPR.r[_Rs_].SL[0]]);
xMOV(edx, ptr32[&cpuRegs.GPR.r[_Rs_].SL[1]]);
if (tval.SD[0]) {
xSUB(eax, tval.SL[0]);
xSBB(edx, tval.SL[1]);
}
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].SL[0]], eax);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].SL[1]], edx);
}
}
void recDSUB_(int info)
{
pxAssert( !(info&PROCESS_EE_XMM) );
if (_Rs_ == _Rt_) {
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].SL[0]], 0);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].SL[1]], 0);
} else if (_Rd_ == _Rs_) {
xMOV(eax, ptr32[&cpuRegs.GPR.r[_Rt_].SL[0]]);
xMOV(edx, ptr32[&cpuRegs.GPR.r[_Rt_].SL[1]]);
xSUB(ptr32[&cpuRegs.GPR.r[_Rd_].SL[0]], eax);
xSBB(ptr32[&cpuRegs.GPR.r[_Rd_].SL[1]], edx);
} else {
xMOV(eax, ptr32[&cpuRegs.GPR.r[_Rs_].SL[0]]);
xMOV(edx, ptr32[&cpuRegs.GPR.r[_Rs_].SL[1]]);
xSUB(eax, ptr32[&cpuRegs.GPR.r[_Rt_].SL[0]]);
xSBB(edx, ptr32[&cpuRegs.GPR.r[_Rt_].SL[1]]);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].SL[0]], eax);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].SL[1]], edx);
}
}
EERECOMPILE_CODE0(DSUB, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED);
//// DSUBU
void recDSUBU(void)
{
recDSUB();
}
//// AND
void recAND_const()
{
g_cpuConstRegs[_Rd_].UD[0] = g_cpuConstRegs[_Rs_].UD[0] & g_cpuConstRegs[_Rt_].UD[0];
}
void recAND_constv(int info, int creg, u32 vreg)
{
pxAssert( !(info & PROCESS_EE_XMM) );
GPR_reg64 cval = g_cpuConstRegs[creg];
for (int i = 0; i < 2; i++) {
if (!cval.UL[i]) {
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], 0);
} else if (_Rd_ == vreg) {
if (cval.SL[i] != -1)
xAND(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], cval.UL[i]);
} else {
xMOV(eax, ptr32[&cpuRegs.GPR.r[vreg].UL[i]]);
if (cval.SL[i] != -1)
xAND(eax, cval.UL[i]);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], eax);
}
}
}
void recAND_consts(int info)
{
recAND_constv(info, _Rs_, _Rt_);
}
void recAND_constt(int info)
{
recAND_constv(info, _Rt_, _Rs_);
}
void recAND_(int info)
{
pxAssert( !(info & PROCESS_EE_XMM) );
u32 rs = _Rs_, rt = _Rt_;
if (_Rd_ == _Rt_)
rs = _Rt_, rt = _Rs_;
for (int i = 0; i < 2; i++) {
if (_Rd_ == rs) {
if (rs == rt)
continue;
xMOV(eax, ptr32[&cpuRegs.GPR.r[rt].UL[i]]);
xAND(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], eax);
} else {
xMOV(eax, ptr32[&cpuRegs.GPR.r[rs].UL[i]]);
if (rs != rt)
xAND(eax, ptr32[&cpuRegs.GPR.r[rt].UL[i]]);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], eax);
}
}
}
EERECOMPILE_CODE0(AND, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED);
//// OR
void recOR_const()
{
g_cpuConstRegs[_Rd_].UD[0] = g_cpuConstRegs[_Rs_].UD[0] | g_cpuConstRegs[_Rt_].UD[0];
}
void recOR_constv(int info, int creg, u32 vreg)
{
pxAssert( !(info & PROCESS_EE_XMM) );
GPR_reg64 cval = g_cpuConstRegs[creg];
for (int i = 0; i < 2; i++) {
if (cval.SL[i] == -1) {
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], -1);
} else if (_Rd_ == vreg) {
if (cval.UL[i])
xOR(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], cval.UL[i]);
} else {
xMOV(eax, ptr32[&cpuRegs.GPR.r[vreg].UL[i]]);
if (cval.UL[i])
xOR(eax, cval.UL[i]);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], eax);
}
}
}
void recOR_consts(int info)
{
recOR_constv(info, _Rs_, _Rt_);
}
void recOR_constt(int info)
{
recOR_constv(info, _Rt_, _Rs_);
}
void recOR_(int info)
{
pxAssert( !(info & PROCESS_EE_XMM) );
u32 rs = _Rs_, rt = _Rt_;
if (_Rd_ == _Rt_)
rs = _Rt_, rt = _Rs_;
for (int i = 0; i < 2; i++) {
if (_Rd_ == rs) {
if (rs == rt)
continue;
xMOV(eax, ptr32[&cpuRegs.GPR.r[rt].UL[i]]);
xOR(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], eax);
} else {
xMOV(eax, ptr32[&cpuRegs.GPR.r[rs].UL[i]]);
if (rs != rt)
xOR(eax, ptr32[&cpuRegs.GPR.r[rt].UL[i]]);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], eax);
}
}
}
EERECOMPILE_CODE0(OR, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED);
//// XOR
void recXOR_const()
{
g_cpuConstRegs[_Rd_].UD[0] = g_cpuConstRegs[_Rs_].UD[0] ^ g_cpuConstRegs[_Rt_].UD[0];
}
void recXOR_constv(int info, int creg, u32 vreg)
{
pxAssert( !(info & PROCESS_EE_XMM) );
GPR_reg64 cval = g_cpuConstRegs[creg];
for (int i = 0; i < 2; i++) {
if (_Rd_ == vreg) {
if (cval.UL[i])
xXOR(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], cval.UL[i]);
} else {
xMOV(eax, ptr32[&cpuRegs.GPR.r[vreg].UL[i]]);
if (cval.UL[i])
xXOR(eax, cval.UL[i]);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], eax);
}
}
}
void recXOR_consts(int info)
{
recXOR_constv(info, _Rs_, _Rt_);
}
void recXOR_constt(int info)
{
recXOR_constv(info, _Rt_, _Rs_);
}
void recXOR_(int info)
{
pxAssert( !(info & PROCESS_EE_XMM) );
u32 rs = _Rs_, rt = _Rt_;
if (_Rd_ == _Rt_)
rs = _Rt_, rt = _Rs_;
for (int i = 0; i < 2; i++) {
if (rs == rt) {
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], 0);
} else if (_Rd_ == rs) {
xMOV(eax, ptr32[&cpuRegs.GPR.r[rt].UL[i]]);
xXOR(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], eax);
} else {
xMOV(eax, ptr32[&cpuRegs.GPR.r[rs].UL[i]]);
xXOR(eax, ptr32[&cpuRegs.GPR.r[rt].UL[i]]);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], eax);
}
}
}
EERECOMPILE_CODE0(XOR, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED);
//// NOR
void recNOR_const()
{
g_cpuConstRegs[_Rd_].UD[0] =~(g_cpuConstRegs[_Rs_].UD[0] | g_cpuConstRegs[_Rt_].UD[0]);
}
void recNOR_constv(int info, int creg, u32 vreg)
{
pxAssert( !(info & PROCESS_EE_XMM) );
GPR_reg64 cval = g_cpuConstRegs[creg];
for (int i = 0; i < 2; i++) {
if (_Rd_ == vreg) {
if (cval.UL[i])
xOR(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], cval.UL[i]);
xNOT(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]]);
} else {
xMOV(eax, ptr32[&cpuRegs.GPR.r[vreg].UL[i]]);
if (cval.UL[i])
xOR(eax, cval.UL[i]);
xNOT(eax);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], eax);
}
}
}
void recNOR_consts(int info)
{
recNOR_constv(info, _Rs_, _Rt_);
}
void recNOR_constt(int info)
{
recNOR_constv(info, _Rt_, _Rs_);
}
void recNOR_(int info)
{
pxAssert( !(info & PROCESS_EE_XMM) );
u32 rs = _Rs_, rt = _Rt_;
if (_Rd_ == _Rt_)
rs = _Rt_, rt = _Rs_;
for (int i = 0; i < 2; i++) {
if (_Rd_ == rs) {
if (rs == rt) {
xNOT(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]]);
continue;
}
xMOV(eax, ptr32[&cpuRegs.GPR.r[rt].UL[i]]);
xOR(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], eax);
xNOT(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]]);
} else {
xMOV(eax, ptr32[&cpuRegs.GPR.r[rs].UL[i]]);
if (rs != rt)
xOR(eax, ptr32[&cpuRegs.GPR.r[rt].UL[i]]);
xNOT(eax);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].UL[i]], eax);
}
}
}
EERECOMPILE_CODE0(NOR, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED);
//// SLT - test with silent hill, lemans
void recSLT_const()
{
g_cpuConstRegs[_Rd_].UD[0] = g_cpuConstRegs[_Rs_].SD[0] < g_cpuConstRegs[_Rt_].SD[0];
}
void recSLTs_const(int info, int sign, int st)
{
pxAssert( !(info & PROCESS_EE_XMM) );
GPR_reg64 cval = g_cpuConstRegs[st ? _Rt_ : _Rs_];
xMOV(eax, 1);
xCMP(ptr32[&cpuRegs.GPR.r[st ? _Rs_ : _Rt_].UL[1]], cval.UL[1]);
xForwardJump8 pass1(st ? (sign ? Jcc_Less : Jcc_Below) : (sign ? Jcc_Greater : Jcc_Above));
xForwardJump8 fail(st ? (sign ? Jcc_Greater : Jcc_Above) : (sign ? Jcc_Less : Jcc_Below));
{
xCMP(ptr32[&cpuRegs.GPR.r[st ? _Rs_ : _Rt_].UL[0]], cval.UL[0]);
xForwardJump8 pass2(st ? Jcc_Below : Jcc_Above);
fail.SetTarget();
xMOV(eax, 0);
pass2.SetTarget();
}
pass1.SetTarget();
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].UL[0]], eax);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].UL[1]], 0);
}
void recSLTs_(int info, int sign)
{
pxAssert( !(info & PROCESS_EE_XMM) );
xMOV(eax, 1);
xMOV(edx, ptr32[&cpuRegs.GPR.r[_Rs_].UL[1]]);
xCMP(edx, ptr32[&cpuRegs.GPR.r[_Rt_].UL[1]]);
xForwardJump8 pass1(sign ? Jcc_Less : Jcc_Below);
xForwardJump8 fail(sign ? Jcc_Greater : Jcc_Above);
{
xMOV(edx, ptr32[&cpuRegs.GPR.r[_Rs_].UL[0]]);
xCMP(edx, ptr32[&cpuRegs.GPR.r[_Rt_].UL[0]]);
xForwardJump8 pass2(Jcc_Below);
fail.SetTarget();
xMOV(eax, 0);
pass2.SetTarget();
}
pass1.SetTarget();
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].UL[0]], eax);
xMOV(ptr32[&cpuRegs.GPR.r[_Rd_].UL[1]], 0);
}
void recSLT_consts(int info)
{
recSLTs_const(info, 1, 0);
}
void recSLT_constt(int info)
{
recSLTs_const(info, 1, 1);
}
void recSLT_(int info)
{
recSLTs_(info, 1);
}
EERECOMPILE_CODE0(SLT, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED);
// SLTU - test with silent hill, lemans
void recSLTU_const()
{
g_cpuConstRegs[_Rd_].UD[0] = g_cpuConstRegs[_Rs_].UD[0] < g_cpuConstRegs[_Rt_].UD[0];
}
void recSLTU_consts(int info)
{
recSLTs_const(info, 0, 0);
}
void recSLTU_constt(int info)
{
recSLTs_const(info, 0, 1);
}
void recSLTU_(int info)
{
recSLTs_(info, 0);
}
EERECOMPILE_CODE0(SLTU, XMMINFO_READS|XMMINFO_READT|XMMINFO_WRITED);
#endif
} } }