Cog/Frameworks/lazyusf/lazyusf/cpu.c
2014-11-23 23:46:10 -08:00

590 lines
16 KiB
C

/*
* 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 "cpu_hle.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;
}
state->OLD_VI_V_SYNC_REG = ~VI_V_SYNC_REG;
CPUHLE_Scan(state);
}
void RefreshScreen (usf_state_t * state){
if (state->OLD_VI_V_SYNC_REG != VI_V_SYNC_REG)
{
state->OLD_VI_V_SYNC_REG = VI_V_SYNC_REG;
if (VI_V_SYNC_REG == 0)
{
state->VI_INTR_TIME = 500000;
}
else
{
state->VI_INTR_TIME = (VI_V_SYNC_REG + 1) * 1500;
if ((VI_V_SYNC_REG & 1) != 0)
{
state->VI_INTR_TIME -= 38;
}
}
}
ChangeTimer(state,ViTimer,state->Timers->Timer + state->Timers->NextTimer[ViTimer] + state->VI_INTR_TIME);
if ((VI_STATUS_REG & 0x10) != 0)
{
if (state->ViFieldNumber == 0)
{
state->ViFieldNumber = 1;
}
else
{
state->ViFieldNumber = 0;
}
}
else
{
state->ViFieldNumber = 0;
}
}
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(state);
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);
}