CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/Core.cpp
Views: 1401
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include "ppsspp_config.h"
19
20
#include <set>
21
#include <chrono>
22
#include <cstdint>
23
#include <mutex>
24
#include <condition_variable>
25
26
#include "Common/System/NativeApp.h"
27
#include "Common/System/System.h"
28
#include "Common/System/Display.h"
29
#include "Common/TimeUtil.h"
30
#include "Common/Thread/ThreadUtil.h"
31
#include "Common/Profiler/Profiler.h"
32
33
#include "Common/GraphicsContext.h"
34
#include "Common/Log.h"
35
#include "Core/Core.h"
36
#include "Core/Config.h"
37
#include "Core/MemMap.h"
38
#include "Core/SaveState.h"
39
#include "Core/System.h"
40
#include "Core/MemFault.h"
41
#include "Core/Debugger/Breakpoints.h"
42
#include "Core/HW/Display.h"
43
#include "Core/MIPS/MIPS.h"
44
#include "Core/HLE/sceNetAdhoc.h"
45
#include "GPU/Debugger/Stepping.h"
46
#include "Core/MIPS/MIPSTracer.h"
47
48
#ifdef _WIN32
49
#include "Common/CommonWindows.h"
50
#include "Windows/InputDevice.h"
51
#endif
52
53
static std::condition_variable m_StepCond;
54
static std::mutex m_hStepMutex;
55
static std::condition_variable m_InactiveCond;
56
static std::mutex m_hInactiveMutex;
57
static bool singleStepPending = false;
58
static int steppingCounter = 0;
59
static const char *steppingReason = "";
60
static uint32_t steppingAddress = 0;
61
static std::set<CoreLifecycleFunc> lifecycleFuncs;
62
static std::set<CoreStopRequestFunc> stopFuncs;
63
static bool windowHidden = false;
64
static bool powerSaving = false;
65
66
static MIPSExceptionInfo g_exceptionInfo;
67
68
void Core_SetGraphicsContext(GraphicsContext *ctx) {
69
PSP_CoreParameter().graphicsContext = ctx;
70
}
71
72
void Core_NotifyWindowHidden(bool hidden) {
73
windowHidden = hidden;
74
// TODO: Wait until we can react?
75
}
76
77
bool Core_IsWindowHidden() {
78
return windowHidden;
79
}
80
81
void Core_ListenLifecycle(CoreLifecycleFunc func) {
82
lifecycleFuncs.insert(func);
83
}
84
85
void Core_NotifyLifecycle(CoreLifecycle stage) {
86
if (stage == CoreLifecycle::STARTING) {
87
Core_ResetException();
88
}
89
90
for (auto func : lifecycleFuncs) {
91
func(stage);
92
}
93
}
94
95
void Core_ListenStopRequest(CoreStopRequestFunc func) {
96
stopFuncs.insert(func);
97
}
98
99
void Core_Stop() {
100
Core_ResetException();
101
Core_UpdateState(CORE_POWERDOWN);
102
for (auto func : stopFuncs) {
103
func();
104
}
105
}
106
107
bool Core_ShouldRunBehind() {
108
// Enforce run-behind if ad-hoc connected
109
return g_Config.bRunBehindPauseMenu || Core_MustRunBehind();
110
}
111
112
bool Core_MustRunBehind() {
113
return __NetAdhocConnected();
114
}
115
116
bool Core_IsStepping() {
117
return coreState == CORE_STEPPING || coreState == CORE_POWERDOWN;
118
}
119
120
bool Core_IsActive() {
121
return coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME || coreStatePending;
122
}
123
124
bool Core_IsInactive() {
125
return coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME && !coreStatePending;
126
}
127
128
static inline void Core_StateProcessed() {
129
if (coreStatePending) {
130
std::lock_guard<std::mutex> guard(m_hInactiveMutex);
131
coreStatePending = false;
132
m_InactiveCond.notify_all();
133
}
134
}
135
136
void Core_WaitInactive() {
137
while (Core_IsActive() && !GPUStepping::IsStepping()) {
138
std::unique_lock<std::mutex> guard(m_hInactiveMutex);
139
m_InactiveCond.wait(guard);
140
}
141
}
142
143
void Core_WaitInactive(int milliseconds) {
144
if (Core_IsActive() && !GPUStepping::IsStepping()) {
145
std::unique_lock<std::mutex> guard(m_hInactiveMutex);
146
m_InactiveCond.wait_for(guard, std::chrono::milliseconds(milliseconds));
147
}
148
}
149
150
void Core_SetPowerSaving(bool mode) {
151
powerSaving = mode;
152
}
153
154
bool Core_GetPowerSaving() {
155
return powerSaving;
156
}
157
158
static bool IsWindowSmall(int pixelWidth, int pixelHeight) {
159
// Can't take this from config as it will not be set if windows is maximized.
160
int w = (int)(pixelWidth * g_display.dpi_scale_x);
161
int h = (int)(pixelHeight * g_display.dpi_scale_y);
162
return g_Config.IsPortrait() ? (h < 480 + 80) : (w < 480 + 80);
163
}
164
165
// TODO: Feels like this belongs elsewhere.
166
bool UpdateScreenScale(int width, int height) {
167
bool smallWindow;
168
169
float g_logical_dpi = System_GetPropertyFloat(SYSPROP_DISPLAY_LOGICAL_DPI);
170
g_display.dpi = System_GetPropertyFloat(SYSPROP_DISPLAY_DPI);
171
172
if (g_display.dpi < 0.0f) {
173
g_display.dpi = 96.0f;
174
}
175
if (g_logical_dpi < 0.0f) {
176
g_logical_dpi = 96.0f;
177
}
178
179
g_display.dpi_scale_x = g_logical_dpi / g_display.dpi;
180
g_display.dpi_scale_y = g_logical_dpi / g_display.dpi;
181
g_display.dpi_scale_real_x = g_display.dpi_scale_x;
182
g_display.dpi_scale_real_y = g_display.dpi_scale_y;
183
184
smallWindow = IsWindowSmall(width, height);
185
if (smallWindow) {
186
g_display.dpi /= 2.0f;
187
g_display.dpi_scale_x *= 2.0f;
188
g_display.dpi_scale_y *= 2.0f;
189
}
190
g_display.pixel_in_dps_x = 1.0f / g_display.dpi_scale_x;
191
g_display.pixel_in_dps_y = 1.0f / g_display.dpi_scale_y;
192
193
int new_dp_xres = (int)(width * g_display.dpi_scale_x);
194
int new_dp_yres = (int)(height * g_display.dpi_scale_y);
195
196
bool dp_changed = new_dp_xres != g_display.dp_xres || new_dp_yres != g_display.dp_yres;
197
bool px_changed = g_display.pixel_xres != width || g_display.pixel_yres != height;
198
199
if (dp_changed || px_changed) {
200
g_display.dp_xres = new_dp_xres;
201
g_display.dp_yres = new_dp_yres;
202
g_display.pixel_xres = width;
203
g_display.pixel_yres = height;
204
NativeResized();
205
return true;
206
}
207
return false;
208
}
209
210
// Used by Windows, SDL, Qt.
211
void UpdateRunLoop(GraphicsContext *ctx) {
212
NativeFrame(ctx);
213
if (windowHidden && g_Config.bPauseWhenMinimized) {
214
sleep_ms(16);
215
return;
216
}
217
}
218
219
// Note: not used on Android.
220
void Core_RunLoop(GraphicsContext *ctx) {
221
if (windowHidden && g_Config.bPauseWhenMinimized) {
222
sleep_ms(16);
223
return;
224
}
225
226
NativeFrame(ctx);
227
}
228
229
void Core_DoSingleStep() {
230
std::lock_guard<std::mutex> guard(m_hStepMutex);
231
singleStepPending = true;
232
m_StepCond.notify_all();
233
}
234
235
void Core_UpdateSingleStep() {
236
std::lock_guard<std::mutex> guard(m_hStepMutex);
237
m_StepCond.notify_all();
238
}
239
240
void Core_SingleStep() {
241
Core_ResetException();
242
currentMIPS->SingleStep();
243
if (coreState == CORE_STEPPING)
244
steppingCounter++;
245
}
246
247
static inline bool Core_WaitStepping() {
248
std::unique_lock<std::mutex> guard(m_hStepMutex);
249
// We only wait 16ms so that we can still draw UI or react to events.
250
double sleepStart = time_now_d();
251
if (!singleStepPending && coreState == CORE_STEPPING)
252
m_StepCond.wait_for(guard, std::chrono::milliseconds(16));
253
double sleepEnd = time_now_d();
254
DisplayNotifySleep(sleepEnd - sleepStart);
255
256
bool result = singleStepPending;
257
singleStepPending = false;
258
return result;
259
}
260
261
void Core_ProcessStepping() {
262
Core_StateProcessed();
263
264
// Check if there's any pending save state actions.
265
SaveState::Process();
266
if (coreState != CORE_STEPPING) {
267
return;
268
}
269
270
// Or any GPU actions.
271
GPUStepping::SingleStep();
272
273
// We're not inside jit now, so it's safe to clear the breakpoints.
274
static int lastSteppingCounter = -1;
275
if (lastSteppingCounter != steppingCounter) {
276
CBreakPoints::ClearTemporaryBreakPoints();
277
System_Notify(SystemNotification::DISASSEMBLY);
278
System_Notify(SystemNotification::MEM_VIEW);
279
lastSteppingCounter = steppingCounter;
280
}
281
282
// Need to check inside the lock to avoid races.
283
bool doStep = Core_WaitStepping();
284
285
// We may still be stepping without singleStepPending to process a save state.
286
if (doStep && coreState == CORE_STEPPING) {
287
Core_SingleStep();
288
// Update disasm dialog.
289
System_Notify(SystemNotification::DISASSEMBLY);
290
System_Notify(SystemNotification::MEM_VIEW);
291
}
292
}
293
294
// Many platforms, like Android, do not call this function but handle things on their own.
295
// Instead they simply call NativeFrame directly.
296
bool Core_Run(GraphicsContext *ctx) {
297
System_Notify(SystemNotification::DISASSEMBLY);
298
while (true) {
299
if (GetUIState() != UISTATE_INGAME) {
300
Core_StateProcessed();
301
if (GetUIState() == UISTATE_EXIT) {
302
// Not sure why we do a final frame here?
303
NativeFrame(ctx);
304
return false;
305
}
306
Core_RunLoop(ctx);
307
continue;
308
}
309
310
switch (coreState) {
311
case CORE_RUNNING:
312
case CORE_STEPPING:
313
Core_StateProcessed();
314
// enter a fast runloop
315
Core_RunLoop(ctx);
316
if (coreState == CORE_POWERDOWN) {
317
Core_StateProcessed();
318
return true;
319
}
320
break;
321
322
case CORE_POWERUP:
323
case CORE_POWERDOWN:
324
case CORE_BOOT_ERROR:
325
case CORE_RUNTIME_ERROR:
326
// Exit loop!!
327
Core_StateProcessed();
328
return true;
329
330
case CORE_NEXTFRAME:
331
return true;
332
}
333
}
334
}
335
336
void Core_EnableStepping(bool step, const char *reason, u32 relatedAddress) {
337
if (step) {
338
// Stop the tracer
339
mipsTracer.stop_tracing();
340
341
Core_UpdateState(CORE_STEPPING);
342
steppingCounter++;
343
_assert_msg_(reason != nullptr, "No reason specified for break");
344
steppingReason = reason;
345
steppingAddress = relatedAddress;
346
} else {
347
// Clear the exception if we resume.
348
Core_ResetException();
349
coreState = CORE_RUNNING;
350
coreStatePending = false;
351
m_StepCond.notify_all();
352
}
353
System_Notify(SystemNotification::DEBUG_MODE_CHANGE);
354
}
355
356
bool Core_NextFrame() {
357
if (coreState == CORE_RUNNING) {
358
coreState = CORE_NEXTFRAME;
359
return true;
360
} else {
361
return false;
362
}
363
}
364
365
int Core_GetSteppingCounter() {
366
return steppingCounter;
367
}
368
369
SteppingReason Core_GetSteppingReason() {
370
SteppingReason r;
371
r.reason = steppingReason;
372
r.relatedAddress = steppingAddress;
373
return r;
374
}
375
376
const char *ExceptionTypeAsString(MIPSExceptionType type) {
377
switch (type) {
378
case MIPSExceptionType::MEMORY: return "Invalid Memory Access";
379
case MIPSExceptionType::BREAK: return "Break";
380
case MIPSExceptionType::BAD_EXEC_ADDR: return "Bad Execution Address";
381
default: return "N/A";
382
}
383
}
384
385
const char *MemoryExceptionTypeAsString(MemoryExceptionType type) {
386
switch (type) {
387
case MemoryExceptionType::UNKNOWN: return "Unknown";
388
case MemoryExceptionType::READ_WORD: return "Read Word";
389
case MemoryExceptionType::WRITE_WORD: return "Write Word";
390
case MemoryExceptionType::READ_BLOCK: return "Read Block";
391
case MemoryExceptionType::WRITE_BLOCK: return "Read/Write Block";
392
case MemoryExceptionType::ALIGNMENT: return "Alignment";
393
default:
394
return "N/A";
395
}
396
}
397
398
const char *ExecExceptionTypeAsString(ExecExceptionType type) {
399
switch (type) {
400
case ExecExceptionType::JUMP: return "CPU Jump";
401
case ExecExceptionType::THREAD: return "Thread switch";
402
default:
403
return "N/A";
404
}
405
}
406
407
void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type) {
408
const char *desc = MemoryExceptionTypeAsString(type);
409
// In jit, we only flush PC when bIgnoreBadMemAccess is off.
410
if ((g_Config.iCpuCore == (int)CPUCore::JIT || g_Config.iCpuCore == (int)CPUCore::JIT_IR) && g_Config.bIgnoreBadMemAccess) {
411
WARN_LOG(Log::MemMap, "%s: Invalid access at %08x (size %08x)", desc, address, accessSize);
412
} else {
413
WARN_LOG(Log::MemMap, "%s: Invalid access at %08x (size %08x) PC %08x LR %08x", desc, address, accessSize, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
414
}
415
416
if (!g_Config.bIgnoreBadMemAccess) {
417
// Try to fetch a call stack, to start with.
418
std::vector<MIPSStackWalk::StackFrame> stackFrames = WalkCurrentStack(-1);
419
std::string stackTrace = FormatStackTrace(stackFrames);
420
WARN_LOG(Log::MemMap, "\n%s", stackTrace.c_str());
421
422
MIPSExceptionInfo &e = g_exceptionInfo;
423
e = {};
424
e.type = MIPSExceptionType::MEMORY;
425
e.info.clear();
426
e.memory_type = type;
427
e.address = address;
428
e.accessSize = accessSize;
429
e.stackTrace = stackTrace;
430
e.pc = pc;
431
Core_EnableStepping(true, "memory.exception", address);
432
}
433
}
434
435
void Core_MemoryExceptionInfo(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo, bool forceReport) {
436
const char *desc = MemoryExceptionTypeAsString(type);
437
// In jit, we only flush PC when bIgnoreBadMemAccess is off.
438
if ((g_Config.iCpuCore == (int)CPUCore::JIT || g_Config.iCpuCore == (int)CPUCore::JIT_IR) && g_Config.bIgnoreBadMemAccess) {
439
WARN_LOG(Log::MemMap, "%s: Invalid access at %08x (size %08x). %.*s", desc, address, accessSize, (int)additionalInfo.length(), additionalInfo.data());
440
} else {
441
WARN_LOG(Log::MemMap, "%s: Invalid access at %08x (size %08x) PC %08x LR %08x %.*s", desc, address, accessSize, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA], (int)additionalInfo.length(), additionalInfo.data());
442
}
443
444
if (!g_Config.bIgnoreBadMemAccess || forceReport) {
445
// Try to fetch a call stack, to start with.
446
std::vector<MIPSStackWalk::StackFrame> stackFrames = WalkCurrentStack(-1);
447
std::string stackTrace = FormatStackTrace(stackFrames);
448
WARN_LOG(Log::MemMap, "\n%s", stackTrace.c_str());
449
450
MIPSExceptionInfo &e = g_exceptionInfo;
451
e = {};
452
e.type = MIPSExceptionType::MEMORY;
453
e.info = additionalInfo;
454
e.memory_type = type;
455
e.address = address;
456
e.accessSize = accessSize;
457
e.stackTrace = stackTrace;
458
e.pc = pc;
459
Core_EnableStepping(true, "memory.exception", address);
460
}
461
}
462
463
// Can't be ignored
464
void Core_ExecException(u32 address, u32 pc, ExecExceptionType type) {
465
const char *desc = ExecExceptionTypeAsString(type);
466
WARN_LOG(Log::MemMap, "%s: Invalid exec address %08x PC %08x LR %08x", desc, address, pc, currentMIPS->r[MIPS_REG_RA]);
467
468
MIPSExceptionInfo &e = g_exceptionInfo;
469
e = {};
470
e.type = MIPSExceptionType::BAD_EXEC_ADDR;
471
e.info.clear();
472
e.exec_type = type;
473
e.address = address;
474
e.accessSize = 4; // size of an instruction
475
e.pc = pc;
476
// This just records the closest value that could be useful as reference.
477
e.ra = currentMIPS->r[MIPS_REG_RA];
478
Core_EnableStepping(true, "cpu.exception", address);
479
}
480
481
void Core_Break(u32 pc) {
482
ERROR_LOG(Log::CPU, "BREAK!");
483
484
MIPSExceptionInfo &e = g_exceptionInfo;
485
e = {};
486
e.type = MIPSExceptionType::BREAK;
487
e.info.clear();
488
e.pc = pc;
489
490
if (!g_Config.bIgnoreBadMemAccess) {
491
Core_EnableStepping(true, "cpu.breakInstruction", currentMIPS->pc);
492
}
493
}
494
495
void Core_ResetException() {
496
g_exceptionInfo.type = MIPSExceptionType::NONE;
497
}
498
499
const MIPSExceptionInfo &Core_GetExceptionInfo() {
500
return g_exceptionInfo;
501
}
502
503