/****************************************************************************
*                                                                           *
* Project 64 - A Nintendo 64 emulator.                                      *
* http://www.pj64-emu.com/                                                  *
* Copyright (C) 2012 Project64. All rights reserved.                        *
*                                                                           *
* License:                                                                  *
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html                        *
*                                                                           *
****************************************************************************/
#include "stdafx.h"

const char * CRegName::GPR[32] = {"r0","at","v0","v1","a0","a1","a2","a3",
					 "t0","t1","t2","t3","t4","t5","t6","t7",
					 "s0","s1","s2","s3","s4","s5","s6","s7",
					 "t8","t9","k0","k1","gp","sp","s8","ra"};

const char *CRegName::GPR_Hi[32] = {"r0.HI","at.HI","v0.HI","v1.HI","a0.HI","a1.HI",
						"a2.HI","a3.HI","t0.HI","t1.HI","t2.HI","t3.HI",
						"t4.HI","t5.HI","t6.HI","t7.HI","s0.HI","s1.HI",
						"s2.HI","s3.HI","s4.HI","s5.HI","s6.HI","s7.HI",
						"t8.HI","t9.HI","k0.HI","k1.HI","gp.HI","sp.HI",
						"s8.HI","ra.HI"};

const char *CRegName::GPR_Lo[32] = {"r0.LO","at.LO","v0.LO","v1.LO","a0.LO","a1.LO",
						"a2.LO","a3.LO","t0.LO","t1.LO","t2.LO","t3.LO",
						"t4.LO","t5.LO","t6.LO","t7.LO","s0.LO","s1.LO",
						"s2.LO","s3.LO","s4.LO","s5.LO","s6.LO","s7.LO",
						"t8.LO","t9.LO","k0.LO","k1.LO","gp.LO","sp.LO",
						"s8.LO","ra.LO"};

const char * CRegName::Cop0[32] = {"Index","Random","EntryLo0","EntryLo1","Context","PageMask","Wired","",
					"BadVAddr","Count","EntryHi","Compare","Status","Cause","EPC","PRId",
					"Config","LLAddr","WatchLo","WatchHi","XContext","","","",
					"","","ECC","CacheErr","TagLo","TagHi","ErrEPC",""};

const char * CRegName::FPR[32] = {"f0","f1","f2","f3","f4","f5","f6","f7",
					 "f8","f9","f10","f11","f12","f13","f14","f15",
					 "f16","f17","f18","f19","f20","f21","f22","f23",
					 "f24","f25","f26","f27","f28","f29","f30","f31"};

const char * CRegName::FPR_Ctrl[32] = {"Revision","Unknown","Unknown","Unknown","Unknown",
					"Unknown","Unknown","Unknown","Unknown","Unknown","Unknown",
					"Unknown","Unknown","Unknown","Unknown","Unknown","Unknown",
					"Unknown","Unknown","Unknown","Unknown","Unknown","Unknown",
					"Unknown","Unknown","Unknown","Unknown","Unknown","Unknown",
					"Unknown","Unknown","FCSR"};

DWORD         * CSystemRegisters::_PROGRAM_COUNTER = NULL;
MIPS_DWORD    * CSystemRegisters::_GPR = NULL;
MIPS_DWORD    * CSystemRegisters::_FPR = NULL;
DWORD         * CSystemRegisters::_CP0 = NULL;
MIPS_DWORD    * CSystemRegisters::_RegHI = NULL;
MIPS_DWORD    * CSystemRegisters::_RegLO = NULL;
float        ** CSystemRegisters::_FPR_S;		
double       ** CSystemRegisters::_FPR_D;
DWORD         * CSystemRegisters::_FPCR = NULL;
DWORD         * CSystemRegisters::_LLBit = NULL;
ROUNDING_MODE * CSystemRegisters::_RoundingModel = NULL;


CP0registers::CP0registers(DWORD * _CP0) :
	INDEX_REGISTER(_CP0[0]),
	RANDOM_REGISTER(_CP0[1]),
	ENTRYLO0_REGISTER(_CP0[2]),
	ENTRYLO1_REGISTER(_CP0[3]),
	CONTEXT_REGISTER(_CP0[4]),
	PAGE_MASK_REGISTER(_CP0[5]),
	WIRED_REGISTER(_CP0[6]),
	BAD_VADDR_REGISTER(_CP0[8]),
	COUNT_REGISTER(_CP0[9]),
	ENTRYHI_REGISTER(_CP0[10]),
	COMPARE_REGISTER(_CP0[11]),
	STATUS_REGISTER(_CP0[12]),
	CAUSE_REGISTER(_CP0[13]),
	EPC_REGISTER(_CP0[14]),
	CONFIG_REGISTER(_CP0[16]),
	TAGLO_REGISTER(_CP0[28]),
	TAGHI_REGISTER(_CP0[29]),
	ERROREPC_REGISTER(_CP0[30]),
	FAKE_CAUSE_REGISTER(_CP0[32])
{
}

Rdram_InterfaceReg::Rdram_InterfaceReg(DWORD * _RdramInterface) :
	RDRAM_CONFIG_REG(_RdramInterface[0]),
	RDRAM_DEVICE_TYPE_REG(_RdramInterface[0]),
	RDRAM_DEVICE_ID_REG(_RdramInterface[1]),
	RDRAM_DELAY_REG(_RdramInterface[2]),
	RDRAM_MODE_REG(_RdramInterface[3]),
	RDRAM_REF_INTERVAL_REG(_RdramInterface[4]),
	RDRAM_REF_ROW_REG(_RdramInterface[5]),
	RDRAM_RAS_INTERVAL_REG(_RdramInterface[6]),
	RDRAM_MIN_INTERVAL_REG(_RdramInterface[7]),
	RDRAM_ADDR_SELECT_REG(_RdramInterface[8]),
	RDRAM_DEVICE_MANUF_REG(_RdramInterface[9])
{
}

Mips_InterfaceReg::Mips_InterfaceReg(DWORD * _MipsInterface) :
	MI_INIT_MODE_REG(_MipsInterface[0]),
	MI_MODE_REG(_MipsInterface[0]),
	MI_VERSION_REG(_MipsInterface[1]),
	MI_NOOP_REG(_MipsInterface[1]),
	MI_INTR_REG(_MipsInterface[2]),
	MI_INTR_MASK_REG(_MipsInterface[3])
{
}

Video_InterfaceReg::Video_InterfaceReg(DWORD * _VideoInterface) :
	VI_STATUS_REG(_VideoInterface[0]),
	VI_CONTROL_REG(_VideoInterface[0]),
	VI_ORIGIN_REG(_VideoInterface[1]),
	VI_DRAM_ADDR_REG(_VideoInterface[1]),
	VI_WIDTH_REG(_VideoInterface[2]),
	VI_H_WIDTH_REG(_VideoInterface[2]),
	VI_INTR_REG(_VideoInterface[3]),
	VI_V_INTR_REG(_VideoInterface[3]),
	VI_CURRENT_REG(_VideoInterface[4]),
	VI_V_CURRENT_LINE_REG(_VideoInterface[4]),
	VI_BURST_REG(_VideoInterface[5]),
	VI_TIMING_REG(_VideoInterface[5]),
	VI_V_SYNC_REG(_VideoInterface[6]),
	VI_H_SYNC_REG(_VideoInterface[7]),
	VI_LEAP_REG(_VideoInterface[8]),
	VI_H_SYNC_LEAP_REG(_VideoInterface[8]),
	VI_H_START_REG(_VideoInterface[9]),
	VI_H_VIDEO_REG(_VideoInterface[9]),
	VI_V_START_REG(_VideoInterface[10]),
	VI_V_VIDEO_REG(_VideoInterface[10]),
	VI_V_BURST_REG(_VideoInterface[11]),
	VI_X_SCALE_REG(_VideoInterface[12]),
	VI_Y_SCALE_REG(_VideoInterface[13])
{
}

AudioInterfaceReg::AudioInterfaceReg(DWORD * _AudioInterface) :
	AI_DRAM_ADDR_REG(_AudioInterface[0]),
	AI_LEN_REG(_AudioInterface[1]),
	AI_CONTROL_REG(_AudioInterface[2]),
	AI_STATUS_REG(_AudioInterface[3]),
	AI_DACRATE_REG(_AudioInterface[4]),
	AI_BITRATE_REG(_AudioInterface[5])
{
}
	
PeripheralInterfaceReg::PeripheralInterfaceReg(DWORD * PeripheralInterface) :
	PI_DRAM_ADDR_REG(PeripheralInterface[0]),
	PI_CART_ADDR_REG(PeripheralInterface[1]),
	PI_RD_LEN_REG(PeripheralInterface[2]),
	PI_WR_LEN_REG(PeripheralInterface[3]),
	PI_STATUS_REG(PeripheralInterface[4]),
	PI_BSD_DOM1_LAT_REG(PeripheralInterface[5]),
	PI_DOMAIN1_REG(PeripheralInterface[5]),
	PI_BSD_DOM1_PWD_REG(PeripheralInterface[6]),
	PI_BSD_DOM1_PGS_REG(PeripheralInterface[7]),
	PI_BSD_DOM1_RLS_REG(PeripheralInterface[8]),
	PI_BSD_DOM2_LAT_REG(PeripheralInterface[9]),
	PI_DOMAIN2_REG(PeripheralInterface[9]),
	PI_BSD_DOM2_PWD_REG(PeripheralInterface[10]),
	PI_BSD_DOM2_PGS_REG(PeripheralInterface[11]),
	PI_BSD_DOM2_RLS_REG(PeripheralInterface[12])
{
}

RDRAMInt_InterfaceReg::RDRAMInt_InterfaceReg(DWORD * RdramInterface) :
	RI_MODE_REG(RdramInterface[0]),
	RI_CONFIG_REG(RdramInterface[1]),
	RI_CURRENT_LOAD_REG(RdramInterface[2]),
	RI_SELECT_REG(RdramInterface[3]),
	RI_COUNT_REG(RdramInterface[4]),
	RI_REFRESH_REG(RdramInterface[4]),
	RI_LATENCY_REG(RdramInterface[5]),
	RI_RERROR_REG(RdramInterface[6]),
	RI_WERROR_REG(RdramInterface[7])
{
}

DisplayControlReg::DisplayControlReg(DWORD * _DisplayProcessor) :
	DPC_START_REG(_DisplayProcessor[0]),
	DPC_END_REG(_DisplayProcessor[1]),
	DPC_CURRENT_REG(_DisplayProcessor[2]),
	DPC_STATUS_REG(_DisplayProcessor[3]),
	DPC_CLOCK_REG(_DisplayProcessor[4]),
	DPC_BUFBUSY_REG(_DisplayProcessor[5]),
	DPC_PIPEBUSY_REG(_DisplayProcessor[6]),
	DPC_TMEM_REG(_DisplayProcessor[7])
{
}

SigProcessor_InterfaceReg::SigProcessor_InterfaceReg(DWORD * _SignalProcessorInterface) :
	SP_MEM_ADDR_REG(_SignalProcessorInterface[0]),
	SP_DRAM_ADDR_REG(_SignalProcessorInterface[1]),
	SP_RD_LEN_REG(_SignalProcessorInterface[2]),
	SP_WR_LEN_REG(_SignalProcessorInterface[3]),
	SP_STATUS_REG(_SignalProcessorInterface[4]),
	SP_DMA_FULL_REG(_SignalProcessorInterface[5]),
	SP_DMA_BUSY_REG(_SignalProcessorInterface[6]),
	SP_SEMAPHORE_REG(_SignalProcessorInterface[7]),
	SP_PC_REG(_SignalProcessorInterface[8]),
	SP_IBIST_REG(_SignalProcessorInterface[9])
{
}

Serial_InterfaceReg::Serial_InterfaceReg(DWORD * SerialInterface) :
	SI_DRAM_ADDR_REG(SerialInterface[0]),
	SI_PIF_ADDR_RD64B_REG(SerialInterface[1]),
	SI_PIF_ADDR_WR64B_REG(SerialInterface[2]),
	SI_STATUS_REG(SerialInterface[3])
{
}

CRegisters::CRegisters(CN64System * System, CSystemEvents * SystemEvents) :
	CP0registers(m_CP0),
	Rdram_InterfaceReg(m_RDRAM_Registers),
	Mips_InterfaceReg(m_Mips_Interface),
	Video_InterfaceReg(m_Video_Interface),
	AudioInterfaceReg(m_Audio_Interface),
	PeripheralInterfaceReg(m_Peripheral_Interface),
	RDRAMInt_InterfaceReg(m_RDRAM_Interface),
	SigProcessor_InterfaceReg(m_SigProcessor_Interface),
	DisplayControlReg(m_Display_ControlReg),
	Serial_InterfaceReg(m_SerialInterface),
	m_System(System),
	m_SystemEvents(SystemEvents)
{ 
	Reset();
}

void CRegisters::Reset()
{
	m_FirstInterupt = true;

	memset(m_GPR,0,sizeof(m_GPR));	
	memset(m_CP0,0,sizeof(m_CP0));	
	memset(m_FPR,0,sizeof(m_FPR));	
	memset(m_FPCR,0,sizeof(m_FPCR));	
	m_HI.DW   = 0;
	m_LO.DW   = 0;
	m_RoundingModel = ROUND_NEAR;
	
	m_LLBit   = 0;

	//Reset System Registers
	memset(m_RDRAM_Interface,0,sizeof(m_RDRAM_Interface));	
	memset(m_RDRAM_Registers,0,sizeof(m_RDRAM_Registers));	
	memset(m_Mips_Interface,0,sizeof(m_Mips_Interface));	
	memset(m_Video_Interface,0,sizeof(m_Video_Interface));	
	memset(m_Display_ControlReg,0,sizeof(m_Display_ControlReg));	
	memset(m_Audio_Interface,0,sizeof(m_Audio_Interface));	
	memset(m_SigProcessor_Interface,0,sizeof(m_SigProcessor_Interface));	
	memset(m_Peripheral_Interface,0,sizeof(m_Peripheral_Interface));	
	memset(m_SerialInterface,0,sizeof(m_SerialInterface));	

	m_AudioIntrReg = 0;
	m_GfxIntrReg = 0;
	m_RspIntrReg = 0;
	
	FixFpuLocations();
}

void CRegisters::SetAsCurrentSystem ( void )
{
	_PROGRAM_COUNTER = &m_PROGRAM_COUNTER;
	_GPR = m_GPR;
	_FPR = m_FPR;
	_CP0 = m_CP0;
	_RegHI = &m_HI;
	_RegLO = &m_LO;
	_FPR_S = m_FPR_S;
	_FPR_D = m_FPR_D;
	_FPCR = m_FPCR;
	_LLBit = &m_LLBit;
	_RoundingModel = &m_RoundingModel;
}

void CRegisters::CheckInterrupts ( void ) 
{
	if (!m_System->bFixedAudio() && (CPU_TYPE)g_Settings->LoadDword(Game_CpuType) != CPU_SyncCores) {
		MI_INTR_REG &= ~MI_INTR_AI;
		MI_INTR_REG |= (m_AudioIntrReg & MI_INTR_AI);
	}
	MI_INTR_REG |= (m_RspIntrReg & MI_INTR_SP);
	MI_INTR_REG |= (m_GfxIntrReg & MI_INTR_DP);
	if ((MI_INTR_MASK_REG & MI_INTR_REG) != 0) {
		FAKE_CAUSE_REGISTER |= CAUSE_IP2;
	} else  {
		FAKE_CAUSE_REGISTER &= ~CAUSE_IP2;
	}

	if (( STATUS_REGISTER & STATUS_IE   ) == 0 ) { return; }
	if (( STATUS_REGISTER & STATUS_EXL  ) != 0 ) { return; }
	if (( STATUS_REGISTER & STATUS_ERL  ) != 0 ) { return; }

	if (( STATUS_REGISTER & FAKE_CAUSE_REGISTER & 0xFF00) != 0) {
		if (m_FirstInterupt)
		{
			m_FirstInterupt = false;
			if (g_Recompiler)
			{
				g_Recompiler->ClearRecompCode_Virt(0x80000000,0x200,CRecompiler::Remove_InitialCode);
			}
		}
		m_SystemEvents->QueueEvent(SysEvent_ExecuteInterrupt);
	}
}

void CRegisters::DoAddressError ( BOOL DelaySlot, DWORD BadVaddr, BOOL FromRead) 
{
	if (bHaveDebugger())
	{
		g_Notify->DisplayError(L"AddressError");
		if (( STATUS_REGISTER & STATUS_EXL  ) != 0 ) { 
			g_Notify->DisplayError(L"EXL set in AddressError Exception");
		}
		if (( STATUS_REGISTER & STATUS_ERL  ) != 0 ) { 
			g_Notify->DisplayError(L"ERL set in AddressError Exception");
		}
	}

	if (FromRead) {
		CAUSE_REGISTER = EXC_RADE;
	} else {
		CAUSE_REGISTER = EXC_WADE;
	}
	BAD_VADDR_REGISTER = BadVaddr;
	if (DelaySlot) {
		CAUSE_REGISTER |= CAUSE_BD;
		EPC_REGISTER = m_PROGRAM_COUNTER - 4;
	} else {
		EPC_REGISTER = m_PROGRAM_COUNTER;
	}
	STATUS_REGISTER |= STATUS_EXL;
	m_PROGRAM_COUNTER = 0x80000180;
}

void CRegisters::FixFpuLocations ( void ) {	
	if ((STATUS_REGISTER & STATUS_FR) == 0) {
		for (int count = 0; count < 32; count ++) {
			m_FPR_S[count] = &m_FPR[count >> 1].F[count & 1];
			m_FPR_D[count] = &m_FPR[count >> 1].D;
		}
	} else {
		for (int count = 0; count < 32; count ++) {
			m_FPR_S[count] = &m_FPR[count].F[1];
			m_FPR_D[count] = &m_FPR[count].D;
		}
	}
}

void CRegisters::DoBreakException ( BOOL DelaySlot) 
{
	if (bHaveDebugger())
	{
		if (( STATUS_REGISTER & STATUS_EXL  ) != 0 ) { 
			g_Notify->DisplayError(L"EXL set in Break Exception");
		}
		if (( STATUS_REGISTER & STATUS_ERL  ) != 0 ) { 
			g_Notify->DisplayError(L"ERL set in Break Exception");
		}
	}

	CAUSE_REGISTER = EXC_BREAK;
	if (DelaySlot) {
		CAUSE_REGISTER |= CAUSE_BD;
		EPC_REGISTER = m_PROGRAM_COUNTER - 4;
	} else {
		EPC_REGISTER = m_PROGRAM_COUNTER;
	}
	STATUS_REGISTER |= STATUS_EXL;
	m_PROGRAM_COUNTER = 0x80000180;
}

void CRegisters::DoCopUnusableException ( BOOL DelaySlot, int Coprocessor )
{
	if (bHaveDebugger())
	{
		if (( STATUS_REGISTER & STATUS_EXL  ) != 0 ) { 
			g_Notify->DisplayError(L"EXL set in Break Exception");
		}
		if (( STATUS_REGISTER & STATUS_ERL  ) != 0 ) { 
			g_Notify->DisplayError(L"ERL set in Break Exception");
		}
	}

	CAUSE_REGISTER = EXC_CPU;
	if (Coprocessor == 1) { CAUSE_REGISTER |= 0x10000000; }
	if (DelaySlot) {
		CAUSE_REGISTER |= CAUSE_BD;
		EPC_REGISTER = m_PROGRAM_COUNTER - 4;
	} else {
		EPC_REGISTER = m_PROGRAM_COUNTER;
	}
	STATUS_REGISTER |= STATUS_EXL;
	m_PROGRAM_COUNTER = 0x80000180;
}


BOOL CRegisters::DoIntrException ( BOOL DelaySlot ) 
{
	if (( STATUS_REGISTER & STATUS_IE   ) == 0 ) { return FALSE; }
	if (( STATUS_REGISTER & STATUS_EXL  ) != 0 ) { return FALSE; }
	if (( STATUS_REGISTER & STATUS_ERL  ) != 0 ) { return FALSE; }
	if (LogOptions.GenerateLog && LogOptions.LogExceptions && !LogOptions.NoInterrupts) {
		LogMessage("%08X: Interupt Generated", m_PROGRAM_COUNTER );
	}
	CAUSE_REGISTER = FAKE_CAUSE_REGISTER;
	CAUSE_REGISTER |= EXC_INT;
	if (DelaySlot) {
		CAUSE_REGISTER |= CAUSE_BD;
		EPC_REGISTER = m_PROGRAM_COUNTER - 4;
	} else {
		EPC_REGISTER = m_PROGRAM_COUNTER;
	}
	STATUS_REGISTER |= STATUS_EXL;
	m_PROGRAM_COUNTER = 0x80000180;
	return TRUE;
}

void CRegisters::DoTLBReadMiss ( BOOL DelaySlot, DWORD BadVaddr ) 
{
	CAUSE_REGISTER = EXC_RMISS;
	BAD_VADDR_REGISTER = BadVaddr;
	CONTEXT_REGISTER &= 0xFF80000F;
	CONTEXT_REGISTER |= (BadVaddr >> 9) & 0x007FFFF0;
	ENTRYHI_REGISTER = (BadVaddr & 0xFFFFE000);
	if ((STATUS_REGISTER & STATUS_EXL) == 0) {
		if (DelaySlot) {
			CAUSE_REGISTER |= CAUSE_BD;
			EPC_REGISTER = m_PROGRAM_COUNTER - 4;
		} else {
			EPC_REGISTER = m_PROGRAM_COUNTER;
		}
		if (g_TLB->AddressDefined(BadVaddr)) 
		{
			m_PROGRAM_COUNTER = 0x80000180;
		} else {
			m_PROGRAM_COUNTER = 0x80000000;
		}
		STATUS_REGISTER |= STATUS_EXL;
	} else {
		if (bHaveDebugger())
		{
			g_Notify->DisplayError(L"TLBMiss - EXL Set\nBadVaddr = %X\nAddress Defined: %s",BadVaddr,g_TLB->AddressDefined(BadVaddr)?"TRUE":"FALSE");
		}
		m_PROGRAM_COUNTER = 0x80000180;
	}
}

void CRegisters::DoSysCallException ( BOOL DelaySlot) 
{
	if (bHaveDebugger())
	{
		if (( STATUS_REGISTER & STATUS_EXL  ) != 0 ) { 
			g_Notify->DisplayError(L"EXL set in SysCall Exception");
		}
		if (( STATUS_REGISTER & STATUS_ERL  ) != 0 ) { 
			g_Notify->DisplayError(L"ERL set in SysCall Exception");
		}
	}

	CAUSE_REGISTER = EXC_SYSCALL;
	if (DelaySlot) {
		CAUSE_REGISTER |= CAUSE_BD;
		EPC_REGISTER = m_PROGRAM_COUNTER - 4;
	} else {
		EPC_REGISTER = m_PROGRAM_COUNTER;
	}
	STATUS_REGISTER |= STATUS_EXL;
	m_PROGRAM_COUNTER = 0x80000180;
}

