/*
 * Project 64 - A Nintendo 64 emulator.
 *
 * (c) Copyright 2001 zilmar (zilmar@emulation64.com) and
 * Jabo (jabo@emulation64.com).
 *
 * pj64 homepage: www.pj64.net
 *
 * Permission to use, copy, modify and distribute Project64 in both binary and
 * source form, for non-commercial purposes, is hereby granted without fee,
 * providing that this license information and copyright notice appear with
 * all copies and any derived work.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event shall the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Project64 is freeware for PERSONAL USE only. Commercial users should
 * seek permission of the copyright holders first. Commercial use includes
 * charging money for Project64 or software derived from Project64.
 *
 * The copyright holders request that bug fixes and improvements to the code
 * should be forwarded to them so if they want them.
 *
 */

#include <stdio.h>
#include <math.h>
#include <float.h>
#include "main.h"
#include "cpu.h"
#include "usf.h"

#ifdef _MSC_VER
#define INLINE      __forceinline
#else
#define INLINE      inline __attribute__((always_inline))
#endif

#include "usf_internal.h"

#define ADDRESS_ERROR_EXCEPTION(Address,FromRead) \
	DoAddressError(state,state->NextInstruction == JUMP,Address,FromRead);\
	state->NextInstruction = JUMP;\
	state->JumpToLocation = state->PROGRAM_COUNTER;\
	return;

//#define TEST_COP1_USABLE_EXCEPTION
#define TEST_COP1_USABLE_EXCEPTION \
	if ((STATUS_REGISTER & STATUS_CU1) == 0) {\
		DoCopUnusableException(state,state->NextInstruction == JUMP,1);\
		state->NextInstruction = JUMP;\
		state->JumpToLocation = state->PROGRAM_COUNTER;\
		return;\
	}

#define TLB_READ_EXCEPTION(Address) \
	DoTLBMiss(state,state->NextInstruction == JUMP,Address);\
	state->NextInstruction = JUMP;\
	state->JumpToLocation = state->PROGRAM_COUNTER;\
	return;

/************************* OpCode functions *************************/
void r4300i_J (usf_state_t * state) {
	state->NextInstruction = DELAY_SLOT;
	state->JumpToLocation = (state->PROGRAM_COUNTER & 0xF0000000) + (state->Opcode.u.d.target << 2);
	TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,0,0);
}

void r4300i_JAL (usf_state_t * state) {
	state->NextInstruction = DELAY_SLOT;
	state->JumpToLocation = (state->PROGRAM_COUNTER & 0xF0000000) + (state->Opcode.u.d.target << 2);
	TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,0,0);
	state->GPR[31].DW= (int32_t)(state->PROGRAM_COUNTER + 8);
}

void r4300i_BEQ (usf_state_t * state) {
	state->NextInstruction = DELAY_SLOT;
	if (state->GPR[state->Opcode.u.b.rs].DW == state->GPR[state->Opcode.u.b.rt].DW) {
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
		TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,state->Opcode.u.b.rs,state->Opcode.u.b.rt);
	} else {
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}

void r4300i_BNE (usf_state_t * state) {
	state->NextInstruction = DELAY_SLOT;
	if (state->GPR[state->Opcode.u.b.rs].DW != state->GPR[state->Opcode.u.b.rt].DW) {
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
		TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,state->Opcode.u.b.rs,state->Opcode.u.b.rt);
	} else {
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}

void r4300i_BLEZ (usf_state_t * state) {
	state->NextInstruction = DELAY_SLOT;
	if (state->GPR[state->Opcode.u.b.rs].DW <= 0) {
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
		TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,state->Opcode.u.b.rs,0);
	} else {
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}

void r4300i_BGTZ (usf_state_t * state) {
	state->NextInstruction = DELAY_SLOT;
	if (state->GPR[state->Opcode.u.b.rs].DW > 0) {
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
		TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,state->Opcode.u.b.rs,0);
	} else {
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}

void r4300i_ADDI (usf_state_t * state) {
	if (state->Opcode.u.b.rt == 0) { return; }
	state->GPR[state->Opcode.u.b.rt].DW = (state->GPR[state->Opcode.u.b.rs].W[0] + ((int16_t)state->Opcode.u.c.immediate));
}

void r4300i_ADDIU (usf_state_t * state) {
	state->GPR[state->Opcode.u.b.rt].DW = (state->GPR[state->Opcode.u.b.rs].W[0] + ((int16_t)state->Opcode.u.c.immediate));
}

void r4300i_SLTI (usf_state_t * state) {
	if (state->GPR[state->Opcode.u.b.rs].DW < (int64_t)((int16_t)state->Opcode.u.c.immediate)) {
		state->GPR[state->Opcode.u.b.rt].DW = 1;
	} else {
		state->GPR[state->Opcode.u.b.rt].DW = 0;
	}
}

void r4300i_SLTIU (usf_state_t * state) {
	int32_t imm32 = (int16_t)state->Opcode.u.c.immediate;
	int64_t imm64;

	imm64 = imm32;
	state->GPR[state->Opcode.u.b.rt].DW = state->GPR[state->Opcode.u.b.rs].UDW < (uint64_t)imm64?1:0;
}

void r4300i_ANDI (usf_state_t * state) {
	state->GPR[state->Opcode.u.b.rt].DW = state->GPR[state->Opcode.u.b.rs].DW & state->Opcode.u.c.immediate;
}

void r4300i_ORI (usf_state_t * state) {
	state->GPR[state->Opcode.u.b.rt].DW = state->GPR[state->Opcode.u.b.rs].DW | state->Opcode.u.c.immediate;
}

void r4300i_XORI (usf_state_t * state) {
	state->GPR[state->Opcode.u.b.rt].DW = state->GPR[state->Opcode.u.b.rs].DW ^ state->Opcode.u.c.immediate;
}

void r4300i_LUI (usf_state_t * state) {
	if (state->Opcode.u.b.rt == 0) { return; }
	state->GPR[state->Opcode.u.b.rt].DW = (int32_t)((int16_t)state->Opcode.u.b.offset << 16);
}

void r4300i_BEQL (usf_state_t * state) {
	if (state->GPR[state->Opcode.u.b.rs].DW == state->GPR[state->Opcode.u.b.rt].DW) {
		state->NextInstruction = DELAY_SLOT;
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
		TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,state->Opcode.u.b.rs,state->Opcode.u.b.rt);
	} else {
		state->NextInstruction = JUMP;
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}

void r4300i_BNEL (usf_state_t * state) {
	if (state->GPR[state->Opcode.u.b.rs].DW != state->GPR[state->Opcode.u.b.rt].DW) {
		state->NextInstruction = DELAY_SLOT;
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
		TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,state->Opcode.u.b.rs,state->Opcode.u.b.rt);
	} else {
		state->NextInstruction = JUMP;
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}

void r4300i_BLEZL (usf_state_t * state) {
	if (state->GPR[state->Opcode.u.b.rs].DW <= 0) {
		state->NextInstruction = DELAY_SLOT;
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
		TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,state->Opcode.u.b.rs,0);
	} else {
		state->NextInstruction = JUMP;
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}

void r4300i_BGTZL (usf_state_t * state) {
	if (state->GPR[state->Opcode.u.b.rs].DW > 0) {
		state->NextInstruction = DELAY_SLOT;
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
		TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,state->Opcode.u.b.rs,0);
	} else {
		state->NextInstruction = JUMP;
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}

void r4300i_DADDIU (usf_state_t * state) {
	state->GPR[state->Opcode.u.b.rt].DW = state->GPR[state->Opcode.u.b.rs].DW + (int64_t)((int16_t)state->Opcode.u.c.immediate);
}

uint64_t LDL_MASK[8] = { 0ULL,0xFFULL,0xFFFFULL,0xFFFFFFULL,0xFFFFFFFFULL,0xFFFFFFFFFFULL, 0xFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFULL };
int32_t LDL_SHIFT[8] = { 0, 8, 16, 24, 32, 40, 48, 56 };

void r4300i_LDL (usf_state_t * state) {
	uint32_t Offset, Address;
	uint64_t Value;

	Address = state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	Offset  = Address & 7;

	if (!r4300i_LD_VAddr(state,(Address & ~7),&Value)) {
		return;
	}
	state->GPR[state->Opcode.u.b.rt].DW = state->GPR[state->Opcode.u.b.rt].DW & LDL_MASK[Offset];
	state->GPR[state->Opcode.u.b.rt].DW += Value << LDL_SHIFT[Offset];
}

uint64_t LDR_MASK[8] = { 0xFFFFFFFFFFFFFF00ULL, 0xFFFFFFFFFFFF0000ULL,
                      0xFFFFFFFFFF000000ULL, 0xFFFFFFFF00000000ULL,
                      0xFFFFFF0000000000ULL, 0xFFFF000000000000ULL,
                      0xFF00000000000000ULL, 0 };
int32_t LDR_SHIFT[8] = { 56, 48, 40, 32, 24, 16, 8, 0 };

void r4300i_LDR (usf_state_t * state) {
	uint32_t Offset, Address;
	uint64_t Value;

	Address = state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	Offset  = Address & 7;

	if (!r4300i_LD_VAddr(state,(Address & ~7),&Value)) {
		return;
	}

	state->GPR[state->Opcode.u.b.rt].DW = state->GPR[state->Opcode.u.b.rt].DW & LDR_MASK[Offset];
	state->GPR[state->Opcode.u.b.rt].DW += Value >> LDR_SHIFT[Offset];

}

void r4300i_LB (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	if (state->Opcode.u.b.rt == 0) { return; }
	if (!r4300i_LB_VAddr(state,Address,&state->GPR[state->Opcode.u.b.rt].UB[0])) {
		TLB_READ_EXCEPTION(Address);
	} else {
		state->GPR[state->Opcode.u.b.rt].DW = state->GPR[state->Opcode.u.b.rt].B[0];
	}
}

void r4300i_LH (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	if ((Address & 1) != 0) { ADDRESS_ERROR_EXCEPTION(Address,1); }
	if (!r4300i_LH_VAddr(state,Address,&state->GPR[state->Opcode.u.b.rt].UHW[0])) {
		//if (ShowTLBMisses) {
            // Too spammy
			//DisplayError(state, "LH TLB: %X",Address);
		//}
		TLB_READ_EXCEPTION(Address);
	} else {
		state->GPR[state->Opcode.u.b.rt].DW = state->GPR[state->Opcode.u.b.rt].HW[0];
	}
}

uint32_t LWL_MASK[4] = { 0,0xFF,0xFFFF,0xFFFFFF };
int32_t LWL_SHIFT[4] = { 0, 8, 16, 24};

void r4300i_LWL (usf_state_t * state) {
	uint32_t Offset, Address, Value;

	Address = state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	Offset  = Address & 3;

	if (!r4300i_LW_VAddr(state,(Address & ~3),&Value)) {
		return;
	}

	state->GPR[state->Opcode.u.b.rt].DW = (int32_t)(state->GPR[state->Opcode.u.b.rt].W[0] & LWL_MASK[Offset]);
	state->GPR[state->Opcode.u.b.rt].DW += (int32_t)(Value << LWL_SHIFT[Offset]);
}

void r4300i_LW (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;


//	if ((Address & 3) != 0) { ADDRESS_ERROR_EXCEPTION(Address,1); }

	if (state->Opcode.u.b.rt == 0) { return; }


	if (!r4300i_LW_VAddr(state,Address,&state->GPR[state->Opcode.u.b.rt].UW[0])) {
		//if (ShowTLBMisses) {
			//printf("LW TLB: %X",Address);
		//}
		TLB_READ_EXCEPTION(Address);
	} else {
		state->GPR[state->Opcode.u.b.rt].DW = state->GPR[state->Opcode.u.b.rt].W[0];
	}
}

void r4300i_LBU (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	if (!r4300i_LB_VAddr(state,Address,&state->GPR[state->Opcode.u.b.rt].UB[0])) {
		//if (ShowTLBMisses) {
            // Too spammy
			//DisplayError(state, "LBU TLB: %X",Address);
		//}
		TLB_READ_EXCEPTION(Address);
	} else {
		state->GPR[state->Opcode.u.b.rt].UDW = state->GPR[state->Opcode.u.b.rt].UB[0];
	}
}

void r4300i_LHU (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	if ((Address & 1) != 0) { ADDRESS_ERROR_EXCEPTION(Address,1); }
	if (!r4300i_LH_VAddr(state,Address,&state->GPR[state->Opcode.u.b.rt].UHW[0])) {
		//if (ShowTLBMisses) {
            // Too spammy
			//DisplayError(state, "LHU TLB: %X",Address);
		//}
		TLB_READ_EXCEPTION(Address);
	} else {
		state->GPR[state->Opcode.u.b.rt].UDW = state->GPR[state->Opcode.u.b.rt].UHW[0];
	}
}

uint32_t LWR_MASK[4] = { 0xFFFFFF00, 0xFFFF0000, 0xFF000000, 0 };
int32_t LWR_SHIFT[4] = { 24, 16 ,8, 0 };

void r4300i_LWR (usf_state_t * state) {
	uint32_t Offset, Address, Value;

	Address = state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	Offset  = Address & 3;

	if (!r4300i_LW_VAddr(state,(Address & ~3),&Value)) {
		return;
	}

	state->GPR[state->Opcode.u.b.rt].DW = (int32_t)(state->GPR[state->Opcode.u.b.rt].W[0] & LWR_MASK[Offset]);
	state->GPR[state->Opcode.u.b.rt].DW += (int32_t)(Value >> LWR_SHIFT[Offset]);
}

void r4300i_LWU (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	if ((Address & 3) != 0) { ADDRESS_ERROR_EXCEPTION(Address,1); }
	if (state->Opcode.u.b.rt == 0) { return; }

	if (!r4300i_LW_VAddr(state,Address,&state->GPR[state->Opcode.u.b.rt].UW[0])) {
		//if (ShowTLBMisses) {
            // Too spammy
			//DisplayError(state, "LWU TLB: %X",Address);
		//}
		TLB_READ_EXCEPTION(Address);
	} else {
		state->GPR[state->Opcode.u.b.rt].UDW = state->GPR[state->Opcode.u.b.rt].UW[0];
	}
}

void r4300i_SB (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	if (!r4300i_SB_VAddr(state,Address,state->GPR[state->Opcode.u.b.rt].UB[0])) {
	}
}

void r4300i_SH (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	if ((Address & 1) != 0) { ADDRESS_ERROR_EXCEPTION(Address,0); }
	if (!r4300i_SH_VAddr(state,Address,state->GPR[state->Opcode.u.b.rt].UHW[0])) {
	}
}

uint32_t SWL_MASK[4] = { 0,0xFF000000,0xFFFF0000,0xFFFFFF00 };
int32_t SWL_SHIFT[4] = { 0, 8, 16, 24 };

void r4300i_SWL (usf_state_t * state) {
	uint32_t Offset, Address, Value;

	Address = state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	Offset  = Address & 3;

	if (!r4300i_LW_VAddr(state,(Address & ~3),&Value)) {
		return;
	}

	Value &= SWL_MASK[Offset];
	Value += state->GPR[state->Opcode.u.b.rt].UW[0] >> SWL_SHIFT[Offset];

	if (!r4300i_SW_VAddr(state,(Address & ~0x03),Value)) {
	}
}


void r4300i_SW (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	if ((Address & 3) != 0) { ADDRESS_ERROR_EXCEPTION(Address,0); }
	if (!r4300i_SW_VAddr(state,Address,state->GPR[state->Opcode.u.b.rt].UW[0])) {
	}
	//TranslateVaddr(&Address);
	//if (Address == 0x00090AA0) {
	//	LogMessage("%X: Write %X to %X",state->PROGRAM_COUNTER,state->GPR[state->Opcode.u.b.rt].UW[0],state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset);
	//}
}

uint64_t SDL_MASK[8] = { 0,0xFF00000000000000ULL,
						0xFFFF000000000000ULL,
						0xFFFFFF0000000000ULL,
						0xFFFFFFFF00000000ULL,
					    0xFFFFFFFFFF000000ULL,
						0xFFFFFFFFFFFF0000ULL,
						0xFFFFFFFFFFFFFF00ULL
					};
int32_t SDL_SHIFT[8] = { 0, 8, 16, 24, 32, 40, 48, 56 };

void r4300i_SDL (usf_state_t * state) {
	uint32_t Offset, Address;
	uint64_t Value;

	Address = state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	Offset  = Address & 7;

	if (!r4300i_LD_VAddr(state,(Address & ~7),&Value)) {
		return;
	}

	Value &= SDL_MASK[Offset];
	Value += state->GPR[state->Opcode.u.b.rt].UDW >> SDL_SHIFT[Offset];

	if (!r4300i_SD_VAddr(state,(Address & ~7),Value)) {
	}
}

uint64_t SDR_MASK[8] = { 0x00FFFFFFFFFFFFFFULL,
					  0x0000FFFFFFFFFFFFULL,
					  0x000000FFFFFFFFFFULL,
					  0x00000000FFFFFFFFULL,
					  0x0000000000FFFFFFULL,
					  0x000000000000FFFFULL,
					  0x00000000000000FFULL,
					  0x0000000000000000ULL
					};
int32_t SDR_SHIFT[8] = { 56,48,40,32,24,16,8,0 };

void r4300i_SDR (usf_state_t * state) {
	uint32_t Offset, Address;
	uint64_t Value;

	Address = state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	Offset  = Address & 7;

	if (!r4300i_LD_VAddr(state,(Address & ~7),&Value)) {
		return;
	}

	Value &= SDR_MASK[Offset];
	Value += state->GPR[state->Opcode.u.b.rt].UDW << SDR_SHIFT[Offset];

	if (!r4300i_SD_VAddr(state,(Address & ~7),Value)) {
	}
}

uint32_t SWR_MASK[4] = { 0x00FFFFFF,0x0000FFFF,0x000000FF,0x00000000 };
int32_t SWR_SHIFT[4] = { 24, 16 , 8, 0  };

void r4300i_SWR (usf_state_t * state) {
	uint32_t Offset, Address, Value;

	Address = state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	Offset  = Address & 3;

	if (!r4300i_LW_VAddr(state,(Address & ~3),&Value)) {
		return;
	}

	Value &= SWR_MASK[Offset];
	Value += state->GPR[state->Opcode.u.b.rt].UW[0] << SWR_SHIFT[Offset];

	if (!r4300i_SW_VAddr(state,(Address & ~0x03),Value)) {
	}
}

void r4300i_CACHE (usf_state_t * state) {
    (void)state;
}

void r4300i_LL (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	uintptr_t ll = 0;
	if ((Address & 3) != 0) { ADDRESS_ERROR_EXCEPTION(Address,1); }

	if (state->Opcode.u.b.rt == 0) { return; }

	if (!r4300i_LW_VAddr(state,Address,&state->GPR[state->Opcode.u.b.rt].UW[0])) {
		//if (ShowTLBMisses) {
            // Too spammy
			//DisplayError(state, "LW TLB: %X",Address);
		//}
		TLB_READ_EXCEPTION(Address);
	} else {
		state->GPR[state->Opcode.u.b.rt].DW = state->GPR[state->Opcode.u.b.rt].W[0];
	}
	state->LLBit = 1;
	state->LLAddr = Address;
	ll = state->LLAddr;
	TranslateVaddr(state, &ll);
	state->LLAddr = (uint32_t) ll;
}

void r4300i_LWC1 (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (uint32_t)((int16_t)state->Opcode.u.b.offset);
	TEST_COP1_USABLE_EXCEPTION
	if ((Address & 3) != 0) { ADDRESS_ERROR_EXCEPTION(Address,1); }
	if (!r4300i_LW_VAddr(state,Address,&*(uint32_t *)state->FPRFloatLocation[state->Opcode.u.f.ft])) {
		//if (ShowTLBMisses) {
            // Too spammy
			//DisplayError(state, "LWC1 TLB: %X",Address);
		//}
		TLB_READ_EXCEPTION(Address);
	}
}

void r4300i_SC (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	if ((Address & 3) != 0) { ADDRESS_ERROR_EXCEPTION(Address,0); }
	if (state->LLBit == 1) {
		if (!r4300i_SW_VAddr(state,Address,state->GPR[state->Opcode.u.b.rt].UW[0])) {
			DisplayError(state, "SW TLB: %X",Address);
		}
	}
	state->GPR[state->Opcode.u.b.rt].UW[0] = state->LLBit;
}

void r4300i_LD (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	if ((Address & 7) != 0) { ADDRESS_ERROR_EXCEPTION(Address,1); }
	if (!r4300i_LD_VAddr(state,Address,&state->GPR[state->Opcode.u.b.rt].UDW)) {
	}
}


void r4300i_LDC1 (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;

	TEST_COP1_USABLE_EXCEPTION
	if ((Address & 7) != 0) { ADDRESS_ERROR_EXCEPTION(Address,1); }
	if (!r4300i_LD_VAddr(state,Address,&*(uint64_t *)state->FPRDoubleLocation[state->Opcode.u.f.ft])) {
	}
}

void r4300i_SWC1 (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	TEST_COP1_USABLE_EXCEPTION
	if ((Address & 3) != 0) { ADDRESS_ERROR_EXCEPTION(Address,0); }

	if (!r4300i_SW_VAddr(state,Address,*(uint32_t *)state->FPRFloatLocation[state->Opcode.u.f.ft])) {
	}
}

void r4300i_SDC1 (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;

	TEST_COP1_USABLE_EXCEPTION
	if ((Address & 7) != 0) { ADDRESS_ERROR_EXCEPTION(Address,0); }
	if (!r4300i_SD_VAddr(state,Address,*(int64_t *)state->FPRDoubleLocation[state->Opcode.u.f.ft])) {
	}
}

void r4300i_SD (usf_state_t * state) {
	uint32_t Address =  state->GPR[state->Opcode.u.c.base].UW[0] + (int16_t)state->Opcode.u.b.offset;
	if ((Address & 7) != 0) { ADDRESS_ERROR_EXCEPTION(Address,0); }
	if (!r4300i_SD_VAddr(state,Address,state->GPR[state->Opcode.u.b.rt].UDW)) {
	}
}
/********************** R4300i state->Opcodes: Special **********************/
void r4300i_SPECIAL_SLL (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = (state->GPR[state->Opcode.u.b.rt].W[0] << state->Opcode.u.e.sa);
}

void r4300i_SPECIAL_SRL (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = (int32_t)(state->GPR[state->Opcode.u.b.rt].UW[0] >> state->Opcode.u.e.sa);
}

void r4300i_SPECIAL_SRA (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = (state->GPR[state->Opcode.u.b.rt].W[0] >> state->Opcode.u.e.sa);
}

void r4300i_SPECIAL_SLLV (usf_state_t * state) {
	if (state->Opcode.u.e.rd == 0) { return; }
	state->GPR[state->Opcode.u.e.rd].DW = (state->GPR[state->Opcode.u.b.rt].W[0] << (state->GPR[state->Opcode.u.b.rs].UW[0] & 0x1F));
}

void r4300i_SPECIAL_SRLV (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = (int32_t)(state->GPR[state->Opcode.u.b.rt].UW[0] >> (state->GPR[state->Opcode.u.b.rs].UW[0] & 0x1F));
}

void r4300i_SPECIAL_SRAV (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = (state->GPR[state->Opcode.u.b.rt].W[0] >> (state->GPR[state->Opcode.u.b.rs].UW[0] & 0x1F));
}

void r4300i_SPECIAL_JR (usf_state_t * state) {
	state->NextInstruction = DELAY_SLOT;
	state->JumpToLocation = state->GPR[state->Opcode.u.b.rs].UW[0];
}

void r4300i_SPECIAL_JALR (usf_state_t * state) {
	state->NextInstruction = DELAY_SLOT;
	state->JumpToLocation = state->GPR[state->Opcode.u.b.rs].UW[0];
	state->GPR[state->Opcode.u.e.rd].DW = (int32_t)(state->PROGRAM_COUNTER + 8);
}

void r4300i_SPECIAL_SYSCALL (usf_state_t * state) {
	DoSysCallException(state, state->NextInstruction == JUMP);
	state->NextInstruction = JUMP;
	state->JumpToLocation = state->PROGRAM_COUNTER;
}

void r4300i_SPECIAL_BREAK (usf_state_t * state) {
	*state->WaitMode=1;
}

void r4300i_SPECIAL_SYNC (usf_state_t * state) {
    (void)state;
}

void r4300i_SPECIAL_MFHI (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = state->HI.DW;
}

void r4300i_SPECIAL_MTHI (usf_state_t * state) {
	state->HI.DW = state->GPR[state->Opcode.u.b.rs].DW;
}

void r4300i_SPECIAL_MFLO (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = state->LO.DW;
}

void r4300i_SPECIAL_MTLO (usf_state_t * state) {
	state->LO.DW = state->GPR[state->Opcode.u.b.rs].DW;
}

void r4300i_SPECIAL_DSLLV (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = state->GPR[state->Opcode.u.b.rt].DW << (state->GPR[state->Opcode.u.b.rs].UW[0] & 0x3F);
}

void r4300i_SPECIAL_DSRLV (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].UDW = state->GPR[state->Opcode.u.b.rt].UDW >> (state->GPR[state->Opcode.u.b.rs].UW[0] & 0x3F);
}

void r4300i_SPECIAL_DSRAV (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = state->GPR[state->Opcode.u.b.rt].DW >> (state->GPR[state->Opcode.u.b.rs].UW[0] & 0x3F);
}

void r4300i_SPECIAL_MULT (usf_state_t * state) {
	state->HI.DW = (int64_t)(state->GPR[state->Opcode.u.b.rs].W[0]) * (int64_t)(state->GPR[state->Opcode.u.b.rt].W[0]);
	state->LO.DW = state->HI.W[0];
	state->HI.DW = state->HI.W[1];
}

void r4300i_SPECIAL_MULTU (usf_state_t * state) {
	state->HI.DW = (uint64_t)(state->GPR[state->Opcode.u.b.rs].UW[0]) * (uint64_t)(state->GPR[state->Opcode.u.b.rt].UW[0]);
	state->LO.DW = state->HI.W[0];
	state->HI.DW = state->HI.W[1];
}

void r4300i_SPECIAL_DIV (usf_state_t * state) {
	if ( state->GPR[state->Opcode.u.b.rt].UDW != 0 ) {
		state->LO.DW = state->GPR[state->Opcode.u.b.rs].W[0] / state->GPR[state->Opcode.u.b.rt].W[0];
		state->HI.DW = state->GPR[state->Opcode.u.b.rs].W[0] % state->GPR[state->Opcode.u.b.rt].W[0];
	} else {
	}
}

void r4300i_SPECIAL_DIVU (usf_state_t * state) {
	if ( state->GPR[state->Opcode.u.b.rt].UDW != 0 ) {
		state->LO.DW = (int32_t)(state->GPR[state->Opcode.u.b.rs].UW[0] / state->GPR[state->Opcode.u.b.rt].UW[0]);
		state->HI.DW = (int32_t)(state->GPR[state->Opcode.u.b.rs].UW[0] % state->GPR[state->Opcode.u.b.rt].UW[0]);
	} else {
	}
}

void r4300i_SPECIAL_DMULT (usf_state_t * state) {
	MIPS_DWORD Tmp[3];

	state->LO.UDW = (uint64_t)state->GPR[state->Opcode.u.b.rs].UW[0] * (uint64_t)state->GPR[state->Opcode.u.b.rt].UW[0];
	Tmp[0].UDW = (int64_t)state->GPR[state->Opcode.u.b.rs].W[1] * (int64_t)(uint64_t)state->GPR[state->Opcode.u.b.rt].UW[0];
	Tmp[1].UDW = (int64_t)(uint64_t)state->GPR[state->Opcode.u.b.rs].UW[0] * (int64_t)state->GPR[state->Opcode.u.b.rt].W[1];
	state->HI.UDW = (int64_t)state->GPR[state->Opcode.u.b.rs].W[1] * (int64_t)state->GPR[state->Opcode.u.b.rt].W[1];

	Tmp[2].UDW = (uint64_t)state->LO.UW[1] + (uint64_t)Tmp[0].UW[0] + (uint64_t)Tmp[1].UW[0];
	state->LO.UDW += ((uint64_t)Tmp[0].UW[0] + (uint64_t)Tmp[1].UW[0]) << 32;
	state->HI.UDW += (uint64_t)Tmp[0].W[1] + (uint64_t)Tmp[1].W[1] + Tmp[2].UW[1];
}

void r4300i_SPECIAL_DMULTU (usf_state_t * state) {
	MIPS_DWORD Tmp[3];

	state->LO.UDW = (uint64_t)state->GPR[state->Opcode.u.b.rs].UW[0] * (uint64_t)state->GPR[state->Opcode.u.b.rt].UW[0];
	Tmp[0].UDW = (uint64_t)state->GPR[state->Opcode.u.b.rs].UW[1] * (uint64_t)state->GPR[state->Opcode.u.b.rt].UW[0];
	Tmp[1].UDW = (uint64_t)state->GPR[state->Opcode.u.b.rs].UW[0] * (uint64_t)state->GPR[state->Opcode.u.b.rt].UW[1];
	state->HI.UDW = (uint64_t)state->GPR[state->Opcode.u.b.rs].UW[1] * (uint64_t)state->GPR[state->Opcode.u.b.rt].UW[1];

	Tmp[2].UDW = (uint64_t)state->LO.UW[1] + (uint64_t)Tmp[0].UW[0] + (uint64_t)Tmp[1].UW[0];
	state->LO.UDW += ((uint64_t)Tmp[0].UW[0] + (uint64_t)Tmp[1].UW[0]) << 32;
	state->HI.UDW += (uint64_t)Tmp[0].UW[1] + (uint64_t)Tmp[1].UW[1] + Tmp[2].UW[1];
}

void r4300i_SPECIAL_DDIV (usf_state_t * state) {
	if ( state->GPR[state->Opcode.u.b.rt].UDW != 0 ) {
		state->LO.DW = state->GPR[state->Opcode.u.b.rs].DW / state->GPR[state->Opcode.u.b.rt].DW;
		state->HI.DW = state->GPR[state->Opcode.u.b.rs].DW % state->GPR[state->Opcode.u.b.rt].DW;
	} else {
	}
}

void r4300i_SPECIAL_DDIVU (usf_state_t * state) {
	if ( state->GPR[state->Opcode.u.b.rt].UDW != 0 ) {
		state->LO.UDW = state->GPR[state->Opcode.u.b.rs].UDW / state->GPR[state->Opcode.u.b.rt].UDW;
		state->HI.UDW = state->GPR[state->Opcode.u.b.rs].UDW % state->GPR[state->Opcode.u.b.rt].UDW;
	} else {
	}
}

void r4300i_SPECIAL_ADD (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = state->GPR[state->Opcode.u.b.rs].W[0] + state->GPR[state->Opcode.u.b.rt].W[0];
}

void r4300i_SPECIAL_ADDU (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = state->GPR[state->Opcode.u.b.rs].W[0] + state->GPR[state->Opcode.u.b.rt].W[0];
}

void r4300i_SPECIAL_SUB (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = state->GPR[state->Opcode.u.b.rs].W[0] - state->GPR[state->Opcode.u.b.rt].W[0];
}

void r4300i_SPECIAL_SUBU (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = state->GPR[state->Opcode.u.b.rs].W[0] - state->GPR[state->Opcode.u.b.rt].W[0];
}

void r4300i_SPECIAL_AND (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = state->GPR[state->Opcode.u.b.rs].DW & state->GPR[state->Opcode.u.b.rt].DW;
}

void r4300i_SPECIAL_OR (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = state->GPR[state->Opcode.u.b.rs].DW | state->GPR[state->Opcode.u.b.rt].DW;
}

void r4300i_SPECIAL_XOR (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = state->GPR[state->Opcode.u.b.rs].DW ^ state->GPR[state->Opcode.u.b.rt].DW;
}

void r4300i_SPECIAL_NOR (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = ~(state->GPR[state->Opcode.u.b.rs].DW | state->GPR[state->Opcode.u.b.rt].DW);
}

void r4300i_SPECIAL_SLT (usf_state_t * state) {
	if (state->GPR[state->Opcode.u.b.rs].DW < state->GPR[state->Opcode.u.b.rt].DW) {
		state->GPR[state->Opcode.u.e.rd].DW = 1;
	} else {
		state->GPR[state->Opcode.u.e.rd].DW = 0;
	}
}

void r4300i_SPECIAL_SLTU (usf_state_t * state) {
	if (state->GPR[state->Opcode.u.b.rs].UDW < state->GPR[state->Opcode.u.b.rt].UDW) {
		state->GPR[state->Opcode.u.e.rd].DW = 1;
	} else {
		state->GPR[state->Opcode.u.e.rd].DW = 0;
	}
}

void r4300i_SPECIAL_DADD (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = state->GPR[state->Opcode.u.b.rs].DW + state->GPR[state->Opcode.u.b.rt].DW;
}

void r4300i_SPECIAL_DADDU (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = state->GPR[state->Opcode.u.b.rs].DW + state->GPR[state->Opcode.u.b.rt].DW;
}

void r4300i_SPECIAL_DSUB (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = state->GPR[state->Opcode.u.b.rs].DW - state->GPR[state->Opcode.u.b.rt].DW;
}

void r4300i_SPECIAL_DSUBU (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = state->GPR[state->Opcode.u.b.rs].DW - state->GPR[state->Opcode.u.b.rt].DW;
}

void r4300i_SPECIAL_TEQ (usf_state_t * state) {
	if (state->GPR[state->Opcode.u.b.rs].DW == state->GPR[state->Opcode.u.b.rt].DW) {
	}
}

void r4300i_SPECIAL_DSLL (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = (state->GPR[state->Opcode.u.b.rt].DW << state->Opcode.u.e.sa);
}

void r4300i_SPECIAL_DSRL (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].UDW = (state->GPR[state->Opcode.u.b.rt].UDW >> state->Opcode.u.e.sa);
}

void r4300i_SPECIAL_DSRA (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = (state->GPR[state->Opcode.u.b.rt].DW >> state->Opcode.u.e.sa);
}

void r4300i_SPECIAL_DSLL32 (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = (state->GPR[state->Opcode.u.b.rt].DW << (state->Opcode.u.e.sa + 32));
}

void r4300i_SPECIAL_DSRL32 (usf_state_t * state) {
   state->GPR[state->Opcode.u.e.rd].UDW = (state->GPR[state->Opcode.u.b.rt].UDW >> (state->Opcode.u.e.sa + 32));
}

void r4300i_SPECIAL_DSRA32 (usf_state_t * state) {
	state->GPR[state->Opcode.u.e.rd].DW = (state->GPR[state->Opcode.u.b.rt].DW >> (state->Opcode.u.e.sa + 32));
}

/********************** R4300i state->Opcodes: RegImm **********************/
void r4300i_REGIMM_BLTZ (usf_state_t * state) {
	state->NextInstruction = DELAY_SLOT;
	if (state->GPR[state->Opcode.u.b.rs].DW < 0) {
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
		TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,state->Opcode.u.b.rs,0);
	} else {
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}

void r4300i_REGIMM_BGEZ (usf_state_t * state) {
	state->NextInstruction = DELAY_SLOT;
	if (state->GPR[state->Opcode.u.b.rs].DW >= 0) {
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
		TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,state->Opcode.u.b.rs,0);
	} else {
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}

void r4300i_REGIMM_BLTZL (usf_state_t * state) {
	if (state->GPR[state->Opcode.u.b.rs].DW < 0) {
		state->NextInstruction = DELAY_SLOT;
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
		TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,state->Opcode.u.b.rs,0);
	} else {
		state->NextInstruction = JUMP;
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}

void r4300i_REGIMM_BGEZL (usf_state_t * state) {
	if (state->GPR[state->Opcode.u.b.rs].DW >= 0) {
		state->NextInstruction = DELAY_SLOT;
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
		TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,state->Opcode.u.b.rs,0);
	} else {
		state->NextInstruction = JUMP;
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}

void r4300i_REGIMM_BLTZAL (usf_state_t * state) {
	state->NextInstruction = DELAY_SLOT;
	if (state->GPR[state->Opcode.u.b.rs].DW < 0) {
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
		TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,state->Opcode.u.b.rs,0);
	} else {
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
	state->GPR[31].DW= (int32_t)(state->PROGRAM_COUNTER + 8);
}

void r4300i_REGIMM_BGEZAL (usf_state_t * state) {
	state->NextInstruction = DELAY_SLOT;
	if (state->GPR[state->Opcode.u.b.rs].DW >= 0) {
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
		TestInterpreterJump(state,state->PROGRAM_COUNTER,state->JumpToLocation,state->Opcode.u.b.rs,0);
	} else {
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
	state->GPR[31].DW = (int32_t)(state->PROGRAM_COUNTER + 8);
}
/************************** COP0 functions **************************/
void r4300i_COP0_MF (usf_state_t * state) {
	state->GPR[state->Opcode.u.b.rt].DW = (int32_t)state->CP0[state->Opcode.u.e.rd];
}

void r4300i_COP0_MT (usf_state_t * state) {
	switch (state->Opcode.u.e.rd) {
	case 0: //Index
	case 2: //EntryLo0
	case 3: //EntryLo1
	case 5: //PageMask
	case 6: //Wired
	case 10: //Entry Hi
	case 14: //EPC
	case 16: //Config
	case 18: //WatchLo
	case 19: //WatchHi
	case 28: //Tag lo
	case 29: //Tag Hi
	case 30: //ErrEPC
		state->CP0[state->Opcode.u.e.rd] = state->GPR[state->Opcode.u.b.rt].UW[0];
		break;
	case 4: //Context
		state->CP0[state->Opcode.u.e.rd] = state->GPR[state->Opcode.u.b.rt].UW[0] & 0xFF800000;
		break;
	case 9: //Count
		state->CP0[state->Opcode.u.e.rd]= state->GPR[state->Opcode.u.b.rt].UW[0];
		ChangeCompareTimer(state);
		break;
	case 11: //Compare
		state->CP0[state->Opcode.u.e.rd] = state->GPR[state->Opcode.u.b.rt].UW[0];
		FAKE_CAUSE_REGISTER &= ~CAUSE_IP7;
		ChangeCompareTimer(state);
		break;
	case 12: //Status
		if ((state->CP0[state->Opcode.u.e.rd] ^ state->GPR[state->Opcode.u.b.rt].UW[0]) != 0) {
			state->CP0[state->Opcode.u.e.rd] = state->GPR[state->Opcode.u.b.rt].UW[0];
			SetFpuLocations(state);
		} else {
			state->CP0[state->Opcode.u.e.rd] = state->GPR[state->Opcode.u.b.rt].UW[0];
		}
		if ((state->CP0[state->Opcode.u.e.rd] & 0x18) != 0) {
		}
		CheckInterrupts(state);
		break;
	case 13: //cause
		state->CP0[state->Opcode.u.e.rd] &= 0xFFFFCFF;
		break;
	default:
		R4300i_UnknownOpcode(state);
	}

}

/************************** COP0 CO functions ***********************/
void r4300i_COP0_CO_TLBR (usf_state_t * state) {
	TLB_Read(state);
}

void r4300i_COP0_CO_TLBWI (usf_state_t * state) {
	WriteTLBEntry(state, INDEX_REGISTER & 0x1F);
}

void r4300i_COP0_CO_TLBWR (usf_state_t * state) {
	WriteTLBEntry(state, RANDOM_REGISTER & 0x1F);
}

void r4300i_COP0_CO_TLBP (usf_state_t * state) {
	TLB_Probe(state);
}

void r4300i_COP0_CO_ERET (usf_state_t * state) {
	state->NextInstruction = JUMP;
	if ((STATUS_REGISTER & STATUS_ERL) != 0) {
		state->JumpToLocation = ERROREPC_REGISTER;
		STATUS_REGISTER &= ~STATUS_ERL;
	} else {
		state->JumpToLocation = EPC_REGISTER;
		STATUS_REGISTER &= ~STATUS_EXL;
	}
	state->LLBit = 0;
	CheckInterrupts(state);
}

/************************** COP1 functions **************************/
void r4300i_COP1_MF (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	state->GPR[state->Opcode.u.b.rt].DW = *(int32_t *)state->FPRFloatLocation[state->Opcode.u.f.fs];
}

void r4300i_COP1_DMF (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	state->GPR[state->Opcode.u.b.rt].DW = *(int64_t *)state->FPRDoubleLocation[state->Opcode.u.f.fs];
}

void r4300i_COP1_CF (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	if (state->Opcode.u.f.fs != 31 && state->Opcode.u.f.fs != 0) {
		return;
	}
	state->GPR[state->Opcode.u.b.rt].DW = (int32_t)state->FPCR[state->Opcode.u.f.fs];
}

void r4300i_COP1_MT (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(int32_t *)state->FPRFloatLocation[state->Opcode.u.f.fs] = state->GPR[state->Opcode.u.b.rt].W[0];
}

void r4300i_COP1_DMT (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(int64_t *)state->FPRDoubleLocation[state->Opcode.u.f.fs] = state->GPR[state->Opcode.u.b.rt].DW;
}

void r4300i_COP1_CT (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	if (state->Opcode.u.f.fs == 31) {
		state->FPCR[state->Opcode.u.f.fs] = state->GPR[state->Opcode.u.b.rt].W[0];
		return;
	}
}

/************************* COP1: BC1 functions ***********************/
void r4300i_COP1_BCF (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	state->NextInstruction = DELAY_SLOT;
	if ((state->FPCR[31] & FPCSR_C) == 0) {
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
	} else {
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}

void r4300i_COP1_BCT (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	state->NextInstruction = DELAY_SLOT;
	if ((state->FPCR[31] & FPCSR_C) != 0) {
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
	} else {
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}

void r4300i_COP1_BCFL (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	if ((state->FPCR[31] & FPCSR_C) == 0) {
		state->NextInstruction = DELAY_SLOT;
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
	} else {
		state->NextInstruction = JUMP;
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}

void r4300i_COP1_BCTL (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	if ((state->FPCR[31] & FPCSR_C) != 0) {
		state->NextInstruction = DELAY_SLOT;
		state->JumpToLocation = state->PROGRAM_COUNTER + ((int16_t)state->Opcode.u.b.offset << 2) + 4;
	} else {
		state->NextInstruction = JUMP;
		state->JumpToLocation = state->PROGRAM_COUNTER + 8;
	}
}
/************************** COP1: S functions ************************/
static INLINE void Float_RoundToInteger32( int32_t * Dest, float * Source ) {
	*Dest = (int32_t)*Source;
}

static INLINE void Float_RoundToInteger64( int64_t * Dest, float * Source ) {
	*Dest = (int64_t)*Source;
}

void r4300i_COP1_S_ADD (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(float *)state->FPRFloatLocation[state->Opcode.u.f.fd] = (*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs] + *(float *)state->FPRFloatLocation[state->Opcode.u.f.ft]);
}

void r4300i_COP1_S_SUB (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(float *)state->FPRFloatLocation[state->Opcode.u.f.fd] = (*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs] - *(float *)state->FPRFloatLocation[state->Opcode.u.f.ft]);
}

void r4300i_COP1_S_MUL (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(float *)state->FPRFloatLocation[state->Opcode.u.f.fd] = (*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs] * *(float *)state->FPRFloatLocation[state->Opcode.u.f.ft]);
}

void r4300i_COP1_S_DIV (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(float *)state->FPRFloatLocation[state->Opcode.u.f.fd] = (*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs] / *(float *)state->FPRFloatLocation[state->Opcode.u.f.ft]);
}

void r4300i_COP1_S_SQRT (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(float *)state->FPRFloatLocation[state->Opcode.u.f.fd] = (float)sqrt(*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_S_ABS (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(float *)state->FPRFloatLocation[state->Opcode.u.f.fd] = (float)fabs(*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_S_MOV (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(float *)state->FPRFloatLocation[state->Opcode.u.f.fd] = *(float *)state->FPRFloatLocation[state->Opcode.u.f.fs];
}

void r4300i_COP1_S_NEG (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(float *)state->FPRFloatLocation[state->Opcode.u.f.fd] = (*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs] * -1.0f);
}

void r4300i_COP1_S_TRUNC_L (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	//_controlfp(_RC_CHOP,_MCW_RC);
	Float_RoundToInteger64(&*(int64_t *)state->FPRDoubleLocation[state->Opcode.u.f.fd],&*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_S_CEIL_L (usf_state_t * state) {	//added by Witten
	TEST_COP1_USABLE_EXCEPTION
	//_controlfp(_RC_UP,_MCW_RC);
	Float_RoundToInteger64(&*(int64_t *)state->FPRDoubleLocation[state->Opcode.u.f.fd],&*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_S_FLOOR_L (usf_state_t * state) {	//added by Witten
	TEST_COP1_USABLE_EXCEPTION
	Float_RoundToInteger64(&*(int64_t *)state->FPRDoubleLocation[state->Opcode.u.f.fd],&*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_S_ROUND_W (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	Float_RoundToInteger32(&*(int32_t *)state->FPRFloatLocation[state->Opcode.u.f.fd],&*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_S_TRUNC_W (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	//_controlfp(_RC_CHOP,_MCW_RC);
	Float_RoundToInteger32(&*(int32_t *)state->FPRFloatLocation[state->Opcode.u.f.fd],&*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_S_CEIL_W (usf_state_t * state) {	//added by Witten
	TEST_COP1_USABLE_EXCEPTION
	//_controlfp(_RC_UP,_MCW_RC);
	Float_RoundToInteger32(&*(int32_t *)state->FPRFloatLocation[state->Opcode.u.f.fd],&*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_S_FLOOR_W (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	Float_RoundToInteger32(&*(int32_t *)state->FPRFloatLocation[state->Opcode.u.f.fd],&*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_S_CVT_D (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fd] = (double)(*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_S_CVT_W (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	Float_RoundToInteger32(&*(int32_t *)state->FPRFloatLocation[state->Opcode.u.f.fd],&*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_S_CVT_L (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	Float_RoundToInteger64(&*(int64_t *)state->FPRDoubleLocation[state->Opcode.u.f.fd],&*(float *)state->FPRFloatLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_S_CMP (usf_state_t * state) {
	int32_t less, equal, unorded, condition;
	float Temp0, Temp1;

	TEST_COP1_USABLE_EXCEPTION

	Temp0 = *(float *)state->FPRFloatLocation[state->Opcode.u.f.fs];
	Temp1 = *(float *)state->FPRFloatLocation[state->Opcode.u.f.ft];

	if(0) {
	//if (_isnan(Temp0) || _isnan(Temp1)) {
		less = 0;
		equal = 0;
		unorded = 1;
		if ((state->Opcode.u.e.funct & 8) != 0) {
		}
	} else {
		less = Temp0 < Temp1;
		equal = Temp0 == Temp1;
		unorded = 0;
	}

	condition = ((state->Opcode.u.e.funct & 4) && less) | ((state->Opcode.u.e.funct & 2) && equal) |
		((state->Opcode.u.e.funct & 1) && unorded);

	if (condition) {
		state->FPCR[31] |= FPCSR_C;
	} else {
		state->FPCR[31] &= ~FPCSR_C;
	}

}

/************************** COP1: D functions ************************/
static INLINE void Double_RoundToInteger32( int32_t * Dest, double * Source ) {
	*Dest = (int32_t)*Source;
}

static INLINE void Double_RoundToInteger64( int64_t * Dest, double * Source ) {
	*Dest = (int64_t)*Source;
}

void r4300i_COP1_D_ADD (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fd] = *(double *)state->FPRDoubleLocation[state->Opcode.u.f.fs] + *(double *)state->FPRDoubleLocation[state->Opcode.u.f.ft];
}

void r4300i_COP1_D_SUB (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fd] = *(double *)state->FPRDoubleLocation[state->Opcode.u.f.fs] - *(double *)state->FPRDoubleLocation[state->Opcode.u.f.ft];
}

void r4300i_COP1_D_MUL (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fd] = *(double *)state->FPRDoubleLocation[state->Opcode.u.f.fs] * *(double *)state->FPRDoubleLocation[state->Opcode.u.f.ft];
}

void r4300i_COP1_D_DIV (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fd] = *(double *)state->FPRDoubleLocation[state->Opcode.u.f.fs] / *(double *)state->FPRDoubleLocation[state->Opcode.u.f.ft];
}

void r4300i_COP1_D_SQRT (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fd] = (double)sqrt(*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_D_ABS (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fd] = fabs(*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_D_MOV (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(int64_t *)state->FPRDoubleLocation[state->Opcode.u.f.fd] = *(int64_t *)state->FPRDoubleLocation[state->Opcode.u.f.fs];
}

void r4300i_COP1_D_NEG (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fd] = (*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fs] * -1.0);
}

void r4300i_COP1_D_TRUNC_L (usf_state_t * state) {	//added by Witten
	TEST_COP1_USABLE_EXCEPTION
	Double_RoundToInteger64(&*(int64_t *)state->FPRFloatLocation[state->Opcode.u.f.fd],&*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fs] );
}

void r4300i_COP1_D_CEIL_L (usf_state_t * state) {	//added by Witten
	TEST_COP1_USABLE_EXCEPTION
	Double_RoundToInteger64(&*(int64_t *)state->FPRFloatLocation[state->Opcode.u.f.fd],&*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fs] );
}

void r4300i_COP1_D_FLOOR_L (usf_state_t * state) {	//added by Witten
	TEST_COP1_USABLE_EXCEPTION
	Double_RoundToInteger64(&*(int64_t *)state->FPRDoubleLocation[state->Opcode.u.f.fd],&*(double *)state->FPRFloatLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_D_ROUND_W (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	Double_RoundToInteger32(&*(int32_t *)state->FPRFloatLocation[state->Opcode.u.f.fd],&*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fs] );
}

void r4300i_COP1_D_TRUNC_W (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	Double_RoundToInteger32(&*(int32_t *)state->FPRFloatLocation[state->Opcode.u.f.fd],&*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fs] );
}

void r4300i_COP1_D_CEIL_W (usf_state_t * state) {	//added by Witten
	TEST_COP1_USABLE_EXCEPTION
	Double_RoundToInteger32(&*(int32_t *)state->FPRFloatLocation[state->Opcode.u.f.fd],&*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fs] );
}

void r4300i_COP1_D_FLOOR_W (usf_state_t * state) {	//added by Witten
	TEST_COP1_USABLE_EXCEPTION
	Double_RoundToInteger32(&*(int32_t *)state->FPRDoubleLocation[state->Opcode.u.f.fd],&*(double *)state->FPRFloatLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_D_CVT_S (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(float *)state->FPRFloatLocation[state->Opcode.u.f.fd] = (float)*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fs];
}

void r4300i_COP1_D_CVT_W (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	Double_RoundToInteger32(&*(int32_t *)state->FPRFloatLocation[state->Opcode.u.f.fd],&*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fs] );
}

void r4300i_COP1_D_CVT_L (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	Double_RoundToInteger64(&*(int64_t *)state->FPRDoubleLocation[state->Opcode.u.f.fd],&*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fs]);
}

void r4300i_COP1_D_CMP (usf_state_t * state) {
	int32_t less, equal, unorded, condition;
	MIPS_DWORD Temp0, Temp1;

	TEST_COP1_USABLE_EXCEPTION

	Temp0.DW = *(int64_t *)state->FPRDoubleLocation[state->Opcode.u.f.fs];
	Temp1.DW = *(int64_t *)state->FPRDoubleLocation[state->Opcode.u.f.ft];

	if(0) {
	//if (_isnan(Temp0.D) || _isnan(Temp1.D)) {
		less = 0;
		equal = 0;
		unorded = 1;
		if ((state->Opcode.u.e.funct & 8) != 0) {
		}
	} else {
		less = Temp0.D < Temp1.D;
		equal = Temp0.D == Temp1.D;
		unorded = 0;
	}

	condition = ((state->Opcode.u.e.funct & 4) && less) | ((state->Opcode.u.e.funct & 2) && equal) |
		((state->Opcode.u.e.funct & 1) && unorded);

	if (condition) {
		state->FPCR[31] |= FPCSR_C;
	} else {
		state->FPCR[31] &= ~FPCSR_C;
	}
}

/************************** COP1: W functions ************************/
void r4300i_COP1_W_CVT_S (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(float *)state->FPRFloatLocation[state->Opcode.u.f.fd] = (float)*(int32_t *)state->FPRFloatLocation[state->Opcode.u.f.fs];
}

void r4300i_COP1_W_CVT_D (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fd] = (double)*(int32_t *)state->FPRFloatLocation[state->Opcode.u.f.fs];
}

/************************** COP1: L functions ************************/
void r4300i_COP1_L_CVT_S (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(float *)state->FPRFloatLocation[state->Opcode.u.f.fd] = (float)*(int64_t *)state->FPRDoubleLocation[state->Opcode.u.f.fs];
}

void r4300i_COP1_L_CVT_D (usf_state_t * state) {
	TEST_COP1_USABLE_EXCEPTION
	*(double *)state->FPRDoubleLocation[state->Opcode.u.f.fd] = (double)*(int64_t *)state->FPRDoubleLocation[state->Opcode.u.f.fs];
}

/************************** Other functions **************************/
void R4300i_UnknownOpcode (usf_state_t * state) {
	DisplayError(state, "Unknown R4300i Opcode.\tPC:%08x\tOp:%08x\n", state->PROGRAM_COUNTER,state->Opcode.u.Hex);
	StopEmulation(state);
}
