diff --git a/CMakeLists.txt b/CMakeLists.txt
index ee980147402d..9d866fe92c01 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -340,9 +340,6 @@ macro(setup_target_project TargetName ProjectDir)
endforeach()
endmacro()
-# Commented-out files are files that don't compile
-# and were disabled in the original MSVC project anyway
-
set(CommonX86
Common/ABI.cpp
Common/ABI.h
@@ -428,6 +425,8 @@ add_library(Common STATIC
Common/Crypto/sha1.h
Common/Crypto/sha256.cpp
Common/Crypto/sha256.h
+ Common/ExceptionHandlerSetup.cpp
+ Common/ExceptionHandlerSetup.h
Common/FileUtil.cpp
Common/FileUtil.h
Common/KeyMap.cpp
diff --git a/Common/CodeBlock.h b/Common/CodeBlock.h
index f60c4b9305da..777fb164a4d2 100644
--- a/Common/CodeBlock.h
+++ b/Common/CodeBlock.h
@@ -19,7 +19,7 @@ class CodeBlockCommon {
CodeBlockCommon() {}
virtual ~CodeBlockCommon() {}
- bool IsInSpace(const u8 *ptr) {
+ bool IsInSpace(const u8 *ptr) const {
return (ptr >= region) && (ptr < (region + region_size));
}
diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj
index 80278606dff2..6486c530e777 100644
--- a/Common/Common.vcxproj
+++ b/Common/Common.vcxproj
@@ -368,6 +368,7 @@
+
@@ -397,6 +398,7 @@
+
@@ -449,6 +451,7 @@
+
true
@@ -521,4 +524,4 @@
-
\ No newline at end of file
+
diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters
index ebe7f5068812..c49d67d6af61 100644
--- a/Common/Common.vcxproj.filters
+++ b/Common/Common.vcxproj.filters
@@ -74,6 +74,8 @@
Vulkan
+
+
@@ -137,6 +139,7 @@
Vulkan
+
@@ -152,4 +155,4 @@
{c14d66ef-5f7c-4565-975a-72774e7ccfb9}
-
\ No newline at end of file
+
diff --git a/Common/ExceptionHandlerSetup.cpp b/Common/ExceptionHandlerSetup.cpp
new file mode 100644
index 000000000000..6cdbefede4f0
--- /dev/null
+++ b/Common/ExceptionHandlerSetup.cpp
@@ -0,0 +1,347 @@
+// Copyright 2008 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+// The corresponding file is called MemTools in the Dolphin project.
+
+#include "ppsspp_config.h"
+
+#include "Common/ExceptionHandlerSetup.h"
+#include
+#include
+#include
+#include
+#include
+
+#include "Common/CommonFuncs.h"
+#include "Common/CommonTypes.h"
+#include "Common/MsgHandler.h"
+#include "Common/Log.h"
+#include "ext/native/thread/threadutil.h"
+
+#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
+#include "Common/MachineContext.h"
+#endif
+
+#if PPSSPP_PLATFORM(IOS)
+#define USE_SIGACTION_ON_APPLE
+#endif
+
+#ifdef __FreeBSD__
+#include
+#endif
+#ifndef _WIN32
+#include // Needed for _POSIX_VERSION
+#endif
+
+static BadAccessHandler g_badAccessHandler;
+
+// We cannot handle exceptions in UWP builds. Bleh.
+#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
+
+static PVOID g_vectoredExceptionHandle;
+
+static LONG NTAPI GlobalExceptionHandler(PEXCEPTION_POINTERS pPtrs) {
+ switch (pPtrs->ExceptionRecord->ExceptionCode) {
+ case EXCEPTION_ACCESS_VIOLATION:
+ {
+ int accessType = (int)pPtrs->ExceptionRecord->ExceptionInformation[0];
+ if (accessType == 8) { // Rule out DEP
+ return (DWORD)EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ // virtual address of the inaccessible data
+ uintptr_t badAddress = (uintptr_t)pPtrs->ExceptionRecord->ExceptionInformation[1];
+ CONTEXT* ctx = pPtrs->ContextRecord;
+
+ if (g_badAccessHandler(badAddress, ctx)) {
+ return (DWORD)EXCEPTION_CONTINUE_EXECUTION;
+ } else {
+ // Let's not prevent debugging.
+ return (DWORD)EXCEPTION_CONTINUE_SEARCH;
+ }
+ }
+
+ case EXCEPTION_STACK_OVERFLOW:
+ // Dolphin has some handling of this for the RET optimization emulation.
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ // No SSE support? Or simply bad codegen?
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ case EXCEPTION_PRIV_INSTRUCTION:
+ // okay, dynarec codegen is obviously broken.
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ case EXCEPTION_IN_PAGE_ERROR:
+ // okay, something went seriously wrong, out of memory?
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ case EXCEPTION_BREAKPOINT:
+ // might want to do something fun with this one day?
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ default:
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+}
+
+void InstallExceptionHandler(BadAccessHandler badAccessHandler) {
+ // Make sure this is only called once per process execution
+ // Instead, could make a Uninstall function, but whatever..
+ if (g_badAccessHandler) {
+ g_badAccessHandler = badAccessHandler;
+ return;
+ }
+
+ INFO_LOG(SYSTEM, "Installing exception handler");
+ g_badAccessHandler = badAccessHandler;
+ g_vectoredExceptionHandle = AddVectoredExceptionHandler(TRUE, GlobalExceptionHandler);
+}
+
+void UninstallExceptionHandler() {
+ RemoveVectoredExceptionHandler(g_vectoredExceptionHandle);
+ g_badAccessHandler = nullptr;
+ INFO_LOG(SYSTEM, "Removed exception handler");
+}
+
+#elif defined(__APPLE__) && !defined(USE_SIGACTION_ON_APPLE)
+
+static void CheckKR(const char* name, kern_return_t kr) {
+ if (kr) {
+ PanicAlert("%s failed: kr=%x", name, kr);
+ }
+}
+
+static void ExceptionThread(mach_port_t port) {
+ setCurrentThreadName("Mach exception thread");
+#pragma pack(4)
+ struct {
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ int64_t code[2];
+ int flavor;
+ mach_msg_type_number_t old_stateCnt;
+ natural_t old_state[x86_THREAD_STATE64_COUNT];
+ mach_msg_trailer_t trailer;
+ } msg_in;
+
+ struct {
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ kern_return_t RetCode;
+ int flavor;
+ mach_msg_type_number_t new_stateCnt;
+ natural_t new_state[x86_THREAD_STATE64_COUNT];
+ } msg_out;
+#pragma pack()
+ memset(&msg_in, 0xee, sizeof(msg_in));
+ memset(&msg_out, 0xee, sizeof(msg_out));
+ mach_msg_header_t* send_msg = nullptr;
+ mach_msg_size_t send_size = 0;
+ mach_msg_option_t option = MACH_RCV_MSG;
+ while (true) {
+ // If this isn't the first run, send the reply message. Then, receive
+ // a message: either a mach_exception_raise_state RPC due to
+ // thread_set_exception_ports, or MACH_NOTIFY_NO_SENDERS due to
+ // mach_port_request_notification.
+ CheckKR("mach_msg_overwrite",
+ mach_msg_overwrite(send_msg, option, send_size, sizeof(msg_in), port,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, &msg_in.Head, 0));
+
+ if (msg_in.Head.msgh_id == MACH_NOTIFY_NO_SENDERS) {
+ // the other thread exited
+ mach_port_destroy(mach_task_self(), port);
+ return;
+ }
+
+ if (msg_in.Head.msgh_id != 2406) {
+ PanicAlert("unknown message received");
+ return;
+ }
+
+ if (msg_in.flavor != x86_THREAD_STATE64) {
+ PanicAlert("unknown flavor %d (expected %d)", msg_in.flavor, x86_THREAD_STATE64);
+ return;
+ }
+
+ x86_thread_state64_t* state = (x86_thread_state64_t*)msg_in.old_state;
+
+ bool ok = g_badAccessHandler((uintptr_t)msg_in.code[1], state);
+
+ // Set up the reply.
+ msg_out.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(msg_in.Head.msgh_bits), 0);
+ msg_out.Head.msgh_remote_port = msg_in.Head.msgh_remote_port;
+ msg_out.Head.msgh_local_port = MACH_PORT_NULL;
+ msg_out.Head.msgh_id = msg_in.Head.msgh_id + 100;
+ msg_out.NDR = msg_in.NDR;
+ if (ok) {
+ msg_out.RetCode = KERN_SUCCESS;
+ msg_out.flavor = x86_THREAD_STATE64;
+ msg_out.new_stateCnt = x86_THREAD_STATE64_COUNT;
+ memcpy(msg_out.new_state, msg_in.old_state, x86_THREAD_STATE64_COUNT * sizeof(natural_t));
+ } else {
+ // Pass the exception to the next handler (debugger or crash).
+ msg_out.RetCode = KERN_FAILURE;
+ msg_out.flavor = 0;
+ msg_out.new_stateCnt = 0;
+ }
+ msg_out.Head.msgh_size =
+ offsetof(__typeof__(msg_out), new_state) + msg_out.new_stateCnt * sizeof(natural_t);
+
+ send_msg = &msg_out.Head;
+ send_size = msg_out.Head.msgh_size;
+ option |= MACH_SEND_MSG;
+ }
+}
+
+void InstallExceptionHandler(BadAccessHandler badAccessHandler) {
+ if (g_badAccessHandler) {
+ // The rest of the setup we don't need to do again.
+ g_badAccessHandler = badAccessHandler;
+ return;
+ }
+ g_badAccessHandler = badAccessHandler;
+
+ INFO_LOG(SYSTEM, "Installing exception handler");
+ mach_port_t port;
+ CheckKR("mach_port_allocate",
+ mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port));
+ std::thread exc_thread(ExceptionThread, port);
+ exc_thread.detach();
+ // Obtain a send right for thread_set_exception_ports to copy...
+ CheckKR("mach_port_insert_right",
+ mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND));
+ // Mach tries the following exception ports in order: thread, task, host.
+ // Debuggers set the task port, so we grab the thread port.
+ CheckKR("thread_set_exception_ports",
+ thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, port,
+ EXCEPTION_STATE | MACH_EXCEPTION_CODES, x86_THREAD_STATE64));
+ // ...and get rid of our copy so that MACH_NOTIFY_NO_SENDERS works.
+ CheckKR("mach_port_mod_refs",
+ mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1));
+ mach_port_t previous;
+ CheckKR("mach_port_request_notification",
+ mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_NO_SENDERS, 0, port,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous));
+}
+
+void UninstallExceptionHandler() {
+}
+
+#elif defined(_POSIX_VERSION)
+
+static struct sigaction old_sa_segv;
+static struct sigaction old_sa_bus;
+
+static void sigsegv_handler(int sig, siginfo_t* info, void* raw_context) {
+ if (sig != SIGSEGV && sig != SIGBUS) {
+ // We are not interested in other signals - handle it as usual.
+ return;
+ }
+ ucontext_t* context = (ucontext_t*)raw_context;
+ int sicode = info->si_code;
+ if (sicode != SEGV_MAPERR && sicode != SEGV_ACCERR) {
+ // Huh? Return.
+ return;
+ }
+ uintptr_t bad_address = (uintptr_t)info->si_addr;
+
+ // Get all the information we can out of the context.
+#ifdef __OpenBSD__
+ ucontext_t* ctx = context;
+#else
+ mcontext_t* ctx = &context->uc_mcontext;
+#endif
+ // assume it's not a write
+ if (!g_badAccessHandler(bad_address,
+#ifdef __APPLE__
+ *ctx
+#else
+ ctx
+#endif
+ )) {
+ // retry and crash
+ // According to the sigaction man page, if sa_flags "SA_SIGINFO" is set to the sigaction
+ // function pointer, otherwise sa_handler contains one of:
+ // SIG_DEF: The 'default' action is performed
+ // SIG_IGN: The signal is ignored
+ // Any other value is a function pointer to a signal handler
+
+ struct sigaction* old_sa;
+ if (sig == SIGSEGV) {
+ old_sa = &old_sa_segv;
+ } else {
+ old_sa = &old_sa_bus;
+ }
+
+ if (old_sa->sa_flags & SA_SIGINFO) {
+ old_sa->sa_sigaction(sig, info, raw_context);
+ return;
+ }
+ if (old_sa->sa_handler == SIG_DFL) {
+ signal(sig, SIG_DFL);
+ return;
+ }
+ if (old_sa->sa_handler == SIG_IGN) {
+ // Ignore signal
+ return;
+ }
+ old_sa->sa_handler(sig);
+ }
+}
+
+void InstallExceptionHandler(BadAccessHandler badAccessHandler) {
+ if (g_badAccessHandler) {
+ g_badAccessHandler = badAccessHandler;
+ return;
+ }
+ NOTICE_LOG(SYSTEM, "Installed exception handler");
+ g_badAccessHandler = badAccessHandler;
+
+ stack_t signal_stack;
+#ifdef __FreeBSD__
+ signal_stack.ss_sp = (char*)malloc(SIGSTKSZ);
+#else
+ signal_stack.ss_sp = malloc(SIGSTKSZ);
+#endif
+ signal_stack.ss_size = SIGSTKSZ;
+ signal_stack.ss_flags = 0;
+ if (sigaltstack(&signal_stack, nullptr))
+ PanicAlert("sigaltstack failed");
+ struct sigaction sa;
+ sa.sa_handler = nullptr;
+ sa.sa_sigaction = &sigsegv_handler;
+ sa.sa_flags = SA_SIGINFO;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGSEGV, &sa, &old_sa_segv);
+#ifdef __APPLE__
+ sigaction(SIGBUS, &sa, &old_sa_bus);
+#endif
+}
+
+void UninstallExceptionHandler() {
+ stack_t signal_stack, old_stack;
+ signal_stack.ss_flags = SS_DISABLE;
+ if (!sigaltstack(&signal_stack, &old_stack) && !(old_stack.ss_flags & SS_DISABLE)) {
+ free(old_stack.ss_sp);
+ }
+ sigaction(SIGSEGV, &old_sa_segv, nullptr);
+#ifdef __APPLE__
+ sigaction(SIGBUS, &old_sa_bus, nullptr);
+#endif
+ NOTICE_LOG(SYSTEM, "Uninstalled exception handler");
+ g_badAccessHandler = nullptr;
+}
+
+#else // Unsupported platform. Could also #error
+
+void InstallExceptionHandler(BadAccessHandler badAccessHandler) {
+ ERROR_LOG(SYSTEM, "Exception handler not implemented on this platform, can't install");
+}
+void UninstallExceptionHandler() { }
+
+#endif
diff --git a/Common/ExceptionHandlerSetup.h b/Common/ExceptionHandlerSetup.h
new file mode 100644
index 000000000000..8f88176e0e4b
--- /dev/null
+++ b/Common/ExceptionHandlerSetup.h
@@ -0,0 +1,17 @@
+// Copyright 2008 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+
+// On Windows, context is a CONTEXT object.
+// On Apple, context is a x86_thread_state64_t.
+// On Unix/Linux, context is a mcontext_t.
+// On OpenBSD, context is a ucontext_t.
+// Ugh, might need to abstract this better.
+typedef bool (*BadAccessHandler)(uintptr_t address, void *context);
+
+void InstallExceptionHandler(BadAccessHandler accessHandler);
+void UninstallExceptionHandler();
diff --git a/Common/MachineContext.h b/Common/MachineContext.h
new file mode 100644
index 000000000000..34e5a280a04d
--- /dev/null
+++ b/Common/MachineContext.h
@@ -0,0 +1,257 @@
+// Copyright 2008 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "ppsspp_config.h"
+
+#if PPSSPP_PLATFORM(WINDOWS)
+#include
+typedef CONTEXT SContext;
+#if PPSSPP_ARCH(AMD64)
+#define CTX_RAX Rax
+#define CTX_RBX Rbx
+#define CTX_RCX Rcx
+#define CTX_RDX Rdx
+#define CTX_RDI Rdi
+#define CTX_RSI Rsi
+#define CTX_RBP Rbp
+#define CTX_RSP Rsp
+#define CTX_R8 R8
+#define CTX_R9 R9
+#define CTX_R10 R10
+#define CTX_R11 R11
+#define CTX_R12 R12
+#define CTX_R13 R13
+#define CTX_R14 R14
+#define CTX_R15 R15
+#define CTX_RIP Rip
+#else
+#define CTX_RAX Eax
+#define CTX_RBX Ebx
+#define CTX_RCX Ecx
+#define CTX_RDX Edx
+#define CTX_RDI Edi
+#define CTX_RSI Esi
+#define CTX_RBP Ebp
+#define CTX_RSP Esp
+#define CTX_RIP Eip
+
+#endif
+#elif defined(__APPLE__) && !defined(USE_SIGACTION_ON_APPLE)
+// for modules:
+#define _XOPEN_SOURCE
+#include
+
+#include
+#include
+#if PPSSPP_ARCH(AMD64)
+typedef x86_thread_state64_t SContext;
+#define CTX_RAX __rax
+#define CTX_RBX __rbx
+#define CTX_RCX __rcx
+#define CTX_RDX __rdx
+#define CTX_RDI __rdi
+#define CTX_RSI __rsi
+#define CTX_RBP __rbp
+#define CTX_RSP __rsp
+#define CTX_R8 __r8
+#define CTX_R9 __r9
+#define CTX_R10 __r10
+#define CTX_R11 __r11
+#define CTX_R12 __r12
+#define CTX_R13 __r13
+#define CTX_R14 __r14
+#define CTX_R15 __r15
+#define CTX_RIP __rip
+#else
+#error No context definition for architecture
+#endif
+#elif defined(__APPLE__)
+#include
+typedef _STRUCT_MCONTEXT64 SContext;
+#define CTX_RAX __ss.__rax
+#define CTX_RBX __ss.__rbx
+#define CTX_RCX __ss.__rcx
+#define CTX_RDX __ss.__rdx
+#define CTX_RDI __ss.__rdi
+#define CTX_RSI __ss.__rsi
+#define CTX_RBP __ss.__rbp
+#define CTX_RSP __ss.__rsp
+#define CTX_R8 __ss.__r8
+#define CTX_R9 __ss.__r9
+#define CTX_R10 __ss.__r10
+#define CTX_R11 __ss.__r11
+#define CTX_R12 __ss.__r12
+#define CTX_R13 __ss.__r13
+#define CTX_R14 __ss.__r14
+#define CTX_R15 __ss.__r15
+#define CTX_RIP __ss.__rip
+#elif defined(__linux__)
+#include
+
+#include
+typedef mcontext_t SContext;
+
+#if PPSSPP_ARCH(AMD64)
+#define CTX_RAX gregs[REG_RAX]
+#define CTX_RBX gregs[REG_RBX]
+#define CTX_RCX gregs[REG_RCX]
+#define CTX_RDX gregs[REG_RDX]
+#define CTX_RDI gregs[REG_RDI]
+#define CTX_RSI gregs[REG_RSI]
+#define CTX_RBP gregs[REG_RBP]
+#define CTX_RSP gregs[REG_RSP]
+#define CTX_R8 gregs[REG_R8]
+#define CTX_R9 gregs[REG_R9]
+#define CTX_R10 gregs[REG_R10]
+#define CTX_R11 gregs[REG_R11]
+#define CTX_R12 gregs[REG_R12]
+#define CTX_R13 gregs[REG_R13]
+#define CTX_R14 gregs[REG_R14]
+#define CTX_R15 gregs[REG_R15]
+#define CTX_RIP gregs[REG_RIP]
+#elif PPSSPP_ARCH(X86)
+#define CTX_RAX gregs[REG_EAX]
+#define CTX_RBX gregs[REG_EBX]
+#define CTX_RCX gregs[REG_ECX]
+#define CTX_RDX gregs[REG_EDX]
+#define CTX_RDI gregs[REG_EDI]
+#define CTX_RSI gregs[REG_ESI]
+#define CTX_RBP gregs[REG_EBP]
+#define CTX_RSP gregs[REG_ESP]
+#define CTX_RIP gregs[REG_EIP]
+#else
+#error No context definition for architecture
+#endif
+#elif defined(__OpenBSD__)
+#include
+typedef ucontext_t SContext;
+#if PPSSPP_ARCH(AMD64)
+#define CTX_RAX sc_rax
+#define CTX_RBX sc_rbx
+#define CTX_RCX sc_rcx
+#define CTX_RDX sc_rdx
+#define CTX_RDI sc_rdi
+#define CTX_RSI sc_rsi
+#define CTX_RBP sc_rbp
+#define CTX_RSP sc_rsp
+#define CTX_R8 sc_r8
+#define CTX_R9 sc_r9
+#define CTX_R10 sc_r10
+#define CTX_R11 sc_r11
+#define CTX_R12 sc_r12
+#define CTX_R13 sc_r13
+#define CTX_R14 sc_r14
+#define CTX_R15 sc_r15
+#define CTX_RIP sc_rip
+#else
+#error No context definition for architecture
+#endif
+#elif defined(__NetBSD__)
+#include
+typedef mcontext_t SContext;
+#if PPSSPP_ARCH(AMD64)
+#define CTX_RAX __gregs[_REG_RAX]
+#define CTX_RBX __gregs[_REG_RBX]
+#define CTX_RCX __gregs[_REG_RCX]
+#define CTX_RDX __gregs[_REG_RDX]
+#define CTX_RDI __gregs[_REG_RDI]
+#define CTX_RSI __gregs[_REG_RSI]
+#define CTX_RBP __gregs[_REG_RBP]
+#define CTX_RSP __gregs[_REG_RSP]
+#define CTX_R8 __gregs[_REG_R8]
+#define CTX_R9 __gregs[_REG_R9]
+#define CTX_R10 __gregs[_REG_R10]
+#define CTX_R11 __gregs[_REG_R11]
+#define CTX_R12 __gregs[_REG_R12]
+#define CTX_R13 __gregs[_REG_R13]
+#define CTX_R14 __gregs[_REG_R14]
+#define CTX_R15 __gregs[_REG_R15]
+#define CTX_RIP __gregs[_REG_RIP]
+#else
+#error No context definition for architecture
+#endif
+#elif defined(__FreeBSD__)
+#include
+typedef mcontext_t SContext;
+#if PPSSPP_ARCH(AMD64)
+#define CTX_RAX mc_rax
+#define CTX_RBX mc_rbx
+#define CTX_RCX mc_rcx
+#define CTX_RDX mc_rdx
+#define CTX_RDI mc_rdi
+#define CTX_RSI mc_rsi
+#define CTX_RBP mc_rbp
+#define CTX_RSP mc_rsp
+#define CTX_R8 mc_r8
+#define CTX_R9 mc_r9
+#define CTX_R10 mc_r10
+#define CTX_R11 mc_r11
+#define CTX_R12 mc_r12
+#define CTX_R13 mc_r13
+#define CTX_R14 mc_r14
+#define CTX_R15 mc_r15
+#define CTX_RIP mc_rip
+#else
+#error No context definition for architecture
+#endif
+#elif defined(__HAIKU__)
+#include
+typedef mcontext_t SContext;
+#if PPSSPP_ARCH(AMD64)
+#define CTX_RAX rax
+#define CTX_RBX rbx
+#define CTX_RCX rcx
+#define CTX_RDX rdx
+#define CTX_RDI rdi
+#define CTX_RSI rsi
+#define CTX_RBP rbp
+#define CTX_RSP rsp
+#define CTX_R8 r8
+#define CTX_R9 r9
+#define CTX_R10 r10
+#define CTX_R11 r11
+#define CTX_R12 r12
+#define CTX_R13 r13
+#define CTX_R14 r14
+#define CTX_R15 r15
+#define CTX_RIP rip
+#else
+#error No context definition for machine
+#endif
+#else
+#error No context definition for OS
+#endif
+
+#if PPSSPP_ARCH(AMD64)
+
+#include
+#define CTX_PC CTX_RIP
+static inline u64* ContextRN(SContext* ctx, int n)
+{
+ static const u8 offsets[] = {
+ offsetof(SContext, CTX_RAX), offsetof(SContext, CTX_RCX), offsetof(SContext, CTX_RDX),
+ offsetof(SContext, CTX_RBX), offsetof(SContext, CTX_RSP), offsetof(SContext, CTX_RBP),
+ offsetof(SContext, CTX_RSI), offsetof(SContext, CTX_RDI), offsetof(SContext, CTX_R8),
+ offsetof(SContext, CTX_R9), offsetof(SContext, CTX_R10), offsetof(SContext, CTX_R11),
+ offsetof(SContext, CTX_R12), offsetof(SContext, CTX_R13), offsetof(SContext, CTX_R14),
+ offsetof(SContext, CTX_R15)};
+ return (u64*)((char*)ctx + offsets[n]);
+}
+
+#elif PPSSPP_ARCH(X86)
+
+#include
+#define CTX_PC CTX_RIP
+
+static inline u32* ContextRN(SContext* ctx, int n)
+{
+ static const u8 offsets[] = {
+ offsetof(SContext, CTX_RAX), offsetof(SContext, CTX_RCX), offsetof(SContext, CTX_RDX),
+ offsetof(SContext, CTX_RBX), offsetof(SContext, CTX_RSP), offsetof(SContext, CTX_RBP),
+ offsetof(SContext, CTX_RSI), offsetof(SContext, CTX_RDI)};
+ return (u32*)((char*)ctx + offsets[n]);
+}
+#endif
diff --git a/Common/x64Analyzer.cpp b/Common/x64Analyzer.cpp
index 1940416d6a9c..0d04346c0457 100644
--- a/Common/x64Analyzer.cpp
+++ b/Common/x64Analyzer.cpp
@@ -17,8 +17,10 @@
#include "x64Analyzer.h"
-bool DisassembleMov(const unsigned char *codePtr, InstructionInfo &info, int accessType)
+bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info)
{
+ int accessType = 0;
+
unsigned const char *startCodePtr = codePtr;
u8 rex = 0;
u8 codeByte = 0;
@@ -80,6 +82,12 @@ bool DisassembleMov(const unsigned char *codePtr, InstructionInfo &info, int acc
modRMbyte = *codePtr++;
hasModRM = true;
}
+
+ // TODO: Add more cases.
+ if ((codeByte & 0xF0) == 0x80)
+ accessType = 1;
+ if ((codeByte & 0xF0) == 0xC0)
+ accessType = 1;
}
else
{
@@ -135,7 +143,6 @@ bool DisassembleMov(const unsigned char *codePtr, InstructionInfo &info, int acc
else
info.displacement = *((s32 *)codePtr);
codePtr += displacementSize;
-
if (accessType == 1)
{
diff --git a/Common/x64Analyzer.h b/Common/x64Analyzer.h
index ad2fed886d50..8ba78005e50a 100644
--- a/Common/x64Analyzer.h
+++ b/Common/x64Analyzer.h
@@ -19,7 +19,7 @@
#include "Common.h"
-struct InstructionInfo
+struct LSInstructionInfo
{
int operandSize; //8, 16, 32, 64
int instructionSize;
@@ -60,4 +60,4 @@ enum AccessType {
OP_ACCESS_WRITE = 1
};
-bool DisassembleMov(const unsigned char *codePtr, InstructionInfo &info, int accessType);
+bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info);
diff --git a/Core/Core.cpp b/Core/Core.cpp
index ecc3d08259c4..dfb28a6fc890 100644
--- a/Core/Core.cpp
+++ b/Core/Core.cpp
@@ -395,6 +395,7 @@ const char *ExceptionTypeAsString(ExceptionType type) {
const char *MemoryExceptionTypeAsString(MemoryExceptionType type) {
switch (type) {
+ case MemoryExceptionType::UNKNOWN: return "Unknown";
case MemoryExceptionType::READ_WORD: return "Read Word";
case MemoryExceptionType::WRITE_WORD: return "Write Word";
case MemoryExceptionType::READ_BLOCK: return "Read Block";
diff --git a/Core/Core.h b/Core/Core.h
index 6cb8004cf4ce..314a2823a15b 100644
--- a/Core/Core.h
+++ b/Core/Core.h
@@ -80,6 +80,8 @@ void Core_SetPowerSaving(bool mode);
bool Core_GetPowerSaving();
enum class MemoryExceptionType {
+ NONE,
+ UNKNOWN,
READ_WORD,
WRITE_WORD,
READ_BLOCK,
diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp
index 2f0016d511e3..251a2c0f9e8c 100644
--- a/Core/CoreTiming.cpp
+++ b/Core/CoreTiming.cpp
@@ -97,6 +97,11 @@ void FireMhzChange() {
}
void SetClockFrequencyHz(int cpuHz) {
+ if (cpuHz <= 0) {
+ // Paranoid check, protecting against division by zero and similar nonsense.
+ return;
+ }
+
// When the mhz changes, we keep track of what "time" it was before hand.
// This way, time always moves forward, even if mhz is changed.
lastGlobalTimeUs = GetGlobalTimeUs();
diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp
index 3a5bcba1a53b..44176dc293f8 100644
--- a/Core/MIPS/ARM/ArmAsm.cpp
+++ b/Core/MIPS/ARM/ArmAsm.cpp
@@ -237,6 +237,7 @@ void ArmJit::GenerateFixedCode() {
CMP(R0, 0);
B_CC(CC_EQ, outerLoop);
+ const uint8_t *quitLoop = GetCodePtr();
SetJumpTarget(badCoreState);
SaveDowncount();
@@ -251,6 +252,12 @@ void ArmJit::GenerateFixedCode() {
POP(9, R4, R5, R6, R7, R8, R9, R10, R11, R_PC); // Returns
+ crashHandler = GetCodePtr();
+ MOVP2R(R0, &coreState);
+ MOVI2R(R1, CORE_RUNTIME_ERROR);
+ STR(R1, R0, 0);
+ B(quitLoop);
+
// Uncomment if you want to see the output...
if (disasm) {
INFO_LOG(JIT, "THE DISASM ========================");
diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h
index 80a3bc6ae962..50322a65bc65 100644
--- a/Core/MIPS/ARM/ArmJit.h
+++ b/Core/MIPS/ARM/ArmJit.h
@@ -51,6 +51,8 @@ class ArmJit : public ArmGen::ARMXCodeBlock, public JitInterface, public MIPSFro
void Compile(u32 em_address) override; // Compiles a block at current MIPS PC
+ const u8 *GetCrashHandler() const override { return crashHandler; }
+ bool CodeInRange(const u8 *ptr) const override { return IsInSpace(ptr); }
bool DescribeCodePtr(const u8 *ptr, std::string &name) override;
MIPSOpcode GetOriginalOp(MIPSOpcode op) override;
@@ -313,6 +315,8 @@ class ArmJit : public ArmGen::ARMXCodeBlock, public JitInterface, public MIPSFro
const u8 *restoreRoundingMode;
const u8 *applyRoundingMode;
+
+ const u8 *crashHandler;
};
} // namespace MIPSComp
diff --git a/Core/MIPS/ARM64/Arm64Asm.cpp b/Core/MIPS/ARM64/Arm64Asm.cpp
index 755e35f57c5d..81f67fa3a351 100644
--- a/Core/MIPS/ARM64/Arm64Asm.cpp
+++ b/Core/MIPS/ARM64/Arm64Asm.cpp
@@ -277,6 +277,7 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) {
CMP(SCRATCH1, 0);
B(CC_EQ, outerLoop);
+ const uint8_t *quitLoop = GetCodePtr();
SetJumpTarget(badCoreState);
SaveStaticRegisters();
@@ -286,6 +287,12 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) {
RET();
+ crashHandler = GetCodePtr();
+ MOVP2R(SCRATCH1_64, &coreState);
+ MOVI2R(SCRATCH2, CORE_RUNTIME_ERROR);
+ STR(INDEX_UNSIGNED, SCRATCH2, SCRATCH1_64, 0);
+ B(quitLoop);
+
// Generate some integer conversion funcs.
// MIPS order!
static const RoundingMode roundModes[8] = { ROUND_N, ROUND_Z, ROUND_P, ROUND_M, ROUND_N, ROUND_Z, ROUND_P, ROUND_M };
diff --git a/Core/MIPS/ARM64/Arm64Jit.h b/Core/MIPS/ARM64/Arm64Jit.h
index 6ad5921d53ef..17734ade594a 100644
--- a/Core/MIPS/ARM64/Arm64Jit.h
+++ b/Core/MIPS/ARM64/Arm64Jit.h
@@ -52,6 +52,8 @@ class Arm64Jit : public Arm64Gen::ARM64CodeBlock, public JitInterface, public MI
void Compile(u32 em_address) override; // Compiles a block at current MIPS PC
const u8 *DoJit(u32 em_address, JitBlock *b);
+ const u8 *GetCrashHandler() const override { return crashHandler; }
+ bool CodeInRange(const u8 *ptr) const override { return IsInSpace(ptr); }
bool DescribeCodePtr(const u8 *ptr, std::string &name) override;
MIPSOpcode GetOriginalOp(MIPSOpcode op) override;
@@ -281,6 +283,8 @@ class Arm64Jit : public Arm64Gen::ARM64CodeBlock, public JitInterface, public MI
const u8 *applyRoundingMode;
const u8 *updateRoundingMode;
+ const u8 *crashHandler;
+
int jitStartOffset;
// Indexed by FPCR FZ:RN bits for convenience. Uses SCRATCH2.
diff --git a/Core/MIPS/IR/IRJit.h b/Core/MIPS/IR/IRJit.h
index d378dcfc87a5..9beabe585ac4 100644
--- a/Core/MIPS/IR/IRJit.h
+++ b/Core/MIPS/IR/IRJit.h
@@ -160,7 +160,12 @@ class IRJit : public JitInterface {
void InvalidateCacheAt(u32 em_address, int length = 4) override;
void UpdateFCR31() override;
+ bool CodeInRange(const u8 *ptr) const override {
+ return false;
+ }
+
const u8 *GetDispatcher() const override { return nullptr; }
+ const u8 *GetCrashHandler() const override { return nullptr; }
void LinkBlock(u8 *exitPoint, const u8 *checkedEntry) override;
void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override;
diff --git a/Core/MIPS/JitCommon/JitCommon.h b/Core/MIPS/JitCommon/JitCommon.h
index bf3023e21d67..08063f866e49 100644
--- a/Core/MIPS/JitCommon/JitCommon.h
+++ b/Core/MIPS/JitCommon/JitCommon.h
@@ -121,8 +121,10 @@ namespace MIPSComp {
public:
virtual ~JitInterface() {}
+ virtual bool CodeInRange(const u8 *ptr) const = 0;
virtual bool DescribeCodePtr(const u8 *ptr, std::string &name) = 0;
virtual const u8 *GetDispatcher() const = 0;
+ virtual const u8 *GetCrashHandler() const = 0;
virtual JitBlockCache *GetBlockCache() = 0;
virtual JitBlockCacheDebugInterface *GetBlockCacheDebugInterface() = 0;
virtual void InvalidateCacheAt(u32 em_address, int length = 4) = 0;
diff --git a/Core/MIPS/MIPS.h b/Core/MIPS/MIPS.h
index 7d2c40e8797d..bcc991035895 100644
--- a/Core/MIPS/MIPS.h
+++ b/Core/MIPS/MIPS.h
@@ -17,6 +17,8 @@
#pragma once
+#include "ppsspp_config.h"
+
#include
#include "util/random/rng.h"
@@ -148,7 +150,7 @@ extern u8 fromvoffset[128];
enum class CPUCore;
-#if defined(PPSSPP_ARCH_X86) || defined(PPSSPP_ARCH_AMD64)
+#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
// Note that CTXREG is offset to point at the first floating point register, intentionally. This is so that a byte offset
// can reach both GPR and FPR regs.
@@ -246,7 +248,7 @@ class MIPSState
static const u32 FCR0_VALUE = 0x00003351;
-#if defined(PPSSPP_ARCH_X86) || defined(PPSSPP_ARCH_AMD64)
+#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
// FPU TEMP0, etc. are swapped in here if necessary (e.g. on x86.)
float tempValues[NUM_X86_FPU_TEMPS];
#endif
diff --git a/Core/MIPS/x86/Asm.cpp b/Core/MIPS/x86/Asm.cpp
index fd1815132d1b..d927ba8c6d87 100644
--- a/Core/MIPS/x86/Asm.cpp
+++ b/Core/MIPS/x86/Asm.cpp
@@ -206,11 +206,21 @@ void Jit::GenerateFixedCode(JitOptions &jo) {
}
J_CC(CC_Z, outerLoop, true);
+ const uint8_t *quitLoop = GetCodePtr();
SetJumpTarget(badCoreState);
RestoreRoundingMode(true);
ABI_PopAllCalleeSavedRegsAndAdjustStack();
RET();
+ crashHandler = GetCodePtr();
+ if (RipAccessible((const void *)&coreState)) {
+ MOV(32, M(&coreState), Imm32(CORE_RUNTIME_ERROR));
+ } else {
+ MOV(PTRBITS, R(RAX), ImmPtr((const void *)&coreState));
+ MOV(32, MatR(RAX), Imm32(CORE_RUNTIME_ERROR));
+ }
+ JMP(quitLoop, true);
+
// Let's spare the pre-generated code from unprotect-reprotect.
endOfPregeneratedCode = AlignCodePage();
EndWrite();
diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp
index 2716f7233a49..0dbdf34d7b2a 100644
--- a/Core/MIPS/x86/Jit.cpp
+++ b/Core/MIPS/x86/Jit.cpp
@@ -442,6 +442,8 @@ bool Jit::DescribeCodePtr(const u8 *ptr, std::string &name) {
name = "enterDispatcher";
else if (ptr == restoreRoundingMode)
name = "restoreRoundingMode";
+ else if (ptr == crashHandler)
+ name = "crashHandler";
else {
u32 jitAddr = blocks.GetAddressFromBlockPtr(ptr);
diff --git a/Core/MIPS/x86/Jit.h b/Core/MIPS/x86/Jit.h
index 0eefe89c40bd..b065d9052216 100644
--- a/Core/MIPS/x86/Jit.h
+++ b/Core/MIPS/x86/Jit.h
@@ -63,6 +63,8 @@ class Jit : public Gen::XCodeBlock, public JitInterface, public MIPSFrontendInte
void Compile(u32 em_address) override; // Compiles a block at current MIPS PC
const u8 *DoJit(u32 em_address, JitBlock *b);
+ const u8 *GetCrashHandler() const override { return crashHandler; }
+ bool CodeInRange(const u8 *ptr) const override { return IsInSpace(ptr); }
bool DescribeCodePtr(const u8 *ptr, std::string &name) override;
void Comp_RunBlock(MIPSOpcode op) override;
@@ -325,6 +327,8 @@ class Jit : public Gen::XCodeBlock, public JitInterface, public MIPSFrontendInte
const u8 *endOfPregeneratedCode;
+ const u8 *crashHandler;
+
friend class JitSafeMem;
friend class JitSafeMemFuncs;
};
diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp
index 9c7a2cce19d1..4c36b7c6087c 100644
--- a/Core/MemMap.cpp
+++ b/Core/MemMap.cpp
@@ -29,6 +29,26 @@
#include "Common/MemArena.h"
#include "Common/ChunkFile.h"
+#ifdef __FreeBSD__
+#include
+#endif
+#ifndef _WIN32
+#include // Needed for _POSIX_VERSION
+#endif
+
+#if PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(X86)
+#include "Common/MachineContext.h"
+#include "Common/x64Analyzer.h"
+#elif PPSSPP_ARCH(ARM64) && !PPSSPP_PLATFORM(IOS)
+#include "Core/Util/DisArm64.h"
+typedef sigcontext SContext;
+#define CTX_PC pc
+#elif PPSSPP_ARCH(ARM) && !PPSSPP_PLATFORM(IOS)
+#include "ext/disarm.h"
+typedef sigcontext SContext;
+#define CTX_PC arm_pc
+#endif
+
#include "Core/MemMap.h"
#include "Core/HDRemaster.h"
#include "Core/MIPS/MIPS.h"
@@ -41,6 +61,8 @@
#include "Core/ConfigValues.h"
#include "Core/HLE/ReplaceTables.h"
#include "Core/MIPS/JitCommon/JitBlockCache.h"
+#include "Core/MIPS/JitCommon/JitCommon.h"
+#include "UI/OnScreenDisplay.h"
namespace Memory {
@@ -85,6 +107,8 @@ u32 g_PSPModel;
std::recursive_mutex g_shutdownLock;
+static int64_t g_numReportedBadAccesses = 0;
+
// We don't declare the IO region in here since its handled by other means.
static MemoryView views[] =
{
@@ -221,16 +245,22 @@ bool MemoryMap_Setup(u32 flags) {
#if !PPSSPP_PLATFORM(ANDROID)
if (g_arena.NeedsProbing()) {
int base_attempts = 0;
-#if defined(_WIN32) && PPSSPP_ARCH(32BIT)
+#if PPSSPP_PLATFORM(WINDOWS) && PPSSPP_ARCH(32BIT)
// Try a whole range of possible bases. Return once we got a valid one.
uintptr_t max_base_addr = 0x7FFF0000 - 0x10000000;
uintptr_t min_base_addr = 0x01000000;
uintptr_t stride = 0x400000;
-#else
+#elif PPSSPP_ARCH(ARM64) && PPSSPP_PLATFORM(IOS)
// iOS
uintptr_t max_base_addr = 0x1FFFF0000ULL - 0x80000000ULL;
uintptr_t min_base_addr = 0x100000000ULL;
uintptr_t stride = 0x800000;
+#else
+ uintptr_t max_base_addr = 0;
+ uintptr_t min_base_addr = 0;
+ uintptr_t stride = 0;
+ ERROR_LOG(MEMMAP, "MemoryMap_Setup: Hit a wrong path, should not be needed on this platform.");
+ return false;
#endif
for (uintptr_t base_addr = min_base_addr; base_addr < max_base_addr; base_addr += stride) {
base_attempts++;
@@ -290,6 +320,8 @@ void Init() {
INFO_LOG(MEMMAP, "Memory system initialized. Base at %p (RAM at @ %p, uncached @ %p)",
base, m_pPhysicalRAM, m_pUncachedRAM);
+
+ g_numReportedBadAccesses = 0;
}
void Reinit() {
@@ -457,4 +489,103 @@ void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength) {
CBreakPoints::ExecMemCheck(_Address, true, _iLength, currentMIPS->pc);
}
+// We do not support crash catching on UWP and iOS.
+// On iOS, the sigcontext struct seems to be missing??
+#if !PPSSPP_PLATFORM(IOS) && !PPSSPP_PLATFORM(UWP)
+
+bool HandleFault(uintptr_t hostAddress, void *ctx) {
+ SContext *context = (SContext *)ctx;
+ const uint8_t *codePtr = (uint8_t *)(context->CTX_PC);
+
+ // TODO: Check that codePtr is within the current JIT space.
+ bool inJitSpace = MIPSComp::jit && MIPSComp::jit->CodeInRange(codePtr);
+ if (!inJitSpace) {
+ // This is a crash in non-jitted code. Not something we want to handle here, ignore.
+ return false;
+ }
+
+ uintptr_t baseAddress = (uintptr_t)base;
+#ifdef MASKED_PSP_MEMORY
+ const uintptr_t addressSpaceSize = 0x40000000ULL;
+#else
+ const uintptr_t addressSpaceSize = 0x100000000ULL;
+#endif
+
+ // Check whether hostAddress is within the PSP memory space, which (likely) means it was a guest executable that did the bad access.
+ if (hostAddress < baseAddress || hostAddress >= baseAddress + addressSpaceSize) {
+ // Host address outside - this was a different kind of crash.
+ return false;
+ }
+
+ // OK, a guest executable did a bad access. Take care of it.
+
+ uint32_t guestAddress = hostAddress - baseAddress;
+
+ // TODO: Share the struct between the various analyzers, that will allow us to share most of
+ // the implementations here.
+ bool success = false;
+
+ MemoryExceptionType type = MemoryExceptionType::NONE;
+
+#if PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(X86)
+ // X86, X86-64. Variable instruction size so need to analyze the mov instruction in detail.
+
+ // To ignore the access, we need to disassemble the instruction and modify context->CTX_PC
+ LSInstructionInfo info;
+ success = X86AnalyzeMOV(codePtr, info);
+#elif PPSSPP_ARCH(ARM64)
+ uint32_t word;
+ memcpy(&word, codePtr, 4);
+ // To ignore the access, we need to disassemble the instruction and modify context->CTX_PC
+ Arm64LSInstructionInfo info;
+ success = Arm64AnalyzeLoadStore((uint64_t)codePtr, word, &info);
+#elif PPSSPP_ARCH(ARM)
+ uint32_t word;
+ memcpy(&word, codePtr, 4);
+ // To ignore the access, we need to disassemble the instruction and modify context->CTX_PC
+ ArmLSInstructionInfo info;
+ success = ArmAnalyzeLoadStore((uint32_t)codePtr, word, &info);
+#endif
+ if (success) {
+ if (info.isMemoryWrite) {
+ type = MemoryExceptionType::WRITE_WORD;
+ } else {
+ type = MemoryExceptionType::READ_WORD;
+ }
+ } else {
+ type = MemoryExceptionType::UNKNOWN;
+ }
+
+ if (success && g_Config.bIgnoreBadMemAccess) {
+ if (!info.isMemoryWrite) {
+ // It was a read. Fill the destination register with 0.
+ // TODO
+ }
+ // Move on to the next instruction. Note that handling bad accesses like this is pretty slow.
+ context->CTX_PC += info.instructionSize;
+ g_numReportedBadAccesses++;
+ if (g_numReportedBadAccesses < 100) {
+ ERROR_LOG(MEMMAP, "Bad memory access detected and ignored: %08x (%p)", guestAddress, (void *)hostAddress);
+ }
+ } else {
+ // Either bIgnoreBadMemAccess is off, or we failed recovery analysis.
+ uint32_t approximatePC = currentMIPS->pc;
+ Core_MemoryException(guestAddress, currentMIPS->pc, type);
+
+ // Redirect execution to a crash handler that will exit the game.
+ context->CTX_PC = (uintptr_t)MIPSComp::jit->GetCrashHandler();
+ ERROR_LOG(MEMMAP, "Bad memory access detected! %08x (%p) Stopping emulation.", guestAddress, (void *)hostAddress);
+ }
+ return true;
+}
+
+#else
+
+bool HandleFault(uintptr_t hostAddress, void *ctx) {
+ ERROR_LOG(MEMMAP, "Exception handling not supported");
+ return false;
+}
+
+#endif
+
} // namespace
diff --git a/Core/MemMap.h b/Core/MemMap.h
index 4f598961f3a6..dcbbad5441f5 100644
--- a/Core/MemMap.h
+++ b/Core/MemMap.h
@@ -20,6 +20,7 @@
#include "ppsspp_config.h"
#include
+#include
#ifndef offsetof
#include
#endif
@@ -147,6 +148,10 @@ Opcode Read_Opcode_JIT(const u32 _Address);
// used by JIT. Reads in the "Locked cache" mode
void Write_Opcode_JIT(const u32 _Address, const Opcode& _Value);
+// Called by exception handlers. We simply filter out accesses to PSP RAM and otherwise
+// just leave it as-is.
+bool HandleFault(uintptr_t hostAddress, void *context);
+
// Should be used by analyzers, disassemblers etc. Does resolve replacements.
Opcode Read_Instruction(const u32 _Address, bool resolveReplacements = false);
Opcode ReadUnchecked_Instruction(const u32 _Address, bool resolveReplacements = false);
diff --git a/Core/System.cpp b/Core/System.cpp
index c8a8b4c769d0..c679bb60b908 100644
--- a/Core/System.cpp
+++ b/Core/System.cpp
@@ -62,6 +62,7 @@
#include "Core/ELF/ParamSFO.h"
#include "Core/SaveState.h"
#include "Common/LogManager.h"
+#include "Common/ExceptionHandlerSetup.h"
#include "Core/HLE/sceAudiocodec.h"
#include "GPU/GPUState.h"
@@ -266,10 +267,11 @@ void CPU_Init() {
return;
}
-
if (coreParameter.updateRecent) {
g_Config.AddRecent(filename);
}
+
+ InstallExceptionHandler(&Memory::HandleFault);
}
PSP_LoadingLock::PSP_LoadingLock() {
@@ -281,6 +283,8 @@ PSP_LoadingLock::~PSP_LoadingLock() {
}
void CPU_Shutdown() {
+ UninstallExceptionHandler();
+
// Since we load on a background thread, wait for startup to complete.
PSP_LoadingLock lock;
PSPLoaders_Shutdown();
diff --git a/Core/Util/DisArm64.cpp b/Core/Util/DisArm64.cpp
index fd17c5441239..e6c983be554e 100644
--- a/Core/Util/DisArm64.cpp
+++ b/Core/Util/DisArm64.cpp
@@ -265,6 +265,40 @@ static void BranchExceptionAndSystem(uint32_t w, uint64_t addr, Instruction *ins
}
}
+bool Arm64AnalyzeLoadStore(uint64_t addr, uint32_t w, Arm64LSInstructionInfo *info) {
+ *info = {};
+ info->instructionSize = 4;
+ int id = (w >> 25) & 0xF;
+ switch (id) {
+ case 4: case 6: case 0xC: case 0xE:
+ break;
+ default:
+ return false; // not the expected instruction
+ }
+
+ info->size = w >> 30;
+ info->Rt = (w & 0x1F);
+ info->Rn = ((w >> 5) & 0x1F);
+ info->Rm = ((w >> 16) & 0x1F);
+ int opc = (w >> 22) & 0x3;
+ if (opc == 0 || opc == 2) {
+ info->isMemoryWrite = true;
+ }
+
+ if (((w >> 27) & 7) == 7) {
+ int V = (w >> 26) & 1;
+ if (V == 0) {
+ info->isIntegerLoadStore = true;
+ } else {
+ info->isFPLoadStore = true;
+ }
+ } else {
+ info->isPairLoadStore = true;
+ // TODO
+ }
+ return true;
+}
+
static void LoadStore(uint32_t w, uint64_t addr, Instruction *instr) {
int size = w >> 30;
int imm9 = SignExtend9((w >> 12) & 0x1FF);
diff --git a/Core/Util/DisArm64.h b/Core/Util/DisArm64.h
index fc55c6c34b8a..ced77f2cc269 100644
--- a/Core/Util/DisArm64.h
+++ b/Core/Util/DisArm64.h
@@ -18,9 +18,28 @@
// Basic ARM64 disassembler.
// No promises of accuracy, mostly just made to debug JIT code.
-#include
+#include
typedef bool (*SymbolCallback)(char *buffer, int bufsize, uint8_t *address);
void Arm64Dis(uint64_t addr, uint32_t w, char *output, int bufsize, bool includeWord, SymbolCallback symbolCallback = nullptr);
+// Information about a load/store instruction.
+struct Arm64LSInstructionInfo {
+ int instructionSize;
+
+ bool isIntegerLoadStore;
+ bool isFPLoadStore;
+ bool isPairLoadStore;
+
+ int size; // 0 = 8-bit, 1 = 16-bit, 2 = 32-bit, 3 = 64-bit
+ bool isMemoryWrite;
+
+ int Rt;
+ int Rn;
+ int Rm;
+
+ // TODO: more.
+};
+
+bool Arm64AnalyzeLoadStore(uint64_t addr, uint32_t op, Arm64LSInstructionInfo *info);
diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp
index 80170e844bc6..7992aeab3155 100644
--- a/UI/EmuScreen.cpp
+++ b/UI/EmuScreen.cpp
@@ -1158,7 +1158,7 @@ void EmuScreen::update() {
PSP_CoreParameter().pixelHeight = pixel_yres * bounds.h / dp_yres;
#endif
- if (!invalid_) {
+ if (!invalid_ && coreState != CORE_RUNTIME_ERROR) {
UpdateUIState(UISTATE_INGAME);
}
@@ -1271,7 +1271,8 @@ static void DrawCrashDump(DrawBuffer *draw2d) {
FontID ubuntu24("UBUNTU24");
char statbuf[4096];
char versionString[256];
- sprintf(versionString, "%s", PPSSPP_GIT_VERSION);
+ snprintf(versionString, sizeof(versionString), "%s", PPSSPP_GIT_VERSION);
+
// TODO: Draw a lot more information. Full register set, and so on.
#ifdef _DEBUG
@@ -1539,8 +1540,10 @@ bool EmuScreen::hasVisibleUI() {
return true;
// Exception information.
- if (coreState == CORE_RUNTIME_ERROR || coreState == CORE_STEPPING)
+ if (coreState == CORE_RUNTIME_ERROR || coreState == CORE_STEPPING) {
return true;
+ }
+
return false;
}
diff --git a/UWP/CommonUWP/CommonUWP.vcxproj b/UWP/CommonUWP/CommonUWP.vcxproj
index a5fe599d1012..88559e8c82a8 100644
--- a/UWP/CommonUWP/CommonUWP.vcxproj
+++ b/UWP/CommonUWP/CommonUWP.vcxproj
@@ -398,6 +398,7 @@
+
@@ -435,6 +436,7 @@
+
diff --git a/UWP/CommonUWP/CommonUWP.vcxproj.filters b/UWP/CommonUWP/CommonUWP.vcxproj.filters
index 566049b279eb..100c103e66d1 100644
--- a/UWP/CommonUWP/CommonUWP.vcxproj.filters
+++ b/UWP/CommonUWP/CommonUWP.vcxproj.filters
@@ -16,6 +16,7 @@
+
@@ -65,6 +66,7 @@
+
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
index 5bdf22bd1893..44ab4a73329a 100644
--- a/android/jni/Android.mk
+++ b/android/jni/Android.mk
@@ -15,6 +15,7 @@ ifeq ($(TARGET_ARCH_ABI),x86)
ARCH_FILES := \
$(SRC)/Common/ABI.cpp \
$(SRC)/Common/x64Emitter.cpp \
+ $(SRC)/Common/x64Analyzer.cpp \
$(SRC)/Common/CPUDetect.cpp \
$(SRC)/Common/Thunk.cpp \
$(SRC)/Core/MIPS/x86/CompALU.cpp \
@@ -36,6 +37,7 @@ ifeq ($(TARGET_ARCH_ABI),x86_64)
ARCH_FILES := \
$(SRC)/Common/ABI.cpp \
$(SRC)/Common/x64Emitter.cpp \
+ $(SRC)/Common/x64Analyzer.cpp \
$(SRC)/Common/CPUDetect.cpp \
$(SRC)/Common/Thunk.cpp \
$(SRC)/Core/MIPS/x86/CompALU.cpp \
@@ -205,6 +207,7 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Common/Crypto/sha256.cpp \
$(SRC)/Common/ChunkFile.cpp \
$(SRC)/Common/ColorConv.cpp \
+ $(SRC)/Common/ExceptionHandlerSetup.cpp \
$(SRC)/Common/KeyMap.cpp \
$(SRC)/Common/LogManager.cpp \
$(SRC)/Common/MemArenaAndroid.cpp \
diff --git a/ext/disarm.cpp b/ext/disarm.cpp
index 90c89c16b5d3..ece10410f4fb 100644
--- a/ext/disarm.cpp
+++ b/ext/disarm.cpp
@@ -72,6 +72,7 @@
#include "base/basictypes.h"
#include "Common/ArmEmitter.h"
+#include "ext/disarm.h"
static const char *CCFlagsStr[] = {
"EQ", // Equal
@@ -759,30 +760,14 @@ static bool DisasmNeon(uint32_t op, char *text) {
return false;
}
+bool ArmAnalyzeLoadStore(uint32_t addr, uint32_t op, ArmLSInstructionInfo *info) {
+ *info = {};
+ info->instructionSize = 4;
+ // TODO
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ return false;
+}
typedef unsigned int word;
diff --git a/ext/disarm.h b/ext/disarm.h
index 9bae68f21567..901e74cd4fc2 100644
--- a/ext/disarm.h
+++ b/ext/disarm.h
@@ -17,18 +17,34 @@
#pragma once
+#include
-// This stuff only disassembles very old ARM but should be sufficient
-// for the basics except for MOVW/MOVT.
-
-
+// This stuff used to only disassemble very old ARM but has now
+// been extended to support most (but not all) modern instructions, including NEON.
// Disarm itself has the license you can see in the cpp file.
// I'm not entirely sure it's 100% gpl compatible but it's nearly
// public domain so meh.
-// The only changes I've done is C++ compat and replaced the main
-// program with this function.
-
const char *ArmRegName(int r);
void ArmDis(unsigned int addr, unsigned int w, char *output, int bufsize, bool includeWord);
+
+// Information about a load/store instruction.
+struct ArmLSInstructionInfo {
+ int instructionSize;
+
+ bool isIntegerLoadStore;
+ bool isFPLoadStore;
+ bool isMultiLoadStore;
+
+ int size; // 0 = 8-bit, 1 = 16-bit, 2 = 32-bit, 3 = 64-bit
+ bool isMemoryWrite;
+
+ int Rt;
+ int Rn;
+ int Rm;
+
+ // TODO: more.
+};
+
+bool ArmAnalyzeLoadStore(uint32_t addr, uint32_t op, ArmLSInstructionInfo *info);
diff --git a/libretro/Makefile.common b/libretro/Makefile.common
index ab7d30ebdfd9..ed78f44cc444 100644
--- a/libretro/Makefile.common
+++ b/libretro/Makefile.common
@@ -143,6 +143,7 @@ SOURCES_CXX += \
SOURCES_CXX += \
$(COMMONDIR)/ChunkFile.cpp \
$(COMMONDIR)/ConsoleListener.cpp \
+ $(COMMONDIR)/ExceptionHandlerSetup.cpp \
$(COMMONDIR)/FileUtil.cpp \
$(COMMONDIR)/KeyMap.cpp \
$(COMMONDIR)/LogManager.cpp \