CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/Core.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include "ppsspp_config.h"1819#include <set>20#include <chrono>21#include <cstdint>22#include <mutex>23#include <condition_variable>2425#include "Common/System/NativeApp.h"26#include "Common/System/System.h"27#include "Common/System/Display.h"28#include "Common/TimeUtil.h"29#include "Common/Thread/ThreadUtil.h"30#include "Common/Profiler/Profiler.h"3132#include "Common/GraphicsContext.h"33#include "Common/Log.h"34#include "Core/Core.h"35#include "Core/Config.h"36#include "Core/MemMap.h"37#include "Core/SaveState.h"38#include "Core/System.h"39#include "Core/MemFault.h"40#include "Core/Debugger/Breakpoints.h"41#include "Core/HW/Display.h"42#include "Core/MIPS/MIPS.h"43#include "Core/HLE/sceNetAdhoc.h"44#include "GPU/Debugger/Stepping.h"45#include "Core/MIPS/MIPSTracer.h"4647#ifdef _WIN3248#include "Common/CommonWindows.h"49#include "Windows/InputDevice.h"50#endif5152static std::condition_variable m_StepCond;53static std::mutex m_hStepMutex;54static std::condition_variable m_InactiveCond;55static std::mutex m_hInactiveMutex;56static bool singleStepPending = false;57static int steppingCounter = 0;58static const char *steppingReason = "";59static uint32_t steppingAddress = 0;60static std::set<CoreLifecycleFunc> lifecycleFuncs;61static std::set<CoreStopRequestFunc> stopFuncs;62static bool windowHidden = false;63static bool powerSaving = false;6465static MIPSExceptionInfo g_exceptionInfo;6667void Core_SetGraphicsContext(GraphicsContext *ctx) {68PSP_CoreParameter().graphicsContext = ctx;69}7071void Core_NotifyWindowHidden(bool hidden) {72windowHidden = hidden;73// TODO: Wait until we can react?74}7576bool Core_IsWindowHidden() {77return windowHidden;78}7980void Core_ListenLifecycle(CoreLifecycleFunc func) {81lifecycleFuncs.insert(func);82}8384void Core_NotifyLifecycle(CoreLifecycle stage) {85if (stage == CoreLifecycle::STARTING) {86Core_ResetException();87}8889for (auto func : lifecycleFuncs) {90func(stage);91}92}9394void Core_ListenStopRequest(CoreStopRequestFunc func) {95stopFuncs.insert(func);96}9798void Core_Stop() {99Core_ResetException();100Core_UpdateState(CORE_POWERDOWN);101for (auto func : stopFuncs) {102func();103}104}105106bool Core_ShouldRunBehind() {107// Enforce run-behind if ad-hoc connected108return g_Config.bRunBehindPauseMenu || Core_MustRunBehind();109}110111bool Core_MustRunBehind() {112return __NetAdhocConnected();113}114115bool Core_IsStepping() {116return coreState == CORE_STEPPING || coreState == CORE_POWERDOWN;117}118119bool Core_IsActive() {120return coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME || coreStatePending;121}122123bool Core_IsInactive() {124return coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME && !coreStatePending;125}126127static inline void Core_StateProcessed() {128if (coreStatePending) {129std::lock_guard<std::mutex> guard(m_hInactiveMutex);130coreStatePending = false;131m_InactiveCond.notify_all();132}133}134135void Core_WaitInactive() {136while (Core_IsActive() && !GPUStepping::IsStepping()) {137std::unique_lock<std::mutex> guard(m_hInactiveMutex);138m_InactiveCond.wait(guard);139}140}141142void Core_WaitInactive(int milliseconds) {143if (Core_IsActive() && !GPUStepping::IsStepping()) {144std::unique_lock<std::mutex> guard(m_hInactiveMutex);145m_InactiveCond.wait_for(guard, std::chrono::milliseconds(milliseconds));146}147}148149void Core_SetPowerSaving(bool mode) {150powerSaving = mode;151}152153bool Core_GetPowerSaving() {154return powerSaving;155}156157static bool IsWindowSmall(int pixelWidth, int pixelHeight) {158// Can't take this from config as it will not be set if windows is maximized.159int w = (int)(pixelWidth * g_display.dpi_scale_x);160int h = (int)(pixelHeight * g_display.dpi_scale_y);161return g_Config.IsPortrait() ? (h < 480 + 80) : (w < 480 + 80);162}163164// TODO: Feels like this belongs elsewhere.165bool UpdateScreenScale(int width, int height) {166bool smallWindow;167168float g_logical_dpi = System_GetPropertyFloat(SYSPROP_DISPLAY_LOGICAL_DPI);169g_display.dpi = System_GetPropertyFloat(SYSPROP_DISPLAY_DPI);170171if (g_display.dpi < 0.0f) {172g_display.dpi = 96.0f;173}174if (g_logical_dpi < 0.0f) {175g_logical_dpi = 96.0f;176}177178g_display.dpi_scale_x = g_logical_dpi / g_display.dpi;179g_display.dpi_scale_y = g_logical_dpi / g_display.dpi;180g_display.dpi_scale_real_x = g_display.dpi_scale_x;181g_display.dpi_scale_real_y = g_display.dpi_scale_y;182183smallWindow = IsWindowSmall(width, height);184if (smallWindow) {185g_display.dpi /= 2.0f;186g_display.dpi_scale_x *= 2.0f;187g_display.dpi_scale_y *= 2.0f;188}189g_display.pixel_in_dps_x = 1.0f / g_display.dpi_scale_x;190g_display.pixel_in_dps_y = 1.0f / g_display.dpi_scale_y;191192int new_dp_xres = (int)(width * g_display.dpi_scale_x);193int new_dp_yres = (int)(height * g_display.dpi_scale_y);194195bool dp_changed = new_dp_xres != g_display.dp_xres || new_dp_yres != g_display.dp_yres;196bool px_changed = g_display.pixel_xres != width || g_display.pixel_yres != height;197198if (dp_changed || px_changed) {199g_display.dp_xres = new_dp_xres;200g_display.dp_yres = new_dp_yres;201g_display.pixel_xres = width;202g_display.pixel_yres = height;203NativeResized();204return true;205}206return false;207}208209// Used by Windows, SDL, Qt.210void UpdateRunLoop(GraphicsContext *ctx) {211NativeFrame(ctx);212if (windowHidden && g_Config.bPauseWhenMinimized) {213sleep_ms(16);214return;215}216}217218// Note: not used on Android.219void Core_RunLoop(GraphicsContext *ctx) {220if (windowHidden && g_Config.bPauseWhenMinimized) {221sleep_ms(16);222return;223}224225NativeFrame(ctx);226}227228void Core_DoSingleStep() {229std::lock_guard<std::mutex> guard(m_hStepMutex);230singleStepPending = true;231m_StepCond.notify_all();232}233234void Core_UpdateSingleStep() {235std::lock_guard<std::mutex> guard(m_hStepMutex);236m_StepCond.notify_all();237}238239void Core_SingleStep() {240Core_ResetException();241currentMIPS->SingleStep();242if (coreState == CORE_STEPPING)243steppingCounter++;244}245246static inline bool Core_WaitStepping() {247std::unique_lock<std::mutex> guard(m_hStepMutex);248// We only wait 16ms so that we can still draw UI or react to events.249double sleepStart = time_now_d();250if (!singleStepPending && coreState == CORE_STEPPING)251m_StepCond.wait_for(guard, std::chrono::milliseconds(16));252double sleepEnd = time_now_d();253DisplayNotifySleep(sleepEnd - sleepStart);254255bool result = singleStepPending;256singleStepPending = false;257return result;258}259260void Core_ProcessStepping() {261Core_StateProcessed();262263// Check if there's any pending save state actions.264SaveState::Process();265if (coreState != CORE_STEPPING) {266return;267}268269// Or any GPU actions.270GPUStepping::SingleStep();271272// We're not inside jit now, so it's safe to clear the breakpoints.273static int lastSteppingCounter = -1;274if (lastSteppingCounter != steppingCounter) {275CBreakPoints::ClearTemporaryBreakPoints();276System_Notify(SystemNotification::DISASSEMBLY);277System_Notify(SystemNotification::MEM_VIEW);278lastSteppingCounter = steppingCounter;279}280281// Need to check inside the lock to avoid races.282bool doStep = Core_WaitStepping();283284// We may still be stepping without singleStepPending to process a save state.285if (doStep && coreState == CORE_STEPPING) {286Core_SingleStep();287// Update disasm dialog.288System_Notify(SystemNotification::DISASSEMBLY);289System_Notify(SystemNotification::MEM_VIEW);290}291}292293// Many platforms, like Android, do not call this function but handle things on their own.294// Instead they simply call NativeFrame directly.295bool Core_Run(GraphicsContext *ctx) {296System_Notify(SystemNotification::DISASSEMBLY);297while (true) {298if (GetUIState() != UISTATE_INGAME) {299Core_StateProcessed();300if (GetUIState() == UISTATE_EXIT) {301// Not sure why we do a final frame here?302NativeFrame(ctx);303return false;304}305Core_RunLoop(ctx);306continue;307}308309switch (coreState) {310case CORE_RUNNING:311case CORE_STEPPING:312Core_StateProcessed();313// enter a fast runloop314Core_RunLoop(ctx);315if (coreState == CORE_POWERDOWN) {316Core_StateProcessed();317return true;318}319break;320321case CORE_POWERUP:322case CORE_POWERDOWN:323case CORE_BOOT_ERROR:324case CORE_RUNTIME_ERROR:325// Exit loop!!326Core_StateProcessed();327return true;328329case CORE_NEXTFRAME:330return true;331}332}333}334335void Core_EnableStepping(bool step, const char *reason, u32 relatedAddress) {336if (step) {337// Stop the tracer338mipsTracer.stop_tracing();339340Core_UpdateState(CORE_STEPPING);341steppingCounter++;342_assert_msg_(reason != nullptr, "No reason specified for break");343steppingReason = reason;344steppingAddress = relatedAddress;345} else {346// Clear the exception if we resume.347Core_ResetException();348coreState = CORE_RUNNING;349coreStatePending = false;350m_StepCond.notify_all();351}352System_Notify(SystemNotification::DEBUG_MODE_CHANGE);353}354355bool Core_NextFrame() {356if (coreState == CORE_RUNNING) {357coreState = CORE_NEXTFRAME;358return true;359} else {360return false;361}362}363364int Core_GetSteppingCounter() {365return steppingCounter;366}367368SteppingReason Core_GetSteppingReason() {369SteppingReason r;370r.reason = steppingReason;371r.relatedAddress = steppingAddress;372return r;373}374375const char *ExceptionTypeAsString(MIPSExceptionType type) {376switch (type) {377case MIPSExceptionType::MEMORY: return "Invalid Memory Access";378case MIPSExceptionType::BREAK: return "Break";379case MIPSExceptionType::BAD_EXEC_ADDR: return "Bad Execution Address";380default: return "N/A";381}382}383384const char *MemoryExceptionTypeAsString(MemoryExceptionType type) {385switch (type) {386case MemoryExceptionType::UNKNOWN: return "Unknown";387case MemoryExceptionType::READ_WORD: return "Read Word";388case MemoryExceptionType::WRITE_WORD: return "Write Word";389case MemoryExceptionType::READ_BLOCK: return "Read Block";390case MemoryExceptionType::WRITE_BLOCK: return "Read/Write Block";391case MemoryExceptionType::ALIGNMENT: return "Alignment";392default:393return "N/A";394}395}396397const char *ExecExceptionTypeAsString(ExecExceptionType type) {398switch (type) {399case ExecExceptionType::JUMP: return "CPU Jump";400case ExecExceptionType::THREAD: return "Thread switch";401default:402return "N/A";403}404}405406void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type) {407const char *desc = MemoryExceptionTypeAsString(type);408// In jit, we only flush PC when bIgnoreBadMemAccess is off.409if ((g_Config.iCpuCore == (int)CPUCore::JIT || g_Config.iCpuCore == (int)CPUCore::JIT_IR) && g_Config.bIgnoreBadMemAccess) {410WARN_LOG(Log::MemMap, "%s: Invalid access at %08x (size %08x)", desc, address, accessSize);411} else {412WARN_LOG(Log::MemMap, "%s: Invalid access at %08x (size %08x) PC %08x LR %08x", desc, address, accessSize, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);413}414415if (!g_Config.bIgnoreBadMemAccess) {416// Try to fetch a call stack, to start with.417std::vector<MIPSStackWalk::StackFrame> stackFrames = WalkCurrentStack(-1);418std::string stackTrace = FormatStackTrace(stackFrames);419WARN_LOG(Log::MemMap, "\n%s", stackTrace.c_str());420421MIPSExceptionInfo &e = g_exceptionInfo;422e = {};423e.type = MIPSExceptionType::MEMORY;424e.info.clear();425e.memory_type = type;426e.address = address;427e.accessSize = accessSize;428e.stackTrace = stackTrace;429e.pc = pc;430Core_EnableStepping(true, "memory.exception", address);431}432}433434void Core_MemoryExceptionInfo(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo, bool forceReport) {435const char *desc = MemoryExceptionTypeAsString(type);436// In jit, we only flush PC when bIgnoreBadMemAccess is off.437if ((g_Config.iCpuCore == (int)CPUCore::JIT || g_Config.iCpuCore == (int)CPUCore::JIT_IR) && g_Config.bIgnoreBadMemAccess) {438WARN_LOG(Log::MemMap, "%s: Invalid access at %08x (size %08x). %.*s", desc, address, accessSize, (int)additionalInfo.length(), additionalInfo.data());439} else {440WARN_LOG(Log::MemMap, "%s: Invalid access at %08x (size %08x) PC %08x LR %08x %.*s", desc, address, accessSize, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA], (int)additionalInfo.length(), additionalInfo.data());441}442443if (!g_Config.bIgnoreBadMemAccess || forceReport) {444// Try to fetch a call stack, to start with.445std::vector<MIPSStackWalk::StackFrame> stackFrames = WalkCurrentStack(-1);446std::string stackTrace = FormatStackTrace(stackFrames);447WARN_LOG(Log::MemMap, "\n%s", stackTrace.c_str());448449MIPSExceptionInfo &e = g_exceptionInfo;450e = {};451e.type = MIPSExceptionType::MEMORY;452e.info = additionalInfo;453e.memory_type = type;454e.address = address;455e.accessSize = accessSize;456e.stackTrace = stackTrace;457e.pc = pc;458Core_EnableStepping(true, "memory.exception", address);459}460}461462// Can't be ignored463void Core_ExecException(u32 address, u32 pc, ExecExceptionType type) {464const char *desc = ExecExceptionTypeAsString(type);465WARN_LOG(Log::MemMap, "%s: Invalid exec address %08x PC %08x LR %08x", desc, address, pc, currentMIPS->r[MIPS_REG_RA]);466467MIPSExceptionInfo &e = g_exceptionInfo;468e = {};469e.type = MIPSExceptionType::BAD_EXEC_ADDR;470e.info.clear();471e.exec_type = type;472e.address = address;473e.accessSize = 4; // size of an instruction474e.pc = pc;475// This just records the closest value that could be useful as reference.476e.ra = currentMIPS->r[MIPS_REG_RA];477Core_EnableStepping(true, "cpu.exception", address);478}479480void Core_Break(u32 pc) {481ERROR_LOG(Log::CPU, "BREAK!");482483MIPSExceptionInfo &e = g_exceptionInfo;484e = {};485e.type = MIPSExceptionType::BREAK;486e.info.clear();487e.pc = pc;488489if (!g_Config.bIgnoreBadMemAccess) {490Core_EnableStepping(true, "cpu.breakInstruction", currentMIPS->pc);491}492}493494void Core_ResetException() {495g_exceptionInfo.type = MIPSExceptionType::NONE;496}497498const MIPSExceptionInfo &Core_GetExceptionInfo() {499return g_exceptionInfo;500}501502503