daedalus/Source/Core/Interpret.cpp
2024-08-05 21:15:57 +10:00

194 lines
5.3 KiB
C++

/*
Copyright (C) 2009 StrmnNrmn
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// Stuff to handle Processor
#include "Base/Types.h"
#include "Core/CPU.h"
#include "Debug/Registers.h" // For REG_?? defines
#include "Core/Memory.h"
#include "Core/Interpret.h"
#include "Core/Interrupt.h"
#include "RomFile/ROMBuffer.h"
#include "Core/R4300.h"
#include "Interface/ConfigOptions.h"
#include "Debug/DBGConsole.h"
#include "Debug/DebugLog.h"
#include "OSHLE/patch.h" // GetCorrectOp
#include "Ultra/ultra_R4300.h"
#include "Base/Macros.h"
#include "Utility/Profiler.h"
#include "Debug/Synchroniser.h"
//*****************************************************************************
// Execute a single MIPS op. The conditionals for the templated arguments
// are completely optimised away by the compiler.
//
// TranslateOp: Use this to translate breakpoints/patches to original op
// before execution.
//*****************************************************************************
template< bool TranslateOp > inline void CPU_EXECUTE_OP()
{
u8 * p_Instruction = 0;
CPU_FETCH_INSTRUCTION( p_Instruction, gCPUState.CurrentPC );
OpCode op_code = *(OpCode*)p_Instruction;
// Cache instruction base pointer (used for SpeedHack() @ R4300.0)
gLastAddress = p_Instruction;
#ifdef DAEDALUS_BREAKPOINTS_ENABLED
if ( TranslateOp )
{
// Handle breakpoints correctly
if (op_code.op == OP_DBG_BKPT)
{
// Turn temporary disable on to allow instr to be processed
// Entry is in lower 26 bits...
u32 breakpoint( op_code.bp_index );
if ( breakpoint < g_BreakPoints.size() )
{
if (g_BreakPoints[ breakpoint ].mEnabled)
{
g_BreakPoints[ breakpoint ].mTemporaryDisable = true;
}
}
}
else
{
op_code = GetCorrectOp( op_code );
}
}
#endif
SYNCH_POINT( DAED_SYNC_REG_PC, gCPUState.CurrentPC, "Program Counter doesn't match" );
SYNCH_POINT( DAED_SYNC_FRAGMENT_PC, gCPUState.CurrentPC + gCPUState.Delay, "Program Counter/Delay doesn't match while interpreting" );
SYNCH_POINT( DAED_SYNC_REG_PC, gCPUState.CPUControl[C0_COUNT]._u32, "Count doesn't match" );
R4300_ExecuteInstruction(op_code);
gGPR[0]._u64 = 0; //Ensure r0 is zero
#ifdef DAEDALUS_PROFILE_EXECUTION
gTotalInstructionsEmulated++;
#endif
SYNCH_POINT( DAED_SYNC_REGS, CPU_ProduceRegisterHash(), "Registers don't match" );
// Increment count register
gCPUState.CPUControl[C0_COUNT]._u32 = gCPUState.CPUControl[C0_COUNT]._u32 + COUNTER_INCREMENT_PER_OP;
if (CPU_ProcessEventCycles( COUNTER_INCREMENT_PER_OP ) )
{
CPU_HANDLE_COUNT_INTERRUPT();
}
switch (gCPUState.Delay)
{
case DO_DELAY:
// We've got a delayed instruction to execute. Increment
// PC as normal, so that subsequent instruction is executed
INCREMENT_PC();
gCPUState.Delay = EXEC_DELAY;
break;
case EXEC_DELAY:
{
//bool backwards( gCPUState.TargetPC <= gCPUState.CurrentPC );
// We've just executed the delayed instr. Now carry out jump as stored in gCPUState.TargetPC;
CPU_SetPC(gCPUState.TargetPC);
gCPUState.Delay = NO_DELAY;
}
break;
case NO_DELAY:
// Normal operation - just increment the PC
INCREMENT_PC();
break;
}
}
//*****************************************************************************
// Keep executing instructions until there are other tasks to do (i.e. gCPUState.GetStuffToDo() is set)
// Process these tasks and loop
//*****************************************************************************
void CPU_Go()
{
DAEDALUS_PROFILE( __FUNCTION__ );
while (CPU_KeepRunning())
{
//
// Keep executing ops as long as there's nothing to do
//
u32 stuff_to_do( gCPUState.GetStuffToDo() );
while(stuff_to_do == 0)
{
CPU_EXECUTE_OP< false >();
stuff_to_do = gCPUState.GetStuffToDo();
}
if (CPU_CheckStuffToDo())
break;
}
}
void Inter_SelectCore()
{
g_pCPUCore = CPU_Go;
}
//*****************************************************************************
// Hacky function to use when debugging
//*****************************************************************************
void CPU_Skip()
{
#ifdef DAEDALUS_DEBUG_CONSOLE
if (CPU_IsRunning())
{
DBGConsole_Msg(0, "Already Running");
return;
}
#endif
INCREMENT_PC();
}
//*****************************************************************************
//
//*****************************************************************************
void CPU_Step()
{
#ifdef DAEDALUS_DEBUG_CONSOLE
if (CPU_IsRunning())
{
DBGConsole_Msg(0, "Already Running");
return;
}
#endif
CPU_CheckStuffToDo();
CPU_EXECUTE_OP< true >();
}