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"
# include "Core/MemMap.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"
2022-01-30 10:46:50 -08:00
# include "Core/HW/Display.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"
2018-06-16 13:53:41 -07:00
# include "GPU/Debugger/Stepping.h"
2024-09-08 18:45:27 +03:00
# include "Core/MIPS/MIPSTracer.h"
2013-12-30 00:11:29 +01:00
2013-02-18 23:25:06 +01:00
# ifdef _WIN32
2018-02-25 10:27:59 +01:00
# include "Common/CommonWindows.h"
2013-03-29 20:51:14 +01:00
# include "Windows/InputDevice.h"
2013-02-18 23:25:06 +01:00
# endif
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 ;
struct StepCommand {
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 ;
}
} ;
static StepCommand g_stepCommand ;
// 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
2014-06-29 13:11:06 +02:00
static bool windowHidden = false ;
2016-07-24 17:04:06 -07:00
static bool powerSaving = 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
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
}
2014-06-29 13:11:06 +02:00
void Core_NotifyWindowHidden ( bool hidden ) {
windowHidden = hidden ;
// TODO: Wait until we can react?
}
2023-08-08 11:21:09 +02:00
bool Core_IsWindowHidden ( ) {
return windowHidden ;
}
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
}
2023-12-30 15:18:56 +01:00
bool Core_ShouldRunBehind ( ) {
2023-12-30 15:31:59 +01:00
// Enforce run-behind if ad-hoc connected
2024-01-21 12:02:30 +01:00
return g_Config . bRunBehindPauseMenu | | Core_MustRunBehind ( ) ;
}
bool Core_MustRunBehind ( ) {
return __NetAdhocConnected ( ) ;
2023-12-30 15:18:56 +01:00
}
2013-10-12 01:44:12 -07:00
bool Core_IsStepping ( ) {
2012-11-01 16:19:01 +01:00
return coreState = = CORE_STEPPING | | coreState = = CORE_POWERDOWN ;
}
2013-10-12 01:44:12 -07:00
bool Core_IsActive ( ) {
2013-06-08 08:32:07 +08:00
return coreState = = CORE_RUNNING | | coreState = = CORE_NEXTFRAME | | coreStatePending ;
}
2013-10-12 01:44:12 -07:00
bool Core_IsInactive ( ) {
2013-02-23 12:59:40 -08:00
return coreState ! = CORE_RUNNING & & coreState ! = CORE_NEXTFRAME & & ! coreStatePending ;
}
2013-02-23 13:21:28 -08:00
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
}
2013-10-12 01:44:12 -07:00
void Core_WaitInactive ( int milliseconds ) {
2022-12-18 21:26:59 -08:00
if ( Core_IsActive ( ) & & ! GPUStepping : : IsStepping ( ) ) {
2017-12-19 15:38:18 +01:00
std : : unique_lock < std : : mutex > guard ( m_hInactiveMutex ) ;
m_InactiveCond . wait_for ( guard , std : : chrono : : milliseconds ( milliseconds ) ) ;
2013-10-12 01:44:12 -07:00
}
2013-02-23 13:21:28 -08:00
}
2016-07-24 17:04:06 -07:00
void Core_SetPowerSaving ( bool mode ) {
powerSaving = mode ;
}
bool Core_GetPowerSaving ( ) {
return powerSaving ;
}
2017-01-17 17:21:00 +07:00
static bool IsWindowSmall ( int pixelWidth , int pixelHeight ) {
// Can't take this from config as it will not be set if windows is maximized.
2023-02-25 13:09:44 +01:00
int w = ( int ) ( pixelWidth * g_display . dpi_scale_x ) ;
int h = ( int ) ( pixelHeight * g_display . dpi_scale_y ) ;
2017-01-17 17:21:00 +07:00
return g_Config . IsPortrait ( ) ? ( h < 480 + 80 ) : ( w < 480 + 80 ) ;
}
2017-01-16 19:08:26 +07:00
// TODO: Feels like this belongs elsewhere.
2017-01-17 17:21:00 +07:00
bool UpdateScreenScale ( int width , int height ) {
bool smallWindow ;
2023-07-22 21:35:21 +08:00
2020-01-05 15:46:27 +08:00
float g_logical_dpi = System_GetPropertyFloat ( SYSPROP_DISPLAY_LOGICAL_DPI ) ;
2023-07-22 21:35:21 +08:00
g_display . dpi = System_GetPropertyFloat ( SYSPROP_DISPLAY_DPI ) ;
if ( g_display . dpi < 0.0f ) {
g_display . dpi = 96.0f ;
}
if ( g_logical_dpi < 0.0f ) {
g_logical_dpi = 96.0f ;
}
2023-02-25 13:09:44 +01:00
g_display . dpi_scale_x = g_logical_dpi / g_display . dpi ;
g_display . dpi_scale_y = g_logical_dpi / g_display . dpi ;
g_display . dpi_scale_real_x = g_display . dpi_scale_x ;
g_display . dpi_scale_real_y = g_display . dpi_scale_y ;
2017-03-12 12:17:35 -07:00
2017-01-17 19:02:47 +07:00
smallWindow = IsWindowSmall ( width , height ) ;
2017-01-17 17:21:00 +07:00
if ( smallWindow ) {
2023-02-25 13:09:44 +01:00
g_display . dpi / = 2.0f ;
g_display . dpi_scale_x * = 2.0f ;
g_display . dpi_scale_y * = 2.0f ;
2017-01-17 17:21:00 +07:00
}
2023-02-25 13:09:44 +01:00
g_display . pixel_in_dps_x = 1.0f / g_display . dpi_scale_x ;
g_display . pixel_in_dps_y = 1.0f / g_display . dpi_scale_y ;
2014-12-28 13:19:19 -08:00
2023-02-25 13:09:44 +01:00
int new_dp_xres = ( int ) ( width * g_display . dpi_scale_x ) ;
int new_dp_yres = ( int ) ( height * g_display . dpi_scale_y ) ;
2014-12-28 13:19:19 -08:00
2023-02-25 13:09:44 +01:00
bool dp_changed = new_dp_xres ! = g_display . dp_xres | | new_dp_yres ! = g_display . dp_yres ;
bool px_changed = g_display . pixel_xres ! = width | | g_display . pixel_yres ! = height ;
2014-12-28 13:19:19 -08:00
if ( dp_changed | | px_changed ) {
2023-02-25 13:09:44 +01:00
g_display . dp_xres = new_dp_xres ;
g_display . dp_yres = new_dp_yres ;
g_display . pixel_xres = width ;
g_display . pixel_yres = height ;
2014-12-28 13:19:19 -08:00
NativeResized ( ) ;
return true ;
}
return false ;
2013-03-29 18:50:08 +01:00
}
2023-08-10 16:33:02 +02:00
// Used by Windows, SDL, Qt.
void UpdateRunLoop ( GraphicsContext * ctx ) {
NativeFrame ( ctx ) ;
2014-06-30 13:04:44 -04:00
if ( windowHidden & & g_Config . bPauseWhenMinimized ) {
2024-11-21 15:25:02 +01:00
sleep_ms ( 16 , " window-hidden " ) ;
2014-06-30 00:17:34 +10:00
return ;
2014-07-01 13:29:39 +10:00
}
2013-10-12 01:40:33 -07:00
}
2023-08-10 16:33:02 +02:00
// Note: not used on Android.
2017-03-14 22:01:18 -07:00
void Core_RunLoop ( GraphicsContext * ctx ) {
2023-08-10 16:33:02 +02:00
if ( windowHidden & & g_Config . bPauseWhenMinimized ) {
2024-11-21 15:25:02 +01:00
sleep_ms ( 16 , " window-hidden " ) ;
2023-08-10 16:33:02 +02:00
return ;
}
2023-08-10 16:35:25 +02:00
NativeFrame ( ctx ) ;
2012-11-01 16:19:01 +01:00
}
2024-11-03 20:49:20 +01:00
bool Core_RequestSingleStep ( CPUStepType type , int stepSize ) {
std : : lock_guard < std : : mutex > guard ( g_stepMutex ) ;
if ( g_stepCommand . type ! = CPUStepType : : None ) {
ERROR_LOG ( Log : : CPU , " Can't submit two steps in one frame " ) ;
return false ;
}
2024-11-25 19:02:17 +01:00
// Out-steps don't need a size.
_dbg_assert_ ( stepSize ! = 0 | | type = = CPUStepType : : Out ) ;
2024-11-03 20:49:20 +01:00
g_stepCommand = { type , stepSize } ;
return true ;
2012-11-01 16:19:01 +01:00
}
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.
static void Core_PerformStep ( 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 ;
// If the current PC is on a breakpoint, the user doesn't want to do nothing.
2024-11-24 15:19:30 +01:00
g_breakpoints . SetSkipFirst ( currentMIPS - > pc ) ;
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-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-11-03 20:49:20 +01:00
void Core_ProcessStepping ( MIPSDebugInterface * cpu ) {
coreStatePending = false ;
2018-04-29 18:08:41 -07:00
// Check if there's any pending save state actions.
SaveState : : Process ( ) ;
if ( coreState ! = CORE_STEPPING ) {
return ;
}
2018-06-16 13:53:41 -07:00
// Or any GPU actions.
GPUStepping : : SingleStep ( ) ;
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-11-03 20:49:20 +01:00
if ( coreState ! = CORE_STEPPING | | g_stepCommand . empty ( ) ) {
return ;
2018-04-29 18:08:41 -07:00
}
2024-11-03 20:49:20 +01:00
Core_ResetException ( ) ;
if ( ! g_stepCommand . empty ( ) ) {
2024-11-25 19:02:17 +01:00
Core_PerformStep ( cpu , g_stepCommand . type , g_stepCommand . stepSize ) ;
2024-11-03 20:49:20 +01:00
if ( g_stepCommand . type = = CPUStepType : : Into ) {
// We're already done. The other step types will resume the CPU.
System_Notify ( SystemNotification : : DISASSEMBLY_AFTERSTEP ) ;
}
g_stepCommand . clear ( ) ;
steppingCounter + + ;
}
// Update disasm dialog.
System_Notify ( SystemNotification : : MEM_VIEW ) ;
2018-04-29 18:08:41 -07:00
}
2017-12-19 15:42:18 +01:00
// Many platforms, like Android, do not call this function but handle things on their own.
2023-08-07 22:44:06 +02:00
// Instead they simply call NativeFrame directly.
2022-11-08 21:59:08 +01:00
bool Core_Run ( GraphicsContext * ctx ) {
2023-03-21 11:21:19 +01:00
System_Notify ( SystemNotification : : DISASSEMBLY ) ;
2017-12-19 17:59:00 +01:00
while ( true ) {
2014-06-22 09:38:46 +02:00
if ( GetUIState ( ) ! = UISTATE_INGAME ) {
if ( GetUIState ( ) = = UISTATE_EXIT ) {
2023-08-10 16:33:02 +02:00
// Not sure why we do a final frame here?
NativeFrame ( ctx ) ;
2022-11-08 21:59:08 +01:00
return false ;
2013-10-12 01:40:33 -07:00
}
2017-03-14 22:01:18 -07:00
Core_RunLoop ( ctx ) ;
2013-10-12 01:40:33 -07:00
continue ;
}
2017-12-19 17:59:00 +01:00
switch ( coreState ) {
2012-11-01 16:19:01 +01:00
case CORE_RUNNING :
2018-04-29 18:38:17 -07:00
case CORE_STEPPING :
2013-09-14 18:43:23 -07:00
// enter a fast runloop
2017-03-14 22:01:18 -07:00
Core_RunLoop ( ctx ) ;
2018-04-29 18:38:17 -07:00
if ( coreState = = CORE_POWERDOWN ) {
2022-11-08 21:59:08 +01:00
return true ;
2018-04-29 18:38:17 -07:00
}
2012-11-01 16:19:01 +01:00
break ;
2013-11-09 20:56:11 -08:00
case CORE_POWERUP :
2012-11-01 16:19:01 +01:00
case CORE_POWERDOWN :
2020-07-04 20:19:56 +02:00
case CORE_BOOT_ERROR :
case CORE_RUNTIME_ERROR :
2013-09-14 18:43:23 -07:00
// Exit loop!!
2022-11-08 21:59:08 +01:00
return true ;
2013-02-23 12:59:40 -08:00
case CORE_NEXTFRAME :
2022-11-08 21:59:08 +01:00
return true ;
2012-11-01 16:19:01 +01: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 ) {
// Stop the tracer
2024-11-03 20:49:20 +01:00
{
std : : lock_guard < std : : mutex > lock ( g_stepMutex ) ;
2024-11-26 00:28:22 +01:00
if ( ! g_stepCommand . 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.
switch ( g_stepCommand . type ) {
case CPUStepType : : Over :
case CPUStepType : : Out :
// Allow overwriting the command.
break ;
2024-11-25 19:02:17 +01:00
default :
ERROR_LOG ( Log : : CPU , " Core_Break called with a step-command already in progress: %s " , g_stepCommand . reason ) ;
return ;
2024-11-25 10:09:21 +01:00
}
2024-11-12 11:25:35 +01:00
}
mipsTracer . stop_tracing ( ) ;
2024-11-25 10:09:21 +01:00
g_stepCommand . type = CPUStepType : : None ;
2024-11-12 11:25:35 +01:00
g_stepCommand . reason = reason ;
g_stepCommand . relatedAddr = relatedAddress ;
2024-11-03 20:49:20 +01:00
steppingCounter + + ;
_assert_msg_ ( reason ! = nullptr , " No reason specified for break " ) ;
Core_UpdateState ( CORE_STEPPING ) ;
}
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 ( ) {
// Clear the exception if we resume.
Core_ResetException ( ) ;
coreState = CORE_RUNNING ;
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 ( ) {
if ( coreState = = CORE_RUNNING ) {
coreState = CORE_NEXTFRAME ;
return true ;
} else {
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 ) ;
if ( ! g_stepCommand . empty ( ) ) {
r . reason = g_stepCommand . reason ;
r . relatedAddress = g_stepCommand . relatedAddr ;
}
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 ;
}