// 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/.

// MIPS is really trivial :)

#include <cmath>

#include "../Core.h"
#include "MIPS.h"
#include "MIPSInt.h"
#include "MIPSTables.h"

#include "../HLE/HLE.h"
#include "../System.h"

#define R(i) (currentMIPS->r[i])
#define RF(i) (*(float*)(&(currentMIPS->r[i])))
#define F(i) (currentMIPS->f[i])
#define FI(i) (*(u32*)(&(currentMIPS->f[i])))
#define FsI(i) (*(s32*)(&(currentMIPS->f[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


inline int is_even(float d) {
	float int_part;
	modff(d / 2.0f, &int_part);
	return 2.0f * int_part == d;
}

// Rounds *.5 to closest even number
float round_ieee_754(float d) {
	float i = floorf(d);
	d -= i;
	if(d < 0.5f)
		return i;
	if(d > 0.5f)
		return i + 1.0f;
	if(is_even(i))
		return i;
	return i + 1.0f;
}

static inline void DelayBranchTo(u32 where)
{
	PC += 4;
	mipsr4k.nextPC = where;
	mipsr4k.inDelaySlot = true;
}

int MIPS_SingleStep()
{
#if defined(ARM)
	u32 op = Memory::ReadUnchecked_U32(mipsr4k.pc);
#else
	u32 op = Memory::Read_Opcode_JIT(mipsr4k.pc);
#endif
	/*
	// Choke on VFPU
	u32 info = MIPSGetInfo(op);
	if (info & IS_VFPU)
	{
		if (!Core_IsStepping() && !GetAsyncKeyState(VK_LSHIFT))
		{
			Core_EnableStepping(true);
			return;
		}
	}*/

	if (mipsr4k.inDelaySlot)
	{
		MIPSInterpret(op);
		if (mipsr4k.inDelaySlot)
		{
			mipsr4k.pc = mipsr4k.nextPC;
			mipsr4k.inDelaySlot = false;
		}
	}
	else
	{
		MIPSInterpret(op);
	}
	return 1;
}



u32 MIPS_GetNextPC()
{
	if (mipsr4k.inDelaySlot)
		return mipsr4k.nextPC;
	else
		return mipsr4k.pc + 4;
}


void MIPS_ClearDelaySlot()
{
	mipsr4k.inDelaySlot = false;
}


namespace MIPSInt
{
	void Int_Cache(u32 op)
	{
		// DEBUG_LOG(CPU,"cache instruction %08x",op);
		PC += 4;
	}

	void Int_Syscall(u32 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(u32 op)
	{
		//DEBUG_LOG(CPU, "sync");
		PC += 4;
	}

	void Int_Break(u32 op)
	{
		DEBUG_LOG(CPU, "BREAK!");
		coreState = CORE_STEPPING;
		PC += 4;
	}

	void Int_RelBranch(u32 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 PC += 8; break; //beql
		case 21: if (R(rt) != R(rs))	DelayBranchTo(addr); else PC += 8; break; //bnel
		case 22: if ((s32)R(rs) <= 0) DelayBranchTo(addr); else PC += 8; break; //blezl
		case 23: if ((s32)R(rs) >	0) DelayBranchTo(addr); else PC += 8; break; //bgtzl

		default:
			_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
			break;
		}
	}

	void Int_RelBranchRI(u32 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 PC += 8; break;//bltzl
		case 3: if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 8; break;//bgezl
		case 16: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) <  0) DelayBranchTo(addr); else PC += 4; break;//bltz
		case 17: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez
		case 18: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) <	0) DelayBranchTo(addr); else PC += 8; break;//bltzl
		case 19: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 8; break;//bgezl
		default:
			_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
			break;
		}
	}


	void Int_VBranch(u32 op)
	{
		int imm = (signed short)(op&0xFFFF)<<2;
		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; //bvf
		case 1: if ( val) DelayBranchTo(addr); else PC += 4; break; //bvt
		case 2: if (!val) DelayBranchTo(addr); else PC += 8; break; //bvfl
		case 3: if ( val) DelayBranchTo(addr); else PC += 8; break; //bvtl
		}
	}

	void Int_FPUBranch(u32 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 PC += 8; break;//bc1fl
		case 3: if ( currentMIPS->fpcond) DelayBranchTo(addr); else PC += 8; break;//bc1tl
		default:
			_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
			break;
		}
	}
	
	void Int_JumpType(u32 op)
	{
		if (mipsr4k.inDelaySlot)
			_dbg_assert_msg_(CPU,0,"Jump in delay slot :(");

		u32 off = ((op & 0x3FFFFFF) << 2);
		u32 addr = (currentMIPS->pc & 0xF0000000) | off;

		switch (op>>26) 
		{
		case 2: DelayBranchTo(addr); break; //j
		case 3: //jal
			R(31) = PC + 8;
			DelayBranchTo(addr);
			break;
		default:
			_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
			break;
		}
	}

	void Int_JumpRegType(u32 op)
	{
		if (mipsr4k.inDelaySlot)
		{
			// There's one of these in Star Soldier at 0881808c, which seems benign - it should probably be ignored.
			if (op == 0x03e00008)
				return;
			_dbg_assert_msg_(CPU,0,"Jump in delay slot :(");
		}


		int rs = (op>>21)&0x1f;
		u32 addr = R(rs);
		switch (op & 0x3f) 
		{
		case 8: //jr
			//			LOG(CPU,"returning from: %08x",PC);
			DelayBranchTo(addr);
			break;
		case 9: //jalr
			R(31) = PC + 8;
			DelayBranchTo(addr);
			break;
		}
	}

	void Int_IType(u32 op)
	{
		s32 simm = (s32)(s16)(op & 0xFFFF);
		u32 uimm = (u32)(u16)(op & 0xFFFF);

		u32 suimm = (u32)simm;

		int rt = _RT;
		int rs = _RS;

		if (rt == 0) //destination register is zero register
			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_(CPU,0,"Trying to interpret instruction that can't be interpreted");
			break;
		}
		PC += 4;
	}

	void Int_StoreSync(u32 op)
	{
		s32 imm = (signed short)(op&0xFFFF);
		int base = ((op >> 21) & 0x1f);
		int rt = (op >> 16) & 0x1f;
		u32 addr = R(base) + imm;

		switch (op >> 26)
		{
		case 48: // ll
			R(rt) = Memory::Read_U32(addr);
			currentMIPS->llBit = 1;
			break;
		case 56: // sc
			if (currentMIPS->llBit) {
				Memory::Write_U32(R(rt), addr);
				R(rt) = 1;
			} else {
				R(rt) = 0;
			}
			break;
		default:
			_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
			break;
		}
		PC += 4;
	}


	void Int_RType3(u32 op)
	{
		int rt = _RT;
		int rs = _RS;
		int rd = _RD;
		static bool has_warned = false;

		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: 
			if (!has_warned) {
				ERROR_LOG(HLE,"WARNING : exception-causing add at %08x", PC);
				has_warned = true;
			}
			R(rd) = R(rs) + R(rt);		break; //add
		case 33: R(rd) = R(rs) + R(rt);		break; //addu
		case 34: 
			if (!has_warned) {
				ERROR_LOG(HLE,"WARNING : exception-causing sub at %08x", PC);
				has_warned = true;
			}
			R(rd) = R(rs) - R(rt);		break; //sub
		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_(CPU,0,"Trying to interpret instruction that can't be interpreted");
			break;
		}
		PC += 4;
	}


	void Int_ITypeMem(u32 op)
	{
		int imm = (signed short)(op&0xFFFF);
		int rt = _RT;
		int rs = _RS;
		u32 addr = R(rs) + imm;

		switch (op >> 26) 
		{
		case 32: R(rt) = (u32)(s32)(s8) Memory::Read_U8(addr); break; //lb
		case 33: R(rt) = (u32)(s32)(s16)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_(CPU,0,"Trying to interpret Mem instruction that can't be interpreted");
			break;
		}
		PC += 4;
	}

	void Int_FPULS(u32 op)
	{
		s32 offset = (s16)(op&0xFFFF);
		int ft = ((op>>16)&0x1f);
		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_(CPU,0,"Trying to interpret FPULS instruction that can't be interpreted");
			break;
		}
		PC += 4;
	}

	void Int_mxc1(u32 op)
	{
		int fs = _FS;
		int rt = _RT;

		switch((op>>21)&0x1f) 
		{
		case 0: R(rt) = FI(fs); break; //mfc1
		case 2: R(rt) = currentMIPS->ReadFCR(fs); break; //cfc1
		case 4: FI(fs) = R(rt);	break; //mtc1
		case 6: currentMIPS->WriteFCR(fs, R(rt)); break; //ctc1
		
		default:
			_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
			break;
		}
		PC += 4;
	}

	void Int_RType2(u32 op)
	{
		int rs = _RS;
		int rd = _RD;
		switch (op & 63)
		{
		case 22:	//clz
			{ //TODO: verify
				int x = 31;
				int count=0;
				while (!(R(rs) & (1<<x)) && x >= 0)
				{
					count++;
					x--;
				}
				R(rd) = count;
			}
			break;
		case 23: //clo
			{ //TODO: verify
				int x = 31;
				int count=0;
				while ((R(rs) & (1<<x)) && x >= 0)
				{
					count++;
					x--;
				}
				R(rd) = count;
			}
			break;
		default:
			_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
			break;
		}
		PC += 4;
	}

	void Int_MulDivType(u32 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: R(rd) = HI; break; //mfhi
		case 17: HI = R(rs); break; //mthi
		case 18: 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;
				} else if (b != 0) {
					LO = (u32)(a / b);
					HI = (u32)(a % b);
				} else {
					LO = HI = 0;	// Not sure what the right thing to do is?
				}
			}
			break;
		case 27: //divu
			{
				u32 a = R(rs);
				u32 b = R(rt);
				if (b != 0) 
				{
					LO = (a/b);
					HI = (a%b);
				} else {
					LO = HI = 0;
				}
			}
			break;

		default:
			_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
			break;
		}
		PC += 4;
	}


	void Int_ShiftType(u32 op)
	{
		int rt = _RT;
		int rs = _RS;
		int rd = _RD;
		int sa = _FD;
		
		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_(CPU,0,"Trying to interpret instruction that can't be interpreted");
			break;
		}
		PC += 4;
	}

	void Int_Allegrex(u32 op)
	{
		int rt = _RT;
		int rd = _RD;
		switch((op>>6)&31)
		{
		case 16: // seb
			R(rd) = (u32)(s32)(s8)(u8)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) = (u32)(s32)(s16)(u16)R(rt);
			break;

		default:
			_dbg_assert_msg_(CPU,0,"Trying to interpret ALLEGREX instruction that can't be interpreted");
			break;
		}
		PC += 4;
	}

	void Int_Allegrex2(u32 op)
	{
		int rt = _RT;
		int rd = _RD;

		switch (op & 0x3ff)
		{
		case 0xA0: //wsbh
			R(rd) = ((R(rt) & 0xFF00FF00) >> 8) | ((R(rt) & 0x00FF00FF) << 8);
			break;
		case 0xE0: //wsbw
			R(rd) = _byteswap_ulong(R(rt));
			break;
		default:
			_dbg_assert_msg_(CPU,0,"Trying to interpret ALLEGREX instruction that can't be interpreted");
			break;
		}
		PC += 4;
	}

	void Int_Special3(u32 op)
	{
		int rs = _RS;
		int rt = _RT;
		int pos = _POS;

		switch (op & 0x3f)
		{
		case 0x0: //ext
			{
				int size = _SIZE + 1;
				R(rt) = (R(rs) >> pos) & ((1<<size) - 1);
			}
			break;
		case 0x4: // ins
			{
				int size = (_SIZE + 1) - pos;
				int sourcemask = (1 << size) - 1;
				int destmask = sourcemask << pos;
				R(rt) = (R(rt) & ~destmask) | ((R(rs)&sourcemask) << pos);
			}
			break;
		}

		PC += 4;
	}

	void Int_FPU2op(u32 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: FsI(fd) = (int)floorf(F(fs)+0.5f); break; //round.w.s
		case 13: FsI(fd) = F(fs)>=0 ? (int)floorf(F(fs)) : (int)ceilf(F(fs)); break;//trunc.w.s
		case 14: FsI(fd) = (int)ceilf (F(fs)); break; //ceil.w.s
		case 15: FsI(fd) = (int)floorf(F(fs)); break; //floor.w.s
		case 32: F(fd) = (float)FsI(fs); break; //cvt.s.w

		case 36:
			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_(CPU,0,"Trying to interpret FPU2Op instruction that can't be interpreted");
			break;
		}
		PC += 4;
	}

	void Int_FPUComp(u32 op)
	{
		int fs = _FS;
		int ft = _FT;
		bool cond;
		switch (op & 0xf)
		{
		case 0: //f
		case 1: //un
		case 8: //sf
		case 9: //ngle
			cond = false;
			break;

		case 2: //eq
		case 10: //seq
		case 3: //ueq
		case 11: //ngl
			cond = (F(fs) == F(ft));
			break;

		case 4: //olt
		case 5: //ult
		case 12: //lt
		case 13: //nge
			cond = (F(fs) < F(ft));
			break;

		case 6: //ole
		case 7: //ule
		case 14: //le
		case 15: //ngt
			cond = (F(fs) <= F(ft));
			break;

		default:
			_dbg_assert_msg_(CPU,0,"Trying to interpret FPUComp instruction that can't be interpreted");
			cond = false;
			break;
		}
		currentMIPS->fpcond = cond;
		PC += 4;
	}

	void Int_FPU3op(u32 op)
	{
		int ft = _FT;
		int fs = _FS;
		int fd = _FD;

		switch (op & 0x3f)
		{
		case 0: F(fd) = F(fs) + F(ft); break; //add
		case 1: F(fd) = F(fs) - F(ft); break; //sub
		case 2: F(fd) = F(fs) * F(ft); break; //mul
		case 3: F(fd) = F(fs) / F(ft); break; //div
		default:
			_dbg_assert_msg_(CPU,0,"Trying to interpret FPU3Op instruction that can't be interpreted");
			break;
		}
		PC += 4;
	}

	void Int_Interrupt(u32 op)
	{
		static int reported = 0;
		switch (op & 1)
		{
		case 0:
			if (!reported) {
				WARN_LOG(CPU,"Disable/Enable Interrupt CPU instruction");
				reported = 1;
			}
			break;
		}
		PC += 4;
	}


	void Int_Emuhack(u32 op)
	{
		_dbg_assert_msg_(CPU,0,"Trying to interpret emuhack instruction that can't be interpreted");
	}


}
