Merge pull request #13092 from hrydgard/error-handling-improvements

Bad memory access handling improvements
This commit is contained in:
Henrik Rydgård 2020-07-13 00:37:28 +02:00 committed by GitHub
commit daf0990692
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 313 additions and 226 deletions

View file

@ -97,6 +97,7 @@ public:
// Core
bool bIgnoreBadMemAccess;
bool bFastMemory;
int iCpuCore;
bool bCheckForNewVersion;

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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"

View file

@ -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++;

View file

@ -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));
}
}

View file

@ -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");

View file

@ -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();

View file

@ -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) {

View file

@ -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;

View file

@ -155,6 +155,9 @@ void __KernelThreadingInit();
void __KernelThreadingDoState(PointerWrap &p);
void __KernelThreadingDoStateLate(PointerWrap &p);
void __KernelThreadingShutdown();
std::string __KernelThreadingSummary();
KernelObject *__KernelThreadObject();
KernelObject *__KernelCallbackObject();

View file

@ -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_;
};
};

View file

@ -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) {

View file

@ -18,7 +18,6 @@
#pragma once
#include "ppsspp_config.h"
#include "Core/Config.h"
#include "Core/HLE/sceUsbCam.h"
#include "Log.h"

View file

@ -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;
}

View file

@ -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:

View file

@ -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)

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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

View file

@ -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 = "";
}

View file

@ -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();

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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();
}

View file

@ -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" };

View file

@ -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);