#include <cmath>
#include "Common/Data/Convert/SmallDataConvert.h"
#include "Common/Math/math_util.h"
#include "Common/BitSet.h"
#include "Common/BitScan.h"
#include "Common/CommonTypes.h"
#include "Core/Config.h"
#include "Core/Core.h"
#include "Core/MemMap.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/MIPS/MIPSInt.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/Reporting.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/HLETables.h"
#include "Core/HLE/ReplaceTables.h"
#define R(i) (currentMIPS->r[i])
#define F(i) (currentMIPS->f[i])
#define FI(i) (currentMIPS->fi[i])
#define FsI(i) (currentMIPS->fs[i])
#define PC (currentMIPS->pc)
#define _SIMM16_SHL2 ((u32)(s32)(s16)(op & 0xFFFF) << 2)
#define _RS ((op>>21) & 0x1F)
#define _RT ((op>>16) & 0x1F)
#define _RD ((op>>11) & 0x1F)
#define _FS ((op>>11) & 0x1F)
#define _FT ((op>>16) & 0x1F)
#define _FD ((op>>6 ) & 0x1F)
#define _POS ((op>>6 ) & 0x1F)
#define _SIZE ((op>>11) & 0x1F)
#define HI currentMIPS->hi
#define LO currentMIPS->lo
static inline void DelayBranchTo(u32 where)
{
if (!Memory::IsValidAddress(where) || (where & 3) != 0) {
Core_ExecException(where, PC, ExecExceptionType::JUMP);
}
PC += 4;
mipsr4k.nextPC = where;
mipsr4k.inDelaySlot = true;
}
static inline void SkipLikely() {
MIPSInfo delaySlot = MIPSGetInfo(Memory::Read_Instruction(PC + 4, true));
if (delaySlot & IS_JUMP) {
PC += 4;
} else {
PC += 8;
--mipsr4k.downcount;
}
}
int MIPS_SingleStep()
{
MIPSOpcode op = Memory::Read_Opcode_JIT(mipsr4k.pc);
if (mipsr4k.inDelaySlot) {
MIPSInterpret(op);
if (mipsr4k.inDelaySlot) {
mipsr4k.pc = mipsr4k.nextPC;
mipsr4k.inDelaySlot = false;
}
} else {
MIPSInterpret(op);
}
return 1;
}
namespace MIPSInt
{
void Int_Cache(MIPSOpcode op)
{
int imm = SignExtend16ToS32(op);
int rs = _RS;
uint32_t addr = R(rs) + imm;
int func = (op >> 16) & 0x1F;
static bool loggedAlignment = false;
switch (func) {
case 8:
if (MIPSComp::jit) {
uint32_t alignedAddr = addr & ~0x3F;
int size = 0x40 + (addr & 0x3F);
MIPSComp::jit->InvalidateCacheAt(alignedAddr, size);
if (!loggedAlignment && (addr & 0x3F) != 0) {
WARN_LOG(Log::JIT, "Unaligned icache invalidation of %08x (%08x + %d) at PC=%08x", addr, R(rs), imm, PC);
loggedAlignment = true;
}
if (alignedAddr <= PC + 4 && alignedAddr + size >= PC - 4) {
WARN_LOG_REPORT_ONCE(icacheInvalidatePC, Log::JIT, "Invalidating address near PC: %08x (%08x + %d) at PC=%08x", addr, R(rs), imm, PC);
}
}
break;
case 24:
break;
case 25:
break;
case 27:
break;
case 30:
break;
default:
DEBUG_LOG(Log::CPU, "cache instruction affecting %08x : function %i", addr, func);
}
PC += 4;
}
void Int_Syscall(MIPSOpcode op)
{
if (mipsr4k.inDelaySlot)
{
mipsr4k.pc = mipsr4k.nextPC;
}
else
{
mipsr4k.pc += 4;
}
mipsr4k.inDelaySlot = false;
CallSyscall(op);
}
void Int_Sync(MIPSOpcode op)
{
PC += 4;
}
void Int_Break(MIPSOpcode op)
{
Reporting::ReportMessage("BREAK instruction hit");
Core_BreakException(PC);
PC += 4;
}
void Int_RelBranch(MIPSOpcode op)
{
int imm = _SIMM16_SHL2;
int rs = _RS;
int rt = _RT;
u32 addr = PC + imm + 4;
switch (op >> 26)
{
case 4: if (R(rt) == R(rs)) DelayBranchTo(addr); else PC += 4; break;
case 5: if (R(rt) != R(rs)) DelayBranchTo(addr); else PC += 4; break;
case 6: if ((s32)R(rs) <= 0) DelayBranchTo(addr); else PC += 4; break;
case 7: if ((s32)R(rs) > 0) DelayBranchTo(addr); else PC += 4; break;
case 20: if (R(rt) == R(rs)) DelayBranchTo(addr); else SkipLikely(); break;
case 21: if (R(rt) != R(rs)) DelayBranchTo(addr); else SkipLikely(); break;
case 22: if ((s32)R(rs) <= 0) DelayBranchTo(addr); else SkipLikely(); break;
case 23: if ((s32)R(rs) > 0) DelayBranchTo(addr); else SkipLikely(); break;
default:
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
break;
}
}
void Int_RelBranchRI(MIPSOpcode op)
{
int imm = _SIMM16_SHL2;
int rs = _RS;
u32 addr = PC + imm + 4;
switch ((op>>16) & 0x1F)
{
case 0: if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;
case 1: if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;
case 2: if ((s32)R(rs) < 0) DelayBranchTo(addr); else SkipLikely(); break;
case 3: if ((s32)R(rs) >= 0) DelayBranchTo(addr); else SkipLikely(); break;
case 16: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;
case 17: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;
case 18: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else SkipLikely(); break;
case 19: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else SkipLikely(); break;
default:
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
break;
}
}
void Int_VBranch(MIPSOpcode op)
{
int imm = _SIMM16_SHL2;
u32 addr = PC + imm + 4;
int imm3 = (op>>18)&7;
int val = (currentMIPS->vfpuCtrl[VFPU_CTRL_CC] >> imm3) & 1;
switch ((op >> 16) & 3)
{
case 0: if (!val) DelayBranchTo(addr); else PC += 4; break;
case 1: if ( val) DelayBranchTo(addr); else PC += 4; break;
case 2: if (!val) DelayBranchTo(addr); else SkipLikely(); break;
case 3: if ( val) DelayBranchTo(addr); else SkipLikely(); break;
}
}
void Int_FPUBranch(MIPSOpcode op)
{
int imm = _SIMM16_SHL2;
u32 addr = PC + imm + 4;
switch((op>>16)&0x1f)
{
case 0: if (!currentMIPS->fpcond) DelayBranchTo(addr); else PC += 4; break;
case 1: if ( currentMIPS->fpcond) DelayBranchTo(addr); else PC += 4; break;
case 2: if (!currentMIPS->fpcond) DelayBranchTo(addr); else SkipLikely(); break;
case 3: if ( currentMIPS->fpcond) DelayBranchTo(addr); else SkipLikely(); break;
default:
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
break;
}
}
void Int_JumpType(MIPSOpcode op)
{
if (mipsr4k.inDelaySlot)
ERROR_LOG(Log::CPU, "Jump in delay slot :(");
u32 off = ((op & 0x03FFFFFF) << 2);
u32 addr = (currentMIPS->pc & 0xF0000000) | off;
switch (op>>26)
{
case 2:
if (!mipsr4k.inDelaySlot)
DelayBranchTo(addr);
break;
case 3:
R(MIPS_REG_RA) = PC + 8;
if (!mipsr4k.inDelaySlot)
DelayBranchTo(addr);
break;
default:
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
break;
}
}
void Int_JumpRegType(MIPSOpcode op)
{
if (mipsr4k.inDelaySlot)
{
ERROR_LOG(Log::CPU, "Jump in delay slot :(");
}
int rs = _RS;
int rd = _RD;
u32 addr = R(rs);
switch (op & 0x3f)
{
case 8:
if (!mipsr4k.inDelaySlot)
DelayBranchTo(addr);
break;
case 9:
if (rd != 0)
R(rd) = PC + 8;
if (!mipsr4k.inDelaySlot)
DelayBranchTo(addr);
break;
}
}
void Int_IType(MIPSOpcode op)
{
u32 uimm = op & 0xFFFF;
u32 suimm = SignExtend16ToU32(op);
s32 simm = SignExtend16ToS32(op);
int rt = _RT;
int rs = _RS;
if (rt == 0) {
PC += 4;
return;
}
switch (op>>26)
{
case 8: R(rt) = R(rs) + simm; break;
case 9: R(rt) = R(rs) + simm; break;
case 10: R(rt) = (s32)R(rs) < simm; break;
case 11: R(rt) = R(rs) < suimm; break;
case 12: R(rt) = R(rs) & uimm; break;
case 13: R(rt) = R(rs) | uimm; break;
case 14: R(rt) = R(rs) ^ uimm; break;
case 15: R(rt) = uimm << 16; break;
default:
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
break;
}
PC += 4;
}
void Int_StoreSync(MIPSOpcode op)
{
int imm = (signed short)(op&0xFFFF);
int rt = _RT;
int rs = _RS;
u32 addr = R(rs) + imm;
switch (op >> 26)
{
case 48:
if (rt != 0) {
R(rt) = Memory::Read_U32(addr);
}
currentMIPS->llBit = 1;
break;
case 56:
if (currentMIPS->llBit) {
Memory::Write_U32(R(rt), addr);
if (rt != 0) {
R(rt) = 1;
}
} else if (rt != 0) {
R(rt) = 0;
}
break;
default:
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
break;
}
PC += 4;
}
void Int_RType3(MIPSOpcode op)
{
int rt = _RT;
int rs = _RS;
int rd = _RD;
if (rd == 0)
{
PC += 4;
return;
}
switch (op & 63)
{
case 10: if (R(rt) == 0) R(rd) = R(rs); break;
case 11: if (R(rt) != 0) R(rd) = R(rs); break;
case 32: R(rd) = R(rs) + R(rt); break;
case 33: R(rd) = R(rs) + R(rt); break;
case 34: R(rd) = R(rs) - R(rt); break;
case 35: R(rd) = R(rs) - R(rt); break;
case 36: R(rd) = R(rs) & R(rt); break;
case 37: R(rd) = R(rs) | R(rt); break;
case 38: R(rd) = R(rs) ^ R(rt); break;
case 39: R(rd) = ~(R(rs) | R(rt)); break;
case 42: R(rd) = (s32)R(rs) < (s32)R(rt); break;
case 43: R(rd) = R(rs) < R(rt); break;
case 44: R(rd) = ((s32)R(rs) > (s32)R(rt)) ? R(rs) : R(rt); break;
case 45: R(rd) = ((s32)R(rs) < (s32)R(rt)) ? R(rs) : R(rt); break;
default:
_dbg_assert_msg_( 0, "Unknown MIPS instruction %08x", op.encoding);
break;
}
PC += 4;
}
void Int_ITypeMem(MIPSOpcode op)
{
int imm = (signed short)(op&0xFFFF);
int rt = _RT;
int rs = _RS;
u32 addr = R(rs) + imm;
if (((op >> 29) & 1) == 0 && rt == 0) {
PC += 4;
return;
}
switch (op >> 26)
{
case 32: R(rt) = SignExtend8ToU32(Memory::Read_U8(addr)); break;
case 33: R(rt) = SignExtend16ToU32(Memory::Read_U16(addr)); break;
case 35: R(rt) = Memory::Read_U32(addr); break;
case 36: R(rt) = Memory::Read_U8 (addr); break;
case 37: R(rt) = Memory::Read_U16(addr); break;
case 40: Memory::Write_U8(R(rt), addr); break;
case 41: Memory::Write_U16(R(rt), addr); break;
case 43: Memory::Write_U32(R(rt), addr); break;
case 34:
{
u32 shift = (addr & 3) * 8;
u32 mem = Memory::Read_U32(addr & 0xfffffffc);
u32 result = ( u32(R(rt)) & (0x00ffffff >> shift) ) | ( mem << (24 - shift) );
R(rt) = result;
}
break;
case 38:
{
u32 shift = (addr & 3) * 8;
u32 mem = Memory::Read_U32(addr & 0xfffffffc);
u32 regval = R(rt);
u32 result = ( regval & (0xffffff00 << (24 - shift)) ) | ( mem >> shift );
R(rt) = result;
}
break;
case 42:
{
u32 shift = (addr & 3) * 8;
u32 mem = Memory::Read_U32(addr & 0xfffffffc);
u32 result = ( ( u32(R(rt)) >> (24 - shift) ) ) | ( mem & (0xffffff00 << shift) );
Memory::Write_U32(result, (addr & 0xfffffffc));
}
break;
case 46:
{
u32 shift = (addr & 3) << 3;
u32 mem = Memory::Read_U32(addr & 0xfffffffc);
u32 result = ( ( u32(R(rt)) << shift ) | (mem & (0x00ffffff >> (24 - shift)) ) );
Memory::Write_U32(result, (addr & 0xfffffffc));
}
break;
default:
_dbg_assert_msg_(false,"Trying to interpret Mem instruction that can't be interpreted");
break;
}
PC += 4;
}
void Int_FPULS(MIPSOpcode op)
{
s32 offset = (s16)(op&0xFFFF);
int ft = _FT;
int rs = _RS;
u32 addr = R(rs) + offset;
switch(op >> 26)
{
case 49: FI(ft) = Memory::Read_U32(addr); break;
case 57: Memory::Write_U32(FI(ft), addr); break;
default:
_dbg_assert_msg_(false,"Trying to interpret FPULS instruction that can't be interpreted");
break;
}
PC += 4;
}
void Int_mxc1(MIPSOpcode op)
{
int fs = _FS;
int rt = _RT;
switch ((op>>21)&0x1f) {
case 0:
if (rt != 0)
R(rt) = FI(fs);
break;
case 2:
if (rt != 0) {
if (fs == 31) {
currentMIPS->fcr31 = (currentMIPS->fcr31 & ~(1<<23)) | ((currentMIPS->fpcond & 1)<<23);
R(rt) = currentMIPS->fcr31;
} else if (fs == 0) {
R(rt) = MIPSState::FCR0_VALUE;
} else {
WARN_LOG_REPORT(Log::CPU, "ReadFCR: Unexpected reg %d", fs);
R(rt) = 0;
}
break;
}
case 4:
FI(fs) = R(rt);
break;
case 6:
{
u32 value = R(rt);
if (fs == 31) {
currentMIPS->fcr31 = value & 0x0181FFFF;
currentMIPS->fpcond = (value >> 23) & 1;
if (MIPSComp::jit) {
MIPSComp::jit->UpdateFCR31();
}
} else {
WARN_LOG_REPORT(Log::CPU, "WriteFCR: Unexpected reg %d (value %08x)", fs, value);
}
DEBUG_LOG(Log::CPU, "FCR%i written to, value %08x", fs, value);
break;
}
default:
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
break;
}
PC += 4;
}
void Int_RType2(MIPSOpcode op)
{
int rs = _RS;
int rd = _RD;
if (rd == 0)
{
PC += 4;
return;
}
switch (op & 63)
{
case 22:
R(rd) = clz32(R(rs));
break;
case 23:
R(rd) = clz32(~R(rs));
break;
default:
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
break;
}
PC += 4;
}
void Int_MulDivType(MIPSOpcode op)
{
int rt = _RT;
int rs = _RS;
int rd = _RD;
switch (op & 63)
{
case 24:
{
s64 result = (s64)(s32)R(rs) * (s64)(s32)R(rt);
u64 resultBits = (u64)(result);
LO = (u32)(resultBits);
HI = (u32)(resultBits>>32);
}
break;
case 25:
{
u64 resultBits = (u64)R(rs) * (u64)R(rt);
LO = (u32)(resultBits);
HI = (u32)(resultBits>>32);
}
break;
case 28:
{
u32 a=R(rs),b=R(rt),hi=HI,lo=LO;
u64 origValBits = (u64)lo | ((u64)(hi)<<32);
s64 origVal = (s64)origValBits;
s64 result = origVal + (s64)(s32)a * (s64)(s32)b;
u64 resultBits = (u64)(result);
LO = (u32)(resultBits);
HI = (u32)(resultBits>>32);
}
break;
case 29:
{
u32 a=R(rs),b=R(rt),hi=HI,lo=LO;
u64 origVal = (u64)lo | ((u64)(hi)<<32);
u64 result = origVal + (u64)a * (u64)b;
LO = (u32)(result);
HI = (u32)(result>>32);
}
break;
case 46:
{
u32 a=R(rs),b=R(rt),hi=HI,lo=LO;
u64 origValBits = (u64)lo | ((u64)(hi)<<32);
s64 origVal = (s64)origValBits;
s64 result = origVal - (s64)(s32)a * (s64)(s32)b;
u64 resultBits = (u64)(result);
LO = (u32)(resultBits);
HI = (u32)(resultBits>>32);
}
break;
case 47:
{
u32 a=R(rs),b=R(rt),hi=HI,lo=LO;
u64 origVal = (u64)lo | ((u64)(hi)<<32);
u64 result = origVal - (u64)a * (u64)b;
LO = (u32)(result);
HI = (u32)(result>>32);
}
break;
case 16: if (rd != 0) R(rd) = HI; break;
case 17: HI = R(rs); break;
case 18: if (rd != 0) R(rd) = LO; break;
case 19: LO = R(rs); break;
case 26:
{
s32 a = (s32)R(rs);
s32 b = (s32)R(rt);
if (a == (s32)0x80000000 && b == -1) {
LO = 0x80000000;
HI = -1;
} else if (b != 0) {
LO = (u32)(a / b);
HI = (u32)(a % b);
} else {
LO = a < 0 ? 1 : -1;
HI = a;
}
}
break;
case 27:
{
u32 a = R(rs);
u32 b = R(rt);
if (b != 0) {
LO = (a/b);
HI = (a%b);
} else {
LO = a <= 0xFFFF ? 0xFFFF : -1;
HI = a;
}
}
break;
default:
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
break;
}
PC += 4;
}
void Int_ShiftType(MIPSOpcode op)
{
int rt = _RT;
int rs = _RS;
int rd = _RD;
int sa = _FD;
if (rd == 0)
{
PC += 4;
return;
}
switch (op & 0x3f)
{
case 0: R(rd) = R(rt) << sa; break;
case 2:
if (_RS == 0)
{
R(rd) = R(rt) >> sa;
break;
}
else if (_RS == 1)
{
R(rd) = __rotr(R(rt), sa);
break;
}
else
goto wrong;
case 3: R(rd) = (u32)(((s32)R(rt)) >> sa); break;
case 4: R(rd) = R(rt) << (R(rs)&0x1F); break;
case 6:
if (_FD == 0)
{
R(rd) = R(rt) >> (R(rs)&0x1F);
break;
}
else if (_FD == 1)
{
R(rd) = __rotr(R(rt), R(rs));
break;
}
else goto wrong;
case 7: R(rd) = (u32)(((s32)R(rt)) >> (R(rs)&0x1F)); break;
default:
wrong:
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
break;
}
PC += 4;
}
void Int_Allegrex(MIPSOpcode op)
{
int rt = _RT;
int rd = _RD;
if (rd == 0)
{
PC += 4;
return;
}
switch((op>>6)&31)
{
case 16:
R(rd) = SignExtend8ToU32(R(rt));
break;
case 20:
{
u32 tmp = 0;
for (int i = 0; i < 32; i++)
{
if (R(rt) & (1 << i))
{
tmp |= (0x80000000 >> i);
}
}
R(rd) = tmp;
}
break;
case 24:
R(rd) = SignExtend16ToU32(R(rt));
break;
default:
_dbg_assert_msg_(false,"Trying to interpret ALLEGREX instruction that can't be interpreted");
break;
}
PC += 4;
}
void Int_Allegrex2(MIPSOpcode op)
{
int rt = _RT;
int rd = _RD;
if (rd == 0)
{
PC += 4;
return;
}
switch (op & 0x3ff)
{
case 0xA0:
R(rd) = ((R(rt) & 0xFF00FF00) >> 8) | ((R(rt) & 0x00FF00FF) << 8);
break;
case 0xE0:
R(rd) = swap32(R(rt));
break;
default:
_dbg_assert_msg_(false,"Trying to interpret ALLEGREX instruction that can't be interpreted");
break;
}
PC += 4;
}
void Int_Special2(MIPSOpcode op)
{
static int reported = 0;
switch (op & 0x3F)
{
case 36:
if (!reported) {
WARN_LOG(Log::CPU, "MFIC Disable/Enable Interrupt CPU instruction");
reported = 1;
}
break;
case 38:
if (!reported) {
WARN_LOG(Log::CPU, "MTIC Disable/Enable Interrupt CPU instruction");
reported = 1;
}
break;
}
PC += 4;
}
void Int_Special3(MIPSOpcode op)
{
int rs = _RS;
int rt = _RT;
int pos = _POS;
if (rt == 0) {
PC += 4;
return;
}
switch (op & 0x3f) {
case 0x0:
{
int size = _SIZE + 1;
u32 sourcemask = 0xFFFFFFFFUL >> (32 - size);
R(rt) = (R(rs) >> pos) & sourcemask;
}
break;
case 0x4:
{
int size = (_SIZE + 1) - pos;
u32 sourcemask = 0xFFFFFFFFUL >> (32 - size);
u32 destmask = sourcemask << pos;
R(rt) = (R(rt) & ~destmask) | ((R(rs)&sourcemask) << pos);
}
break;
}
PC += 4;
}
void Int_FPU2op(MIPSOpcode op)
{
int fs = _FS;
int fd = _FD;
switch (op & 0x3f)
{
case 4: F(fd) = sqrtf(F(fs)); break;
case 5: F(fd) = fabsf(F(fs)); break;
case 6: F(fd) = F(fs); break;
case 7: F(fd) = -F(fs); break;
case 12:
case 13:
case 14:
case 15:
if (my_isnanorinf(F(fs)))
{
FsI(fd) = my_isinf(F(fs)) && F(fs) < 0.0f ? -2147483648LL : 2147483647LL;
break;
}
switch (op & 0x3f)
{
case 12: FsI(fd) = (int)floorf(F(fs)+0.5f); break;
case 13:
if (F(fs) >= 0.0f) {
FsI(fd) = (int)floorf(F(fs));
if (FsI(fd) == -2147483648LL) {
FsI(fd) = 2147483647LL;
}
} else {
FsI(fd) = (int)ceilf(F(fs));
}
break;
case 14: FsI(fd) = (int)ceilf (F(fs)); break;
case 15: FsI(fd) = (int)floorf(F(fs)); break;
}
break;
case 32: F(fd) = (float)FsI(fs); break;
case 36:
if (my_isnanorinf(F(fs)))
{
FsI(fd) = my_isinf(F(fs)) && F(fs) < 0.0f ? -2147483648LL : 2147483647LL;
break;
}
switch (currentMIPS->fcr31 & 3)
{
case 0: FsI(fd) = (int)round_ieee_754(F(fs)); break;
case 1: FsI(fd) = (int)F(fs); break;
case 2: FsI(fd) = (int)ceilf(F(fs)); break;
case 3: FsI(fd) = (int)floorf(F(fs)); break;
}
break;
default:
_dbg_assert_msg_(false,"Trying to interpret FPU2Op instruction that can't be interpreted");
break;
}
PC += 4;
}
void Int_FPUComp(MIPSOpcode op)
{
int fs = _FS;
int ft = _FT;
bool cond;
switch (op & 0xf)
{
case 0:
case 8:
cond = false;
break;
case 1:
case 9:
cond = my_isnan(F(fs)) || my_isnan(F(ft));
break;
case 2:
case 10:
cond = !my_isnan(F(fs)) && !my_isnan(F(ft)) && (F(fs) == F(ft));
break;
case 3:
case 11:
cond = (F(fs) == F(ft)) || my_isnan(F(fs)) || my_isnan(F(ft));
break;
case 4:
case 12:
cond = (F(fs) < F(ft));
break;
case 5:
case 13:
cond = (F(fs) < F(ft)) || my_isnan(F(fs)) || my_isnan(F(ft));
break;
case 6:
case 14:
cond = (F(fs) <= F(ft));
break;
case 7:
case 15:
cond = (F(fs) <= F(ft)) || my_isnan(F(fs)) || my_isnan(F(ft));
break;
default:
_dbg_assert_msg_(false,"Trying to interpret FPUComp instruction that can't be interpreted");
cond = false;
break;
}
currentMIPS->fpcond = cond;
PC += 4;
}
void Int_FPU3op(MIPSOpcode op)
{
int ft = _FT;
int fs = _FS;
int fd = _FD;
switch (op & 0x3f)
{
case 0: F(fd) = F(fs) + F(ft); break;
case 1: F(fd) = F(fs) - F(ft); break;
case 2:
if ((my_isinf(F(fs)) && F(ft) == 0.0f) || (my_isinf(F(ft)) && F(fs) == 0.0f)) {
FI(fd) = 0x7fc00000;
} else {
F(fd) = F(fs) * F(ft);
}
break;
case 3: F(fd) = F(fs) / F(ft); break;
default:
_dbg_assert_msg_(false,"Trying to interpret FPU3Op instruction that can't be interpreted");
break;
}
PC += 4;
}
void Int_Interrupt(MIPSOpcode op)
{
static int reported = 0;
switch (op & 1)
{
case 0:
if (!reported) {
Reporting::ReportMessage("INTERRUPT instruction hit (%08x) at %08x", op.encoding, currentMIPS->pc);
WARN_LOG(Log::CPU, "Disable/Enable Interrupt CPU instruction");
reported = 1;
}
break;
}
PC += 4;
}
void Int_Emuhack(MIPSOpcode op)
{
if (((op >> 24) & 3) != EMUOP_CALL_REPLACEMENT) {
_dbg_assert_msg_(false, "Trying to interpret emuhack instruction that can't be interpreted");
}
_assert_((PC & 3) == 0);
int index = op.encoding & 0xFFFFFF;
const ReplacementTableEntry *entry = GetReplacementFunc(index);
if (entry && entry->replaceFunc && (entry->flags & REPFLAG_DISABLED) == 0) {
int cycles = entry->replaceFunc();
if (entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) {
MIPSInterpret(Memory::Read_Instruction(PC, true));
} else if (cycles < 0) {
currentMIPS->downcount += cycles;
} else {
PC = currentMIPS->r[MIPS_REG_RA];
currentMIPS->downcount -= cycles;
}
} else {
if (!entry || !entry->replaceFunc) {
ERROR_LOG(Log::CPU, "Bad replacement function index %i", index);
}
MIPSInterpret(Memory::Read_Instruction(PC, true));
}
}
}