2012-11-01 16:19:01 +01:00
// Copyright (c) 2012- PPSSPP Project.
// 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
2012-11-04 23:01:49 +01:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 16:19:01 +01:00
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2017-02-24 18:59:41 +01:00
# include "ppsspp_config.h"
2013-09-28 00:32:45 -07:00
# include <set>
2017-12-19 15:38:18 +01:00
# include <chrono>
2021-10-23 16:56:15 -07:00
# include <cstdint>
2017-02-27 21:57:46 +01:00
# include <mutex>
2017-12-19 15:38:18 +01:00
# include <condition_variable>
2013-12-30 10:17:11 +01:00
2020-10-04 10:30:18 +02:00
# include "Common/System/NativeApp.h"
# include "Common/System/System.h"
2020-10-04 10:10:55 +02:00
# include "Common/System/Display.h"
2020-08-15 20:53:08 +02:00
# include "Common/TimeUtil.h"
2020-10-01 09:27:25 +02:00
# include "Common/Thread/ThreadUtil.h"
2020-10-04 10:04:01 +02:00
# include "Common/Profiler/Profiler.h"
2012-11-01 16:19:01 +01:00
2018-05-02 07:14:46 -07:00
# include "Common/GraphicsContext.h"
2020-07-04 20:30:05 +02:00
# include "Common/Log.h"
2013-03-29 18:50:08 +01:00
# include "Core/Core.h"
# include "Core/Config.h"
2024-12-08 11:47:12 +01:00
# include "Core/HLE/HLE.h"
2024-11-03 20:49:20 +01:00
# include "Core/MIPS/MIPSDebugInterface.h"
2013-09-14 18:43:23 -07:00
# include "Core/SaveState.h"
2013-03-29 18:50:08 +01:00
# include "Core/System.h"
2023-03-31 10:08:12 +02:00
# include "Core/MemFault.h"
2018-05-02 07:14:46 -07:00
# include "Core/Debugger/Breakpoints.h"
2013-03-29 18:50:08 +01:00
# include "Core/MIPS/MIPS.h"
2024-11-02 22:25:16 +01:00
# include "Core/MIPS/MIPSAnalyst.h"
2023-12-30 15:31:59 +01:00
# include "Core/HLE/sceNetAdhoc.h"
2024-09-08 18:45:27 +03:00
# include "Core/MIPS/MIPSTracer.h"
2013-12-30 00:11:29 +01:00
2024-12-08 11:47:12 +01:00
# include "GPU/Debugger/Stepping.h"
# include "GPU/GPU.h"
# include "GPU/GPUCommon.h"
2012-11-01 16:19:01 +01:00
2024-11-03 20:49:20 +01:00
// Step command to execute next
static std : : mutex g_stepMutex ;
2024-12-02 14:03:58 +01:00
struct CPUStepCommand {
2024-11-03 20:49:20 +01:00
CPUStepType type ;
2024-11-25 19:02:17 +01:00
int stepSize ;
2024-11-03 20:49:20 +01:00
const char * reason ;
u32 relatedAddr ;
bool empty ( ) const {
return type = = CPUStepType : : None ;
}
void clear ( ) {
type = CPUStepType : : None ;
2024-11-25 19:02:17 +01:00
stepSize = 0 ;
2024-11-03 20:49:20 +01:00
reason = " " ;
relatedAddr = 0 ;
}
} ;
2024-12-02 14:03:58 +01:00
static CPUStepCommand g_cpuStepCommand ;
2024-11-03 20:49:20 +01:00
// This is so that external threads can wait for the CPU to become inactive.
2017-12-19 15:38:18 +01:00
static std : : condition_variable m_InactiveCond ;
2017-02-27 21:57:46 +01:00
static std : : mutex m_hInactiveMutex ;
2024-11-03 20:49:20 +01:00
2018-04-19 21:14:01 -07:00
static int steppingCounter = 0 ;
2018-06-11 14:54:42 -07:00
static std : : set < CoreLifecycleFunc > lifecycleFuncs ;
static std : : set < CoreStopRequestFunc > stopFuncs ;
2024-11-03 20:49:20 +01:00
2024-12-08 11:54:58 +01:00
// This can be read and written from ANYWHERE.
volatile CoreState coreState = CORE_STEPPING_CPU ;
2024-12-08 15:00:11 +01:00
CoreState preGeCoreState = CORE_BOOT_ERROR ;
2024-12-08 11:54:58 +01:00
// If true, core state has been changed, but JIT has probably not noticed yet.
volatile bool coreStatePending = false ;
2016-07-24 17:04:06 -07:00
static bool powerSaving = false ;
2024-12-08 15:12:13 +01:00
static bool g_breakAfterFrame = false ;
2013-03-29 20:51:14 +01:00
2023-04-28 13:38:16 +02:00
static MIPSExceptionInfo g_exceptionInfo ;
2020-07-04 20:57:05 +02:00
2024-12-08 11:47:12 +01:00
// This is called on EmuThread before RunLoop.
2024-12-09 23:49:37 +01:00
static bool Core_ProcessStepping ( MIPSDebugInterface * cpu ) ;
2024-12-08 11:47:12 +01:00
2016-01-01 14:08:23 +01:00
void Core_SetGraphicsContext ( GraphicsContext * ctx ) {
2023-08-10 16:33:02 +02:00
PSP_CoreParameter ( ) . graphicsContext = ctx ;
2016-01-01 14:08:23 +01:00
}
2018-04-22 08:33:22 -07:00
void Core_ListenLifecycle ( CoreLifecycleFunc func ) {
2018-06-11 14:54:42 -07:00
lifecycleFuncs . insert ( func ) ;
2013-09-28 00:32:45 -07:00
}
2018-04-22 08:33:22 -07:00
void Core_NotifyLifecycle ( CoreLifecycle stage ) {
2021-08-08 23:01:35 -07:00
if ( stage = = CoreLifecycle : : STARTING ) {
2021-08-08 23:15:27 -07:00
Core_ResetException ( ) ;
2021-08-08 23:01:35 -07:00
}
2018-06-11 14:54:42 -07:00
for ( auto func : lifecycleFuncs ) {
func ( stage ) ;
2013-10-12 01:44:12 -07:00
}
2013-09-28 00:32:45 -07:00
}
2018-06-11 14:54:42 -07:00
void Core_ListenStopRequest ( CoreStopRequestFunc func ) {
stopFuncs . insert ( func ) ;
}
2013-10-12 01:44:12 -07:00
void Core_Stop ( ) {
2021-08-08 23:15:27 -07:00
Core_ResetException ( ) ;
2013-02-23 12:59:40 -08:00
Core_UpdateState ( CORE_POWERDOWN ) ;
2018-06-11 14:54:42 -07:00
for ( auto func : stopFuncs ) {
func ( ) ;
}
2012-11-01 16:19:01 +01:00
}
2024-12-08 11:54:58 +01:00
void Core_UpdateState ( CoreState newState ) {
if ( ( coreState = = CORE_RUNNING_CPU | | coreState = = CORE_NEXTFRAME ) & & newState ! = CORE_RUNNING_CPU )
coreStatePending = true ;
coreState = newState ;
}
2013-10-12 01:44:12 -07:00
bool Core_IsStepping ( ) {
2024-12-01 21:04:21 +01:00
return coreState = = CORE_STEPPING_CPU | | coreState = = CORE_POWERDOWN ;
2012-11-01 16:19:01 +01:00
}
2013-10-12 01:44:12 -07:00
bool Core_IsActive ( ) {
2024-12-01 21:04:21 +01:00
return coreState = = CORE_RUNNING_CPU | | coreState = = CORE_NEXTFRAME | | coreStatePending ;
2013-06-08 08:32:07 +08:00
}
2013-10-12 01:44:12 -07:00
bool Core_IsInactive ( ) {
2024-12-01 21:04:21 +01:00
return coreState ! = CORE_RUNNING_CPU & & coreState ! = CORE_NEXTFRAME & & ! coreStatePending ;
2013-02-23 12:59:40 -08:00
}
2013-02-23 13:21:28 -08:00
2024-12-02 13:15:53 +01:00
void Core_StateProcessed ( ) {
2024-11-29 17:16:59 +01:00
if ( coreStatePending ) {
std : : lock_guard < std : : mutex > guard ( m_hInactiveMutex ) ;
coreStatePending = false ;
m_InactiveCond . notify_all ( ) ;
}
}
2013-10-12 01:44:12 -07:00
void Core_WaitInactive ( ) {
2022-12-18 21:26:59 -08:00
while ( Core_IsActive ( ) & & ! GPUStepping : : IsStepping ( ) ) {
2017-12-19 15:38:18 +01:00
std : : unique_lock < std : : mutex > guard ( m_hInactiveMutex ) ;
m_InactiveCond . wait ( guard ) ;
2013-10-12 01:44:12 -07:00
}
2013-02-23 12:59:40 -08:00
}
2016-07-24 17:04:06 -07:00
void Core_SetPowerSaving ( bool mode ) {
powerSaving = mode ;
}
bool Core_GetPowerSaving ( ) {
return powerSaving ;
}
2024-12-08 11:47:12 +01:00
void Core_RunLoopUntil ( u64 globalticks ) {
while ( true ) {
switch ( coreState ) {
case CORE_POWERDOWN :
case CORE_BOOT_ERROR :
case CORE_RUNTIME_ERROR :
case CORE_NEXTFRAME :
return ;
case CORE_STEPPING_CPU :
case CORE_STEPPING_GE :
2024-12-09 23:49:37 +01:00
if ( Core_ProcessStepping ( currentDebugMIPS ) ) {
return ;
}
break ;
2024-12-08 11:47:12 +01:00
case CORE_RUNNING_CPU :
mipsr4k . RunLoopUntil ( globalticks ) ;
2024-12-08 15:12:13 +01:00
if ( g_breakAfterFrame & & coreState = = CORE_NEXTFRAME ) {
g_breakAfterFrame = false ;
coreState = CORE_STEPPING_CPU ;
}
2024-12-08 11:47:12 +01:00
break ; // Will loop around to go to RUNNING_GE or NEXTFRAME, which will exit.
case CORE_RUNNING_GE :
switch ( gpu - > ProcessDLQueue ( ) ) {
case DLResult : : Break :
GPUStepping : : EnterStepping ( ) ;
break ;
case DLResult : : Error :
// We should elegantly report the error, or I guess ignore it.
hleFinishSyscallAfterGe ( ) ;
2024-12-08 15:00:11 +01:00
coreState = preGeCoreState ;
2024-12-08 11:47:12 +01:00
break ;
case DLResult : : Stall :
case DLResult : : Done :
// Done executing for now
hleFinishSyscallAfterGe ( ) ;
2024-12-08 15:00:11 +01:00
coreState = preGeCoreState ;
2024-12-08 11:47:12 +01:00
break ;
default :
_dbg_assert_ ( false ) ;
hleFinishSyscallAfterGe ( ) ;
2024-12-08 15:00:11 +01:00
coreState = preGeCoreState ;
2024-12-08 11:47:12 +01:00
break ;
}
break ;
}
}
}
2024-12-08 15:00:11 +01:00
// Should only be called from GPUCommon functions (called from sceGe functions).
void Core_SwitchToGe ( ) {
// TODO: This should be an atomic exchange. Or we add bitflags into coreState.
preGeCoreState = coreState ;
coreState = CORE_RUNNING_GE ;
}
2024-12-02 14:03:58 +01:00
bool Core_RequestCPUStep ( CPUStepType type , int stepSize ) {
2024-11-03 20:49:20 +01:00
std : : lock_guard < std : : mutex > guard ( g_stepMutex ) ;
2024-12-02 14:03:58 +01:00
if ( g_cpuStepCommand . type ! = CPUStepType : : None ) {
ERROR_LOG ( Log : : CPU , " Can't submit two steps in one host frame " ) ;
2024-11-03 20:49:20 +01:00
return false ;
}
2024-12-08 15:12:13 +01:00
// Some step types don't need a size.
switch ( type ) {
case CPUStepType : : Out :
case CPUStepType : : Frame :
break ;
default :
_dbg_assert_ ( stepSize ! = 0 ) ;
break ;
}
2024-12-02 14:03:58 +01:00
g_cpuStepCommand = { type , stepSize } ;
return true ;
}
2024-11-03 20:49:20 +01:00
// Handles more advanced step types (used by the debugger).
// stepSize is to support stepping through compound instructions like fused lui+ladd (li).
// Yes, our disassembler does support those.
// Doesn't return the new address, as that's just mips->getPC().
// Internal use.
2024-12-02 14:03:58 +01:00
static void Core_PerformCPUStep ( MIPSDebugInterface * cpu , CPUStepType stepType , int stepSize ) {
2024-11-02 22:16:06 +01:00
switch ( stepType ) {
case CPUStepType : : Into :
{
u32 currentPc = cpu - > GetPC ( ) ;
u32 newAddress = currentPc + stepSize ;
// If the current PC is on a breakpoint, the user still wants the step to happen.
2024-11-24 15:19:30 +01:00
g_breakpoints . SetSkipFirst ( currentPc ) ;
2024-11-03 20:49:20 +01:00
for ( int i = 0 ; i < ( int ) ( newAddress - currentPc ) / 4 ; i + + ) {
currentMIPS - > SingleStep ( ) ;
2024-11-02 22:16:06 +01:00
}
2024-11-25 19:02:17 +01:00
break ;
2024-11-02 22:16:06 +01:00
}
2024-11-02 22:25:16 +01:00
case CPUStepType : : Over :
{
u32 currentPc = cpu - > GetPC ( ) ;
u32 breakpointAddress = currentPc + stepSize ;
2024-11-24 15:19:30 +01:00
g_breakpoints . SetSkipFirst ( currentPc ) ;
2024-11-02 22:25:16 +01:00
MIPSAnalyst : : MipsOpcodeInfo info = MIPSAnalyst : : GetOpcodeInfo ( cpu , cpu - > GetPC ( ) ) ;
2024-11-25 19:02:17 +01:00
// TODO: Doing a step over in a delay slot is a bit .. unclear. Maybe just do a single step.
2024-11-02 22:25:16 +01:00
if ( info . isBranch ) {
if ( info . isConditional = = false ) {
if ( info . isLinkedBranch ) { // jal, jalr
// it's a function call with a delay slot - skip that too
breakpointAddress + = cpu - > getInstructionSize ( 0 ) ;
} else { // j, ...
// in case of absolute branches, set the breakpoint at the branch target
breakpointAddress = info . branchTarget ;
}
} else { // beq, ...
if ( info . conditionMet ) {
breakpointAddress = info . branchTarget ;
} else {
breakpointAddress = currentPc + 2 * cpu - > getInstructionSize ( 0 ) ;
}
}
2024-11-25 19:02:17 +01:00
g_breakpoints . AddBreakPoint ( breakpointAddress , true ) ;
Core_Resume ( ) ;
} else {
// If not a branch, just do a simple single-step, no point in involving the breakpoint machinery.
for ( int i = 0 ; i < ( int ) ( breakpointAddress - currentPc ) / 4 ; i + + ) {
currentMIPS - > SingleStep ( ) ;
}
2024-11-02 22:25:16 +01:00
}
2024-11-03 18:11:33 +01:00
break ;
2024-11-02 22:25:16 +01:00
}
2024-11-02 22:30:28 +01:00
case CPUStepType : : Out :
{
u32 entry = cpu - > GetPC ( ) ;
u32 stackTop = 0 ;
auto threads = GetThreadsInfo ( ) ;
for ( size_t i = 0 ; i < threads . size ( ) ; i + + ) {
if ( threads [ i ] . isCurrent ) {
entry = threads [ i ] . entrypoint ;
stackTop = threads [ i ] . initialStack ;
break ;
}
}
auto frames = MIPSStackWalk : : Walk ( cpu - > GetPC ( ) , cpu - > GetRegValue ( 0 , 31 ) , cpu - > GetRegValue ( 0 , 29 ) , entry , stackTop ) ;
if ( frames . size ( ) < 2 ) {
// Failure. PC not moving.
2024-11-03 18:11:33 +01:00
return ;
2024-11-02 22:30:28 +01:00
}
u32 breakpointAddress = frames [ 1 ] . pc ;
2024-11-24 15:19:30 +01:00
g_breakpoints . AddBreakPoint ( breakpointAddress , true ) ;
2024-11-02 22:30:28 +01:00
Core_Resume ( ) ;
2024-11-03 18:11:33 +01:00
break ;
2024-11-02 22:30:28 +01:00
}
2024-12-08 15:12:13 +01:00
case CPUStepType : : Frame :
{
g_breakAfterFrame = true ;
Core_Resume ( ) ;
break ;
}
2024-11-02 22:16:06 +01:00
default :
// Not yet implemented
2024-11-03 18:11:33 +01:00
break ;
2024-11-02 22:16:06 +01:00
}
2012-11-01 16:19:01 +01:00
}
2024-12-09 23:49:37 +01:00
static bool Core_ProcessStepping ( MIPSDebugInterface * cpu ) {
2024-11-29 17:16:59 +01:00
Core_StateProcessed ( ) ;
2018-04-29 18:08:41 -07:00
// Check if there's any pending save state actions.
SaveState : : Process ( ) ;
2024-12-01 22:00:15 +01:00
switch ( coreState ) {
case CORE_STEPPING_CPU :
2024-12-02 14:03:58 +01:00
case CORE_STEPPING_GE :
2024-12-09 23:49:37 +01:00
case CORE_RUNNING_GE :
2024-12-01 22:00:15 +01:00
// All good
break ;
default :
// Nothing to do.
2024-12-09 23:49:37 +01:00
return true ;
2018-04-29 18:08:41 -07:00
}
2018-06-16 13:53:41 -07:00
// Or any GPU actions.
2024-12-02 14:03:58 +01:00
// Legacy stepping code.
2024-12-03 08:55:13 +01:00
GPUStepping : : ProcessStepping ( ) ;
2018-06-16 13:53:41 -07:00
2024-12-09 23:49:37 +01:00
if ( coreState = = CORE_RUNNING_GE ) {
// Retry, to get it done this frame.
return false ;
}
2018-05-01 22:18:33 -07:00
// We're not inside jit now, so it's safe to clear the breakpoints.
2018-06-23 10:58:30 -07:00
static int lastSteppingCounter = - 1 ;
if ( lastSteppingCounter ! = steppingCounter ) {
2024-11-24 15:19:30 +01:00
g_breakpoints . ClearTemporaryBreakPoints ( ) ;
2024-11-03 17:33:33 +01:00
System_Notify ( SystemNotification : : DISASSEMBLY_AFTERSTEP ) ;
2023-03-21 11:21:19 +01:00
System_Notify ( SystemNotification : : MEM_VIEW ) ;
2018-06-23 10:58:30 -07:00
lastSteppingCounter = steppingCounter ;
}
2018-04-29 18:08:41 -07:00
// Need to check inside the lock to avoid races.
2024-11-03 20:49:20 +01:00
std : : lock_guard < std : : mutex > guard ( g_stepMutex ) ;
2018-04-29 18:08:41 -07:00
2024-12-02 14:03:58 +01:00
if ( coreState ! = CORE_STEPPING_CPU | | g_cpuStepCommand . empty ( ) ) {
2024-12-09 23:49:37 +01:00
return true ;
2018-04-29 18:08:41 -07:00
}
2024-11-03 20:49:20 +01:00
Core_ResetException ( ) ;
2024-12-02 14:03:58 +01:00
if ( ! g_cpuStepCommand . empty ( ) ) {
Core_PerformCPUStep ( cpu , g_cpuStepCommand . type , g_cpuStepCommand . stepSize ) ;
if ( g_cpuStepCommand . type = = CPUStepType : : Into ) {
2024-11-03 20:49:20 +01:00
// We're already done. The other step types will resume the CPU.
System_Notify ( SystemNotification : : DISASSEMBLY_AFTERSTEP ) ;
}
2024-12-02 14:03:58 +01:00
g_cpuStepCommand . clear ( ) ;
2024-11-03 20:49:20 +01:00
steppingCounter + + ;
}
// Update disasm dialog.
System_Notify ( SystemNotification : : MEM_VIEW ) ;
2024-12-09 23:49:37 +01:00
return true ;
2018-04-29 18:08:41 -07:00
}
2024-11-03 20:49:20 +01:00
// Free-threaded (hm, possibly except tracing).
2024-11-01 22:52:47 +01:00
void Core_Break ( const char * reason , u32 relatedAddress ) {
2024-12-03 10:59:41 +01:00
if ( coreState ! = CORE_RUNNING_CPU ) {
2024-12-06 00:40:21 +01:00
ERROR_LOG ( Log : : CPU , " Core_Break only works in the CORE_RUNNING_CPU state " ) ;
2024-12-03 10:59:41 +01:00
return ;
}
2024-11-03 20:49:20 +01:00
{
2024-12-10 21:50:14 +01:00
// Stop the tracer
2024-11-03 20:49:20 +01:00
std : : lock_guard < std : : mutex > lock ( g_stepMutex ) ;
2024-12-02 14:03:58 +01:00
if ( ! g_cpuStepCommand . empty ( ) & & Core_IsStepping ( ) ) {
2024-11-25 10:09:21 +01:00
// If we're in a failed step that uses a temp breakpoint, we need to be able to override it here.
2024-12-02 14:03:58 +01:00
switch ( g_cpuStepCommand . type ) {
2024-11-25 10:09:21 +01:00
case CPUStepType : : Over :
case CPUStepType : : Out :
// Allow overwriting the command.
break ;
2024-11-25 19:02:17 +01:00
default :
2024-12-02 14:03:58 +01:00
ERROR_LOG ( Log : : CPU , " Core_Break called with a step-command already in progress: %s " , g_cpuStepCommand . reason ) ;
2024-11-25 19:02:17 +01:00
return ;
2024-11-25 10:09:21 +01:00
}
2024-11-12 11:25:35 +01:00
}
mipsTracer . stop_tracing ( ) ;
2024-12-02 14:03:58 +01:00
g_cpuStepCommand . type = CPUStepType : : None ;
g_cpuStepCommand . reason = reason ;
g_cpuStepCommand . relatedAddr = relatedAddress ;
2024-11-03 20:49:20 +01:00
steppingCounter + + ;
_assert_msg_ ( reason ! = nullptr , " No reason specified for break " ) ;
2024-12-01 21:04:21 +01:00
Core_UpdateState ( CORE_STEPPING_CPU ) ;
2024-11-03 20:49:20 +01:00
}
2023-03-21 11:40:48 +01:00
System_Notify ( SystemNotification : : DEBUG_MODE_CHANGE ) ;
2012-11-01 16:19:01 +01:00
}
2018-04-19 21:14:01 -07:00
2024-11-03 20:49:20 +01:00
// Free-threaded (or at least should be)
2024-11-01 22:52:47 +01:00
void Core_Resume ( ) {
2024-12-08 15:00:11 +01:00
// If the current PC is on a breakpoint, the user doesn't want to do nothing.
2024-12-10 01:38:35 +01:00
if ( currentMIPS ) {
g_breakpoints . SetSkipFirst ( currentMIPS - > pc ) ;
}
2024-12-02 14:03:58 +01:00
// Handle resuming from GE.
if ( coreState = = CORE_STEPPING_GE ) {
coreState = CORE_RUNNING_GE ;
return ;
}
2024-11-01 22:52:47 +01:00
// Clear the exception if we resume.
Core_ResetException ( ) ;
2024-12-01 21:04:21 +01:00
coreState = CORE_RUNNING_CPU ;
2024-11-02 22:44:08 +01:00
System_Notify ( SystemNotification : : DEBUG_MODE_CHANGE ) ;
2024-11-01 22:52:47 +01:00
}
2024-11-03 20:49:20 +01:00
// Should be called from the EmuThread.
2020-07-04 20:57:05 +02:00
bool Core_NextFrame ( ) {
2024-12-09 12:00:15 +01:00
CoreState coreState = : : coreState ;
2024-12-02 14:03:58 +01:00
_dbg_assert_ ( coreState ! = CORE_STEPPING_GE & & coreState ! = CORE_RUNNING_GE ) ;
2024-12-09 23:49:37 +01:00
if ( coreState = = CORE_RUNNING_CPU ) {
2024-12-09 12:00:15 +01:00
: : coreState = CORE_NEXTFRAME ;
2020-07-04 20:57:05 +02:00
return true ;
2024-12-09 23:49:37 +01:00
} else if ( coreState = = CORE_STEPPING_CPU ) {
// All good, just stepping through so no need to switch to the NextFrame coreState though, that'd
// just lose our stepping state.
INFO_LOG ( Log : : System , " Reached end-of-frame while stepping the CPU (this is ok) " ) ;
return true ;
2020-07-04 20:57:05 +02:00
} else {
2024-12-09 23:49:37 +01:00
ERROR_LOG ( Log : : System , " Core_NextFrame called with wrong core state %s " , CoreStateToString ( coreState ) ) ;
2020-07-04 20:57:05 +02:00
return false ;
}
}
2018-04-19 21:14:01 -07:00
int Core_GetSteppingCounter ( ) {
return steppingCounter ;
}
2020-07-04 20:30:05 +02:00
2021-10-23 17:22:09 -07:00
SteppingReason Core_GetSteppingReason ( ) {
SteppingReason r ;
2024-11-03 20:49:20 +01:00
std : : lock_guard < std : : mutex > lock ( g_stepMutex ) ;
2024-12-02 14:03:58 +01:00
if ( ! g_cpuStepCommand . empty ( ) ) {
r . reason = g_cpuStepCommand . reason ;
r . relatedAddress = g_cpuStepCommand . relatedAddr ;
2024-11-03 20:49:20 +01:00
}
2021-10-23 17:22:09 -07:00
return r ;
}
2023-04-28 13:38:16 +02:00
const char * ExceptionTypeAsString ( MIPSExceptionType type ) {
2020-07-04 20:30:05 +02:00
switch ( type ) {
2023-04-28 13:38:16 +02:00
case MIPSExceptionType : : MEMORY : return " Invalid Memory Access " ;
case MIPSExceptionType : : BREAK : return " Break " ;
case MIPSExceptionType : : BAD_EXEC_ADDR : return " Bad Execution Address " ;
2020-07-04 20:57:05 +02:00
default : return " N/A " ;
2020-07-04 20:30:05 +02:00
}
2020-07-04 20:57:05 +02:00
}
2020-07-04 20:30:05 +02:00
2019-02-12 13:29:37 +01:00
const char * MemoryExceptionTypeAsString ( MemoryExceptionType type ) {
2020-07-04 20:57:05 +02:00
switch ( type ) {
2020-07-13 09:17:42 +02:00
case MemoryExceptionType : : UNKNOWN : return " Unknown " ;
2020-07-04 20:57:05 +02:00
case MemoryExceptionType : : READ_WORD : return " Read Word " ;
case MemoryExceptionType : : WRITE_WORD : return " Write Word " ;
case MemoryExceptionType : : READ_BLOCK : return " Read Block " ;
case MemoryExceptionType : : WRITE_BLOCK : return " Read/Write Block " ;
2022-08-21 13:24:10 -07:00
case MemoryExceptionType : : ALIGNMENT : return " Alignment " ;
2020-07-04 20:57:05 +02:00
default :
return " N/A " ;
}
}
2020-07-12 21:59:08 -07:00
const char * ExecExceptionTypeAsString ( ExecExceptionType type ) {
switch ( type ) {
case ExecExceptionType : : JUMP : return " CPU Jump " ;
case ExecExceptionType : : THREAD : return " Thread switch " ;
default :
return " N/A " ;
}
}
2023-01-01 19:22:41 +01:00
void Core_MemoryException ( u32 address , u32 accessSize , u32 pc , MemoryExceptionType type ) {
2019-02-12 13:29:37 +01:00
const char * desc = MemoryExceptionTypeAsString ( type ) ;
2020-07-04 20:30:05 +02:00
// In jit, we only flush PC when bIgnoreBadMemAccess is off.
2023-08-20 18:36:06 -07:00
if ( ( g_Config . iCpuCore = = ( int ) CPUCore : : JIT | | g_Config . iCpuCore = = ( int ) CPUCore : : JIT_IR ) & & g_Config . bIgnoreBadMemAccess ) {
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : MemMap , " %s: Invalid access at %08x (size %08x) " , desc , address , accessSize ) ;
2020-07-04 20:30:05 +02:00
} else {
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : MemMap , " %s: Invalid access at %08x (size %08x) PC %08x LR %08x " , desc , address , accessSize , currentMIPS - > pc , currentMIPS - > r [ MIPS_REG_RA ] ) ;
2020-07-04 20:30:05 +02:00
}
if ( ! g_Config . bIgnoreBadMemAccess ) {
2023-03-31 10:08:12 +02:00
// Try to fetch a call stack, to start with.
std : : vector < MIPSStackWalk : : StackFrame > stackFrames = WalkCurrentStack ( - 1 ) ;
std : : string stackTrace = FormatStackTrace ( stackFrames ) ;
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : MemMap , " \n %s " , stackTrace . c_str ( ) ) ;
2023-03-31 10:08:12 +02:00
2023-04-28 13:38:16 +02:00
MIPSExceptionInfo & e = g_exceptionInfo ;
2019-02-12 13:29:37 +01:00
e = { } ;
2023-04-28 13:38:16 +02:00
e . type = MIPSExceptionType : : MEMORY ;
2022-09-30 12:26:30 +03:00
e . info . clear ( ) ;
2019-02-12 13:29:37 +01:00
e . memory_type = type ;
e . address = address ;
2023-01-01 19:22:41 +01:00
e . accessSize = accessSize ;
2023-03-31 10:08:12 +02:00
e . stackTrace = stackTrace ;
2019-02-12 13:29:37 +01:00
e . pc = pc ;
2024-11-01 22:52:47 +01:00
Core_Break ( " memory.exception " , address ) ;
2020-07-04 20:30:05 +02:00
}
}
2024-01-15 10:36:32 +01:00
void Core_MemoryExceptionInfo ( u32 address , u32 accessSize , u32 pc , MemoryExceptionType type , std : : string_view additionalInfo , bool forceReport ) {
2020-07-15 12:38:05 +02:00
const char * desc = MemoryExceptionTypeAsString ( type ) ;
// In jit, we only flush PC when bIgnoreBadMemAccess is off.
2023-08-20 18:36:06 -07:00
if ( ( g_Config . iCpuCore = = ( int ) CPUCore : : JIT | | g_Config . iCpuCore = = ( int ) CPUCore : : JIT_IR ) & & g_Config . bIgnoreBadMemAccess ) {
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : MemMap , " %s: Invalid access at %08x (size %08x). %.*s " , desc , address , accessSize , ( int ) additionalInfo . length ( ) , additionalInfo . data ( ) ) ;
2020-07-15 12:38:05 +02:00
} else {
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : MemMap , " %s: Invalid access at %08x (size %08x) PC %08x LR %08x %.*s " , desc , address , accessSize , currentMIPS - > pc , currentMIPS - > r [ MIPS_REG_RA ] , ( int ) additionalInfo . length ( ) , additionalInfo . data ( ) ) ;
2020-07-15 12:38:05 +02:00
}
2022-12-30 12:21:05 +01:00
if ( ! g_Config . bIgnoreBadMemAccess | | forceReport ) {
2023-03-31 10:08:12 +02:00
// Try to fetch a call stack, to start with.
std : : vector < MIPSStackWalk : : StackFrame > stackFrames = WalkCurrentStack ( - 1 ) ;
std : : string stackTrace = FormatStackTrace ( stackFrames ) ;
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : MemMap , " \n %s " , stackTrace . c_str ( ) ) ;
2023-03-31 10:08:12 +02:00
2023-04-28 13:38:16 +02:00
MIPSExceptionInfo & e = g_exceptionInfo ;
2020-07-15 12:38:05 +02:00
e = { } ;
2023-04-28 13:38:16 +02:00
e . type = MIPSExceptionType : : MEMORY ;
2020-07-15 12:38:05 +02:00
e . info = additionalInfo ;
e . memory_type = type ;
e . address = address ;
2023-04-16 19:10:20 -07:00
e . accessSize = accessSize ;
2023-03-31 10:08:12 +02:00
e . stackTrace = stackTrace ;
2020-07-15 12:38:05 +02:00
e . pc = pc ;
2024-11-01 22:52:47 +01:00
Core_Break ( " memory.exception " , address ) ;
2020-07-15 12:38:05 +02:00
}
}
2022-12-30 12:21:05 +01:00
// Can't be ignored
2020-07-12 21:59:08 -07:00
void Core_ExecException ( u32 address , u32 pc , ExecExceptionType type ) {
const char * desc = ExecExceptionTypeAsString ( type ) ;
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : MemMap , " %s: Invalid exec address %08x PC %08x LR %08x " , desc , address , pc , currentMIPS - > r [ MIPS_REG_RA ] ) ;
2020-07-12 21:59:08 -07:00
2023-04-28 13:38:16 +02:00
MIPSExceptionInfo & e = g_exceptionInfo ;
2021-02-09 09:38:03 +01:00
e = { } ;
2023-04-28 13:38:16 +02:00
e . type = MIPSExceptionType : : BAD_EXEC_ADDR ;
2022-09-30 12:26:30 +03:00
e . info . clear ( ) ;
2021-02-09 09:38:03 +01:00
e . exec_type = type ;
e . address = address ;
2023-01-01 19:22:41 +01:00
e . accessSize = 4 ; // size of an instruction
2021-02-09 09:38:03 +01:00
e . pc = pc ;
2022-08-21 14:09:52 -07:00
// This just records the closest value that could be useful as reference.
e . ra = currentMIPS - > r [ MIPS_REG_RA ] ;
2024-11-01 22:52:47 +01:00
Core_Break ( " cpu.exception " , address ) ;
2020-07-12 21:59:08 -07:00
}
2024-11-01 22:52:47 +01:00
void Core_BreakException ( u32 pc ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : CPU , " BREAK! " ) ;
2020-07-04 20:57:05 +02:00
2023-04-28 13:38:16 +02:00
MIPSExceptionInfo & e = g_exceptionInfo ;
2020-07-04 20:57:05 +02:00
e = { } ;
2023-04-28 13:38:16 +02:00
e . type = MIPSExceptionType : : BREAK ;
2022-09-30 12:26:30 +03:00
e . info . clear ( ) ;
2022-08-21 14:09:52 -07:00
e . pc = pc ;
2020-07-04 20:57:05 +02:00
2020-07-04 20:30:05 +02:00
if ( ! g_Config . bIgnoreBadMemAccess ) {
2024-11-01 22:52:47 +01:00
Core_Break ( " cpu.breakInstruction " , currentMIPS - > pc ) ;
2020-07-04 20:30:05 +02:00
}
}
2020-07-04 20:57:05 +02:00
2021-08-08 23:15:27 -07:00
void Core_ResetException ( ) {
2023-04-28 13:38:16 +02:00
g_exceptionInfo . type = MIPSExceptionType : : NONE ;
2021-08-08 23:15:27 -07:00
}
2023-04-28 13:38:16 +02:00
const MIPSExceptionInfo & Core_GetExceptionInfo ( ) {
2020-07-04 20:57:05 +02:00
return g_exceptionInfo ;
}