mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge pull request #13092 from hrydgard/error-handling-improvements
Bad memory access handling improvements
This commit is contained in:
commit
daf0990692
30 changed files with 313 additions and 226 deletions
|
@ -97,6 +97,7 @@ public:
|
|||
|
||||
// Core
|
||||
bool bIgnoreBadMemAccess;
|
||||
|
||||
bool bFastMemory;
|
||||
int iCpuCore;
|
||||
bool bCheckForNewVersion;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "profiler/profiler.h"
|
||||
|
||||
#include "Common/GraphicsContext.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Host.h"
|
||||
|
@ -44,7 +45,6 @@
|
|||
#include "Windows/InputDevice.h"
|
||||
#endif
|
||||
|
||||
|
||||
// Time until we stop considering the core active without user input.
|
||||
// Should this be configurable? 2 hours currently.
|
||||
static const double ACTIVITY_IDLE_TIMEOUT = 2.0 * 3600.0;
|
||||
|
@ -63,6 +63,8 @@ static double lastKeepAwake = 0.0;
|
|||
static GraphicsContext *graphicsContext;
|
||||
static bool powerSaving = false;
|
||||
|
||||
static ExceptionInfo g_exceptionInfo;
|
||||
|
||||
void Core_SetGraphicsContext(GraphicsContext *ctx) {
|
||||
graphicsContext = ctx;
|
||||
PSP_CoreParameter().graphicsContext = graphicsContext;
|
||||
|
@ -339,7 +341,8 @@ void Core_Run(GraphicsContext *ctx) {
|
|||
|
||||
case CORE_POWERUP:
|
||||
case CORE_POWERDOWN:
|
||||
case CORE_ERROR:
|
||||
case CORE_BOOT_ERROR:
|
||||
case CORE_RUNTIME_ERROR:
|
||||
// Exit loop!!
|
||||
Core_StateProcessed();
|
||||
|
||||
|
@ -364,6 +367,75 @@ void Core_EnableStepping(bool step) {
|
|||
}
|
||||
}
|
||||
|
||||
bool Core_NextFrame() {
|
||||
if (coreState == CORE_RUNNING) {
|
||||
coreState = CORE_NEXTFRAME;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int Core_GetSteppingCounter() {
|
||||
return steppingCounter;
|
||||
}
|
||||
|
||||
const char *ExceptionTypeAsString(ExceptionType type) {
|
||||
switch (type) {
|
||||
case ExceptionType::MEMORY: return "Invalid Memory Access";
|
||||
case ExceptionType::BREAK: return "Break";
|
||||
case ExceptionType::BAD_EXEC_ADDR: return "Bad Execution Address";
|
||||
default: return "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
const char *MemoryExceptionTypeAsString(MemoryExceptionType type) {
|
||||
switch (type) {
|
||||
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";
|
||||
default:
|
||||
return "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
void Core_MemoryException(u32 address, u32 pc, MemoryExceptionType type) {
|
||||
const char *desc = MemoryExceptionTypeAsString(type);
|
||||
// In jit, we only flush PC when bIgnoreBadMemAccess is off.
|
||||
if (g_Config.iCpuCore == (int)CPUCore::JIT && g_Config.bIgnoreBadMemAccess) {
|
||||
WARN_LOG(MEMMAP, "%s: Invalid address %08x", desc, address);
|
||||
} else {
|
||||
WARN_LOG(MEMMAP, "%s: Invalid address %08x PC %08x LR %08x", desc, address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
|
||||
}
|
||||
|
||||
if (!g_Config.bIgnoreBadMemAccess) {
|
||||
ExceptionInfo &e = g_exceptionInfo;
|
||||
e = {};
|
||||
e.type = ExceptionType::MEMORY;
|
||||
e.info = "";
|
||||
e.memory_type = type;
|
||||
e.address = address;
|
||||
e.pc = pc;
|
||||
Core_EnableStepping(true);
|
||||
host->SetDebugMode(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Core_Break() {
|
||||
ERROR_LOG(CPU, "BREAK!");
|
||||
|
||||
ExceptionInfo &e = g_exceptionInfo;
|
||||
e = {};
|
||||
e.type = ExceptionType::BREAK;
|
||||
e.info = "";
|
||||
|
||||
if (!g_Config.bIgnoreBadMemAccess) {
|
||||
Core_EnableStepping(true);
|
||||
host->SetDebugMode(true);
|
||||
}
|
||||
}
|
||||
|
||||
const ExceptionInfo &Core_GetExceptionInfo() {
|
||||
return g_exceptionInfo;
|
||||
}
|
||||
|
|
34
Core/Core.h
34
Core/Core.h
|
@ -32,6 +32,8 @@ void Core_SetGraphicsContext(GraphicsContext *ctx);
|
|||
|
||||
// called from gui
|
||||
void Core_EnableStepping(bool step);
|
||||
|
||||
bool Core_NextFrame();
|
||||
void Core_DoSingleStep();
|
||||
void Core_UpdateSingleStep();
|
||||
void Core_ProcessStepping();
|
||||
|
@ -76,3 +78,35 @@ void Core_NotifyActivity();
|
|||
|
||||
void Core_SetPowerSaving(bool mode);
|
||||
bool Core_GetPowerSaving();
|
||||
|
||||
enum class MemoryExceptionType {
|
||||
READ_WORD,
|
||||
WRITE_WORD,
|
||||
READ_BLOCK,
|
||||
WRITE_BLOCK,
|
||||
};
|
||||
|
||||
void Core_MemoryException(u32 address, u32 pc, MemoryExceptionType type);
|
||||
void Core_Break();
|
||||
|
||||
enum class ExceptionType {
|
||||
NONE,
|
||||
MEMORY,
|
||||
BREAK,
|
||||
BAD_EXEC_ADDR,
|
||||
};
|
||||
|
||||
struct ExceptionInfo {
|
||||
ExceptionType type;
|
||||
std::string info;
|
||||
|
||||
// Memory exception info
|
||||
MemoryExceptionType memory_type;
|
||||
uint32_t pc;
|
||||
uint32_t address;
|
||||
};
|
||||
|
||||
const ExceptionInfo &Core_GetExceptionInfo();
|
||||
|
||||
const char *ExceptionTypeAsString(ExceptionType type);
|
||||
const char *MemoryExceptionTypeAsString(MemoryExceptionType type);
|
||||
|
|
|
@ -469,7 +469,7 @@ void HLEReturnFromMipsCall() {
|
|||
|
||||
if ((stackData->nextOff & 0x0000000F) != 0 || !Memory::IsValidAddress(sp + stackData->nextOff)) {
|
||||
ERROR_LOG(HLE, "Corrupt stack on HLE mips call return: %08x", stackData->nextOff);
|
||||
Core_UpdateState(CORE_ERROR);
|
||||
Core_UpdateState(CORE_RUNTIME_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -482,9 +482,10 @@ void HLEReturnFromMipsCall() {
|
|||
while ((finalMarker->nextOff & 0x0000000F) == 0 && Memory::IsValidAddress(finalMarker.ptr + finalMarker->nextOff)) {
|
||||
finalMarker.ptr += finalMarker->nextOff;
|
||||
}
|
||||
|
||||
if (finalMarker->nextOff != 0xFFFFFFFF) {
|
||||
ERROR_LOG(HLE, "Corrupt stack on HLE mips call return action: %08x", finalMarker->nextOff);
|
||||
Core_UpdateState(CORE_ERROR);
|
||||
Core_UpdateState(CORE_RUNTIME_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "net/resolve.h"
|
||||
#include "Common/ChunkFile.h"
|
||||
|
||||
#include "Core/Config.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "Core/CoreParameter.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
#include "Core/HLE/FunctionWrappers.h"
|
||||
|
@ -791,8 +792,7 @@ void __DisplayFlip(int cyclesLate) {
|
|||
const bool fbReallyDirty = gpu->FramebufferReallyDirty();
|
||||
if (fbReallyDirty || noRecentFlip || postEffectRequiresFlip) {
|
||||
// Check first though, might've just quit / been paused.
|
||||
if (coreState == CORE_RUNNING) {
|
||||
coreState = CORE_NEXTFRAME;
|
||||
if (Core_NextFrame()) {
|
||||
gpu->CopyDisplayToOutput(fbReallyDirty);
|
||||
if (fbReallyDirty) {
|
||||
actualFlips++;
|
||||
|
|
|
@ -555,7 +555,7 @@ static VFSFileSystem *flash0System = nullptr;
|
|||
|
||||
static void __IoManagerThread() {
|
||||
setCurrentThreadName("IO");
|
||||
while (ioManagerThreadEnabled && coreState != CORE_ERROR && coreState != CORE_POWERDOWN) {
|
||||
while (ioManagerThreadEnabled && coreState != CORE_BOOT_ERROR && coreState != CORE_RUNTIME_ERROR && coreState != CORE_POWERDOWN) {
|
||||
ioManager.RunEventsUntil(CoreTiming::GetTicks() + msToCycles(1000));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -297,6 +297,12 @@ bool __KernelIsRunning() {
|
|||
return kernelRunning;
|
||||
}
|
||||
|
||||
std::string __KernelStateSummary() {
|
||||
std::string threadSummary = __KernelThreadingSummary();
|
||||
return StringFromFormat("%s", threadSummary.c_str());
|
||||
}
|
||||
|
||||
|
||||
void sceKernelExitGame()
|
||||
{
|
||||
INFO_LOG(SCEKERNEL, "sceKernelExitGame");
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/Swap.h"
|
||||
|
@ -374,6 +375,9 @@ void __KernelDoState(PointerWrap &p);
|
|||
bool __KernelIsRunning();
|
||||
bool __KernelLoadExec(const char *filename, SceKernelLoadExecParam *param);
|
||||
|
||||
// For crash reporting.
|
||||
std::string __KernelStateSummary();
|
||||
|
||||
int sceKernelLoadExec(const char *filename, u32 paramPtr);
|
||||
|
||||
void sceKernelExitGame();
|
||||
|
|
|
@ -1837,7 +1837,7 @@ int sceKernelLoadExec(const char *filename, u32 paramPtr)
|
|||
std::string error_string;
|
||||
if (!__KernelLoadExec(exec_filename.c_str(), paramPtr, &error_string)) {
|
||||
ERROR_LOG(SCEMODULE, "sceKernelLoadExec failed: %s", error_string.c_str());
|
||||
Core_UpdateState(CORE_ERROR);
|
||||
Core_UpdateState(CORE_RUNTIME_ERROR);
|
||||
return -1;
|
||||
}
|
||||
if (gpu) {
|
||||
|
|
|
@ -372,6 +372,8 @@ public:
|
|||
|
||||
class PSPThread : public KernelObject {
|
||||
public:
|
||||
PSPThread() : debug(currentMIPS, context) {}
|
||||
|
||||
const char *GetName() override { return nt.name; }
|
||||
const char *GetTypeName() override { return "Thread"; }
|
||||
void GetQuickInfo(char *ptr, int size) override
|
||||
|
@ -491,10 +493,6 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
PSPThread() : debug(currentMIPS, context) {
|
||||
currentStack.start = 0;
|
||||
}
|
||||
|
||||
// Can't use a destructor since savestates will call that too.
|
||||
void Cleanup()
|
||||
{
|
||||
|
@ -573,14 +571,14 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
NativeThread nt;
|
||||
NativeThread nt{};
|
||||
|
||||
ThreadWaitInfo waitInfo;
|
||||
SceUID moduleId;
|
||||
ThreadWaitInfo waitInfo{};
|
||||
SceUID moduleId = -1;
|
||||
|
||||
bool isProcessingCallbacks;
|
||||
u32 currentMipscallId;
|
||||
SceUID currentCallbackId;
|
||||
bool isProcessingCallbacks = false;
|
||||
u32 currentMipscallId = -1;
|
||||
SceUID currentCallbackId = -1;
|
||||
|
||||
PSPThreadContext context;
|
||||
KernelThreadDebugInterface debug;
|
||||
|
@ -597,7 +595,7 @@ public:
|
|||
// These are stacks that aren't "active" right now, but will pop off once the func returns.
|
||||
std::vector<StackInfo> pushedStacks;
|
||||
|
||||
StackInfo currentStack;
|
||||
StackInfo currentStack{};
|
||||
|
||||
// For thread end.
|
||||
std::vector<SceUID> waitingThreads;
|
||||
|
@ -1184,6 +1182,10 @@ void __KernelThreadingShutdown() {
|
|||
pendingDeleteThreads.clear();
|
||||
}
|
||||
|
||||
std::string __KernelThreadingSummary() {
|
||||
return StringFromFormat("Cur thread: %s", __GetCurrentThread()->GetName());
|
||||
}
|
||||
|
||||
const char *__KernelGetThreadName(SceUID threadID)
|
||||
{
|
||||
u32 error;
|
||||
|
|
|
@ -155,6 +155,9 @@ void __KernelThreadingInit();
|
|||
void __KernelThreadingDoState(PointerWrap &p);
|
||||
void __KernelThreadingDoStateLate(PointerWrap &p);
|
||||
void __KernelThreadingShutdown();
|
||||
|
||||
std::string __KernelThreadingSummary();
|
||||
|
||||
KernelObject *__KernelThreadObject();
|
||||
KernelObject *__KernelCallbackObject();
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ public:
|
|||
protected:
|
||||
void ProcessEvent(AsyncIOEvent ref) override;
|
||||
bool ShouldExitEventLoop() override {
|
||||
return coreState == CORE_ERROR || coreState == CORE_POWERDOWN;
|
||||
return coreState == CORE_BOOT_ERROR || coreState == CORE_RUNTIME_ERROR || coreState == CORE_POWERDOWN;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -106,4 +106,4 @@ private:
|
|||
std::condition_variable resultsWait_;
|
||||
std::set<u32> resultsPending_;
|
||||
std::map<u32, AsyncIOResult> results_;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "Camera.h"
|
||||
#include "Core/Config.h"
|
||||
|
||||
void convert_frame(int inw, int inh, unsigned char *inData, AVPixelFormat inFormat,
|
||||
int outw, int outh, unsigned char **outData, int *outLen) {
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/HLE/sceUsbCam.h"
|
||||
#include "Log.h"
|
||||
|
||||
|
|
|
@ -258,7 +258,7 @@ bool LoadFile(FileLoader **fileLoaderPtr, std::string *error_string) {
|
|||
}
|
||||
else if (ebootType == IdentifiedFileType::PSP_PS1_PBP) {
|
||||
*error_string = "PS1 EBOOTs are not supported by PPSSPP.";
|
||||
coreState = CORE_ERROR;
|
||||
coreState = CORE_BOOT_ERROR;
|
||||
return false;
|
||||
}
|
||||
std::string path = fileLoader->Path();
|
||||
|
@ -270,7 +270,7 @@ bool LoadFile(FileLoader **fileLoaderPtr, std::string *error_string) {
|
|||
return Load_PSP_ELF_PBP(fileLoader, error_string);
|
||||
} else {
|
||||
*error_string = "No EBOOT.PBP, misidentified game";
|
||||
coreState = CORE_ERROR;
|
||||
coreState = CORE_BOOT_ERROR;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -352,7 +352,7 @@ bool LoadFile(FileLoader **fileLoaderPtr, std::string *error_string) {
|
|||
break;
|
||||
}
|
||||
|
||||
coreState = CORE_ERROR;
|
||||
coreState = CORE_BOOT_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -947,10 +947,7 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) {
|
|||
}
|
||||
|
||||
case IROp::Break:
|
||||
if (!g_Config.bIgnoreBadMemAccess) {
|
||||
Core_EnableStepping(true);
|
||||
host->SetDebugMode(true);
|
||||
}
|
||||
Core_Break();
|
||||
return mips->pc + 4;
|
||||
|
||||
case IROp::SetCtrlVFPU:
|
||||
|
|
|
@ -215,7 +215,7 @@ unsigned int MIPSDebugInterface::readMemory(unsigned int address)
|
|||
|
||||
bool MIPSDebugInterface::isAlive()
|
||||
{
|
||||
return PSP_IsInited() && coreState != CORE_ERROR && coreState != CORE_POWERDOWN;
|
||||
return PSP_IsInited() && coreState != CORE_BOOT_ERROR && coreState != CORE_RUNTIME_ERROR && coreState != CORE_POWERDOWN;
|
||||
}
|
||||
|
||||
bool MIPSDebugInterface::isBreakpoint(unsigned int address)
|
||||
|
|
|
@ -152,11 +152,7 @@ namespace MIPSInt
|
|||
void Int_Break(MIPSOpcode op)
|
||||
{
|
||||
Reporting::ReportMessage("BREAK instruction hit");
|
||||
ERROR_LOG(CPU, "BREAK!");
|
||||
if (!g_Config.bIgnoreBadMemAccess) {
|
||||
Core_EnableStepping(true);
|
||||
host->SetDebugMode(true);
|
||||
}
|
||||
Core_Break();
|
||||
PC += 4;
|
||||
}
|
||||
|
||||
|
|
|
@ -747,7 +747,7 @@ void Jit::WriteExitDestInReg(X64Reg reg) {
|
|||
if (g_Config.bIgnoreBadMemAccess) {
|
||||
CMP(32, R(EAX), Imm32(0));
|
||||
FixupBranch skip = J_CC(CC_NE);
|
||||
ABI_CallFunctionA((const void *)&Core_UpdateState, Imm32(CORE_ERROR));
|
||||
ABI_CallFunctionA((const void *)&Core_UpdateState, Imm32(CORE_RUNTIME_ERROR));
|
||||
SetJumpTarget(skip);
|
||||
}
|
||||
|
||||
|
|
|
@ -52,8 +52,7 @@ typedef void (*readFn16)(u16&, const u32);
|
|||
typedef void (*readFn32)(u32&, const u32);
|
||||
typedef void (*readFn64)(u64&, const u32);
|
||||
|
||||
namespace Memory
|
||||
{
|
||||
namespace Memory {
|
||||
// Base is a pointer to the base of the memory map. Yes, some MMU tricks
|
||||
// are used to set up a full GC or Wii memory map in process memory. on
|
||||
// 32-bit, you have to mask your offsets with 0x3FFFFFFF. This means that
|
||||
|
@ -165,16 +164,6 @@ inline u8* GetPointerUnchecked(const u32 address) {
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef SAFE_MEMORY
|
||||
u32 ReadUnchecked_U32(const u32 _Address);
|
||||
// ONLY for use by GUI and fast interpreter
|
||||
u8 ReadUnchecked_U8(const u32 _Address);
|
||||
u16 ReadUnchecked_U16(const u32 _Address);
|
||||
void WriteUnchecked_U8(const u8 _Data, const u32 _Address);
|
||||
void WriteUnchecked_U16(const u16 _Data, const u32 _Address);
|
||||
void WriteUnchecked_U32(const u32 _Data, const u32 _Address);
|
||||
#else
|
||||
|
||||
inline u32 ReadUnchecked_U32(const u32 address) {
|
||||
#ifdef MASKED_PSP_MEMORY
|
||||
return *(u32_le *)(base + (address & MEMVIEW32_MASK));
|
||||
|
@ -239,8 +228,6 @@ inline void WriteUnchecked_U8(u8 data, u32 address) {
|
|||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
inline float Read_Float(u32 address)
|
||||
{
|
||||
u32 ifloat = Read_U32(address);
|
||||
|
@ -274,18 +261,15 @@ inline const char* GetCharPointer(const u32 address) {
|
|||
return (const char *)GetPointer(address);
|
||||
}
|
||||
|
||||
inline void MemcpyUnchecked(void *to_data, const u32 from_address, const u32 len)
|
||||
{
|
||||
inline void MemcpyUnchecked(void *to_data, const u32 from_address, const u32 len) {
|
||||
memcpy(to_data, GetPointerUnchecked(from_address), len);
|
||||
}
|
||||
|
||||
inline void MemcpyUnchecked(const u32 to_address, const void *from_data, const u32 len)
|
||||
{
|
||||
inline void MemcpyUnchecked(const u32 to_address, const void *from_data, const u32 len) {
|
||||
memcpy(GetPointerUnchecked(to_address), from_data, len);
|
||||
}
|
||||
|
||||
inline void MemcpyUnchecked(const u32 to_address, const u32 from_address, const u32 len)
|
||||
{
|
||||
inline void MemcpyUnchecked(const u32 to_address, const u32 from_address, const u32 len) {
|
||||
MemcpyUnchecked(GetPointer(to_address), from_address, len);
|
||||
}
|
||||
|
||||
|
@ -327,7 +311,7 @@ inline bool IsValidRange(const u32 address, const u32 size) {
|
|||
return IsValidAddress(address) && ValidSize(address, size) == size;
|
||||
}
|
||||
|
||||
};
|
||||
} // namespace Memory
|
||||
|
||||
template <typename T>
|
||||
struct PSPPointer
|
||||
|
@ -462,44 +446,36 @@ inline u32 PSP_GetKernelMemoryEnd() { return 0x08400000;}
|
|||
// game through sceKernelVolatileMemTryLock.
|
||||
|
||||
inline u32 PSP_GetUserMemoryBase() { return 0x08800000;}
|
||||
|
||||
inline u32 PSP_GetDefaultLoadAddress() { return 0;}
|
||||
//inline u32 PSP_GetDefaultLoadAddress() { return 0x0898dab0;}
|
||||
inline u32 PSP_GetVidMemBase() { return 0x04000000;}
|
||||
inline u32 PSP_GetVidMemEnd() { return 0x04800000;}
|
||||
|
||||
template <typename T>
|
||||
inline bool operator==(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs)
|
||||
{
|
||||
inline bool operator==(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
|
||||
return lhs.ptr == rhs.ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool operator!=(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs)
|
||||
{
|
||||
inline bool operator!=(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
|
||||
return lhs.ptr != rhs.ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool operator<(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs)
|
||||
{
|
||||
inline bool operator<(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
|
||||
return lhs.ptr < rhs.ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool operator>(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs)
|
||||
{
|
||||
inline bool operator>(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
|
||||
return lhs.ptr > rhs.ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool operator<=(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs)
|
||||
{
|
||||
inline bool operator<=(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
|
||||
return lhs.ptr <= rhs.ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool operator>=(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs)
|
||||
{
|
||||
inline bool operator>=(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
|
||||
return lhs.ptr >= rhs.ptr;
|
||||
}
|
||||
|
|
|
@ -26,17 +26,7 @@
|
|||
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
|
||||
namespace Memory
|
||||
{
|
||||
|
||||
// =================================
|
||||
// From Memmap.cpp
|
||||
// ----------------
|
||||
|
||||
// Read and write shortcuts
|
||||
|
||||
// GetPointer must always return an address in the bottom 32 bits of address space, so that 64-bit
|
||||
// programs don't have problems directly addressing any part of memory.
|
||||
namespace Memory {
|
||||
|
||||
u8 *GetPointer(const u32 address) {
|
||||
if ((address & 0x3E000000) == 0x08000000) {
|
||||
|
@ -52,16 +42,12 @@ u8 *GetPointer(const u32 address) {
|
|||
// More RAM (remasters, etc.)
|
||||
return GetPointerUnchecked(address);
|
||||
} else {
|
||||
ERROR_LOG(MEMMAP, "Unknown GetPointer %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
|
||||
static bool reported = false;
|
||||
if (!reported) {
|
||||
Reporting::ReportMessage("Unknown GetPointer %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
|
||||
reported = true;
|
||||
}
|
||||
if (!g_Config.bIgnoreBadMemAccess) {
|
||||
Core_EnableStepping(true);
|
||||
host->SetDebugMode(true);
|
||||
}
|
||||
Core_MemoryException(address, currentMIPS->pc, MemoryExceptionType::WRITE_BLOCK);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -69,10 +55,6 @@ u8 *GetPointer(const u32 address) {
|
|||
template <typename T>
|
||||
inline void ReadFromHardware(T &var, const u32 address) {
|
||||
// TODO: Figure out the fastest order of tests for both read and write (they are probably different).
|
||||
// TODO: Make sure this represents the mirrors in a correct way.
|
||||
|
||||
// Could just do a base-relative read, too.... TODO
|
||||
|
||||
if ((address & 0x3E000000) == 0x08000000) {
|
||||
// RAM
|
||||
var = *((const T*)GetPointerUnchecked(address));
|
||||
|
@ -86,29 +68,18 @@ inline void ReadFromHardware(T &var, const u32 address) {
|
|||
// More RAM (remasters, etc.)
|
||||
var = *((const T*)GetPointerUnchecked(address));
|
||||
} else {
|
||||
// In jit, we only flush PC when bIgnoreBadMemAccess is off.
|
||||
if (g_Config.iCpuCore == (int)CPUCore::JIT && g_Config.bIgnoreBadMemAccess) {
|
||||
WARN_LOG(MEMMAP, "ReadFromHardware: Invalid address %08x", address);
|
||||
} else {
|
||||
WARN_LOG(MEMMAP, "ReadFromHardware: Invalid address %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
|
||||
}
|
||||
static bool reported = false;
|
||||
if (!reported) {
|
||||
Reporting::ReportMessage("ReadFromHardware: Invalid address %08x near PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
|
||||
reported = true;
|
||||
}
|
||||
if (!g_Config.bIgnoreBadMemAccess) {
|
||||
Core_EnableStepping(true);
|
||||
host->SetDebugMode(true);
|
||||
}
|
||||
Core_MemoryException(address, currentMIPS->pc, MemoryExceptionType::READ_WORD);
|
||||
var = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void WriteToHardware(u32 address, const T data) {
|
||||
// Could just do a base-relative write, too.... TODO
|
||||
|
||||
if ((address & 0x3E000000) == 0x08000000) {
|
||||
// RAM
|
||||
*(T*)GetPointerUnchecked(address) = data;
|
||||
|
@ -122,32 +93,21 @@ inline void WriteToHardware(u32 address, const T data) {
|
|||
// More RAM (remasters, etc.)
|
||||
*(T*)GetPointerUnchecked(address) = data;
|
||||
} else {
|
||||
// In jit, we only flush PC when bIgnoreBadMemAccess is off.
|
||||
if (g_Config.iCpuCore == (int)CPUCore::JIT && g_Config.bIgnoreBadMemAccess) {
|
||||
WARN_LOG(MEMMAP, "WriteToHardware: Invalid address %08x", address);
|
||||
} else {
|
||||
WARN_LOG(MEMMAP, "WriteToHardware: Invalid address %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
|
||||
}
|
||||
static bool reported = false;
|
||||
if (!reported) {
|
||||
Reporting::ReportMessage("WriteToHardware: Invalid address %08x near PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
|
||||
reported = true;
|
||||
}
|
||||
if (!g_Config.bIgnoreBadMemAccess) {
|
||||
Core_EnableStepping(true);
|
||||
host->SetDebugMode(true);
|
||||
}
|
||||
Core_MemoryException(address, currentMIPS->pc, MemoryExceptionType::WRITE_WORD);
|
||||
}
|
||||
}
|
||||
|
||||
// =====================
|
||||
|
||||
bool IsRAMAddress(const u32 address) {
|
||||
if ((address & 0x3E000000) == 0x08000000) {
|
||||
return true;
|
||||
} else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) {
|
||||
} else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) {
|
||||
return true;
|
||||
} else {
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -160,102 +120,52 @@ bool IsScratchpadAddress(const u32 address) {
|
|||
return (address & 0xBFFF0000) == 0x00010000 && (address & 0x0000FFFF) < SCRATCHPAD_SIZE;
|
||||
}
|
||||
|
||||
u8 Read_U8(const u32 _Address)
|
||||
{
|
||||
u8 _var = 0;
|
||||
ReadFromHardware<u8>(_var, _Address);
|
||||
return (u8)_var;
|
||||
u8 Read_U8(const u32 address) {
|
||||
u8 value = 0;
|
||||
ReadFromHardware<u8>(value, address);
|
||||
return (u8)value;
|
||||
}
|
||||
|
||||
u16 Read_U16(const u32 _Address)
|
||||
{
|
||||
u16_le _var = 0;
|
||||
ReadFromHardware<u16_le>(_var, _Address);
|
||||
return (u16)_var;
|
||||
u16 Read_U16(const u32 address) {
|
||||
u16_le value = 0;
|
||||
ReadFromHardware<u16_le>(value, address);
|
||||
return (u16)value;
|
||||
}
|
||||
|
||||
u32 Read_U32(const u32 _Address)
|
||||
{
|
||||
u32_le _var = 0;
|
||||
ReadFromHardware<u32_le>(_var, _Address);
|
||||
return _var;
|
||||
u32 Read_U32(const u32 address) {
|
||||
u32_le value = 0;
|
||||
ReadFromHardware<u32_le>(value, address);
|
||||
return value;
|
||||
}
|
||||
|
||||
u64 Read_U64(const u32 _Address)
|
||||
{
|
||||
u64_le _var = 0;
|
||||
ReadFromHardware<u64_le>(_var, _Address);
|
||||
return _var;
|
||||
u64 Read_U64(const u32 address) {
|
||||
u64_le value = 0;
|
||||
ReadFromHardware<u64_le>(value, address);
|
||||
return value;
|
||||
}
|
||||
|
||||
u32 Read_U8_ZX(const u32 _Address)
|
||||
{
|
||||
return (u32)Read_U8(_Address);
|
||||
u32 Read_U8_ZX(const u32 address) {
|
||||
return (u32)Read_U8(address);
|
||||
}
|
||||
|
||||
u32 Read_U16_ZX(const u32 _Address)
|
||||
{
|
||||
return (u32)Read_U16(_Address);
|
||||
u32 Read_U16_ZX(const u32 address) {
|
||||
return (u32)Read_U16(address);
|
||||
}
|
||||
|
||||
void Write_U8(const u8 _Data, const u32 _Address)
|
||||
{
|
||||
WriteToHardware<u8>(_Address, _Data);
|
||||
void Write_U8(const u8 _Data, const u32 address) {
|
||||
WriteToHardware<u8>(address, _Data);
|
||||
}
|
||||
|
||||
void Write_U16(const u16 _Data, const u32 _Address)
|
||||
{
|
||||
WriteToHardware<u16_le>(_Address, _Data);
|
||||
void Write_U16(const u16 _Data, const u32 address) {
|
||||
WriteToHardware<u16_le>(address, _Data);
|
||||
}
|
||||
|
||||
void Write_U32(const u32 _Data, const u32 _Address)
|
||||
{
|
||||
WriteToHardware<u32_le>(_Address, _Data);
|
||||
void Write_U32(const u32 _Data, const u32 address) {
|
||||
WriteToHardware<u32_le>(address, _Data);
|
||||
}
|
||||
|
||||
void Write_U64(const u64 _Data, const u32 _Address)
|
||||
{
|
||||
WriteToHardware<u64_le>(_Address, _Data);
|
||||
void Write_U64(const u64 _Data, const u32 address) {
|
||||
WriteToHardware<u64_le>(address, _Data);
|
||||
}
|
||||
|
||||
#ifdef SAFE_MEMORY
|
||||
|
||||
u8 ReadUnchecked_U8(const u32 _Address)
|
||||
{
|
||||
u8 _var = 0;
|
||||
ReadFromHardware<u8>(_var, _Address);
|
||||
return _var;
|
||||
}
|
||||
|
||||
u16 ReadUnchecked_U16(const u32 _Address)
|
||||
{
|
||||
u16_le _var = 0;
|
||||
ReadFromHardware<u16_le>(_var, _Address);
|
||||
return _var;
|
||||
}
|
||||
|
||||
u32 ReadUnchecked_U32(const u32 _Address)
|
||||
{
|
||||
u32_le _var = 0;
|
||||
ReadFromHardware<u32_le>(_var, _Address);
|
||||
return _var;
|
||||
}
|
||||
|
||||
void WriteUnchecked_U8(const u8 _iValue, const u32 _Address)
|
||||
{
|
||||
WriteToHardware<u8>(_Address, _iValue);
|
||||
}
|
||||
|
||||
void WriteUnchecked_U16(const u16 _iValue, const u32 _Address)
|
||||
{
|
||||
WriteToHardware<u16_le>(_Address, _iValue);
|
||||
}
|
||||
|
||||
void WriteUnchecked_U32(const u32 _iValue, const u32 _Address)
|
||||
{
|
||||
WriteToHardware<u32_le>(_Address, _iValue);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Memory
|
||||
|
|
|
@ -278,7 +278,7 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) {
|
|||
} else {
|
||||
*error_string = "A PSP game couldn't be found on the disc.";
|
||||
}
|
||||
coreState = CORE_ERROR;
|
||||
coreState = CORE_BOOT_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -303,7 +303,7 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) {
|
|||
if (success && coreState == CORE_POWERUP) {
|
||||
coreState = PSP_CoreParameter().startBreak ? CORE_STEPPING : CORE_RUNNING;
|
||||
} else {
|
||||
coreState = CORE_ERROR;
|
||||
coreState = CORE_BOOT_ERROR;
|
||||
// TODO: This is a crummy way to communicate the error...
|
||||
PSP_CoreParameter().fileToStart = "";
|
||||
}
|
||||
|
@ -374,7 +374,7 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) {
|
|||
// If root is not a subpath of path, we can't boot the game.
|
||||
if (!startsWith(pathNorm, rootNorm)) {
|
||||
*error_string = "Cannot boot ELF located outside mountRoot.";
|
||||
coreState = CORE_ERROR;
|
||||
coreState = CORE_BOOT_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -433,7 +433,7 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) {
|
|||
if (success && coreState == CORE_POWERUP) {
|
||||
coreState = PSP_CoreParameter().startBreak ? CORE_STEPPING : CORE_RUNNING;
|
||||
} else {
|
||||
coreState = CORE_ERROR;
|
||||
coreState = CORE_BOOT_ERROR;
|
||||
// TODO: This is a crummy way to communicate the error...
|
||||
PSP_CoreParameter().fileToStart = "";
|
||||
}
|
||||
|
@ -457,7 +457,7 @@ bool Load_PSP_GE_Dump(FileLoader *fileLoader, std::string *error_string) {
|
|||
if (success && coreState == CORE_POWERUP) {
|
||||
coreState = PSP_CoreParameter().startBreak ? CORE_STEPPING : CORE_RUNNING;
|
||||
} else {
|
||||
coreState = CORE_ERROR;
|
||||
coreState = CORE_BOOT_ERROR;
|
||||
// TODO: This is a crummy way to communicate the error...
|
||||
PSP_CoreParameter().fileToStart = "";
|
||||
}
|
||||
|
|
|
@ -97,13 +97,19 @@ bool coreCollectDebugStatsForced = false;
|
|||
|
||||
// This can be read and written from ANYWHERE.
|
||||
volatile CoreState coreState = CORE_STEPPING;
|
||||
// Note: intentionally not used for CORE_NEXTFRAME.
|
||||
// If true, core state has been changed, but JIT has probably not noticed yet.
|
||||
volatile bool coreStatePending = false;
|
||||
|
||||
static volatile CPUThreadState cpuThreadState = CPU_THREAD_NOT_RUNNING;
|
||||
|
||||
static GPUBackend gpuBackend;
|
||||
static std::string gpuBackendDevice;
|
||||
|
||||
// Ugly!
|
||||
static bool pspIsInited = false;
|
||||
static bool pspIsIniting = false;
|
||||
static bool pspIsQuitting = false;
|
||||
|
||||
void ResetUIState() {
|
||||
globalUIState = UISTATE_MENU;
|
||||
}
|
||||
|
@ -328,11 +334,6 @@ void Core_UpdateDebugStats(bool collectStats) {
|
|||
gpuStats.ResetFrame();
|
||||
}
|
||||
|
||||
// Ugly!
|
||||
static bool pspIsInited = false;
|
||||
static bool pspIsIniting = false;
|
||||
static bool pspIsQuitting = false;
|
||||
|
||||
bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) {
|
||||
if (pspIsIniting || pspIsQuitting) {
|
||||
return false;
|
||||
|
@ -433,7 +434,7 @@ void PSP_Shutdown() {
|
|||
// Make sure things know right away that PSP memory, etc. is going away.
|
||||
pspIsQuitting = true;
|
||||
if (coreState == CORE_RUNNING)
|
||||
Core_UpdateState(CORE_ERROR);
|
||||
Core_UpdateState(CORE_POWERDOWN);
|
||||
|
||||
#ifndef MOBILE_DEVICE
|
||||
if (g_Config.bFuncHashMap) {
|
||||
|
@ -486,7 +487,7 @@ void PSP_RunLoopWhileState() {
|
|||
|
||||
void PSP_RunLoopUntil(u64 globalticks) {
|
||||
SaveState::Process();
|
||||
if (coreState == CORE_POWERDOWN || coreState == CORE_ERROR) {
|
||||
if (coreState == CORE_POWERDOWN || coreState == CORE_BOOT_ERROR || coreState == CORE_RUNTIME_ERROR) {
|
||||
return;
|
||||
} else if (coreState == CORE_STEPPING) {
|
||||
Core_ProcessStepping();
|
||||
|
|
|
@ -101,12 +101,20 @@ void InitSysDirectories();
|
|||
|
||||
// RUNNING must be at 0, NEXTFRAME must be at 1.
|
||||
enum CoreState {
|
||||
// Emulation is running normally.
|
||||
CORE_RUNNING = 0,
|
||||
// Emulation was running normally, just reached the end of a frame.
|
||||
CORE_NEXTFRAME = 1,
|
||||
CORE_STEPPING,
|
||||
// Emulation is paused, CPU thread is sleeping.
|
||||
CORE_STEPPING, // Can be used for recoverable runtime errors (ignored memory exceptions)
|
||||
// Core is being powered up.
|
||||
CORE_POWERUP,
|
||||
// Core is being powered down.
|
||||
CORE_POWERDOWN,
|
||||
CORE_ERROR,
|
||||
// An error happened at boot.
|
||||
CORE_BOOT_ERROR,
|
||||
// Unrecoverable runtime error. Recoverable errors should use CORE_STEPPING.
|
||||
CORE_RUNTIME_ERROR,
|
||||
};
|
||||
|
||||
extern bool coreCollectDebugStats;
|
||||
|
|
|
@ -139,7 +139,7 @@ struct ThreadEventQueue : public B {
|
|||
|
||||
// Don't run if it's not running, but wait for startup.
|
||||
if (!eventsRunning_) {
|
||||
if (eventsHaveRun_ || coreState == CORE_ERROR || coreState == CORE_POWERDOWN) {
|
||||
if (eventsHaveRun_ || coreState == CORE_BOOT_ERROR || coreState == CORE_RUNTIME_ERROR || coreState == CORE_POWERDOWN) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,10 +28,10 @@
|
|||
#include "Common/Common.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/ConfigValues.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreParameter.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/System.h"
|
||||
#include "GPU/Common/DrawEngineCommon.h"
|
||||
#include "GPU/Common/FramebufferCommon.h"
|
||||
#include "GPU/Common/PostShader.h"
|
||||
|
@ -831,7 +831,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) {
|
|||
currentRenderVfb_ = 0;
|
||||
|
||||
if (displayFramebufPtr_ == 0) {
|
||||
if (coreState == CORE_STEPPING)
|
||||
if (Core_IsStepping())
|
||||
VERBOSE_LOG(FRAMEBUF, "Display disabled, displaying only black");
|
||||
else
|
||||
DEBUG_LOG(FRAMEBUF, "Display disabled, displaying only black");
|
||||
|
@ -925,7 +925,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) {
|
|||
displayFramebuf_ = vfb;
|
||||
|
||||
if (vfb->fbo) {
|
||||
if (coreState == CORE_STEPPING)
|
||||
if (Core_IsStepping())
|
||||
VERBOSE_LOG(FRAMEBUF, "Displaying FBO %08x", vfb->fb_address);
|
||||
else
|
||||
DEBUG_LOG(FRAMEBUF, "Displaying FBO %08x", vfb->fb_address);
|
||||
|
|
|
@ -136,7 +136,9 @@ EmuScreen::EmuScreen(const std::string &filename)
|
|||
frameStep_ = false;
|
||||
lastNumFlips = gpuStats.numFlips;
|
||||
startDumping = false;
|
||||
|
||||
// Make sure we don't leave it at powerdown after the last game.
|
||||
// TODO: This really should be handled elsewhere if it isn't.
|
||||
if (coreState == CORE_POWERDOWN)
|
||||
coreState = CORE_STEPPING;
|
||||
|
||||
|
@ -1263,6 +1265,53 @@ static void DrawDebugStats(DrawBuffer *draw2d, const Bounds &bounds) {
|
|||
draw2d->SetFontScale(1.0f, 1.0f);
|
||||
}
|
||||
|
||||
static void DrawCrashDump(DrawBuffer *draw2d) {
|
||||
const ExceptionInfo &info = Core_GetExceptionInfo();
|
||||
|
||||
FontID ubuntu24("UBUNTU24");
|
||||
char statbuf[4096];
|
||||
char versionString[256];
|
||||
sprintf(versionString, "%s", PPSSPP_GIT_VERSION);
|
||||
// TODO: Draw a lot more information. Full register set, and so on.
|
||||
|
||||
#ifdef _DEBUG
|
||||
char build[] = "Debug";
|
||||
#else
|
||||
char build[] = "Release";
|
||||
#endif
|
||||
snprintf(statbuf, sizeof(statbuf), R"(%s
|
||||
Game ID (Title): %s (%s)
|
||||
PPSSPP build: %s (%s)
|
||||
)",
|
||||
ExceptionTypeAsString(info.type),
|
||||
g_paramSFO.GetDiscID().c_str(),
|
||||
g_paramSFO.GetValueString("TITLE").c_str(),
|
||||
versionString,
|
||||
build
|
||||
);
|
||||
|
||||
draw2d->SetFontScale(.7f, .7f);
|
||||
int x = 20;
|
||||
int y = 50;
|
||||
draw2d->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
|
||||
y += 100;
|
||||
|
||||
if (info.type == ExceptionType::MEMORY) {
|
||||
snprintf(statbuf, sizeof(statbuf), R"(
|
||||
Access: %s at %08x
|
||||
PC: %08x)",
|
||||
MemoryExceptionTypeAsString(info.memory_type),
|
||||
info.address,
|
||||
info.pc);
|
||||
draw2d->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
|
||||
y += 120;
|
||||
}
|
||||
|
||||
std::string kernelState = __KernelStateSummary();
|
||||
|
||||
draw2d->DrawTextShadow(ubuntu24, kernelState.c_str(), x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
|
||||
}
|
||||
|
||||
static void DrawAudioDebugStats(DrawBuffer *draw2d, const Bounds &bounds) {
|
||||
FontID ubuntu24("UBUNTU24");
|
||||
char statbuf[4096] = { 0 };
|
||||
|
@ -1403,23 +1452,39 @@ void EmuScreen::render() {
|
|||
PSP_RunLoopWhileState();
|
||||
|
||||
// Hopefully coreState is now CORE_NEXTFRAME
|
||||
if (coreState == CORE_NEXTFRAME) {
|
||||
// set back to running for the next frame
|
||||
switch (coreState) {
|
||||
case CORE_NEXTFRAME:
|
||||
// Reached the end of the frame, all good. Set back to running for the next frame
|
||||
coreState = CORE_RUNNING;
|
||||
} else if (coreState == CORE_STEPPING) {
|
||||
// If we're stepping, it's convenient not to clear the screen entirely, so we copy display to output.
|
||||
// This won't work in non-buffered, but that's fine.
|
||||
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_Stepping");
|
||||
// Just to make sure.
|
||||
if (PSP_IsInited()) {
|
||||
gpu->CopyDisplayToOutput(true);
|
||||
break;
|
||||
case CORE_STEPPING:
|
||||
case CORE_RUNTIME_ERROR:
|
||||
{
|
||||
// If there's an exception, display information.
|
||||
const ExceptionInfo &info = Core_GetExceptionInfo();
|
||||
if (info.type != ExceptionType::NONE) {
|
||||
// Clear to blue background screen
|
||||
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE, 0xFF900000 }, "EmuScreen_RuntimeError");
|
||||
// The info is drawn later in renderUI
|
||||
} else {
|
||||
// If we're stepping, it's convenient not to clear the screen entirely, so we copy display to output.
|
||||
// This won't work in non-buffered, but that's fine.
|
||||
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_Stepping");
|
||||
// Just to make sure.
|
||||
if (PSP_IsInited()) {
|
||||
gpu->CopyDisplayToOutput(true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Didn't actually reach the end of the frame, ran out of the blockTicks cycles.
|
||||
// In this case we need to bind and wipe the backbuffer, at least.
|
||||
// It's possible we never ended up outputted anything - make sure we have the backbuffer cleared
|
||||
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_NoFrame");
|
||||
break;
|
||||
}
|
||||
|
||||
checkPowerDown();
|
||||
|
||||
PSP_EndHostFrame();
|
||||
|
@ -1464,6 +1529,9 @@ bool EmuScreen::hasVisibleUI() {
|
|||
if (g_Config.bShowDebugStats || g_Config.bShowDeveloperMenu || g_Config.bShowAudioDebug || g_Config.bShowFrameProfiler)
|
||||
return true;
|
||||
|
||||
// Exception information.
|
||||
if (coreState == CORE_RUNTIME_ERROR || coreState == CORE_STEPPING)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1523,6 +1591,14 @@ void EmuScreen::renderUI() {
|
|||
DrawProfile(*ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (coreState == CORE_RUNTIME_ERROR || coreState == CORE_STEPPING) {
|
||||
const ExceptionInfo &info = Core_GetExceptionInfo();
|
||||
if (info.type != ExceptionType::NONE) {
|
||||
DrawCrashDump(draw2d);
|
||||
}
|
||||
}
|
||||
|
||||
ctx->Flush();
|
||||
}
|
||||
|
||||
|
|
|
@ -779,6 +779,7 @@ void GameSettingsScreen::CreateViews() {
|
|||
systemSettings->Add(new ItemHeader(sy->T("Emulation")));
|
||||
|
||||
systemSettings->Add(new CheckBox(&g_Config.bFastMemory, sy->T("Fast Memory", "Fast Memory (Unstable)")))->OnClick.Handle(this, &GameSettingsScreen::OnJitAffectingSetting);
|
||||
systemSettings->Add(new CheckBox(&g_Config.bIgnoreBadMemAccess, sy->T("Ignore bad memory accesses")));
|
||||
|
||||
systemSettings->Add(new CheckBox(&g_Config.bSeparateIOThread, sy->T("I/O on thread (experimental)")))->SetEnabled(!PSP_IsInited());
|
||||
static const char *ioTimingMethods[] = { "Fast (lag on slow storage)", "Host (bugs, less lag)", "Simulate UMD delays" };
|
||||
|
|
|
@ -1354,7 +1354,7 @@ namespace MainWindow {
|
|||
|
||||
void UpdateCommands() {
|
||||
static GlobalUIState lastGlobalUIState = UISTATE_PAUSEMENU;
|
||||
static CoreState lastCoreState = CORE_ERROR;
|
||||
static CoreState lastCoreState = CORE_BOOT_ERROR;
|
||||
|
||||
HMENU menu = GetMenu(GetHWND());
|
||||
EnableMenuItem(menu, ID_DEBUG_LOG, !g_Config.bEnableLogging);
|
||||
|
|
Loading…
Add table
Reference in a new issue