text: /*
   * 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 <stdint.h>
  #include <string.h>
  
  #include "main.h"
  #include "cpu.h"
  #include "usf.h"
  #include "audio.h"
  #include "registers.h"
  #include "rsp.h"
  
  #include "usf_internal.h"
  
  #include <stdlib.h>
  
  void ChangeCompareTimer(usf_state_t * state) {
  	uint32_t NextCompare = COMPARE_REGISTER - COUNT_REGISTER;
  	if ((NextCompare & 0x80000000) != 0) {  NextCompare = 0x7FFFFFFF; }
  	if (NextCompare == 0) { NextCompare = 0x1; }
  	ChangeTimer(state,CompareTimer,NextCompare);
  }
  
  void ChangeTimer(usf_state_t * state, int32_t Type, int32_t Value) {
  	if (Value == 0) {
  		state->Timers->NextTimer[Type] = 0;
  		state->Timers->Active[Type] = 0;
  		return;
  	}
  	state->Timers->NextTimer[Type] = Value - state->Timers->Timer;
  	state->Timers->Active[Type] = 1;
  	CheckTimer(state);
  }
  
  void CheckTimer (usf_state_t * state) {
  	int32_t count;
  
  	for (count = 0; count < MaxTimers; count++) {
  		if (!state->Timers->Active[count]) { continue; }
  		if (!(count == CompareTimer && state->Timers->NextTimer[count] == 0x7FFFFFFF)) {
  			state->Timers->NextTimer[count] += state->Timers->Timer;
  		}
  	}
  	state->Timers->CurrentTimerType = -1;
  	state->Timers->Timer = 0x7FFFFFFF;
  	for (count = 0; count < MaxTimers; count++) {
  		if (!state->Timers->Active[count]) { continue; }
  		if (state->Timers->NextTimer[count] >= state->Timers->Timer) { continue; }
  		state->Timers->Timer = state->Timers->NextTimer[count];
  		state->Timers->CurrentTimerType = count;
  	}
  	if (state->Timers->CurrentTimerType == -1) {
  		DisplayError(state, "No active timers ???\nEmulation Stopped");
  		StopEmulation(state);
  	}
  	for (count = 0; count < MaxTimers; count++) {
  		if (!state->Timers->Active[count]) { continue; }
  		if (!(count == CompareTimer && state->Timers->NextTimer[count] == 0x7FFFFFFF)) {
  			state->Timers->NextTimer[count] -= state->Timers->Timer;
  		}
  	}
  
  	if (state->Timers->NextTimer[CompareTimer] == 0x7FFFFFFF) {
  		uint32_t NextCompare = COMPARE_REGISTER - COUNT_REGISTER;
  		if ((NextCompare & 0x80000000) == 0 && NextCompare != 0x7FFFFFFF) {
  			ChangeCompareTimer(state);
  		}
  	}
  }
  
  
  
  void CloseCpu (usf_state_t * state) {
  	uint32_t count = 0;
  
  	if(!state->MemChunk) return;
  	if (!state->cpu_running) { return; }
  
  	state->cpu_running = 0;
  
  	for (count = 0; count < 3; count ++ ) {
  		state->CPU_Action->CloseCPU = 1;
  		state->CPU_Action->DoSomething = 1;
  	}
  
  	state->CPURunning = 0;
  }
  
  int32_t DelaySlotEffectsCompare (usf_state_t * state, uint32_t PC, uint32_t Reg1, uint32_t Reg2) {
  	OPCODE Command;
  
  	if (!r4300i_LW_VAddr(state, PC + 4, (uint32_t*)&Command.u.Hex)) {
  		return 1;
  	}
  
  	switch (Command.u.b.op) {
  	case R4300i_SPECIAL:
  		switch (Command.u.e.funct) {
  		case R4300i_SPECIAL_SLL:
  		case R4300i_SPECIAL_SRL:
  		case R4300i_SPECIAL_SRA:
  		case R4300i_SPECIAL_SLLV:
  		case R4300i_SPECIAL_SRLV:
  		case R4300i_SPECIAL_SRAV:
  		case R4300i_SPECIAL_MFHI:
  		case R4300i_SPECIAL_MTHI:
  		case R4300i_SPECIAL_MFLO:
  		case R4300i_SPECIAL_MTLO:
  		case R4300i_SPECIAL_DSLLV:
  		case R4300i_SPECIAL_DSRLV:
  		case R4300i_SPECIAL_DSRAV:
  		case R4300i_SPECIAL_ADD:
  		case R4300i_SPECIAL_ADDU:
  		case R4300i_SPECIAL_SUB:
  		case R4300i_SPECIAL_SUBU:
  		case R4300i_SPECIAL_AND:
  		case R4300i_SPECIAL_OR:
  		case R4300i_SPECIAL_XOR:
  		case R4300i_SPECIAL_NOR:
  		case R4300i_SPECIAL_SLT:
  		case R4300i_SPECIAL_SLTU:
  		case R4300i_SPECIAL_DADD:
  		case R4300i_SPECIAL_DADDU:
  		case R4300i_SPECIAL_DSUB:
  		case R4300i_SPECIAL_DSUBU:
  		case R4300i_SPECIAL_DSLL:
  		case R4300i_SPECIAL_DSRL:
  		case R4300i_SPECIAL_DSRA:
  		case R4300i_SPECIAL_DSLL32:
  		case R4300i_SPECIAL_DSRL32:
  		case R4300i_SPECIAL_DSRA32:
  			if (Command.u.e.rd == 0) { return 0; }
  			if (Command.u.e.rd == Reg1) { return 1; }
  			if (Command.u.e.rd == Reg2) { return 1; }
  			break;
  		case R4300i_SPECIAL_MULT:
  		case R4300i_SPECIAL_MULTU:
  		case R4300i_SPECIAL_DIV:
  		case R4300i_SPECIAL_DIVU:
  		case R4300i_SPECIAL_DMULT:
  		case R4300i_SPECIAL_DMULTU:
  		case R4300i_SPECIAL_DDIV:
  		case R4300i_SPECIAL_DDIVU:
  			break;
  		default:
  			return 1;
  		}
  		break;
  	case R4300i_CP0:
  		switch (Command.u.b.rs) {
  		case R4300i_COP0_MT: break;
  		case R4300i_COP0_MF:
  			if (Command.u.b.rt == 0) { return 0; }
  			if (Command.u.b.rt == Reg1) { return 1; }
  			if (Command.u.b.rt == Reg2) { return 1; }
  			break;
  		default:
  			if ( (Command.u.b.rs & 0x10 ) != 0 ) {
  				switch( state->Opcode.u.e.funct ) {
  				case R4300i_COP0_CO_TLBR: break;
  				case R4300i_COP0_CO_TLBWI: break;
  				case R4300i_COP0_CO_TLBWR: break;
  				case R4300i_COP0_CO_TLBP: break;
  				default:
  					return 1;
  				}
  				return 1;
  			}
  		}
  		break;
  	case R4300i_CP1:
  		switch (Command.u.f.fmt) {
  		case R4300i_COP1_MF:
  			if (Command.u.b.rt == 0) { return 0; }
  			if (Command.u.b.rt == Reg1) { return 1; }
  			if (Command.u.b.rt == Reg2) { return 1; }
  			break;
  		case R4300i_COP1_CF: break;
  		case R4300i_COP1_MT: break;
  		case R4300i_COP1_CT: break;
  		case R4300i_COP1_S: break;
  		case R4300i_COP1_D: break;
  		case R4300i_COP1_W: break;
  		case R4300i_COP1_L: break;
  			return 1;
  		}
  		break;
  	case R4300i_ANDI:
  	case R4300i_ORI:
  	case R4300i_XORI:
  	case R4300i_LUI:
  	case R4300i_ADDI:
  	case R4300i_ADDIU:
  	case R4300i_SLTI:
  	case R4300i_SLTIU:
  	case R4300i_DADDI:
  	case R4300i_DADDIU:
  	case R4300i_LB:
  	case R4300i_LH:
  	case R4300i_LW:
  	case R4300i_LWL:
  	case R4300i_LWR:
  	case R4300i_LDL:
  	case R4300i_LDR:
  	case R4300i_LBU:
  	case R4300i_LHU:
  	case R4300i_LD:
  	case R4300i_LWC1:
  	case R4300i_LDC1:
  		if (Command.u.b.rt == 0) { return 0; }
  		if (Command.u.b.rt == Reg1) { return 1; }
  		if (Command.u.b.rt == Reg2) { return 1; }
  		break;
  	case R4300i_CACHE: break;
  	case R4300i_SB: break;
  	case R4300i_SH: break;
  	case R4300i_SW: break;
  	case R4300i_SWR: break;
  	case R4300i_SWL: break;
  	case R4300i_SWC1: break;
  	case R4300i_SDC1: break;
  	case R4300i_SD: break;
  	default:
  
  		return 1;
  	}
  	return 0;
  }
  
  int32_t DelaySlotEffectsJump (usf_state_t * state, uint32_t JumpPC) {
  	OPCODE Command;
  
  	if (!r4300i_LW_VAddr(state, JumpPC, &Command.u.Hex)) { return 1; }
  
  	switch (Command.u.b.op) {
  	case R4300i_SPECIAL:
  		switch (Command.u.e.funct) {
  		case R4300i_SPECIAL_JR:	return DelaySlotEffectsCompare(state,JumpPC,Command.u.b.rs,0);
  		case R4300i_SPECIAL_JALR: return DelaySlotEffectsCompare(state,JumpPC,Command.u.b.rs,31);
  		}
  		break;
  	case R4300i_REGIMM:
  		switch (Command.u.b.rt) {
  		case R4300i_REGIMM_BLTZ:
  		case R4300i_REGIMM_BGEZ:
  		case R4300i_REGIMM_BLTZL:
  		case R4300i_REGIMM_BGEZL:
  		case R4300i_REGIMM_BLTZAL:
  		case R4300i_REGIMM_BGEZAL:
  			return DelaySlotEffectsCompare(state,JumpPC,Command.u.b.rs,0);
  		}
  		break;
  	case R4300i_JAL:
  	case R4300i_SPECIAL_JALR: return DelaySlotEffectsCompare(state,JumpPC,31,0); break;
  	case R4300i_J: return 0;
  	case R4300i_BEQ:
  	case R4300i_BNE:
  	case R4300i_BLEZ:
  	case R4300i_BGTZ:
  		return DelaySlotEffectsCompare(state,JumpPC,Command.u.b.rs,Command.u.b.rt);
  	case R4300i_CP1:
  		switch (Command.u.f.fmt) {
  		case R4300i_COP1_BC:
  			switch (Command.u.f.ft) {
  			case R4300i_COP1_BC_BCF:
  			case R4300i_COP1_BC_BCT:
  			case R4300i_COP1_BC_BCFL:
  			case R4300i_COP1_BC_BCTL:
  				{
  					int32_t EffectDelaySlot;
  					OPCODE NewCommand;
  
  					if (!r4300i_LW_VAddr(state, JumpPC + 4, &NewCommand.u.Hex)) { return 1; }
  
  					EffectDelaySlot = 0;
  					if (NewCommand.u.b.op == R4300i_CP1) {
  						if (NewCommand.u.f.fmt == R4300i_COP1_S && (NewCommand.u.e.funct & 0x30) == 0x30 ) {
  							EffectDelaySlot = 1;
  						}
  						if (NewCommand.u.f.fmt == R4300i_COP1_D && (NewCommand.u.e.funct & 0x30) == 0x30 ) {
  							EffectDelaySlot = 1;
  						}
  					}
  					return EffectDelaySlot;
  				}
  				break;
  			}
  			break;
  		}
  		break;
  	case R4300i_BEQL:
  	case R4300i_BNEL:
  	case R4300i_BLEZL:
  	case R4300i_BGTZL:
  		return DelaySlotEffectsCompare(state,JumpPC,Command.u.b.rs,Command.u.b.rt);
  	}
  	return 1;
  }
  
  void DoSomething ( usf_state_t * state ) {
  	if (state->CPU_Action->CloseCPU) {
  		//StopEmulation();
  		state->cpu_running = 0;
  		//printf("Stopping?\n");
  	}
  	if (state->CPU_Action->CheckInterrupts) {
  		state->CPU_Action->CheckInterrupts = 0;
  		CheckInterrupts(state);
  	}
  	if (state->CPU_Action->DoInterrupt) {
  		state->CPU_Action->DoInterrupt = 0;
  		DoIntrException(state, 0);
  	}
  
  
  	state->CPU_Action->DoSomething = 0;
  
  	if (state->CPU_Action->DoInterrupt) { state->CPU_Action->DoSomething = 1; }
  }
  
  void InPermLoop ( usf_state_t * state ) {
  	// *** Changed ***/
  	if (state->CPU_Action->DoInterrupt) { return; }
  
  	/* Interrupts enabled */
  	if (( STATUS_REGISTER & STATUS_IE  ) == 0 ) { goto InterruptsDisabled; }
  	if (( STATUS_REGISTER & STATUS_EXL ) != 0 ) { goto InterruptsDisabled; }
  	if (( STATUS_REGISTER & STATUS_ERL ) != 0 ) { goto InterruptsDisabled; }
  	if (( STATUS_REGISTER & 0xFF00) == 0) { goto InterruptsDisabled; }
  
  	/* check sound playing */
  
  	/* check RSP running */
  	/* check RDP running */
  	if (state->Timers->Timer >= 0) {
  		COUNT_REGISTER += state->Timers->Timer + 1;
  		state->Timers->Timer = -1;
  	}
  	return;
  
  InterruptsDisabled:
  	DisplayError(state, "Stuck in Permanent Loop");
  	StopEmulation(state);
  }
  
  void ReadFromMem(const void * source, void * target, uint32_t length, uint32_t *offset) {
  	memcpy((uint8_t*)target,((uint8_t*)source)+*offset,length);
  	*offset+=length;
  }
  
  
  uint32_t Machine_LoadStateFromRAM(usf_state_t * state, void * savestatespace) {
  	uint8_t LoadHeader[0x40];
  	uint32_t Value, count, SaveRDRAMSize, offset=0;
  
  	ReadFromMem( savestatespace,&Value,sizeof(Value),&offset);
  	if (Value != 0x23D8A6C8) { return 0; }
  	ReadFromMem( savestatespace,&SaveRDRAMSize,sizeof(SaveRDRAMSize),&offset);
  	ReadFromMem( savestatespace,&LoadHeader,0x40,&offset);
  
  	state->Timers->CurrentTimerType = -1;
  	state->Timers->Timer = 0;
  	for (count = 0; count < MaxTimers; count ++) { state->Timers->Active[count] = 0; }
  
  	//fix rdram size
  	if (SaveRDRAMSize != state->RdramSize) {
  		// dothis :)
  	}		
  
  	state->RdramSize = SaveRDRAMSize;
  
  	ReadFromMem( savestatespace,&Value,sizeof(Value),&offset);
  	ChangeTimer(state,ViTimer,Value);
  	ReadFromMem( savestatespace,&state->PROGRAM_COUNTER,sizeof(state->PROGRAM_COUNTER),&offset);
  	ReadFromMem( savestatespace,state->GPR,sizeof(int64_t)*32,&offset);
  	ReadFromMem( savestatespace,state->FPR,sizeof(int64_t)*32,&offset);
  	ReadFromMem( savestatespace,state->CP0,sizeof(uint32_t)*32,&offset);
  	ReadFromMem( savestatespace,state->FPCR,sizeof(uint32_t)*32,&offset);
  	ReadFromMem( savestatespace,&state->HI,sizeof(int64_t),&offset);
  	ReadFromMem( savestatespace,&state->LO,sizeof(int64_t),&offset);
  	ReadFromMem( savestatespace,state->RegRDRAM,sizeof(uint32_t)*10,&offset);
  	ReadFromMem( savestatespace,state->RegSP,sizeof(uint32_t)*10,&offset);
  	ReadFromMem( savestatespace,state->RegDPC,sizeof(uint32_t)*10,&offset);
  	ReadFromMem( savestatespace,state->RegMI,sizeof(uint32_t)*4,&offset);
  	ReadFromMem( savestatespace,state->RegVI,sizeof(uint32_t)*14,&offset);
  	ReadFromMem( savestatespace,state->RegAI,sizeof(uint32_t)*6,&offset);
  	ReadFromMem( savestatespace,state->RegPI,sizeof(uint32_t)*13,&offset);
  	ReadFromMem( savestatespace,state->RegRI,sizeof(uint32_t)*8,&offset);
  	ReadFromMem( savestatespace,state->RegSI,sizeof(uint32_t)*4,&offset);
  	ReadFromMem( savestatespace,state->tlb,sizeof(TLB)*32,&offset);
  	ReadFromMem( savestatespace,(uint8_t*)state->PIF_Ram,0x40,&offset);
  	ReadFromMem( savestatespace,state->RDRAM,SaveRDRAMSize,&offset);
  	ReadFromMem( savestatespace,state->DMEM,0x1000,&offset);
  	ReadFromMem( savestatespace,state->IMEM,0x1000,&offset);
  
  	state->CP0[32] = 0;
  
  	SetupTLB(state);
  	ChangeCompareTimer(state);
  	AI_STATUS_REG = 0;
  	AiDacrateChanged(state, AI_DACRATE_REG);
  
  //	StartAiInterrupt(state);
  
  	SetFpuLocations(state); // important if FR=1
  
  	return 1;
  }
  
  void StartEmulationFromSave ( usf_state_t * state, void * savestate ) {
  	uint32_t count = 0;
  	
  	//printf("Starting generic Cpu\n");
  
  	//CloseCpu();
  	memset(state->N64MEM, 0, state->RdramSize);
  	
  	memset(state->DMEM, 0, 0x1000);
  	memset(state->IMEM, 0, 0x1000);
  	memset(state->TLB_Map, 0, 0x100000 * sizeof(uintptr_t) + 0x10000);
  
  	memset(state->CPU_Action,0,sizeof(*state->CPU_Action));
  	state->WrittenToRom = 0;
  
  	InitilizeTLB(state);
  
  	SetupRegisters(state, state->Registers);
  
  	BuildInterpreter(state);
  
  	state->Timers->CurrentTimerType = -1;
  	state->Timers->Timer = 0;
  
  	for (count = 0; count < MaxTimers; count ++) { state->Timers->Active[count] = 0; }
  	ChangeTimer(state,ViTimer,5000);
  	ChangeCompareTimer(state);
  	state->ViFieldNumber = 0;
  	state->CPURunning = 1;
  	*state->WaitMode = 0;
  
  	init_rsp(state);
  
  	Machine_LoadStateFromRAM(state, savestate);
  
  	state->SampleRate = 48681812 / (AI_DACRATE_REG + 1);
  
  	if(state->enableFIFOfull) {
  		const float VSyncTiming = 789000.0f;
  		double BytesPerSecond = 48681812.0 / (AI_DACRATE_REG + 1) * 4;
  		double CountsPerSecond = (double)(((double)VSyncTiming) * (double)60.0);
  		double CountsPerByte = (double)CountsPerSecond / (double)BytesPerSecond;
  		uint32_t IntScheduled = (uint32_t)((double)AI_LEN_REG * CountsPerByte);
  
  		ChangeTimer(state,AiTimer,IntScheduled);
  		AI_STATUS_REG|=0x40000000;
  	}
  }
  
  
  void RefreshScreen (usf_state_t * state){
  	ChangeTimer(state, ViTimer, 300000);
  
  }
  
  void RunRsp (usf_state_t * state) {
  	if ( ( SP_STATUS_REG & SP_STATUS_HALT ) == 0) {
  		if ( ( SP_STATUS_REG & SP_STATUS_BROKE ) == 0 ) {
  
  			uint32_t Task = *( uint32_t *)(state->DMEM + 0xFC0);
  
  			switch (Task) {
  			case 1:	{
  					MI_INTR_REG |= 0x20;
  
  					SP_STATUS_REG |= (0x0203 );
  					if ((SP_STATUS_REG & SP_STATUS_INTR_BREAK) != 0 )
  						MI_INTR_REG |= 1;
  
  					CheckInterrupts(state);
  
  					DPC_STATUS_REG &= ~0x0002;
  					return;
  
  				}
  				break;
  			case 2: {
  
  					break;
  				}
  				break;
  			default:
  
  				break;
  			}
  
  			real_run_rsp(state, 100);
  			SP_STATUS_REG |= (0x0203 );
  			if ((SP_STATUS_REG & SP_STATUS_INTR_BREAK) != 0 ) {
  				MI_INTR_REG |= 1;
  				CheckInterrupts(state);
  			}
  
  		}
  	}
  }
  
  void TimerDone (usf_state_t * state) {
  	switch (state->Timers->CurrentTimerType) {
  	case CompareTimer:
  		if(state->enablecompare)
  			FAKE_CAUSE_REGISTER |= CAUSE_IP7;
  		//CheckInterrupts();
  		ChangeCompareTimer(state);
  		break;
  	case ViTimer:
  		RefreshScreen(state);
  		MI_INTR_REG |= MI_INTR_VI;
  		CheckInterrupts(state);
  		*state->WaitMode=0;
  		break;
  	case AiTimer:
  		ChangeTimer(state,AiTimer,0);
  		AI_STATUS_REG=0;
          state->AudioIntrReg|=4;
  		//CheckInterrupts(state);
  		break;
  	}
  	CheckTimer(state);
  }
  