mirror of https://github.com/PCSX2/pcsx2.git
270 lines
6.3 KiB
C++
270 lines
6.3 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 "iR5900.h"
|
|
#include "R5900OpcodeTables.h"
|
|
|
|
|
|
namespace R5900 {
|
|
namespace Dynarec {
|
|
|
|
// R5900 branch helper!
|
|
// Recompiles code for a branch test and/or skip, complete with delay slot
|
|
// handling. Note, for "likely" branches use iDoBranchImm_Likely instead, which
|
|
// handles delay slots differently.
|
|
// Parameters:
|
|
// jmpSkip - This parameter is the result of the appropriate J32 instruction
|
|
// (usually JZ32 or JNZ32).
|
|
void recDoBranchImm( u32* jmpSkip, bool isLikely )
|
|
{
|
|
// All R5900 branches use this format:
|
|
const u32 branchTo = ((s32)_Imm_ * 4) + pc;
|
|
|
|
// First up is the Branch Taken Path : Save the recompiler's state, compile the
|
|
// DelaySlot, and issue a BranchTest insertion. The state is reloaded below for
|
|
// the "did not branch" path (maintains consts, register allocations, and other optimizations).
|
|
|
|
SaveBranchState();
|
|
recompileNextInstruction(1);
|
|
SetBranchImm(branchTo);
|
|
|
|
// Jump target when the branch is *not* taken, skips the branchtest code
|
|
// insertion above.
|
|
x86SetJ32(jmpSkip);
|
|
|
|
// if it's a likely branch then we'll need to skip the delay slot here, since
|
|
// MIPS cancels the delay slot instruction when branches aren't taken.
|
|
LoadBranchState();
|
|
if( !isLikely )
|
|
{
|
|
pc -= 4; // instruction rewinder for delay slot, if non-likely.
|
|
recompileNextInstruction(1);
|
|
}
|
|
SetBranchImm(pc); // start a new recompiled block.
|
|
}
|
|
|
|
void recDoBranchImm_Likely( u32* jmpSkip )
|
|
{
|
|
recDoBranchImm( jmpSkip, true );
|
|
}
|
|
|
|
namespace OpcodeImpl {
|
|
|
|
////////////////////////////////////////////////////
|
|
//static void recCACHE() {
|
|
// MOV32ItoM( (uptr)&cpuRegs.code, cpuRegs.code );
|
|
// MOV32ItoM( (uptr)&cpuRegs.pc, pc );
|
|
// iFlushCall(FLUSH_EVERYTHING);
|
|
// CALLFunc( (uptr)CACHE );
|
|
// //branch = 2;
|
|
//
|
|
// CMP32ItoM((int)&cpuRegs.pc, pc);
|
|
// j8Ptr[0] = JE8(0);
|
|
// RET();
|
|
// x86SetJ8(j8Ptr[0]);
|
|
//}
|
|
|
|
|
|
void recPREF()
|
|
{
|
|
}
|
|
|
|
void recSYNC()
|
|
{
|
|
}
|
|
|
|
void recMFSA()
|
|
{
|
|
int mmreg;
|
|
if (!_Rd_) return;
|
|
|
|
mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rd_, MODE_WRITE);
|
|
if( mmreg >= 0 ) {
|
|
SSE_MOVLPS_M64_to_XMM(mmreg, (uptr)&cpuRegs.sa);
|
|
}
|
|
else if( (mmreg = _checkMMXreg(MMX_GPR+_Rd_, MODE_WRITE)) >= 0 ) {
|
|
MOVDMtoMMX(mmreg, (uptr)&cpuRegs.sa);
|
|
SetMMXstate();
|
|
}
|
|
else {
|
|
MOV32MtoR(EAX, (uptr)&cpuRegs.sa);
|
|
_deleteEEreg(_Rd_, 0);
|
|
MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rd_].UL[0], EAX);
|
|
MOV32ItoM((uptr)&cpuRegs.GPR.r[_Rd_].UL[1], 0);
|
|
}
|
|
}
|
|
|
|
// SA is 4-bit and contains the amount of bytes to shift
|
|
void recMTSA()
|
|
{
|
|
if( GPR_IS_CONST1(_Rs_) ) {
|
|
MOV32ItoM((uptr)&cpuRegs.sa, g_cpuConstRegs[_Rs_].UL[0] & 0xf );
|
|
}
|
|
else {
|
|
int mmreg;
|
|
|
|
if( (mmreg = _checkXMMreg(XMMTYPE_GPRREG, _Rs_, MODE_READ)) >= 0 ) {
|
|
SSE_MOVSS_XMM_to_M32((uptr)&cpuRegs.sa, mmreg);
|
|
}
|
|
else if( (mmreg = _checkMMXreg(MMX_GPR+_Rs_, MODE_READ)) >= 0 ) {
|
|
MOVDMMXtoM((uptr)&cpuRegs.sa, mmreg);
|
|
SetMMXstate();
|
|
}
|
|
else {
|
|
MOV32MtoR(EAX, (uptr)&cpuRegs.GPR.r[_Rs_].UL[0]);
|
|
MOV32RtoM((uptr)&cpuRegs.sa, EAX);
|
|
}
|
|
AND32ItoM((uptr)&cpuRegs.sa, 0xf);
|
|
}
|
|
}
|
|
|
|
void recMTSAB()
|
|
{
|
|
if( GPR_IS_CONST1(_Rs_) ) {
|
|
MOV32ItoM((uptr)&cpuRegs.sa, ((g_cpuConstRegs[_Rs_].UL[0] & 0xF) ^ (_Imm_ & 0xF)) );
|
|
}
|
|
else {
|
|
_eeMoveGPRtoR(EAX, _Rs_);
|
|
AND32ItoR(EAX, 0xF);
|
|
XOR32ItoR(EAX, _Imm_&0xf);
|
|
MOV32RtoM((uptr)&cpuRegs.sa, EAX);
|
|
}
|
|
}
|
|
|
|
void recMTSAH()
|
|
{
|
|
if( GPR_IS_CONST1(_Rs_) ) {
|
|
MOV32ItoM((uptr)&cpuRegs.sa, ((g_cpuConstRegs[_Rs_].UL[0] & 0x7) ^ (_Imm_ & 0x7)) << 1);
|
|
}
|
|
else {
|
|
_eeMoveGPRtoR(EAX, _Rs_);
|
|
AND32ItoR(EAX, 0x7);
|
|
XOR32ItoR(EAX, _Imm_&0x7);
|
|
SHL32ItoR(EAX, 1);
|
|
MOV32RtoM((uptr)&cpuRegs.sa, EAX);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
void recNULL()
|
|
{
|
|
Console.Error("EE: Unimplemented op %x", cpuRegs.code);
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
void recUnknown()
|
|
{
|
|
// TODO : Unknown ops should throw an exception.
|
|
Console.Error("EE: Unrecognized op %x", cpuRegs.code);
|
|
}
|
|
|
|
void recMMI_Unknown()
|
|
{
|
|
// TODO : Unknown ops should throw an exception.
|
|
Console.Error("EE: Unrecognized MMI op %x", cpuRegs.code);
|
|
}
|
|
|
|
void recCOP0_Unknown()
|
|
{
|
|
// TODO : Unknown ops should throw an exception.
|
|
Console.Error("EE: Unrecognized COP0 op %x", cpuRegs.code);
|
|
}
|
|
|
|
void recCOP1_Unknown()
|
|
{
|
|
// TODO : Unknown ops should throw an exception.
|
|
Console.Error("EE: Unrecognized FPU/COP1 op %x", cpuRegs.code);
|
|
}
|
|
|
|
/**********************************************************
|
|
* UNHANDLED YET OPCODES
|
|
*
|
|
**********************************************************/
|
|
|
|
// Suikoden 3 uses it a lot
|
|
void recCACHE() //Interpreter only!
|
|
{
|
|
//MOV32ItoM( (uptr)&cpuRegs.code, (u32)cpuRegs.code );
|
|
//MOV32ItoM( (uptr)&cpuRegs.pc, (u32)pc );
|
|
//iFlushCall(FLUSH_EVERYTHING);
|
|
//CALLFunc( (uptr)R5900::Interpreter::OpcodeImpl::CACHE );
|
|
//branch = 2;
|
|
}
|
|
|
|
void recTGE()
|
|
{
|
|
recBranchCall( R5900::Interpreter::OpcodeImpl::TGE );
|
|
}
|
|
|
|
void recTGEU()
|
|
{
|
|
recBranchCall( R5900::Interpreter::OpcodeImpl::TGEU );
|
|
}
|
|
|
|
void recTLT()
|
|
{
|
|
recBranchCall( R5900::Interpreter::OpcodeImpl::TLT );
|
|
}
|
|
|
|
void recTLTU()
|
|
{
|
|
recBranchCall( R5900::Interpreter::OpcodeImpl::TLTU );
|
|
}
|
|
|
|
void recTEQ()
|
|
{
|
|
recBranchCall( R5900::Interpreter::OpcodeImpl::TEQ );
|
|
}
|
|
|
|
void recTNE()
|
|
{
|
|
recBranchCall( R5900::Interpreter::OpcodeImpl::TNE );
|
|
}
|
|
|
|
void recTGEI()
|
|
{
|
|
recBranchCall( R5900::Interpreter::OpcodeImpl::TGEI );
|
|
}
|
|
|
|
void recTGEIU()
|
|
{
|
|
recBranchCall( R5900::Interpreter::OpcodeImpl::TGEIU );
|
|
}
|
|
|
|
void recTLTI()
|
|
{
|
|
recBranchCall( R5900::Interpreter::OpcodeImpl::TLTI );
|
|
}
|
|
|
|
void recTLTIU()
|
|
{
|
|
recBranchCall( R5900::Interpreter::OpcodeImpl::TLTIU );
|
|
}
|
|
|
|
void recTEQI()
|
|
{
|
|
recBranchCall( R5900::Interpreter::OpcodeImpl::TEQI );
|
|
}
|
|
|
|
void recTNEI()
|
|
{
|
|
recBranchCall( R5900::Interpreter::OpcodeImpl::TNEI );
|
|
}
|
|
|
|
} }} // end Namespace R5900::Dynarec::OpcodeImpl
|