/* * 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 #include #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 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) { 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); }