2012-11-01 15:19:01 +00: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 22:01:49 +00:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 15:19:01 +00: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 17:59:41 +00:00
# include "ppsspp_config.h"
2013-09-28 07:32:45 +00:00
# include <set>
2017-12-19 14:38:18 +00:00
# include <chrono>
2021-10-23 23:56:15 +00:00
# include <cstdint>
2017-02-27 20:57:46 +00:00
# include <mutex>
2017-12-19 14:38:18 +00:00
# include <condition_variable>
2013-12-30 09:17:11 +00:00
2020-10-04 08:30:18 +00:00
# include "Common/System/NativeApp.h"
# include "Common/System/System.h"
2020-10-04 08:10:55 +00:00
# include "Common/System/Display.h"
2020-08-15 18:53:08 +00:00
# include "Common/TimeUtil.h"
2020-10-01 07:27:25 +00:00
# include "Common/Thread/ThreadUtil.h"
2020-10-04 08:04:01 +00:00
# include "Common/Profiler/Profiler.h"
2013-03-29 17:50:08 +00:00
2018-05-02 14:14:46 +00:00
# include "Common/GraphicsContext.h"
2020-07-04 18:30:05 +00:00
# include "Common/Log.h"
2013-03-29 17:50:08 +00:00
# include "Core/Core.h"
# include "Core/Config.h"
# include "Core/MemMap.h"
2024-11-03 19:49:20 +00:00
# include "Core/MIPS/MIPSDebugInterface.h"
2013-09-15 01:43:23 +00:00
# include "Core/SaveState.h"
2013-03-29 17:50:08 +00:00
# include "Core/System.h"
2023-03-31 08:08:12 +00:00
# include "Core/MemFault.h"
2018-05-02 14:14:46 +00:00
# include "Core/Debugger/Breakpoints.h"
2022-01-30 18:46:50 +00:00
# include "Core/HW/Display.h"
2013-03-29 17:50:08 +00:00
# include "Core/MIPS/MIPS.h"
2024-11-02 21:25:16 +00:00
# include "Core/MIPS/MIPSAnalyst.h"
2023-12-30 14:31:59 +00:00
# include "Core/HLE/sceNetAdhoc.h"
2018-06-16 20:53:41 +00:00
# include "GPU/Debugger/Stepping.h"
2024-09-08 15:45:27 +00:00
# include "Core/MIPS/MIPSTracer.h"
2013-12-29 23:11:29 +00:00
2013-02-18 22:25:06 +00:00
# ifdef _WIN32
2018-02-25 09:27:59 +00:00
# include "Common/CommonWindows.h"
2013-03-29 19:51:14 +00:00
# include "Windows/InputDevice.h"
2013-02-18 22:25:06 +00:00
# endif
2012-11-01 15:19:01 +00:00
2024-11-03 19:49:20 +00:00
// Step command to execute next
static std : : mutex g_stepMutex ;
struct StepCommand {
CPUStepType type ;
int param ;
const char * reason ;
u32 relatedAddr ;
bool empty ( ) const {
return type = = CPUStepType : : None ;
}
void clear ( ) {
type = CPUStepType : : None ;
param = 0 ;
reason = " " ;
relatedAddr = 0 ;
}
} ;
static StepCommand g_stepCommand ;
// This is so that external threads can wait for the CPU to become inactive.
2017-12-19 14:38:18 +00:00
static std : : condition_variable m_InactiveCond ;
2017-02-27 20:57:46 +00:00
static std : : mutex m_hInactiveMutex ;
2024-11-03 19:49:20 +00:00
2018-04-20 04:14:01 +00:00
static int steppingCounter = 0 ;
2018-06-11 21:54:42 +00:00
static std : : set < CoreLifecycleFunc > lifecycleFuncs ;
static std : : set < CoreStopRequestFunc > stopFuncs ;
2024-11-03 19:49:20 +00:00
2014-06-29 11:11:06 +00:00
static bool windowHidden = false ;
2016-07-25 00:04:06 +00:00
static bool powerSaving = false ;
2013-02-23 20:59:40 +00:00
2023-04-28 11:38:16 +00:00
static MIPSExceptionInfo g_exceptionInfo ;
2020-07-04 18:57:05 +00:00
2016-01-01 13:08:23 +00:00
void Core_SetGraphicsContext ( GraphicsContext * ctx ) {
2023-08-10 14:33:02 +00:00
PSP_CoreParameter ( ) . graphicsContext = ctx ;
2016-01-01 13:08:23 +00:00
}
2014-06-29 11:11:06 +00:00
void Core_NotifyWindowHidden ( bool hidden ) {
windowHidden = hidden ;
// TODO: Wait until we can react?
}
2023-08-08 09:21:09 +00:00
bool Core_IsWindowHidden ( ) {
return windowHidden ;
}
2018-04-22 15:33:22 +00:00
void Core_ListenLifecycle ( CoreLifecycleFunc func ) {
2018-06-11 21:54:42 +00:00
lifecycleFuncs . insert ( func ) ;
2013-09-28 07:32:45 +00:00
}
2018-04-22 15:33:22 +00:00
void Core_NotifyLifecycle ( CoreLifecycle stage ) {
2021-08-09 06:01:35 +00:00
if ( stage = = CoreLifecycle : : STARTING ) {
2021-08-09 06:15:27 +00:00
Core_ResetException ( ) ;
2021-08-09 06:01:35 +00:00
}
2018-06-11 21:54:42 +00:00
for ( auto func : lifecycleFuncs ) {
func ( stage ) ;
2013-10-12 08:44:12 +00:00
}
2013-09-28 07:32:45 +00:00
}
2018-06-11 21:54:42 +00:00
void Core_ListenStopRequest ( CoreStopRequestFunc func ) {
stopFuncs . insert ( func ) ;
}
2013-10-12 08:44:12 +00:00
void Core_Stop ( ) {
2021-08-09 06:15:27 +00:00
Core_ResetException ( ) ;
2013-02-23 20:59:40 +00:00
Core_UpdateState ( CORE_POWERDOWN ) ;
2018-06-11 21:54:42 +00:00
for ( auto func : stopFuncs ) {
func ( ) ;
}
2012-11-01 15:19:01 +00:00
}
2023-12-30 14:18:56 +00:00
bool Core_ShouldRunBehind ( ) {
2023-12-30 14:31:59 +00:00
// Enforce run-behind if ad-hoc connected
2024-01-21 11:02:30 +00:00
return g_Config . bRunBehindPauseMenu | | Core_MustRunBehind ( ) ;
}
bool Core_MustRunBehind ( ) {
return __NetAdhocConnected ( ) ;
2023-12-30 14:18:56 +00:00
}
2013-10-12 08:44:12 +00:00
bool Core_IsStepping ( ) {
2012-11-01 15:19:01 +00:00
return coreState = = CORE_STEPPING | | coreState = = CORE_POWERDOWN ;
}
2013-10-12 08:44:12 +00:00
bool Core_IsActive ( ) {
2013-06-08 00:32:07 +00:00
return coreState = = CORE_RUNNING | | coreState = = CORE_NEXTFRAME | | coreStatePending ;
}
2013-10-12 08:44:12 +00:00
bool Core_IsInactive ( ) {
2013-02-23 20:59:40 +00:00
return coreState ! = CORE_RUNNING & & coreState ! = CORE_NEXTFRAME & & ! coreStatePending ;
}
2013-02-23 21:21:28 +00:00
2013-10-12 08:44:12 +00:00
void Core_WaitInactive ( ) {
2022-12-19 05:26:59 +00:00
while ( Core_IsActive ( ) & & ! GPUStepping : : IsStepping ( ) ) {
2017-12-19 14:38:18 +00:00
std : : unique_lock < std : : mutex > guard ( m_hInactiveMutex ) ;
m_InactiveCond . wait ( guard ) ;
2013-10-12 08:44:12 +00:00
}
2013-02-23 20:59:40 +00:00
}
2013-10-12 08:44:12 +00:00
void Core_WaitInactive ( int milliseconds ) {
2022-12-19 05:26:59 +00:00
if ( Core_IsActive ( ) & & ! GPUStepping : : IsStepping ( ) ) {
2017-12-19 14:38:18 +00:00
std : : unique_lock < std : : mutex > guard ( m_hInactiveMutex ) ;
m_InactiveCond . wait_for ( guard , std : : chrono : : milliseconds ( milliseconds ) ) ;
2013-10-12 08:44:12 +00:00
}
2013-02-23 21:21:28 +00:00
}
2016-07-25 00:04:06 +00:00
void Core_SetPowerSaving ( bool mode ) {
powerSaving = mode ;
}
bool Core_GetPowerSaving ( ) {
return powerSaving ;
}
2017-01-17 10:21:00 +00: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 12:09:44 +00:00
int w = ( int ) ( pixelWidth * g_display . dpi_scale_x ) ;
int h = ( int ) ( pixelHeight * g_display . dpi_scale_y ) ;
2017-01-17 10:21:00 +00:00
return g_Config . IsPortrait ( ) ? ( h < 480 + 80 ) : ( w < 480 + 80 ) ;
}
2017-01-16 12:08:26 +00:00
// TODO: Feels like this belongs elsewhere.
2017-01-17 10:21:00 +00:00
bool UpdateScreenScale ( int width , int height ) {
bool smallWindow ;
2023-07-22 13:35:21 +00:00
2020-01-05 07:46:27 +00:00
float g_logical_dpi = System_GetPropertyFloat ( SYSPROP_DISPLAY_LOGICAL_DPI ) ;
2023-07-22 13:35:21 +00: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 12:09:44 +00: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 19:17:35 +00:00
2017-01-17 12:02:47 +00:00
smallWindow = IsWindowSmall ( width , height ) ;
2017-01-17 10:21:00 +00:00
if ( smallWindow ) {
2023-02-25 12:09:44 +00:00
g_display . dpi / = 2.0f ;
g_display . dpi_scale_x * = 2.0f ;
g_display . dpi_scale_y * = 2.0f ;
2017-01-17 10:21:00 +00:00
}
2023-02-25 12:09:44 +00: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 21:19:19 +00:00
2023-02-25 12:09:44 +00: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 21:19:19 +00:00
2023-02-25 12:09:44 +00: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 21:19:19 +00:00
if ( dp_changed | | px_changed ) {
2023-02-25 12:09:44 +00: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 21:19:19 +00:00
NativeResized ( ) ;
return true ;
}
return false ;
2013-03-29 17:50:08 +00:00
}
2023-08-10 14:33:02 +00:00
// Used by Windows, SDL, Qt.
void UpdateRunLoop ( GraphicsContext * ctx ) {
NativeFrame ( ctx ) ;
2014-06-30 17:04:44 +00:00
if ( windowHidden & & g_Config . bPauseWhenMinimized ) {
2014-07-01 03:29:39 +00:00
sleep_ms ( 16 ) ;
2014-06-29 14:17:34 +00:00
return ;
2014-07-01 03:29:39 +00:00
}
2013-10-12 08:40:33 +00:00
}
2023-08-10 14:33:02 +00:00
// Note: not used on Android.
2017-03-15 05:01:18 +00:00
void Core_RunLoop ( GraphicsContext * ctx ) {
2023-08-10 14:33:02 +00:00
if ( windowHidden & & g_Config . bPauseWhenMinimized ) {
sleep_ms ( 16 ) ;
return ;
}
2023-08-10 14:35:25 +00:00
NativeFrame ( ctx ) ;
2012-11-01 15:19:01 +00:00
}
2024-11-03 19:49:20 +00: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 ;
}
g_stepCommand = { type , stepSize } ;
return true ;
2012-11-01 15:19:01 +00:00
}
2024-11-02 21:16:06 +00:00
// See comment in header.
2024-11-03 19:49:20 +00: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 21:16:06 +00: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-02 21:25:16 +00:00
CBreakPoints : : SetSkipFirst ( currentPc ) ;
2024-11-03 19:49:20 +00:00
for ( int i = 0 ; i < ( int ) ( newAddress - currentPc ) / 4 ; i + + ) {
currentMIPS - > SingleStep ( ) ;
2024-11-02 21:16:06 +00:00
}
2024-11-03 17:11:33 +00:00
return ;
2024-11-02 21:16:06 +00:00
}
2024-11-02 21:25:16 +00:00
case CPUStepType : : Over :
{
u32 currentPc = cpu - > GetPC ( ) ;
u32 breakpointAddress = currentPc + stepSize ;
CBreakPoints : : SetSkipFirst ( currentPc ) ;
MIPSAnalyst : : MipsOpcodeInfo info = MIPSAnalyst : : GetOpcodeInfo ( cpu , cpu - > GetPC ( ) ) ;
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 ) ;
}
}
}
CBreakPoints : : AddBreakPoint ( breakpointAddress , true ) ;
Core_Resume ( ) ;
2024-11-03 17:11:33 +00:00
break ;
2024-11-02 21:25:16 +00:00
}
2024-11-02 21:30:28 +00: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 17:11:33 +00:00
return ;
2024-11-02 21:30:28 +00:00
}
u32 breakpointAddress = frames [ 1 ] . pc ;
// If the current PC is on a breakpoint, the user doesn't want to do nothing.
CBreakPoints : : SetSkipFirst ( currentMIPS - > pc ) ;
CBreakPoints : : AddBreakPoint ( breakpointAddress , true ) ;
Core_Resume ( ) ;
2024-11-03 17:11:33 +00:00
break ;
2024-11-02 21:30:28 +00:00
}
2024-11-02 21:16:06 +00:00
default :
// Not yet implemented
2024-11-03 17:11:33 +00:00
break ;
2024-11-02 21:16:06 +00:00
}
2012-11-01 15:19:01 +00:00
}
2024-11-03 19:49:20 +00:00
void Core_ProcessStepping ( MIPSDebugInterface * cpu ) {
coreStatePending = false ;
2018-04-30 01:08:41 +00:00
// Check if there's any pending save state actions.
SaveState : : Process ( ) ;
if ( coreState ! = CORE_STEPPING ) {
return ;
}
2018-06-16 20:53:41 +00:00
// Or any GPU actions.
GPUStepping : : SingleStep ( ) ;
2018-05-02 05:18:33 +00:00
// We're not inside jit now, so it's safe to clear the breakpoints.
2018-06-23 17:58:30 +00:00
static int lastSteppingCounter = - 1 ;
if ( lastSteppingCounter ! = steppingCounter ) {
CBreakPoints : : ClearTemporaryBreakPoints ( ) ;
2024-11-03 16:33:33 +00:00
System_Notify ( SystemNotification : : DISASSEMBLY_AFTERSTEP ) ;
2023-03-21 10:21:19 +00:00
System_Notify ( SystemNotification : : MEM_VIEW ) ;
2018-06-23 17:58:30 +00:00
lastSteppingCounter = steppingCounter ;
}
2018-04-30 01:08:41 +00:00
// Need to check inside the lock to avoid races.
2024-11-03 19:49:20 +00:00
std : : lock_guard < std : : mutex > guard ( g_stepMutex ) ;
2018-04-30 01:08:41 +00:00
2024-11-03 19:49:20 +00:00
if ( coreState ! = CORE_STEPPING | | g_stepCommand . empty ( ) ) {
return ;
}
2024-11-02 22:03:48 +00:00
2024-11-03 19:49:20 +00:00
Core_ResetException ( ) ;
2024-11-02 21:16:06 +00:00
2024-11-03 19:49:20 +00:00
if ( ! g_stepCommand . empty ( ) ) {
Core_PerformStep ( cpu , g_stepCommand . type , g_stepCommand . param ) ;
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 + + ;
2018-04-30 01:08:41 +00:00
}
2024-11-03 19:49:20 +00:00
// Update disasm dialog.
System_Notify ( SystemNotification : : MEM_VIEW ) ;
2018-04-30 01:08:41 +00:00
}
2017-12-19 14:42:18 +00:00
// Many platforms, like Android, do not call this function but handle things on their own.
2023-08-07 20:44:06 +00:00
// Instead they simply call NativeFrame directly.
2022-11-08 20:59:08 +00:00
bool Core_Run ( GraphicsContext * ctx ) {
2023-03-21 10:21:19 +00:00
System_Notify ( SystemNotification : : DISASSEMBLY ) ;
2017-12-19 16:59:00 +00:00
while ( true ) {
2014-06-22 07:38:46 +00:00
if ( GetUIState ( ) ! = UISTATE_INGAME ) {
if ( GetUIState ( ) = = UISTATE_EXIT ) {
2023-08-10 14:33:02 +00:00
// Not sure why we do a final frame here?
NativeFrame ( ctx ) ;
2022-11-08 20:59:08 +00:00
return false ;
2013-10-12 08:40:33 +00:00
}
2017-03-15 05:01:18 +00:00
Core_RunLoop ( ctx ) ;
2013-10-12 08:40:33 +00:00
continue ;
}
2017-12-19 16:59:00 +00:00
switch ( coreState ) {
2012-11-01 15:19:01 +00:00
case CORE_RUNNING :
2018-04-30 01:38:17 +00:00
case CORE_STEPPING :
2013-09-15 01:43:23 +00:00
// enter a fast runloop
2017-03-15 05:01:18 +00:00
Core_RunLoop ( ctx ) ;
2018-04-30 01:38:17 +00:00
if ( coreState = = CORE_POWERDOWN ) {
2022-11-08 20:59:08 +00:00
return true ;
2018-04-30 01:38:17 +00:00
}
2012-11-01 15:19:01 +00:00
break ;
2013-11-10 04:56:11 +00:00
case CORE_POWERUP :
2012-11-01 15:19:01 +00:00
case CORE_POWERDOWN :
2020-07-04 18:19:56 +00:00
case CORE_BOOT_ERROR :
case CORE_RUNTIME_ERROR :
2013-09-15 01:43:23 +00:00
// Exit loop!!
2022-11-08 20:59:08 +00:00
return true ;
2013-02-23 20:59:40 +00:00
case CORE_NEXTFRAME :
2022-11-08 20:59:08 +00:00
return true ;
2012-11-01 15:19:01 +00:00
}
}
}
2024-11-03 19:49:20 +00:00
// Free-threaded (hm, possibly except tracing).
2024-11-01 21:52:47 +00:00
void Core_Break ( const char * reason , u32 relatedAddress ) {
// Stop the tracer
mipsTracer . stop_tracing ( ) ;
2024-11-03 19:49:20 +00:00
{
std : : lock_guard < std : : mutex > lock ( g_stepMutex ) ;
steppingCounter + + ;
_assert_msg_ ( reason ! = nullptr , " No reason specified for break " ) ;
Core_UpdateState ( CORE_STEPPING ) ;
}
2023-03-21 10:40:48 +00:00
System_Notify ( SystemNotification : : DEBUG_MODE_CHANGE ) ;
2012-11-01 15:19:01 +00:00
}
2018-04-20 04:14:01 +00:00
2024-11-03 19:49:20 +00:00
// Free-threaded (or at least should be)
2024-11-01 21:52:47 +00:00
void Core_Resume ( ) {
// Clear the exception if we resume.
Core_ResetException ( ) ;
coreState = CORE_RUNNING ;
2024-11-02 21:44:08 +00:00
System_Notify ( SystemNotification : : DEBUG_MODE_CHANGE ) ;
2024-11-01 21:52:47 +00:00
}
2024-11-03 19:49:20 +00:00
// Should be called from the EmuThread.
2020-07-04 18:57:05 +00:00
bool Core_NextFrame ( ) {
if ( coreState = = CORE_RUNNING ) {
coreState = CORE_NEXTFRAME ;
return true ;
} else {
return false ;
}
}
2018-04-20 04:14:01 +00:00
int Core_GetSteppingCounter ( ) {
return steppingCounter ;
}
2020-07-04 18:30:05 +00:00
2021-10-24 00:22:09 +00:00
SteppingReason Core_GetSteppingReason ( ) {
SteppingReason r ;
2024-11-03 19:49:20 +00: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-24 00:22:09 +00:00
return r ;
}
2023-04-28 11:38:16 +00:00
const char * ExceptionTypeAsString ( MIPSExceptionType type ) {
2020-07-04 18:57:05 +00:00
switch ( type ) {
2023-04-28 11:38:16 +00: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 18:57:05 +00:00
default : return " N/A " ;
}
}
2019-02-12 12:29:37 +00:00
const char * MemoryExceptionTypeAsString ( MemoryExceptionType type ) {
2020-07-04 18:30:05 +00:00
switch ( type ) {
2020-07-13 07:17:42 +00:00
case MemoryExceptionType : : UNKNOWN : return " Unknown " ;
2020-07-04 18:57:05 +00: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 20:24:10 +00:00
case MemoryExceptionType : : ALIGNMENT : return " Alignment " ;
2020-07-04 18:57:05 +00:00
default :
return " N/A " ;
2020-07-04 18:30:05 +00:00
}
2020-07-04 18:57:05 +00:00
}
2020-07-04 18:30:05 +00:00
2020-07-13 04:59:08 +00: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 18:22:41 +00:00
void Core_MemoryException ( u32 address , u32 accessSize , u32 pc , MemoryExceptionType type ) {
2019-02-12 12:29:37 +00:00
const char * desc = MemoryExceptionTypeAsString ( type ) ;
2020-07-04 18:30:05 +00:00
// In jit, we only flush PC when bIgnoreBadMemAccess is off.
2023-08-21 01:36:06 +00:00
if ( ( g_Config . iCpuCore = = ( int ) CPUCore : : JIT | | g_Config . iCpuCore = = ( int ) CPUCore : : JIT_IR ) & & g_Config . bIgnoreBadMemAccess ) {
2024-07-14 12:42:59 +00:00
WARN_LOG ( Log : : MemMap , " %s: Invalid access at %08x (size %08x) " , desc , address , accessSize ) ;
2020-07-04 18:30:05 +00:00
} else {
2024-07-14 12:42:59 +00: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 18:30:05 +00:00
}
if ( ! g_Config . bIgnoreBadMemAccess ) {
2023-03-31 08:08:12 +00: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 12:42:59 +00:00
WARN_LOG ( Log : : MemMap , " \n %s " , stackTrace . c_str ( ) ) ;
2023-03-31 08:08:12 +00:00
2023-04-28 11:38:16 +00:00
MIPSExceptionInfo & e = g_exceptionInfo ;
2019-02-12 12:29:37 +00:00
e = { } ;
2023-04-28 11:38:16 +00:00
e . type = MIPSExceptionType : : MEMORY ;
2022-09-30 09:26:30 +00:00
e . info . clear ( ) ;
2019-02-12 12:29:37 +00:00
e . memory_type = type ;
e . address = address ;
2023-01-01 18:22:41 +00:00
e . accessSize = accessSize ;
2023-03-31 08:08:12 +00:00
e . stackTrace = stackTrace ;
2019-02-12 12:29:37 +00:00
e . pc = pc ;
2024-11-01 21:52:47 +00:00
Core_Break ( " memory.exception " , address ) ;
2020-07-04 18:30:05 +00:00
}
}
2024-01-15 09:36:32 +00:00
void Core_MemoryExceptionInfo ( u32 address , u32 accessSize , u32 pc , MemoryExceptionType type , std : : string_view additionalInfo , bool forceReport ) {
2020-07-15 10:38:05 +00:00
const char * desc = MemoryExceptionTypeAsString ( type ) ;
// In jit, we only flush PC when bIgnoreBadMemAccess is off.
2023-08-21 01:36:06 +00:00
if ( ( g_Config . iCpuCore = = ( int ) CPUCore : : JIT | | g_Config . iCpuCore = = ( int ) CPUCore : : JIT_IR ) & & g_Config . bIgnoreBadMemAccess ) {
2024-07-14 12:42:59 +00:00
WARN_LOG ( Log : : MemMap , " %s: Invalid access at %08x (size %08x). %.*s " , desc , address , accessSize , ( int ) additionalInfo . length ( ) , additionalInfo . data ( ) ) ;
2020-07-15 10:38:05 +00:00
} else {
2024-07-14 12:42:59 +00: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 10:38:05 +00:00
}
2022-12-30 11:21:05 +00:00
if ( ! g_Config . bIgnoreBadMemAccess | | forceReport ) {
2023-03-31 08:08:12 +00: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 12:42:59 +00:00
WARN_LOG ( Log : : MemMap , " \n %s " , stackTrace . c_str ( ) ) ;
2023-03-31 08:08:12 +00:00
2023-04-28 11:38:16 +00:00
MIPSExceptionInfo & e = g_exceptionInfo ;
2020-07-15 10:38:05 +00:00
e = { } ;
2023-04-28 11:38:16 +00:00
e . type = MIPSExceptionType : : MEMORY ;
2020-07-15 10:38:05 +00:00
e . info = additionalInfo ;
e . memory_type = type ;
e . address = address ;
2023-04-17 02:10:20 +00:00
e . accessSize = accessSize ;
2023-03-31 08:08:12 +00:00
e . stackTrace = stackTrace ;
2020-07-15 10:38:05 +00:00
e . pc = pc ;
2024-11-01 21:52:47 +00:00
Core_Break ( " memory.exception " , address ) ;
2020-07-15 10:38:05 +00:00
}
}
2022-12-30 11:21:05 +00:00
// Can't be ignored
2020-07-13 04:59:08 +00:00
void Core_ExecException ( u32 address , u32 pc , ExecExceptionType type ) {
const char * desc = ExecExceptionTypeAsString ( type ) ;
2024-07-14 12:42:59 +00:00
WARN_LOG ( Log : : MemMap , " %s: Invalid exec address %08x PC %08x LR %08x " , desc , address , pc , currentMIPS - > r [ MIPS_REG_RA ] ) ;
2020-07-13 04:59:08 +00:00
2023-04-28 11:38:16 +00:00
MIPSExceptionInfo & e = g_exceptionInfo ;
2021-02-09 08:38:03 +00:00
e = { } ;
2023-04-28 11:38:16 +00:00
e . type = MIPSExceptionType : : BAD_EXEC_ADDR ;
2022-09-30 09:26:30 +00:00
e . info . clear ( ) ;
2021-02-09 08:38:03 +00:00
e . exec_type = type ;
e . address = address ;
2023-01-01 18:22:41 +00:00
e . accessSize = 4 ; // size of an instruction
2021-02-09 08:38:03 +00:00
e . pc = pc ;
2022-08-21 21:09:52 +00:00
// This just records the closest value that could be useful as reference.
e . ra = currentMIPS - > r [ MIPS_REG_RA ] ;
2024-11-01 21:52:47 +00:00
Core_Break ( " cpu.exception " , address ) ;
2020-07-13 04:59:08 +00:00
}
2024-11-01 21:52:47 +00:00
void Core_BreakException ( u32 pc ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG ( Log : : CPU , " BREAK! " ) ;
2020-07-04 18:57:05 +00:00
2023-04-28 11:38:16 +00:00
MIPSExceptionInfo & e = g_exceptionInfo ;
2020-07-04 18:57:05 +00:00
e = { } ;
2023-04-28 11:38:16 +00:00
e . type = MIPSExceptionType : : BREAK ;
2022-09-30 09:26:30 +00:00
e . info . clear ( ) ;
2022-08-21 21:09:52 +00:00
e . pc = pc ;
2020-07-04 18:57:05 +00:00
2020-07-04 18:30:05 +00:00
if ( ! g_Config . bIgnoreBadMemAccess ) {
2024-11-01 21:52:47 +00:00
Core_Break ( " cpu.breakInstruction " , currentMIPS - > pc ) ;
2020-07-04 18:30:05 +00:00
}
}
2020-07-04 18:57:05 +00:00
2021-08-09 06:15:27 +00:00
void Core_ResetException ( ) {
2023-04-28 11:38:16 +00:00
g_exceptionInfo . type = MIPSExceptionType : : NONE ;
2021-08-09 06:15:27 +00:00
}
2023-04-28 11:38:16 +00:00
const MIPSExceptionInfo & Core_GetExceptionInfo ( ) {
2020-07-04 18:57:05 +00:00
return g_exceptionInfo ;
}