ppsspp/Core/MIPS/MIPSInt.cpp
Unknown W. Brackets 0fc3619d1d interp: Handle jumps in branch delay slots better.
This matches tests from a PSP-2000.  Seems to consistently run the
instruction even if likely, which writes rd.

If the likely branch is not taken, the jump in the delay slot is taken.
However, it should cancel the rd write (not implemented here.)
2022-09-03 13:15:21 -07:00

1046 lines
25 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#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/Host.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"
#include "Core/System.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 _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));
// Don't actually skip if it is a jump (seen in Brooktown High.)
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 = (s16)(op & 0xFFFF);
int rs = _RS;
int addr = R(rs) + imm;
int func = (op >> 16) & 0x1F;
// It appears that a cache line is 0x40 (64) bytes, loops in games
// issue the cache instruction at that interval.
// These codes might be PSP-specific, they don't match regular MIPS cache codes very well
// NOTE: If you add support for more, make sure they are handled in the various Jit::Comp_Cache.
switch (func) {
// Icache
case 8:
// Invalidate the instruction cache at this address.
// We assume the CPU won't be reset during this, so no locking.
if (MIPSComp::jit) {
MIPSComp::jit->InvalidateCacheAt(addr, 0x40);
}
break;
// Dcache
case 24:
// "Create Dirty Exclusive" - for avoiding a cacheline fill before writing to it.
// Will cause garbage on the real machine so we just ignore it, the app will overwrite the cacheline.
break;
case 25: // Hit Invalidate - zaps the line if present in cache. Should not writeback???? scary.
// No need to do anything.
break;
case 27: // D-cube. Hit Writeback Invalidate. Tony Hawk Underground 2
break;
case 30: // GTA LCS, a lot. Fill (prefetch). Tony Hawk Underground 2
break;
default:
DEBUG_LOG(CPU, "cache instruction affecting %08x : function %i", addr, func);
}
PC += 4;
}
void Int_Syscall(MIPSOpcode op)
{
// Need to pre-move PC, as CallSyscall may result in a rescheduling!
// To do this neater, we'll need a little generated kernel loop that syscall can jump to and then RFI from
// but I don't see a need to bother.
if (mipsr4k.inDelaySlot)
{
mipsr4k.pc = mipsr4k.nextPC;
}
else
{
mipsr4k.pc += 4;
}
mipsr4k.inDelaySlot = false;
CallSyscall(op);
}
void Int_Sync(MIPSOpcode op)
{
//DEBUG_LOG(CPU, "sync");
PC += 4;
}
void Int_Break(MIPSOpcode op)
{
Reporting::ReportMessage("BREAK instruction hit");
Core_Break(PC);
PC += 4;
}
void Int_RelBranch(MIPSOpcode op)
{
int imm = (signed short)(op&0xFFFF)<<2;
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; //beq
case 5: if (R(rt) != R(rs)) DelayBranchTo(addr); else PC += 4; break; //bne
case 6: if ((s32)R(rs) <= 0) DelayBranchTo(addr); else PC += 4; break; //blez
case 7: if ((s32)R(rs) > 0) DelayBranchTo(addr); else PC += 4; break; //bgtz
case 20: if (R(rt) == R(rs)) DelayBranchTo(addr); else SkipLikely(); break; //beql
case 21: if (R(rt) != R(rs)) DelayBranchTo(addr); else SkipLikely(); break; //bnel
case 22: if ((s32)R(rs) <= 0) DelayBranchTo(addr); else SkipLikely(); break; //blezl
case 23: if ((s32)R(rs) > 0) DelayBranchTo(addr); else SkipLikely(); break; //bgtzl
default:
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
break;
}
}
void Int_RelBranchRI(MIPSOpcode op)
{
int imm = (signed short)(op&0xFFFF)<<2;
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;//bltz
case 1: if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez
case 2: if ((s32)R(rs) < 0) DelayBranchTo(addr); else SkipLikely(); break;//bltzl
case 3: if ((s32)R(rs) >= 0) DelayBranchTo(addr); else SkipLikely(); break;//bgezl
case 16: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltzal
case 17: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgezal
case 18: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else SkipLikely(); break;//bltzall
case 19: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else SkipLikely(); break;//bgezall
default:
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
break;
}
}
void Int_VBranch(MIPSOpcode op)
{
int imm = (signed short)(op&0xFFFF)<<2;
u32 addr = PC + imm + 4;
// x, y, z, w, any, all, (invalid), (invalid)
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; //bvf
case 1: if ( val) DelayBranchTo(addr); else PC += 4; break; //bvt
case 2: if (!val) DelayBranchTo(addr); else SkipLikely(); break; //bvfl
case 3: if ( val) DelayBranchTo(addr); else SkipLikely(); break; //bvtl
}
}
void Int_FPUBranch(MIPSOpcode op)
{
int imm = (signed short)(op&0xFFFF)<<2;
u32 addr = PC + imm + 4;
switch((op>>16)&0x1f)
{
case 0: if (!currentMIPS->fpcond) DelayBranchTo(addr); else PC += 4; break;//bc1f
case 1: if ( currentMIPS->fpcond) DelayBranchTo(addr); else PC += 4; break;//bc1t
case 2: if (!currentMIPS->fpcond) DelayBranchTo(addr); else SkipLikely(); break;//bc1fl
case 3: if ( currentMIPS->fpcond) DelayBranchTo(addr); else SkipLikely(); break;//bc1tl
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(CPU, "Jump in delay slot :(");
u32 off = ((op & 0x03FFFFFF) << 2);
u32 addr = (currentMIPS->pc & 0xF0000000) | off;
switch (op>>26)
{
case 2: //j
if (!mipsr4k.inDelaySlot)
DelayBranchTo(addr);
break;
case 3: //jal
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)
{
// There's one of these in Star Soldier at 0881808c, which seems benign.
ERROR_LOG(CPU, "Jump in delay slot :(");
}
int rs = _RS;
int rd = _RD;
u32 addr = R(rs);
switch (op & 0x3f)
{
case 8: //jr
if (!mipsr4k.inDelaySlot)
DelayBranchTo(addr);
break;
case 9: //jalr
if (rd != 0)
R(rd) = PC + 8;
// Update rd, but otherwise do not take the branch if we're branching.
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) { //destination register is zero register
PC += 4;
return; //nop
}
switch (op>>26)
{
case 8: R(rt) = R(rs) + simm; break; //addi
case 9: R(rt) = R(rs) + simm; break; //addiu
case 10: R(rt) = (s32)R(rs) < simm; break; //slti
case 11: R(rt) = R(rs) < suimm; break; //sltiu
case 12: R(rt) = R(rs) & uimm; break; //andi
case 13: R(rt) = R(rs) | uimm; break; //ori
case 14: R(rt) = R(rs) ^ uimm; break; //xori
case 15: R(rt) = uimm << 16; break; //lui
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: // ll
if (rt != 0) {
R(rt) = Memory::Read_U32(addr);
}
currentMIPS->llBit = 1;
break;
case 56: // sc
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;
// Don't change $zr.
if (rd == 0)
{
PC += 4;
return;
}
switch (op & 63)
{
case 10: if (R(rt) == 0) R(rd) = R(rs); break; //movz
case 11: if (R(rt) != 0) R(rd) = R(rs); break; //movn
case 32: R(rd) = R(rs) + R(rt); break; //add (exception on overflow)
case 33: R(rd) = R(rs) + R(rt); break; //addu
case 34: R(rd) = R(rs) - R(rt); break; //sub (exception on overflow)
case 35: R(rd) = R(rs) - R(rt); break; //subu
case 36: R(rd) = R(rs) & R(rt); break; //and
case 37: R(rd) = R(rs) | R(rt); break; //or
case 38: R(rd) = R(rs) ^ R(rt); break; //xor
case 39: R(rd) = ~(R(rs) | R(rt)); break; //nor
case 42: R(rd) = (s32)R(rs) < (s32)R(rt); break; //slt
case 43: R(rd) = R(rs) < R(rt); break; //sltu
case 44: R(rd) = ((s32)R(rs) > (s32)R(rt)) ? R(rs) : R(rt); break; //max
case 45: R(rd) = ((s32)R(rs) < (s32)R(rt)) ? R(rs) : R(rt); break;//min
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) {
// Don't load anything into $zr
PC += 4;
return;
}
switch (op >> 26)
{
case 32: R(rt) = SignExtend8ToU32(Memory::Read_U8(addr)); break; //lb
case 33: R(rt) = SignExtend16ToU32(Memory::Read_U16(addr)); break; //lh
case 35: R(rt) = Memory::Read_U32(addr); break; //lw
case 36: R(rt) = Memory::Read_U8 (addr); break; //lbu
case 37: R(rt) = Memory::Read_U16(addr); break; //lhu
case 40: Memory::Write_U8(R(rt), addr); break; //sb
case 41: Memory::Write_U16(R(rt), addr); break; //sh
case 43: Memory::Write_U32(R(rt), addr); break; //sw
// When there's an LWL and an LWR together, we should be able to peephole optimize that
// into a single non-alignment-checking LW.
case 34: //lwl
{
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: //lwr
{
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: //swl
{
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: //swr
{
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; //lwc1
case 57: Memory::Write_U32(FI(ft), addr); break; //swc1
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: //mfc1
if (rt != 0)
R(rt) = FI(fs);
break;
case 2: //cfc1
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(CPU, "ReadFCR: Unexpected reg %d", fs);
R(rt) = 0;
}
break;
}
case 4: //mtc1
FI(fs) = R(rt);
break;
case 6: //ctc1
{
u32 value = R(rt);
if (fs == 31) {
currentMIPS->fcr31 = value & 0x0181FFFF;
currentMIPS->fpcond = (value >> 23) & 1;
// Don't bother locking, assuming the CPU can't be reset now anyway.
if (MIPSComp::jit) {
// In case of DISABLE, we need to tell jit we updated FCR31.
MIPSComp::jit->UpdateFCR31();
}
} else {
WARN_LOG_REPORT(CPU, "WriteFCR: Unexpected reg %d (value %08x)", fs, value);
}
DEBUG_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;
// Don't change $zr.
if (rd == 0)
{
PC += 4;
return;
}
switch (op & 63)
{
case 22: //clz
R(rd) = clz32(R(rs));
break;
case 23: //clo
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: //mult
{
s64 result = (s64)(s32)R(rs) * (s64)(s32)R(rt);
u64 resultBits = (u64)(result);
LO = (u32)(resultBits);
HI = (u32)(resultBits>>32);
}
break;
case 25: //multu
{
u64 resultBits = (u64)R(rs) * (u64)R(rt);
LO = (u32)(resultBits);
HI = (u32)(resultBits>>32);
}
break;
case 28: //madd
{
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: //maddu
{
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: //msub
{
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: //msubu
{
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; //mfhi
case 17: HI = R(rs); break; //mthi
case 18: if (rd != 0) R(rd) = LO; break; //mflo
case 19: LO = R(rs); break; //mtlo
case 26: //div
{
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: //divu
{
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;
// Don't change $zr.
if (rd == 0)
{
PC += 4;
return;
}
switch (op & 0x3f)
{
case 0: R(rd) = R(rt) << sa; break; //sll
case 2:
if (_RS == 0) //srl
{
R(rd) = R(rt) >> sa;
break;
}
else if (_RS == 1) //rotr
{
R(rd) = __rotr(R(rt), sa);
break;
}
else
goto wrong;
case 3: R(rd) = (u32)(((s32)R(rt)) >> sa); break; //sra
case 4: R(rd) = R(rt) << (R(rs)&0x1F); break; //sllv
case 6:
if (_FD == 0) //srlv
{
R(rd) = R(rt) >> (R(rs)&0x1F);
break;
}
else if (_FD == 1) // rotrv
{
R(rd) = __rotr(R(rt), R(rs));
break;
}
else goto wrong;
case 7: R(rd) = (u32)(((s32)R(rt)) >> (R(rs)&0x1F)); break; //srav
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;
// Don't change $zr.
if (rd == 0)
{
PC += 4;
return;
}
switch((op>>6)&31)
{
case 16: // seb
R(rd) = SignExtend8ToU32(R(rt));
break;
case 20: // bitrev
{
u32 tmp = 0;
for (int i = 0; i < 32; i++)
{
if (R(rt) & (1 << i))
{
tmp |= (0x80000000 >> i);
}
}
R(rd) = tmp;
}
break;
case 24: // seh
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;
// Don't change $zr.
if (rd == 0)
{
PC += 4;
return;
}
switch (op & 0x3ff)
{
case 0xA0: //wsbh
R(rd) = ((R(rt) & 0xFF00FF00) >> 8) | ((R(rt) & 0x00FF00FF) << 8);
break;
case 0xE0: //wsbw
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: // mfic
if (!reported) {
Reporting::ReportMessage("MFIC instruction hit (%08x) at %08x", op.encoding, currentMIPS->pc);
WARN_LOG(CPU,"MFIC Disable/Enable Interrupt CPU instruction");
reported = 1;
}
break;
case 38: // mtic
if (!reported) {
Reporting::ReportMessage("MTIC instruction hit (%08x) at %08x", op.encoding, currentMIPS->pc);
WARN_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;
// Don't change $zr.
if (rt == 0) {
PC += 4;
return;
}
switch (op & 0x3f) {
case 0x0: //ext
{
int size = _SIZE + 1;
u32 sourcemask = 0xFFFFFFFFUL >> (32 - size);
R(rt) = (R(rs) >> pos) & sourcemask;
}
break;
case 0x4: //ins
{
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; //sqrt
case 5: F(fd) = fabsf(F(fs)); break; //abs
case 6: F(fd) = F(fs); break; //mov
case 7: F(fd) = -F(fs); break; //neg
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; //round.w.s
case 13: //trunc.w.s
if (F(fs) >= 0.0f) {
FsI(fd) = (int)floorf(F(fs));
// Overflow, but it was positive.
if (FsI(fd) == -2147483648LL) {
FsI(fd) = 2147483647LL;
}
} else {
// Overflow happens to be the right value anyway.
FsI(fd) = (int)ceilf(F(fs));
}
break;
case 14: FsI(fd) = (int)ceilf (F(fs)); break; //ceil.w.s
case 15: FsI(fd) = (int)floorf(F(fs)); break; //floor.w.s
}
break;
case 32: F(fd) = (float)FsI(fs); break; //cvt.s.w
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; // RINT_0
case 1: FsI(fd) = (int)F(fs); break; // CAST_1
case 2: FsI(fd) = (int)ceilf(F(fs)); break; // CEIL_2
case 3: FsI(fd) = (int)floorf(F(fs)); break; // FLOOR_3
}
break; //cvt.w.s
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: //f
case 8: //sf
cond = false;
break;
case 1: //un
case 9: //ngle
cond = my_isnan(F(fs)) || my_isnan(F(ft));
break;
case 2: //eq
case 10: //seq
cond = !my_isnan(F(fs)) && !my_isnan(F(ft)) && (F(fs) == F(ft));
break;
case 3: //ueq
case 11: //ngl
cond = (F(fs) == F(ft)) || my_isnan(F(fs)) || my_isnan(F(ft));
break;
case 4: //olt
case 12: //lt
cond = (F(fs) < F(ft));
break;
case 5: //ult
case 13: //nge
cond = (F(fs) < F(ft)) || my_isnan(F(fs)) || my_isnan(F(ft));
break;
case 6: //ole
case 14: //le
cond = (F(fs) <= F(ft));
break;
case 7: //ule
case 15: //ngt
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; // add.s
case 1: F(fd) = F(fs) - F(ft); break; // sub.s
case 2: // mul.s
if ((my_isinf(F(fs)) && F(ft) == 0.0f) || (my_isinf(F(ft)) && F(fs) == 0.0f)) {
// Must be positive NAN, see #12519.
FI(fd) = 0x7fc00000;
} else {
F(fd) = F(fs) * F(ft);
}
break;
case 3: F(fd) = F(fs) / F(ft); break; // div.s
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(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");
}
// It's a replacement func!
int index = op.encoding & 0xFFFFFF;
const ReplacementTableEntry *entry = GetReplacementFunc(index);
if (entry && entry->replaceFunc && (entry->flags & REPFLAG_DISABLED) == 0) {
entry->replaceFunc();
if (entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) {
// Interpret the original instruction under the hook.
MIPSInterpret(Memory::Read_Instruction(PC, true));
} else {
PC = currentMIPS->r[MIPS_REG_RA];
}
} else {
if (!entry || !entry->replaceFunc) {
ERROR_LOG(CPU, "Bad replacement function index %i", index);
}
// Interpret the original instruction under it.
MIPSInterpret(Memory::Read_Instruction(PC, true));
}
}
}