Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/core/cpu_core.cpp
4802 views
1
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "cpu_core.h"
5
#include "bus.h"
6
#include "cpu_code_cache_private.h"
7
#include "cpu_core_private.h"
8
#include "cpu_disasm.h"
9
#include "cpu_pgxp.h"
10
#include "gte.h"
11
#include "host.h"
12
#include "pcdrv.h"
13
#include "pio.h"
14
#include "settings.h"
15
#include "system.h"
16
#include "timing_event.h"
17
18
#include "util/state_wrapper.h"
19
20
#include "common/align.h"
21
#include "common/fastjmp.h"
22
#include "common/file_system.h"
23
#include "common/log.h"
24
#include "common/path.h"
25
26
#include "fmt/format.h"
27
28
#include <cstdio>
29
30
LOG_CHANNEL(CPU);
31
32
namespace CPU {
33
enum class ExecutionBreakType
34
{
35
None,
36
ExecuteOneInstruction,
37
SingleStep,
38
Breakpoint,
39
};
40
41
static void UpdateLoadDelay();
42
static void Branch(u32 target);
43
static void FlushLoadDelay();
44
static void FlushPipeline();
45
46
static u32 GetExceptionVector(bool debug_exception = false);
47
static void RaiseException(u32 CAUSE_bits, u32 EPC, u32 vector);
48
49
static u32 ReadReg(Reg rs);
50
static void WriteReg(Reg rd, u32 value);
51
static void WriteRegDelayed(Reg rd, u32 value);
52
53
static void DispatchCop0Breakpoint();
54
static bool IsCop0ExecutionBreakpointUnmasked();
55
static void Cop0ExecutionBreakpointCheck();
56
template<MemoryAccessType type>
57
static void Cop0DataBreakpointCheck(VirtualMemoryAddress address);
58
59
static BreakpointList& GetBreakpointList(BreakpointType type);
60
static bool CheckBreakpointList(BreakpointType type, VirtualMemoryAddress address);
61
static void ExecutionBreakpointCheck();
62
template<MemoryAccessType type>
63
static void MemoryBreakpointCheck(VirtualMemoryAddress address);
64
65
#ifdef _DEBUG
66
static void TracePrintInstruction();
67
#endif
68
69
static void DisassembleAndPrint(u32 addr, bool regs, const char* prefix);
70
static void PrintInstruction(u32 bits, u32 pc, bool regs, const char* prefix);
71
static void LogInstruction(u32 bits, u32 pc, bool regs);
72
73
static void HandleWriteSyscall();
74
static void HandlePutcSyscall();
75
static void HandlePutsSyscall();
76
77
static void CheckForExecutionModeChange();
78
[[noreturn]] static void ExecuteInterpreter();
79
80
template<PGXPMode pgxp_mode, bool debug>
81
static void ExecuteInstruction();
82
83
template<PGXPMode pgxp_mode, bool debug>
84
[[noreturn]] static void ExecuteImpl();
85
86
static bool FetchInstruction();
87
static bool FetchInstructionForInterpreterFallback();
88
template<bool add_ticks, bool icache_read = false, u32 word_count = 1, bool raise_exceptions>
89
static bool DoInstructionRead(PhysicalMemoryAddress address, u32* data);
90
template<MemoryAccessType type, MemoryAccessSize size>
91
static bool DoSafeMemoryAccess(VirtualMemoryAddress address, u32& value);
92
template<MemoryAccessType type, MemoryAccessSize size>
93
static bool DoAlignmentCheck(VirtualMemoryAddress address);
94
static bool ReadMemoryByte(VirtualMemoryAddress addr, u8* value);
95
static bool ReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value);
96
static bool ReadMemoryWord(VirtualMemoryAddress addr, u32* value);
97
static bool WriteMemoryByte(VirtualMemoryAddress addr, u32 value);
98
static bool WriteMemoryHalfWord(VirtualMemoryAddress addr, u32 value);
99
static bool WriteMemoryWord(VirtualMemoryAddress addr, u32 value);
100
101
constinit State g_state;
102
bool TRACE_EXECUTION = false;
103
104
static fastjmp_buf s_jmp_buf;
105
106
static std::FILE* s_log_file = nullptr;
107
static bool s_log_file_opened = false;
108
static bool s_trace_to_log = false;
109
110
static constexpr u32 INVALID_BREAKPOINT_PC = UINT32_C(0xFFFFFFFF);
111
static std::array<std::vector<Breakpoint>, static_cast<u32>(BreakpointType::Count)> s_breakpoints;
112
static u32 s_breakpoint_counter = 1;
113
static u32 s_last_breakpoint_check_pc = INVALID_BREAKPOINT_PC;
114
static CPUExecutionMode s_current_execution_mode = CPUExecutionMode::Interpreter;
115
static ExecutionBreakType s_break_type = ExecutionBreakType::None;
116
} // namespace CPU
117
118
bool CPU::IsTraceEnabled()
119
{
120
return s_trace_to_log;
121
}
122
123
void CPU::StartTrace()
124
{
125
if (s_trace_to_log)
126
return;
127
128
s_trace_to_log = true;
129
if (UpdateDebugDispatcherFlag())
130
System::InterruptExecution();
131
}
132
133
void CPU::StopTrace()
134
{
135
if (!s_trace_to_log)
136
return;
137
138
if (s_log_file)
139
std::fclose(s_log_file);
140
141
s_log_file_opened = false;
142
s_trace_to_log = false;
143
if (UpdateDebugDispatcherFlag())
144
System::InterruptExecution();
145
}
146
147
void CPU::WriteToExecutionLog(const char* format, ...)
148
{
149
if (!s_log_file_opened) [[unlikely]]
150
{
151
s_log_file = FileSystem::OpenCFile(Path::Combine(EmuFolders::DataRoot, "cpu_log.txt").c_str(), "wb");
152
s_log_file_opened = true;
153
}
154
155
if (s_log_file)
156
{
157
std::va_list ap;
158
va_start(ap, format);
159
std::vfprintf(s_log_file, format, ap);
160
va_end(ap);
161
162
#ifdef _DEBUG
163
std::fflush(s_log_file);
164
#endif
165
}
166
}
167
168
void CPU::Initialize()
169
{
170
// From nocash spec.
171
g_state.cop0_regs.PRID = UINT32_C(0x00000002);
172
173
s_current_execution_mode = g_settings.cpu_execution_mode;
174
g_state.using_debug_dispatcher = false;
175
g_state.using_interpreter = (s_current_execution_mode == CPUExecutionMode::Interpreter);
176
for (BreakpointList& bps : s_breakpoints)
177
bps.clear();
178
s_breakpoint_counter = 1;
179
s_last_breakpoint_check_pc = INVALID_BREAKPOINT_PC;
180
s_break_type = ExecutionBreakType::None;
181
182
UpdateMemoryPointers();
183
UpdateDebugDispatcherFlag();
184
}
185
186
void CPU::Shutdown()
187
{
188
ClearBreakpoints();
189
StopTrace();
190
}
191
192
void CPU::Reset()
193
{
194
g_state.exception_raised = false;
195
g_state.bus_error = false;
196
197
g_state.regs = {};
198
199
g_state.cop0_regs.BPC = 0;
200
g_state.cop0_regs.BDA = 0;
201
g_state.cop0_regs.TAR = 0;
202
g_state.cop0_regs.BadVaddr = 0;
203
g_state.cop0_regs.BDAM = 0;
204
g_state.cop0_regs.BPCM = 0;
205
g_state.cop0_regs.EPC = 0;
206
g_state.cop0_regs.dcic.bits = 0;
207
g_state.cop0_regs.sr.bits = 0;
208
g_state.cop0_regs.cause.bits = 0;
209
210
ClearICache();
211
UpdateMemoryPointers();
212
UpdateDebugDispatcherFlag();
213
214
GTE::Reset();
215
216
if (g_settings.gpu_pgxp_enable)
217
PGXP::Reset();
218
219
// This consumes cycles, so do it first.
220
SetPC(RESET_VECTOR);
221
222
g_state.downcount = 0;
223
g_state.pending_ticks = 0;
224
g_state.gte_completion_tick = 0;
225
g_state.muldiv_completion_tick = 0;
226
}
227
228
bool CPU::DoState(StateWrapper& sw)
229
{
230
sw.Do(&g_state.pending_ticks);
231
sw.Do(&g_state.downcount);
232
sw.DoEx(&g_state.gte_completion_tick, 78, static_cast<u32>(0));
233
sw.DoEx(&g_state.muldiv_completion_tick, 80, static_cast<u32>(0));
234
sw.DoArray(g_state.regs.r, static_cast<u32>(Reg::count));
235
sw.Do(&g_state.pc);
236
sw.Do(&g_state.npc);
237
sw.Do(&g_state.cop0_regs.BPC);
238
sw.Do(&g_state.cop0_regs.BDA);
239
sw.Do(&g_state.cop0_regs.TAR);
240
sw.Do(&g_state.cop0_regs.BadVaddr);
241
sw.Do(&g_state.cop0_regs.BDAM);
242
sw.Do(&g_state.cop0_regs.BPCM);
243
sw.Do(&g_state.cop0_regs.EPC);
244
sw.Do(&g_state.cop0_regs.PRID);
245
sw.Do(&g_state.cop0_regs.sr.bits);
246
sw.Do(&g_state.cop0_regs.cause.bits);
247
sw.Do(&g_state.cop0_regs.dcic.bits);
248
sw.Do(&g_state.next_instruction.bits);
249
sw.Do(&g_state.current_instruction.bits);
250
sw.Do(&g_state.current_instruction_pc);
251
sw.Do(&g_state.current_instruction_in_branch_delay_slot);
252
sw.Do(&g_state.current_instruction_was_branch_taken);
253
sw.Do(&g_state.next_instruction_is_branch_delay_slot);
254
sw.Do(&g_state.branch_was_taken);
255
sw.Do(&g_state.exception_raised);
256
sw.DoEx(&g_state.bus_error, 61, false);
257
if (sw.GetVersion() < 59) [[unlikely]]
258
{
259
bool interrupt_delay;
260
sw.Do(&interrupt_delay);
261
}
262
sw.Do(&g_state.load_delay_reg);
263
sw.Do(&g_state.load_delay_value);
264
sw.Do(&g_state.next_load_delay_reg);
265
sw.Do(&g_state.next_load_delay_value);
266
267
// Compatibility with old states.
268
if (sw.GetVersion() < 59) [[unlikely]]
269
{
270
g_state.load_delay_reg =
271
static_cast<Reg>(std::min(static_cast<u8>(g_state.load_delay_reg), static_cast<u8>(Reg::count)));
272
g_state.next_load_delay_reg =
273
static_cast<Reg>(std::min(static_cast<u8>(g_state.load_delay_reg), static_cast<u8>(Reg::count)));
274
}
275
276
sw.Do(&g_state.cache_control.bits);
277
sw.DoBytes(g_state.scratchpad.data(), g_state.scratchpad.size());
278
279
if (!GTE::DoState(sw)) [[unlikely]]
280
return false;
281
282
if (sw.GetVersion() < 48) [[unlikely]]
283
{
284
DebugAssert(sw.IsReading());
285
ClearICache();
286
}
287
else
288
{
289
sw.Do(&g_state.icache_tags);
290
sw.Do(&g_state.icache_data);
291
}
292
293
sw.DoEx(&g_state.using_interpreter, 67, g_state.using_interpreter);
294
295
if (sw.IsReading())
296
{
297
// Trigger an execution mode change if the state was/wasn't using the interpreter.
298
s_current_execution_mode =
299
g_state.using_interpreter ?
300
CPUExecutionMode::Interpreter :
301
((g_settings.cpu_execution_mode == CPUExecutionMode::Interpreter) ? CPUExecutionMode::CachedInterpreter :
302
g_settings.cpu_execution_mode);
303
g_state.gte_completion_tick = 0;
304
g_state.muldiv_completion_tick = 0;
305
UpdateMemoryPointers();
306
UpdateDebugDispatcherFlag();
307
}
308
309
return !sw.HasError();
310
}
311
312
void CPU::SetPC(u32 new_pc)
313
{
314
DebugAssert(Common::IsAlignedPow2(new_pc, 4));
315
g_state.npc = new_pc;
316
FlushPipeline();
317
}
318
319
ALWAYS_INLINE_RELEASE void CPU::Branch(u32 target)
320
{
321
if (!Common::IsAlignedPow2(target, 4))
322
{
323
// The BadVaddr and EPC must be set to the fetching address, not the instruction about to execute.
324
g_state.cop0_regs.BadVaddr = target;
325
RaiseException(Cop0Registers::CAUSE::MakeValueForException(Exception::AdEL, false, false, 0), target);
326
return;
327
}
328
329
g_state.npc = target;
330
g_state.branch_was_taken = true;
331
}
332
333
ALWAYS_INLINE_RELEASE u32 CPU::GetExceptionVector(bool debug_exception /* = false*/)
334
{
335
const u32 base = g_state.cop0_regs.sr.BEV ? UINT32_C(0xbfc00100) : UINT32_C(0x80000000);
336
return base | (debug_exception ? UINT32_C(0x00000040) : UINT32_C(0x00000080));
337
}
338
339
ALWAYS_INLINE_RELEASE void CPU::RaiseException(u32 CAUSE_bits, u32 EPC, u32 vector)
340
{
341
g_state.cop0_regs.EPC = EPC;
342
g_state.cop0_regs.cause.bits = (g_state.cop0_regs.cause.bits & ~Cop0Registers::CAUSE::EXCEPTION_WRITE_MASK) |
343
(CAUSE_bits & Cop0Registers::CAUSE::EXCEPTION_WRITE_MASK);
344
345
#if defined(_DEBUG) || defined(_DEVEL)
346
if (g_state.cop0_regs.cause.Excode != Exception::INT && g_state.cop0_regs.cause.Excode != Exception::Syscall &&
347
g_state.cop0_regs.cause.Excode != Exception::BP)
348
{
349
DEV_LOG("Exception {} at 0x{:08X} (epc=0x{:08X}, BD={}, CE={})",
350
static_cast<u8>(g_state.cop0_regs.cause.Excode.GetValue()), g_state.current_instruction_pc,
351
g_state.cop0_regs.EPC, g_state.cop0_regs.cause.BD ? "true" : "false",
352
g_state.cop0_regs.cause.CE.GetValue());
353
DisassembleAndPrint(g_state.current_instruction_pc, 4u, 0u);
354
if (s_trace_to_log)
355
{
356
CPU::WriteToExecutionLog("Exception %u at 0x%08X (epc=0x%08X, BD=%s, CE=%u)\n",
357
static_cast<u8>(g_state.cop0_regs.cause.Excode.GetValue()),
358
g_state.current_instruction_pc, g_state.cop0_regs.EPC,
359
g_state.cop0_regs.cause.BD ? "true" : "false", g_state.cop0_regs.cause.CE.GetValue());
360
}
361
}
362
#endif
363
364
if (g_state.cop0_regs.cause.BD)
365
{
366
// TAR is set to the address which was being fetched in this instruction, or the next instruction to execute if the
367
// exception hadn't occurred in the delay slot.
368
g_state.cop0_regs.EPC -= UINT32_C(4);
369
g_state.cop0_regs.TAR = g_state.pc;
370
}
371
372
// current -> previous, switch to kernel mode and disable interrupts
373
g_state.cop0_regs.sr.mode_bits <<= 2;
374
375
// flush the pipeline - we don't want to execute the previously fetched instruction
376
g_state.npc = vector;
377
g_state.exception_raised = true;
378
FlushPipeline();
379
}
380
381
ALWAYS_INLINE_RELEASE void CPU::DispatchCop0Breakpoint()
382
{
383
// When a breakpoint address match occurs the PSX jumps to 80000040h (ie. unlike normal exceptions, not to 80000080h).
384
// The Excode value in the CAUSE register is set to 09h (same as BREAK opcode), and EPC contains the return address,
385
// as usually. One of the first things to be done in the exception handler is to disable breakpoints (eg. if the
386
// any-jump break is enabled, then it must be disabled BEFORE jumping from 80000040h to the actual exception handler).
387
RaiseException(Cop0Registers::CAUSE::MakeValueForException(
388
Exception::BP, g_state.current_instruction_in_branch_delay_slot,
389
g_state.current_instruction_was_branch_taken, g_state.current_instruction.cop.cop_n),
390
g_state.current_instruction_pc, GetExceptionVector(true));
391
}
392
393
void CPU::RaiseException(u32 CAUSE_bits, u32 EPC)
394
{
395
RaiseException(CAUSE_bits, EPC, GetExceptionVector());
396
}
397
398
void CPU::RaiseException(Exception excode)
399
{
400
RaiseException(Cop0Registers::CAUSE::MakeValueForException(excode, g_state.current_instruction_in_branch_delay_slot,
401
g_state.current_instruction_was_branch_taken,
402
g_state.current_instruction.cop.cop_n),
403
g_state.current_instruction_pc, GetExceptionVector());
404
}
405
406
void CPU::RaiseBreakException(u32 CAUSE_bits, u32 EPC, u32 instruction_bits)
407
{
408
if (g_settings.pcdrv_enable)
409
{
410
// Load delays need to be flushed, because the break HLE might read a register which
411
// is currently being loaded, and on real hardware there isn't a hazard here.
412
FlushLoadDelay();
413
414
if (PCDrv::HandleSyscall(instruction_bits, g_state.regs))
415
{
416
// immediately return
417
g_state.npc = EPC + 4;
418
FlushPipeline();
419
return;
420
}
421
}
422
else
423
{
424
WARNING_LOG("PCDrv is not enabled, break HLE will not be executed.");
425
}
426
427
// normal exception
428
RaiseException(CAUSE_bits, EPC, GetExceptionVector());
429
}
430
431
void CPU::SetIRQRequest(bool state)
432
{
433
// Only uses bit 10.
434
constexpr u32 bit = (1u << 10);
435
const u32 old_cause = g_state.cop0_regs.cause.bits;
436
g_state.cop0_regs.cause.bits = (g_state.cop0_regs.cause.bits & ~bit) | (state ? bit : 0u);
437
if (old_cause ^ g_state.cop0_regs.cause.bits && state)
438
CheckForPendingInterrupt();
439
}
440
441
ALWAYS_INLINE_RELEASE void CPU::UpdateLoadDelay()
442
{
443
// the old value is needed in case the delay slot instruction overwrites the same register
444
g_state.regs.r[static_cast<u8>(g_state.load_delay_reg)] = g_state.load_delay_value;
445
g_state.load_delay_reg = g_state.next_load_delay_reg;
446
g_state.load_delay_value = g_state.next_load_delay_value;
447
g_state.next_load_delay_reg = Reg::count;
448
}
449
450
ALWAYS_INLINE_RELEASE void CPU::FlushLoadDelay()
451
{
452
g_state.next_load_delay_reg = Reg::count;
453
g_state.regs.r[static_cast<u8>(g_state.load_delay_reg)] = g_state.load_delay_value;
454
g_state.load_delay_reg = Reg::count;
455
}
456
457
ALWAYS_INLINE_RELEASE void CPU::FlushPipeline()
458
{
459
// loads are flushed
460
FlushLoadDelay();
461
462
// not in a branch delay slot
463
g_state.branch_was_taken = false;
464
g_state.next_instruction_is_branch_delay_slot = false;
465
g_state.current_instruction_pc = g_state.pc;
466
467
// prefetch the next instruction
468
FetchInstruction();
469
470
// and set it as the next one to execute
471
g_state.current_instruction.bits = g_state.next_instruction.bits;
472
g_state.current_instruction_in_branch_delay_slot = false;
473
g_state.current_instruction_was_branch_taken = false;
474
}
475
476
ALWAYS_INLINE u32 CPU::ReadReg(Reg rs)
477
{
478
return g_state.regs.r[static_cast<u8>(rs)];
479
}
480
481
ALWAYS_INLINE void CPU::WriteReg(Reg rd, u32 value)
482
{
483
g_state.regs.r[static_cast<u8>(rd)] = value;
484
g_state.load_delay_reg = (rd == g_state.load_delay_reg) ? Reg::count : g_state.load_delay_reg;
485
486
// prevent writes to $zero from going through - better than branching/cmov
487
g_state.regs.zero = 0;
488
}
489
490
ALWAYS_INLINE_RELEASE void CPU::WriteRegDelayed(Reg rd, u32 value)
491
{
492
DebugAssert(g_state.next_load_delay_reg == Reg::count);
493
if (rd == Reg::zero)
494
return;
495
496
// double load delays ignore the first value
497
if (g_state.load_delay_reg == rd)
498
g_state.load_delay_reg = Reg::count;
499
500
// save the old value, if something else overwrites this reg we want to preserve it
501
g_state.next_load_delay_reg = rd;
502
g_state.next_load_delay_value = value;
503
}
504
505
ALWAYS_INLINE_RELEASE bool CPU::IsCop0ExecutionBreakpointUnmasked()
506
{
507
static constexpr const u32 code_address_ranges[][2] = {
508
// KUSEG
509
{Bus::RAM_BASE, Bus::RAM_BASE | Bus::RAM_8MB_MASK},
510
{Bus::BIOS_BASE, Bus::BIOS_BASE | Bus::BIOS_MASK},
511
512
// KSEG0
513
{0x80000000u | Bus::RAM_BASE, 0x80000000u | Bus::RAM_BASE | Bus::RAM_8MB_MASK},
514
{0x80000000u | Bus::BIOS_BASE, 0x80000000u | Bus::BIOS_BASE | Bus::BIOS_MASK},
515
516
// KSEG1
517
{0xA0000000u | Bus::RAM_BASE, 0xA0000000u | Bus::RAM_BASE | Bus::RAM_8MB_MASK},
518
{0xA0000000u | Bus::BIOS_BASE, 0xA0000000u | Bus::BIOS_BASE | Bus::BIOS_MASK},
519
};
520
521
const u32 bpc = g_state.cop0_regs.BPC;
522
const u32 bpcm = g_state.cop0_regs.BPCM;
523
const u32 masked_bpc = bpc & bpcm;
524
for (const auto [range_start, range_end] : code_address_ranges)
525
{
526
if (masked_bpc >= (range_start & bpcm) && masked_bpc <= (range_end & bpcm))
527
return true;
528
}
529
530
return false;
531
}
532
533
ALWAYS_INLINE_RELEASE void CPU::Cop0ExecutionBreakpointCheck()
534
{
535
if (!g_state.cop0_regs.dcic.ExecutionBreakpointsEnabled())
536
return;
537
538
const u32 pc = g_state.current_instruction_pc;
539
const u32 bpc = g_state.cop0_regs.BPC;
540
const u32 bpcm = g_state.cop0_regs.BPCM;
541
542
// Break condition is "((PC XOR BPC) AND BPCM)=0".
543
if (bpcm == 0 || ((pc ^ bpc) & bpcm) != 0u)
544
return;
545
546
DEV_LOG("Cop0 execution breakpoint at {:08X}", pc);
547
g_state.cop0_regs.dcic.status_any_break = true;
548
g_state.cop0_regs.dcic.status_bpc_code_break = true;
549
DispatchCop0Breakpoint();
550
}
551
552
template<MemoryAccessType type>
553
ALWAYS_INLINE_RELEASE void CPU::Cop0DataBreakpointCheck(VirtualMemoryAddress address)
554
{
555
if constexpr (type == MemoryAccessType::Read)
556
{
557
if (!g_state.cop0_regs.dcic.DataReadBreakpointsEnabled())
558
return;
559
}
560
else
561
{
562
if (!g_state.cop0_regs.dcic.DataWriteBreakpointsEnabled())
563
return;
564
}
565
566
// Break condition is "((addr XOR BDA) AND BDAM)=0".
567
const u32 bda = g_state.cop0_regs.BDA;
568
const u32 bdam = g_state.cop0_regs.BDAM;
569
if (bdam == 0 || ((address ^ bda) & bdam) != 0u)
570
return;
571
572
DEV_LOG("Cop0 data breakpoint for {:08X} at {:08X}", address, g_state.current_instruction_pc);
573
574
g_state.cop0_regs.dcic.status_any_break = true;
575
g_state.cop0_regs.dcic.status_bda_data_break = true;
576
if constexpr (type == MemoryAccessType::Read)
577
g_state.cop0_regs.dcic.status_bda_data_read_break = true;
578
else
579
g_state.cop0_regs.dcic.status_bda_data_write_break = true;
580
581
DispatchCop0Breakpoint();
582
}
583
584
#ifdef _DEBUG
585
586
void CPU::TracePrintInstruction()
587
{
588
const u32 pc = g_state.current_instruction_pc;
589
const u32 bits = g_state.current_instruction.bits;
590
591
TinyString instr;
592
TinyString comment;
593
DisassembleInstruction(&instr, pc, bits);
594
DisassembleInstructionComment(&comment, pc, bits);
595
if (!comment.empty())
596
{
597
for (u32 i = instr.length(); i < 30; i++)
598
instr.append(' ');
599
instr.append("; ");
600
instr.append(comment);
601
}
602
603
std::printf("%08x: %08x %s\n", pc, bits, instr.c_str());
604
}
605
606
#endif
607
608
void CPU::PrintInstruction(u32 bits, u32 pc, bool regs, const char* prefix)
609
{
610
TinyString instr;
611
DisassembleInstruction(&instr, pc, bits);
612
if (regs)
613
{
614
TinyString comment;
615
DisassembleInstructionComment(&comment, pc, bits);
616
if (!comment.empty())
617
{
618
for (u32 i = instr.length(); i < 30; i++)
619
instr.append(' ');
620
instr.append("; ");
621
instr.append(comment);
622
}
623
}
624
625
DEV_LOG("{}{:08x}: {:08x} {}", prefix, pc, bits, instr);
626
}
627
628
void CPU::LogInstruction(u32 bits, u32 pc, bool regs)
629
{
630
TinyString instr;
631
DisassembleInstruction(&instr, pc, bits);
632
if (regs)
633
{
634
TinyString comment;
635
DisassembleInstructionComment(&comment, pc, bits);
636
if (!comment.empty())
637
{
638
for (u32 i = instr.length(); i < 30; i++)
639
instr.append(' ');
640
instr.append("; ");
641
instr.append(comment);
642
}
643
}
644
645
WriteToExecutionLog("%08x: %08x %s\n", pc, bits, instr.c_str());
646
}
647
648
void CPU::HandleWriteSyscall()
649
{
650
const auto& regs = g_state.regs;
651
if (regs.a0 != 1) // stdout
652
return;
653
654
u32 addr = regs.a1;
655
const u32 count = regs.a2;
656
for (u32 i = 0; i < count; i++)
657
{
658
u8 value;
659
if (!SafeReadMemoryByte(addr++, &value) || value == 0)
660
break;
661
662
Bus::AddTTYCharacter(static_cast<char>(value));
663
}
664
}
665
666
void CPU::HandlePutcSyscall()
667
{
668
const auto& regs = g_state.regs;
669
if (regs.a0 != 0)
670
Bus::AddTTYCharacter(static_cast<char>(regs.a0));
671
}
672
673
void CPU::HandlePutsSyscall()
674
{
675
const auto& regs = g_state.regs;
676
677
u32 addr = regs.a0;
678
for (u32 i = 0; i < 1024; i++)
679
{
680
u8 value;
681
if (!SafeReadMemoryByte(addr++, &value) || value == 0)
682
break;
683
684
Bus::AddTTYCharacter(static_cast<char>(value));
685
}
686
}
687
688
void CPU::HandleA0Syscall()
689
{
690
const auto& regs = g_state.regs;
691
const u32 call = regs.t1;
692
if (call == 0x03)
693
HandleWriteSyscall();
694
else if (call == 0x09 || call == 0x3c)
695
HandlePutcSyscall();
696
else if (call == 0x3e)
697
HandlePutsSyscall();
698
}
699
700
void CPU::HandleB0Syscall()
701
{
702
const auto& regs = g_state.regs;
703
const u32 call = regs.t1;
704
if (call == 0x35)
705
HandleWriteSyscall();
706
else if (call == 0x3b || call == 0x3d)
707
HandlePutcSyscall();
708
else if (call == 0x3f)
709
HandlePutsSyscall();
710
}
711
712
const std::array<CPU::DebuggerRegisterListEntry, CPU::NUM_DEBUGGER_REGISTER_LIST_ENTRIES>
713
CPU::g_debugger_register_list = {{{"zero", &CPU::g_state.regs.zero},
714
{"at", &CPU::g_state.regs.at},
715
{"v0", &CPU::g_state.regs.v0},
716
{"v1", &CPU::g_state.regs.v1},
717
{"a0", &CPU::g_state.regs.a0},
718
{"a1", &CPU::g_state.regs.a1},
719
{"a2", &CPU::g_state.regs.a2},
720
{"a3", &CPU::g_state.regs.a3},
721
{"t0", &CPU::g_state.regs.t0},
722
{"t1", &CPU::g_state.regs.t1},
723
{"t2", &CPU::g_state.regs.t2},
724
{"t3", &CPU::g_state.regs.t3},
725
{"t4", &CPU::g_state.regs.t4},
726
{"t5", &CPU::g_state.regs.t5},
727
{"t6", &CPU::g_state.regs.t6},
728
{"t7", &CPU::g_state.regs.t7},
729
{"s0", &CPU::g_state.regs.s0},
730
{"s1", &CPU::g_state.regs.s1},
731
{"s2", &CPU::g_state.regs.s2},
732
{"s3", &CPU::g_state.regs.s3},
733
{"s4", &CPU::g_state.regs.s4},
734
{"s5", &CPU::g_state.regs.s5},
735
{"s6", &CPU::g_state.regs.s6},
736
{"s7", &CPU::g_state.regs.s7},
737
{"t8", &CPU::g_state.regs.t8},
738
{"t9", &CPU::g_state.regs.t9},
739
{"k0", &CPU::g_state.regs.k0},
740
{"k1", &CPU::g_state.regs.k1},
741
{"gp", &CPU::g_state.regs.gp},
742
{"sp", &CPU::g_state.regs.sp},
743
{"fp", &CPU::g_state.regs.fp},
744
{"ra", &CPU::g_state.regs.ra},
745
{"hi", &CPU::g_state.regs.hi},
746
{"lo", &CPU::g_state.regs.lo},
747
{"pc", &CPU::g_state.pc},
748
749
{"COP0_SR", &CPU::g_state.cop0_regs.sr.bits},
750
{"COP0_CAUSE", &CPU::g_state.cop0_regs.cause.bits},
751
{"COP0_EPC", &CPU::g_state.cop0_regs.EPC},
752
{"COP0_BadVAddr", &CPU::g_state.cop0_regs.BadVaddr},
753
754
{"V0_XY", &CPU::g_state.gte_regs.r32[0]},
755
{"V0_Z", &CPU::g_state.gte_regs.r32[1]},
756
{"V1_XY", &CPU::g_state.gte_regs.r32[2]},
757
{"V1_Z", &CPU::g_state.gte_regs.r32[3]},
758
{"V2_XY", &CPU::g_state.gte_regs.r32[4]},
759
{"V2_Z", &CPU::g_state.gte_regs.r32[5]},
760
{"RGBC", &CPU::g_state.gte_regs.r32[6]},
761
{"OTZ", &CPU::g_state.gte_regs.r32[7]},
762
{"IR0", &CPU::g_state.gte_regs.r32[8]},
763
{"IR1", &CPU::g_state.gte_regs.r32[9]},
764
{"IR2", &CPU::g_state.gte_regs.r32[10]},
765
{"IR3", &CPU::g_state.gte_regs.r32[11]},
766
{"SXY0", &CPU::g_state.gte_regs.r32[12]},
767
{"SXY1", &CPU::g_state.gte_regs.r32[13]},
768
{"SXY2", &CPU::g_state.gte_regs.r32[14]},
769
{"SXYP", &CPU::g_state.gte_regs.r32[15]},
770
{"SZ0", &CPU::g_state.gte_regs.r32[16]},
771
{"SZ1", &CPU::g_state.gte_regs.r32[17]},
772
{"SZ2", &CPU::g_state.gte_regs.r32[18]},
773
{"SZ3", &CPU::g_state.gte_regs.r32[19]},
774
{"RGB0", &CPU::g_state.gte_regs.r32[20]},
775
{"RGB1", &CPU::g_state.gte_regs.r32[21]},
776
{"RGB2", &CPU::g_state.gte_regs.r32[22]},
777
{"RES1", &CPU::g_state.gte_regs.r32[23]},
778
{"MAC0", &CPU::g_state.gte_regs.r32[24]},
779
{"MAC1", &CPU::g_state.gte_regs.r32[25]},
780
{"MAC2", &CPU::g_state.gte_regs.r32[26]},
781
{"MAC3", &CPU::g_state.gte_regs.r32[27]},
782
{"IRGB", &CPU::g_state.gte_regs.r32[28]},
783
{"ORGB", &CPU::g_state.gte_regs.r32[29]},
784
{"LZCS", &CPU::g_state.gte_regs.r32[30]},
785
{"LZCR", &CPU::g_state.gte_regs.r32[31]},
786
{"RT_0", &CPU::g_state.gte_regs.r32[32]},
787
{"RT_1", &CPU::g_state.gte_regs.r32[33]},
788
{"RT_2", &CPU::g_state.gte_regs.r32[34]},
789
{"RT_3", &CPU::g_state.gte_regs.r32[35]},
790
{"RT_4", &CPU::g_state.gte_regs.r32[36]},
791
{"TRX", &CPU::g_state.gte_regs.r32[37]},
792
{"TRY", &CPU::g_state.gte_regs.r32[38]},
793
{"TRZ", &CPU::g_state.gte_regs.r32[39]},
794
{"LLM_0", &CPU::g_state.gte_regs.r32[40]},
795
{"LLM_1", &CPU::g_state.gte_regs.r32[41]},
796
{"LLM_2", &CPU::g_state.gte_regs.r32[42]},
797
{"LLM_3", &CPU::g_state.gte_regs.r32[43]},
798
{"LLM_4", &CPU::g_state.gte_regs.r32[44]},
799
{"RBK", &CPU::g_state.gte_regs.r32[45]},
800
{"GBK", &CPU::g_state.gte_regs.r32[46]},
801
{"BBK", &CPU::g_state.gte_regs.r32[47]},
802
{"LCM_0", &CPU::g_state.gte_regs.r32[48]},
803
{"LCM_1", &CPU::g_state.gte_regs.r32[49]},
804
{"LCM_2", &CPU::g_state.gte_regs.r32[50]},
805
{"LCM_3", &CPU::g_state.gte_regs.r32[51]},
806
{"LCM_4", &CPU::g_state.gte_regs.r32[52]},
807
{"RFC", &CPU::g_state.gte_regs.r32[53]},
808
{"GFC", &CPU::g_state.gte_regs.r32[54]},
809
{"BFC", &CPU::g_state.gte_regs.r32[55]},
810
{"OFX", &CPU::g_state.gte_regs.r32[56]},
811
{"OFY", &CPU::g_state.gte_regs.r32[57]},
812
{"H", &CPU::g_state.gte_regs.r32[58]},
813
{"DQA", &CPU::g_state.gte_regs.r32[59]},
814
{"DQB", &CPU::g_state.gte_regs.r32[60]},
815
{"ZSF3", &CPU::g_state.gte_regs.r32[61]},
816
{"ZSF4", &CPU::g_state.gte_regs.r32[62]},
817
{"FLAG", &CPU::g_state.gte_regs.r32[63]}}};
818
819
ALWAYS_INLINE static constexpr bool AddOverflow(u32 old_value, u32 add_value, u32* new_value)
820
{
821
#if defined(__clang__) || defined(__GNUC__)
822
return __builtin_add_overflow(static_cast<s32>(old_value), static_cast<s32>(add_value),
823
reinterpret_cast<s32*>(new_value));
824
#else
825
*new_value = old_value + add_value;
826
return (((*new_value ^ old_value) & (*new_value ^ add_value)) & UINT32_C(0x80000000)) != 0;
827
#endif
828
}
829
830
ALWAYS_INLINE static constexpr bool SubOverflow(u32 old_value, u32 sub_value, u32* new_value)
831
{
832
#if defined(__clang__) || defined(__GNUC__)
833
return __builtin_sub_overflow(static_cast<s32>(old_value), static_cast<s32>(sub_value),
834
reinterpret_cast<s32*>(new_value));
835
#else
836
*new_value = old_value - sub_value;
837
return (((*new_value ^ old_value) & (old_value ^ sub_value)) & UINT32_C(0x80000000)) != 0;
838
#endif
839
}
840
841
void CPU::DisassembleAndPrint(u32 addr, bool regs, const char* prefix)
842
{
843
u32 bits = 0;
844
SafeReadMemoryWord(addr, &bits);
845
PrintInstruction(bits, addr, regs, prefix);
846
}
847
848
void CPU::DisassembleAndPrint(u32 addr, u32 instructions_before /* = 0 */, u32 instructions_after /* = 0 */)
849
{
850
u32 disasm_addr = addr - (instructions_before * sizeof(u32));
851
for (u32 i = 0; i < instructions_before; i++)
852
{
853
DisassembleAndPrint(disasm_addr, false, "");
854
disasm_addr += sizeof(u32);
855
}
856
857
// <= to include the instruction itself
858
for (u32 i = 0; i <= instructions_after; i++)
859
{
860
DisassembleAndPrint(disasm_addr, (i == 0), (i == 0) ? "---->" : "");
861
disasm_addr += sizeof(u32);
862
}
863
}
864
865
template<PGXPMode pgxp_mode, bool debug>
866
ALWAYS_INLINE_RELEASE void CPU::ExecuteInstruction()
867
{
868
restart_instruction:
869
const Instruction inst = g_state.current_instruction;
870
871
#if 0
872
if (g_state.current_instruction_pc == 0x80030000)
873
{
874
TRACE_EXECUTION = true;
875
__debugbreak();
876
}
877
#endif
878
879
#ifdef _DEBUG
880
if (TRACE_EXECUTION)
881
TracePrintInstruction();
882
#endif
883
884
// Skip nops. Makes PGXP-CPU quicker, but also the regular interpreter.
885
if (inst.bits == 0)
886
return;
887
888
switch (inst.op)
889
{
890
case InstructionOp::funct:
891
{
892
switch (inst.r.funct)
893
{
894
case InstructionFunct::sll:
895
{
896
const u32 rtVal = ReadReg(inst.r.rt);
897
const u32 rdVal = rtVal << inst.r.shamt;
898
WriteReg(inst.r.rd, rdVal);
899
900
if constexpr (pgxp_mode >= PGXPMode::CPU)
901
PGXP::CPU_SLL(inst, rtVal);
902
}
903
break;
904
905
case InstructionFunct::srl:
906
{
907
const u32 rtVal = ReadReg(inst.r.rt);
908
const u32 rdVal = rtVal >> inst.r.shamt;
909
WriteReg(inst.r.rd, rdVal);
910
911
if constexpr (pgxp_mode >= PGXPMode::CPU)
912
PGXP::CPU_SRL(inst, rtVal);
913
}
914
break;
915
916
case InstructionFunct::sra:
917
{
918
const u32 rtVal = ReadReg(inst.r.rt);
919
const u32 rdVal = static_cast<u32>(static_cast<s32>(rtVal) >> inst.r.shamt);
920
WriteReg(inst.r.rd, rdVal);
921
922
if constexpr (pgxp_mode >= PGXPMode::CPU)
923
PGXP::CPU_SRA(inst, rtVal);
924
}
925
break;
926
927
case InstructionFunct::sllv:
928
{
929
const u32 rtVal = ReadReg(inst.r.rt);
930
const u32 shamt = ReadReg(inst.r.rs) & UINT32_C(0x1F);
931
const u32 rdVal = rtVal << shamt;
932
if constexpr (pgxp_mode >= PGXPMode::CPU)
933
PGXP::CPU_SLLV(inst, rtVal, shamt);
934
935
WriteReg(inst.r.rd, rdVal);
936
}
937
break;
938
939
case InstructionFunct::srlv:
940
{
941
const u32 rtVal = ReadReg(inst.r.rt);
942
const u32 shamt = ReadReg(inst.r.rs) & UINT32_C(0x1F);
943
const u32 rdVal = rtVal >> shamt;
944
WriteReg(inst.r.rd, rdVal);
945
946
if constexpr (pgxp_mode >= PGXPMode::CPU)
947
PGXP::CPU_SRLV(inst, rtVal, shamt);
948
}
949
break;
950
951
case InstructionFunct::srav:
952
{
953
const u32 rtVal = ReadReg(inst.r.rt);
954
const u32 shamt = ReadReg(inst.r.rs) & UINT32_C(0x1F);
955
const u32 rdVal = static_cast<u32>(static_cast<s32>(rtVal) >> shamt);
956
WriteReg(inst.r.rd, rdVal);
957
958
if constexpr (pgxp_mode >= PGXPMode::CPU)
959
PGXP::CPU_SRAV(inst, rtVal, shamt);
960
}
961
break;
962
963
case InstructionFunct::and_:
964
{
965
const u32 rsVal = ReadReg(inst.r.rs);
966
const u32 rtVal = ReadReg(inst.r.rt);
967
const u32 new_value = rsVal & rtVal;
968
WriteReg(inst.r.rd, new_value);
969
970
if constexpr (pgxp_mode >= PGXPMode::CPU)
971
PGXP::CPU_AND_(inst, rsVal, rtVal);
972
}
973
break;
974
975
case InstructionFunct::or_:
976
{
977
const u32 rsVal = ReadReg(inst.r.rs);
978
const u32 rtVal = ReadReg(inst.r.rt);
979
const u32 new_value = rsVal | rtVal;
980
WriteReg(inst.r.rd, new_value);
981
982
if constexpr (pgxp_mode >= PGXPMode::CPU)
983
PGXP::CPU_OR_(inst, rsVal, rtVal);
984
else if constexpr (pgxp_mode >= PGXPMode::Memory)
985
PGXP::TryMove(inst.r.rd, inst.r.rs, inst.r.rt);
986
}
987
break;
988
989
case InstructionFunct::xor_:
990
{
991
const u32 rsVal = ReadReg(inst.r.rs);
992
const u32 rtVal = ReadReg(inst.r.rt);
993
const u32 new_value = rsVal ^ rtVal;
994
WriteReg(inst.r.rd, new_value);
995
996
if constexpr (pgxp_mode >= PGXPMode::CPU)
997
PGXP::CPU_XOR_(inst, rsVal, rtVal);
998
else if constexpr (pgxp_mode >= PGXPMode::Memory)
999
PGXP::TryMove(inst.r.rd, inst.r.rs, inst.r.rt);
1000
}
1001
break;
1002
1003
case InstructionFunct::nor:
1004
{
1005
const u32 rsVal = ReadReg(inst.r.rs);
1006
const u32 rtVal = ReadReg(inst.r.rt);
1007
const u32 new_value = ~(rsVal | rtVal);
1008
WriteReg(inst.r.rd, new_value);
1009
1010
if constexpr (pgxp_mode >= PGXPMode::CPU)
1011
PGXP::CPU_NOR(inst, rsVal, rtVal);
1012
}
1013
break;
1014
1015
case InstructionFunct::add:
1016
{
1017
const u32 rsVal = ReadReg(inst.r.rs);
1018
const u32 rtVal = ReadReg(inst.r.rt);
1019
u32 rdVal;
1020
if (AddOverflow(rsVal, rtVal, &rdVal)) [[unlikely]]
1021
{
1022
RaiseException(Exception::Ov);
1023
return;
1024
}
1025
1026
WriteReg(inst.r.rd, rdVal);
1027
1028
if constexpr (pgxp_mode == PGXPMode::CPU)
1029
PGXP::CPU_ADD(inst, rsVal, rtVal);
1030
else if constexpr (pgxp_mode >= PGXPMode::Memory)
1031
PGXP::TryMove(inst.r.rd, inst.r.rs, inst.r.rt);
1032
}
1033
break;
1034
1035
case InstructionFunct::addu:
1036
{
1037
const u32 rsVal = ReadReg(inst.r.rs);
1038
const u32 rtVal = ReadReg(inst.r.rt);
1039
const u32 rdVal = rsVal + rtVal;
1040
WriteReg(inst.r.rd, rdVal);
1041
1042
if constexpr (pgxp_mode >= PGXPMode::CPU)
1043
PGXP::CPU_ADD(inst, rsVal, rtVal);
1044
else if constexpr (pgxp_mode >= PGXPMode::Memory)
1045
PGXP::TryMove(inst.r.rd, inst.r.rs, inst.r.rt);
1046
}
1047
break;
1048
1049
case InstructionFunct::sub:
1050
{
1051
const u32 rsVal = ReadReg(inst.r.rs);
1052
const u32 rtVal = ReadReg(inst.r.rt);
1053
u32 rdVal;
1054
if (SubOverflow(rsVal, rtVal, &rdVal)) [[unlikely]]
1055
{
1056
RaiseException(Exception::Ov);
1057
return;
1058
}
1059
1060
WriteReg(inst.r.rd, rdVal);
1061
1062
if constexpr (pgxp_mode >= PGXPMode::CPU)
1063
PGXP::CPU_SUB(inst, rsVal, rtVal);
1064
}
1065
break;
1066
1067
case InstructionFunct::subu:
1068
{
1069
const u32 rsVal = ReadReg(inst.r.rs);
1070
const u32 rtVal = ReadReg(inst.r.rt);
1071
const u32 rdVal = rsVal - rtVal;
1072
WriteReg(inst.r.rd, rdVal);
1073
1074
if constexpr (pgxp_mode >= PGXPMode::CPU)
1075
PGXP::CPU_SUB(inst, rsVal, rtVal);
1076
}
1077
break;
1078
1079
case InstructionFunct::slt:
1080
{
1081
const u32 rsVal = ReadReg(inst.r.rs);
1082
const u32 rtVal = ReadReg(inst.r.rt);
1083
const u32 result = BoolToUInt32(static_cast<s32>(rsVal) < static_cast<s32>(rtVal));
1084
WriteReg(inst.r.rd, result);
1085
1086
if constexpr (pgxp_mode >= PGXPMode::CPU)
1087
PGXP::CPU_SLT(inst, rsVal, rtVal);
1088
}
1089
break;
1090
1091
case InstructionFunct::sltu:
1092
{
1093
const u32 rsVal = ReadReg(inst.r.rs);
1094
const u32 rtVal = ReadReg(inst.r.rt);
1095
const u32 result = BoolToUInt32(rsVal < rtVal);
1096
WriteReg(inst.r.rd, result);
1097
1098
if constexpr (pgxp_mode >= PGXPMode::CPU)
1099
PGXP::CPU_SLTU(inst, rsVal, rtVal);
1100
}
1101
break;
1102
1103
case InstructionFunct::mfhi:
1104
{
1105
const u32 value = g_state.regs.hi;
1106
WriteReg(inst.r.rd, value);
1107
1108
StallUntilMulDivComplete();
1109
1110
if constexpr (pgxp_mode >= PGXPMode::CPU)
1111
PGXP::CPU_MOVE(static_cast<u32>(inst.r.rd.GetValue()), static_cast<u32>(Reg::hi), value);
1112
}
1113
break;
1114
1115
case InstructionFunct::mthi:
1116
{
1117
const u32 value = ReadReg(inst.r.rs);
1118
g_state.regs.hi = value;
1119
1120
StallUntilMulDivComplete();
1121
1122
if constexpr (pgxp_mode >= PGXPMode::CPU)
1123
PGXP::CPU_MOVE(static_cast<u32>(Reg::hi), static_cast<u32>(inst.r.rs.GetValue()), value);
1124
}
1125
break;
1126
1127
case InstructionFunct::mflo:
1128
{
1129
const u32 value = g_state.regs.lo;
1130
WriteReg(inst.r.rd, value);
1131
1132
StallUntilMulDivComplete();
1133
1134
if constexpr (pgxp_mode >= PGXPMode::CPU)
1135
PGXP::CPU_MOVE(static_cast<u32>(inst.r.rd.GetValue()), static_cast<u32>(Reg::lo), value);
1136
}
1137
break;
1138
1139
case InstructionFunct::mtlo:
1140
{
1141
const u32 value = ReadReg(inst.r.rs);
1142
g_state.regs.lo = value;
1143
1144
StallUntilMulDivComplete();
1145
1146
if constexpr (pgxp_mode == PGXPMode::CPU)
1147
PGXP::CPU_MOVE(static_cast<u32>(Reg::lo), static_cast<u32>(inst.r.rs.GetValue()), value);
1148
}
1149
break;
1150
1151
case InstructionFunct::mult:
1152
{
1153
const u32 lhs = ReadReg(inst.r.rs);
1154
const u32 rhs = ReadReg(inst.r.rt);
1155
const u64 result =
1156
static_cast<u64>(static_cast<s64>(SignExtend64(lhs)) * static_cast<s64>(SignExtend64(rhs)));
1157
1158
g_state.regs.hi = Truncate32(result >> 32);
1159
g_state.regs.lo = Truncate32(result);
1160
1161
StallUntilMulDivComplete();
1162
AddMulDivTicks(GetMultTicks(static_cast<s32>(lhs)));
1163
1164
if constexpr (pgxp_mode >= PGXPMode::CPU)
1165
PGXP::CPU_MULT(inst, lhs, rhs);
1166
}
1167
break;
1168
1169
case InstructionFunct::multu:
1170
{
1171
const u32 lhs = ReadReg(inst.r.rs);
1172
const u32 rhs = ReadReg(inst.r.rt);
1173
const u64 result = ZeroExtend64(lhs) * ZeroExtend64(rhs);
1174
1175
g_state.regs.hi = Truncate32(result >> 32);
1176
g_state.regs.lo = Truncate32(result);
1177
1178
StallUntilMulDivComplete();
1179
AddMulDivTicks(GetMultTicks(lhs));
1180
1181
if constexpr (pgxp_mode >= PGXPMode::CPU)
1182
PGXP::CPU_MULTU(inst, lhs, rhs);
1183
}
1184
break;
1185
1186
case InstructionFunct::div:
1187
{
1188
const s32 num = static_cast<s32>(ReadReg(inst.r.rs));
1189
const s32 denom = static_cast<s32>(ReadReg(inst.r.rt));
1190
1191
if (denom == 0)
1192
{
1193
// divide by zero
1194
g_state.regs.lo = (num >= 0) ? UINT32_C(0xFFFFFFFF) : UINT32_C(1);
1195
g_state.regs.hi = static_cast<u32>(num);
1196
}
1197
else if (static_cast<u32>(num) == UINT32_C(0x80000000) && denom == -1)
1198
{
1199
// unrepresentable
1200
g_state.regs.lo = UINT32_C(0x80000000);
1201
g_state.regs.hi = 0;
1202
}
1203
else
1204
{
1205
g_state.regs.lo = static_cast<u32>(num / denom);
1206
g_state.regs.hi = static_cast<u32>(num % denom);
1207
}
1208
1209
StallUntilMulDivComplete();
1210
AddMulDivTicks(GetDivTicks());
1211
1212
if constexpr (pgxp_mode >= PGXPMode::CPU)
1213
PGXP::CPU_DIV(inst, num, denom);
1214
}
1215
break;
1216
1217
case InstructionFunct::divu:
1218
{
1219
const u32 num = ReadReg(inst.r.rs);
1220
const u32 denom = ReadReg(inst.r.rt);
1221
1222
if (denom == 0)
1223
{
1224
// divide by zero
1225
g_state.regs.lo = UINT32_C(0xFFFFFFFF);
1226
g_state.regs.hi = static_cast<u32>(num);
1227
}
1228
else
1229
{
1230
g_state.regs.lo = num / denom;
1231
g_state.regs.hi = num % denom;
1232
}
1233
1234
StallUntilMulDivComplete();
1235
AddMulDivTicks(GetDivTicks());
1236
1237
if constexpr (pgxp_mode >= PGXPMode::CPU)
1238
PGXP::CPU_DIVU(inst, num, denom);
1239
}
1240
break;
1241
1242
case InstructionFunct::jr:
1243
{
1244
g_state.next_instruction_is_branch_delay_slot = true;
1245
const u32 target = ReadReg(inst.r.rs);
1246
Branch(target);
1247
}
1248
break;
1249
1250
case InstructionFunct::jalr:
1251
{
1252
g_state.next_instruction_is_branch_delay_slot = true;
1253
const u32 target = ReadReg(inst.r.rs);
1254
WriteReg(inst.r.rd, g_state.npc);
1255
Branch(target);
1256
}
1257
break;
1258
1259
case InstructionFunct::syscall:
1260
{
1261
RaiseException(Exception::Syscall);
1262
}
1263
break;
1264
1265
case InstructionFunct::break_:
1266
{
1267
RaiseBreakException(Cop0Registers::CAUSE::MakeValueForException(
1268
Exception::BP, g_state.current_instruction_in_branch_delay_slot,
1269
g_state.current_instruction_was_branch_taken, g_state.current_instruction.cop.cop_n),
1270
g_state.current_instruction_pc, g_state.current_instruction.bits);
1271
}
1272
break;
1273
1274
default:
1275
{
1276
RaiseException(Exception::RI);
1277
break;
1278
}
1279
}
1280
}
1281
break;
1282
1283
case InstructionOp::lui:
1284
{
1285
const u32 value = inst.i.imm_zext32() << 16;
1286
WriteReg(inst.i.rt, value);
1287
1288
if constexpr (pgxp_mode >= PGXPMode::CPU)
1289
PGXP::CPU_LUI(inst);
1290
}
1291
break;
1292
1293
case InstructionOp::andi:
1294
{
1295
const u32 rsVal = ReadReg(inst.i.rs);
1296
const u32 new_value = rsVal & inst.i.imm_zext32();
1297
WriteReg(inst.i.rt, new_value);
1298
1299
if constexpr (pgxp_mode >= PGXPMode::CPU)
1300
PGXP::CPU_ANDI(inst, rsVal);
1301
}
1302
break;
1303
1304
case InstructionOp::ori:
1305
{
1306
const u32 rsVal = ReadReg(inst.i.rs);
1307
const u32 imm = inst.i.imm_zext32();
1308
const u32 rtVal = rsVal | imm;
1309
WriteReg(inst.i.rt, rtVal);
1310
1311
if constexpr (pgxp_mode >= PGXPMode::CPU)
1312
PGXP::CPU_ORI(inst, rsVal);
1313
else if constexpr (pgxp_mode >= PGXPMode::Memory)
1314
PGXP::TryMoveImm(inst.r.rd, inst.r.rs, imm);
1315
}
1316
break;
1317
1318
case InstructionOp::xori:
1319
{
1320
const u32 rsVal = ReadReg(inst.i.rs);
1321
const u32 imm = inst.i.imm_zext32();
1322
const u32 new_value = ReadReg(inst.i.rs) ^ imm;
1323
WriteReg(inst.i.rt, new_value);
1324
1325
if constexpr (pgxp_mode >= PGXPMode::CPU)
1326
PGXP::CPU_XORI(inst, rsVal);
1327
else if constexpr (pgxp_mode >= PGXPMode::Memory)
1328
PGXP::TryMoveImm(inst.r.rd, inst.r.rs, imm);
1329
}
1330
break;
1331
1332
case InstructionOp::addi:
1333
{
1334
const u32 rsVal = ReadReg(inst.i.rs);
1335
const u32 imm = inst.i.imm_sext32();
1336
u32 rtVal;
1337
if (AddOverflow(rsVal, imm, &rtVal)) [[unlikely]]
1338
{
1339
RaiseException(Exception::Ov);
1340
return;
1341
}
1342
1343
WriteReg(inst.i.rt, rtVal);
1344
1345
if constexpr (pgxp_mode >= PGXPMode::CPU)
1346
PGXP::CPU_ADDI(inst, rsVal);
1347
else if constexpr (pgxp_mode >= PGXPMode::Memory)
1348
PGXP::TryMoveImm(inst.r.rd, inst.r.rs, imm);
1349
}
1350
break;
1351
1352
case InstructionOp::addiu:
1353
{
1354
const u32 rsVal = ReadReg(inst.i.rs);
1355
const u32 imm = inst.i.imm_sext32();
1356
const u32 rtVal = rsVal + imm;
1357
WriteReg(inst.i.rt, rtVal);
1358
1359
if constexpr (pgxp_mode >= PGXPMode::CPU)
1360
PGXP::CPU_ADDI(inst, rsVal);
1361
else if constexpr (pgxp_mode >= PGXPMode::Memory)
1362
PGXP::TryMoveImm(inst.r.rd, inst.r.rs, imm);
1363
}
1364
break;
1365
1366
case InstructionOp::slti:
1367
{
1368
const u32 rsVal = ReadReg(inst.i.rs);
1369
const u32 result = BoolToUInt32(static_cast<s32>(rsVal) < static_cast<s32>(inst.i.imm_sext32()));
1370
WriteReg(inst.i.rt, result);
1371
1372
if constexpr (pgxp_mode >= PGXPMode::CPU)
1373
PGXP::CPU_SLTI(inst, rsVal);
1374
}
1375
break;
1376
1377
case InstructionOp::sltiu:
1378
{
1379
const u32 result = BoolToUInt32(ReadReg(inst.i.rs) < inst.i.imm_sext32());
1380
WriteReg(inst.i.rt, result);
1381
1382
if constexpr (pgxp_mode >= PGXPMode::CPU)
1383
PGXP::CPU_SLTIU(inst, ReadReg(inst.i.rs));
1384
}
1385
break;
1386
1387
case InstructionOp::lb:
1388
{
1389
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
1390
if constexpr (debug)
1391
{
1392
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
1393
MemoryBreakpointCheck<MemoryAccessType::Read>(addr);
1394
}
1395
1396
u8 value;
1397
if (!ReadMemoryByte(addr, &value)) [[unlikely]]
1398
return;
1399
1400
const u32 sxvalue = SignExtend32(value);
1401
1402
WriteRegDelayed(inst.i.rt, sxvalue);
1403
1404
if constexpr (pgxp_mode >= PGXPMode::Memory)
1405
PGXP::CPU_LBx(inst, addr, sxvalue);
1406
}
1407
break;
1408
1409
case InstructionOp::lh:
1410
{
1411
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
1412
if constexpr (debug)
1413
{
1414
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
1415
MemoryBreakpointCheck<MemoryAccessType::Read>(addr);
1416
}
1417
1418
u16 value;
1419
if (!ReadMemoryHalfWord(addr, &value)) [[unlikely]]
1420
return;
1421
1422
const u32 sxvalue = SignExtend32(value);
1423
WriteRegDelayed(inst.i.rt, sxvalue);
1424
1425
if constexpr (pgxp_mode >= PGXPMode::Memory)
1426
PGXP::CPU_LH(inst, addr, sxvalue);
1427
}
1428
break;
1429
1430
case InstructionOp::lw:
1431
{
1432
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
1433
if constexpr (debug)
1434
{
1435
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
1436
MemoryBreakpointCheck<MemoryAccessType::Read>(addr);
1437
}
1438
1439
u32 value;
1440
if (!ReadMemoryWord(addr, &value)) [[unlikely]]
1441
return;
1442
1443
WriteRegDelayed(inst.i.rt, value);
1444
1445
if constexpr (pgxp_mode >= PGXPMode::Memory)
1446
PGXP::CPU_LW(inst, addr, value);
1447
}
1448
break;
1449
1450
case InstructionOp::lbu:
1451
{
1452
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
1453
if constexpr (debug)
1454
{
1455
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
1456
MemoryBreakpointCheck<MemoryAccessType::Read>(addr);
1457
}
1458
1459
u8 value;
1460
if (!ReadMemoryByte(addr, &value)) [[unlikely]]
1461
return;
1462
1463
const u32 zxvalue = ZeroExtend32(value);
1464
WriteRegDelayed(inst.i.rt, zxvalue);
1465
1466
if constexpr (pgxp_mode >= PGXPMode::Memory)
1467
PGXP::CPU_LBx(inst, addr, zxvalue);
1468
}
1469
break;
1470
1471
case InstructionOp::lhu:
1472
{
1473
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
1474
if constexpr (debug)
1475
{
1476
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
1477
MemoryBreakpointCheck<MemoryAccessType::Read>(addr);
1478
}
1479
1480
u16 value;
1481
if (!ReadMemoryHalfWord(addr, &value)) [[unlikely]]
1482
return;
1483
1484
const u32 zxvalue = ZeroExtend32(value);
1485
WriteRegDelayed(inst.i.rt, zxvalue);
1486
1487
if constexpr (pgxp_mode >= PGXPMode::Memory)
1488
PGXP::CPU_LHU(inst, addr, zxvalue);
1489
}
1490
break;
1491
1492
case InstructionOp::lwl:
1493
case InstructionOp::lwr:
1494
{
1495
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
1496
const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3);
1497
if constexpr (debug)
1498
{
1499
Cop0DataBreakpointCheck<MemoryAccessType::Read>(aligned_addr);
1500
MemoryBreakpointCheck<MemoryAccessType::Read>(aligned_addr);
1501
}
1502
1503
u32 aligned_value;
1504
if (!ReadMemoryWord(aligned_addr, &aligned_value)) [[unlikely]]
1505
return;
1506
1507
// Bypasses load delay. No need to check the old value since this is the delay slot or it's not relevant.
1508
const u32 existing_value = (inst.i.rt == g_state.load_delay_reg) ? g_state.load_delay_value : ReadReg(inst.i.rt);
1509
if constexpr (pgxp_mode >= PGXPMode::Memory)
1510
PGXP::CPU_LWx(inst, addr, existing_value);
1511
1512
const u8 shift = (Truncate8(addr) & u8(3)) * u8(8);
1513
u32 new_value;
1514
if (inst.op == InstructionOp::lwl)
1515
{
1516
const u32 mask = UINT32_C(0x00FFFFFF) >> shift;
1517
new_value = (existing_value & mask) | (aligned_value << (24 - shift));
1518
}
1519
else
1520
{
1521
const u32 mask = UINT32_C(0xFFFFFF00) << (24 - shift);
1522
new_value = (existing_value & mask) | (aligned_value >> shift);
1523
}
1524
1525
WriteRegDelayed(inst.i.rt, new_value);
1526
}
1527
break;
1528
1529
case InstructionOp::sb:
1530
{
1531
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
1532
if constexpr (debug)
1533
{
1534
Cop0DataBreakpointCheck<MemoryAccessType::Write>(addr);
1535
MemoryBreakpointCheck<MemoryAccessType::Write>(addr);
1536
}
1537
1538
const u32 value = ReadReg(inst.i.rt);
1539
WriteMemoryByte(addr, value);
1540
1541
if constexpr (pgxp_mode >= PGXPMode::Memory)
1542
PGXP::CPU_SB(inst, addr, value);
1543
}
1544
break;
1545
1546
case InstructionOp::sh:
1547
{
1548
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
1549
if constexpr (debug)
1550
{
1551
Cop0DataBreakpointCheck<MemoryAccessType::Write>(addr);
1552
MemoryBreakpointCheck<MemoryAccessType::Write>(addr);
1553
}
1554
1555
const u32 value = ReadReg(inst.i.rt);
1556
WriteMemoryHalfWord(addr, value);
1557
1558
if constexpr (pgxp_mode >= PGXPMode::Memory)
1559
PGXP::CPU_SH(inst, addr, value);
1560
}
1561
break;
1562
1563
case InstructionOp::sw:
1564
{
1565
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
1566
if constexpr (debug)
1567
{
1568
Cop0DataBreakpointCheck<MemoryAccessType::Write>(addr);
1569
MemoryBreakpointCheck<MemoryAccessType::Write>(addr);
1570
}
1571
1572
const u32 value = ReadReg(inst.i.rt);
1573
WriteMemoryWord(addr, value);
1574
1575
if constexpr (pgxp_mode >= PGXPMode::Memory)
1576
PGXP::CPU_SW(inst, addr, value);
1577
}
1578
break;
1579
1580
case InstructionOp::swl:
1581
case InstructionOp::swr:
1582
{
1583
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
1584
const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3);
1585
if constexpr (debug)
1586
{
1587
Cop0DataBreakpointCheck<MemoryAccessType::Write>(aligned_addr);
1588
MemoryBreakpointCheck<MemoryAccessType::Write>(aligned_addr);
1589
}
1590
1591
const u32 reg_value = ReadReg(inst.i.rt);
1592
u32 mem_value;
1593
if (!ReadMemoryWord(aligned_addr, &mem_value)) [[unlikely]]
1594
return;
1595
1596
if constexpr (pgxp_mode >= PGXPMode::Memory)
1597
PGXP::CPU_SWx(inst, addr, reg_value);
1598
1599
const u8 shift = (Truncate8(addr) & u8(3)) * u8(8);
1600
u32 new_value;
1601
if (inst.op == InstructionOp::swl)
1602
{
1603
const u32 mem_mask = UINT32_C(0xFFFFFF00) << shift;
1604
new_value = (mem_value & mem_mask) | (reg_value >> (24 - shift));
1605
}
1606
else
1607
{
1608
const u32 mem_mask = UINT32_C(0x00FFFFFF) >> (24 - shift);
1609
new_value = (mem_value & mem_mask) | (reg_value << shift);
1610
}
1611
1612
WriteMemoryWord(aligned_addr, new_value);
1613
}
1614
break;
1615
1616
case InstructionOp::j:
1617
{
1618
g_state.next_instruction_is_branch_delay_slot = true;
1619
Branch((g_state.pc & UINT32_C(0xF0000000)) | (inst.j.target << 2));
1620
}
1621
break;
1622
1623
case InstructionOp::jal:
1624
{
1625
WriteReg(Reg::ra, g_state.npc);
1626
g_state.next_instruction_is_branch_delay_slot = true;
1627
Branch((g_state.pc & UINT32_C(0xF0000000)) | (inst.j.target << 2));
1628
}
1629
break;
1630
1631
case InstructionOp::beq:
1632
{
1633
// We're still flagged as a branch delay slot even if the branch isn't taken.
1634
g_state.next_instruction_is_branch_delay_slot = true;
1635
const bool branch = (ReadReg(inst.i.rs) == ReadReg(inst.i.rt));
1636
if (branch)
1637
Branch(g_state.pc + (inst.i.imm_sext32() << 2));
1638
}
1639
break;
1640
1641
case InstructionOp::bne:
1642
{
1643
g_state.next_instruction_is_branch_delay_slot = true;
1644
const bool branch = (ReadReg(inst.i.rs) != ReadReg(inst.i.rt));
1645
if (branch)
1646
Branch(g_state.pc + (inst.i.imm_sext32() << 2));
1647
}
1648
break;
1649
1650
case InstructionOp::bgtz:
1651
{
1652
g_state.next_instruction_is_branch_delay_slot = true;
1653
const bool branch = (static_cast<s32>(ReadReg(inst.i.rs)) > 0);
1654
if (branch)
1655
Branch(g_state.pc + (inst.i.imm_sext32() << 2));
1656
}
1657
break;
1658
1659
case InstructionOp::blez:
1660
{
1661
g_state.next_instruction_is_branch_delay_slot = true;
1662
const bool branch = (static_cast<s32>(ReadReg(inst.i.rs)) <= 0);
1663
if (branch)
1664
Branch(g_state.pc + (inst.i.imm_sext32() << 2));
1665
}
1666
break;
1667
1668
case InstructionOp::b:
1669
{
1670
g_state.next_instruction_is_branch_delay_slot = true;
1671
const u8 rt = static_cast<u8>(inst.i.rt.GetValue());
1672
1673
// bgez is the inverse of bltz, so simply do ltz and xor the result
1674
const bool bgez = ConvertToBoolUnchecked(rt & u8(1));
1675
const bool branch = (static_cast<s32>(ReadReg(inst.i.rs)) < 0) ^ bgez;
1676
1677
// register is still linked even if the branch isn't taken
1678
const bool link = (rt & u8(0x1E)) == u8(0x10);
1679
if (link)
1680
WriteReg(Reg::ra, g_state.npc);
1681
1682
if (branch)
1683
Branch(g_state.pc + (inst.i.imm_sext32() << 2));
1684
}
1685
break;
1686
1687
case InstructionOp::cop0:
1688
{
1689
if (InUserMode() && !g_state.cop0_regs.sr.CU0)
1690
{
1691
WARNING_LOG("Coprocessor 0 not present in user mode");
1692
RaiseException(Exception::CpU);
1693
return;
1694
}
1695
1696
if (inst.cop.IsCommonInstruction())
1697
{
1698
switch (inst.cop.CommonOp())
1699
{
1700
case CopCommonInstruction::mfcn:
1701
{
1702
u32 value;
1703
1704
switch (static_cast<Cop0Reg>(inst.r.rd.GetValue()))
1705
{
1706
case Cop0Reg::BPC:
1707
value = g_state.cop0_regs.BPC;
1708
break;
1709
1710
case Cop0Reg::BPCM:
1711
value = g_state.cop0_regs.BPCM;
1712
break;
1713
1714
case Cop0Reg::BDA:
1715
value = g_state.cop0_regs.BDA;
1716
break;
1717
1718
case Cop0Reg::BDAM:
1719
value = g_state.cop0_regs.BDAM;
1720
break;
1721
1722
case Cop0Reg::DCIC:
1723
value = g_state.cop0_regs.dcic.bits;
1724
break;
1725
1726
case Cop0Reg::JUMPDEST:
1727
value = g_state.cop0_regs.TAR;
1728
break;
1729
1730
case Cop0Reg::BadVaddr:
1731
value = g_state.cop0_regs.BadVaddr;
1732
break;
1733
1734
case Cop0Reg::SR:
1735
value = g_state.cop0_regs.sr.bits;
1736
break;
1737
1738
case Cop0Reg::CAUSE:
1739
value = g_state.cop0_regs.cause.bits;
1740
break;
1741
1742
case Cop0Reg::EPC:
1743
value = g_state.cop0_regs.EPC;
1744
break;
1745
1746
case Cop0Reg::PRID:
1747
value = g_state.cop0_regs.PRID;
1748
break;
1749
1750
default:
1751
RaiseException(Exception::RI);
1752
return;
1753
}
1754
1755
WriteRegDelayed(inst.r.rt, value);
1756
1757
if constexpr (pgxp_mode == PGXPMode::CPU)
1758
PGXP::CPU_MFC0(inst, value);
1759
}
1760
break;
1761
1762
case CopCommonInstruction::mtcn:
1763
{
1764
u32 value = ReadReg(inst.r.rt);
1765
[[maybe_unused]] const u32 orig_value = value;
1766
1767
switch (static_cast<Cop0Reg>(inst.r.rd.GetValue()))
1768
{
1769
case Cop0Reg::BPC:
1770
{
1771
g_state.cop0_regs.BPC = value;
1772
DEV_LOG("COP0 BPC <- {:08X}", value);
1773
}
1774
break;
1775
1776
case Cop0Reg::BPCM:
1777
{
1778
g_state.cop0_regs.BPCM = value;
1779
DEV_LOG("COP0 BPCM <- {:08X}", value);
1780
if (UpdateDebugDispatcherFlag())
1781
ExitExecution();
1782
}
1783
break;
1784
1785
case Cop0Reg::BDA:
1786
{
1787
g_state.cop0_regs.BDA = value;
1788
DEV_LOG("COP0 BDA <- {:08X}", value);
1789
}
1790
break;
1791
1792
case Cop0Reg::BDAM:
1793
{
1794
g_state.cop0_regs.BDAM = value;
1795
DEV_LOG("COP0 BDAM <- {:08X}", value);
1796
}
1797
break;
1798
1799
case Cop0Reg::JUMPDEST:
1800
{
1801
WARNING_LOG("Ignoring write to Cop0 JUMPDEST");
1802
}
1803
break;
1804
1805
case Cop0Reg::DCIC:
1806
{
1807
g_state.cop0_regs.dcic.bits = (g_state.cop0_regs.dcic.bits & ~Cop0Registers::DCIC::WRITE_MASK) |
1808
(value & Cop0Registers::DCIC::WRITE_MASK);
1809
DEV_LOG("COP0 DCIC <- {:08X} (now {:08X})", value, g_state.cop0_regs.dcic.bits);
1810
value = g_state.cop0_regs.dcic.bits;
1811
if (UpdateDebugDispatcherFlag())
1812
ExitExecution();
1813
}
1814
break;
1815
1816
case Cop0Reg::SR:
1817
{
1818
g_state.cop0_regs.sr.bits = (g_state.cop0_regs.sr.bits & ~Cop0Registers::SR::WRITE_MASK) |
1819
(value & Cop0Registers::SR::WRITE_MASK);
1820
DEBUG_LOG("COP0 SR <- {:08X} (now {:08X})", value, g_state.cop0_regs.sr.bits);
1821
value = g_state.cop0_regs.sr.bits;
1822
UpdateMemoryPointers();
1823
CheckForPendingInterrupt();
1824
}
1825
break;
1826
1827
case Cop0Reg::CAUSE:
1828
{
1829
g_state.cop0_regs.cause.bits = (g_state.cop0_regs.cause.bits & ~Cop0Registers::CAUSE::WRITE_MASK) |
1830
(value & Cop0Registers::CAUSE::WRITE_MASK);
1831
DEBUG_LOG("COP0 CAUSE <- {:08X} (now {:08X})", value, g_state.cop0_regs.cause.bits);
1832
value = g_state.cop0_regs.cause.bits;
1833
CheckForPendingInterrupt();
1834
}
1835
break;
1836
1837
[[unlikely]] default:
1838
RaiseException(Exception::RI);
1839
return;
1840
}
1841
1842
if constexpr (pgxp_mode == PGXPMode::CPU)
1843
PGXP::CPU_MTC0(inst, value, orig_value);
1844
}
1845
break;
1846
1847
default:
1848
[[unlikely]] ERROR_LOG("Unhandled instruction at {:08X}: {:08X}", g_state.current_instruction_pc,
1849
inst.bits);
1850
break;
1851
}
1852
}
1853
else
1854
{
1855
switch (inst.cop.Cop0Op())
1856
{
1857
case Cop0Instruction::rfe:
1858
{
1859
// restore mode
1860
g_state.cop0_regs.sr.mode_bits =
1861
(g_state.cop0_regs.sr.mode_bits & UINT32_C(0b110000)) | (g_state.cop0_regs.sr.mode_bits >> 2);
1862
CheckForPendingInterrupt();
1863
}
1864
break;
1865
1866
case Cop0Instruction::tlbr:
1867
case Cop0Instruction::tlbwi:
1868
case Cop0Instruction::tlbwr:
1869
case Cop0Instruction::tlbp:
1870
RaiseException(Exception::RI);
1871
return;
1872
1873
default:
1874
[[unlikely]] ERROR_LOG("Unhandled instruction at {:08X}: {:08X}", g_state.current_instruction_pc,
1875
inst.bits);
1876
break;
1877
}
1878
}
1879
}
1880
break;
1881
1882
case InstructionOp::cop2:
1883
{
1884
if (!g_state.cop0_regs.sr.CE2) [[unlikely]]
1885
{
1886
WARNING_LOG("Coprocessor 2 not enabled");
1887
RaiseException(Exception::CpU);
1888
return;
1889
}
1890
1891
if (inst.cop.IsCommonInstruction())
1892
{
1893
// TODO: Combine with cop0.
1894
switch (inst.cop.CommonOp())
1895
{
1896
case CopCommonInstruction::cfcn:
1897
{
1898
StallUntilGTEComplete();
1899
1900
const u32 value = GTE::ReadRegister(static_cast<u32>(inst.r.rd.GetValue()) + 32);
1901
WriteRegDelayed(inst.r.rt, value);
1902
1903
if constexpr (pgxp_mode >= PGXPMode::Memory)
1904
PGXP::CPU_MFC2(inst, value);
1905
}
1906
break;
1907
1908
case CopCommonInstruction::ctcn:
1909
{
1910
const u32 value = ReadReg(inst.r.rt);
1911
GTE::WriteRegister(static_cast<u32>(inst.r.rd.GetValue()) + 32, value);
1912
1913
if constexpr (pgxp_mode >= PGXPMode::Memory)
1914
PGXP::CPU_MTC2(inst, value);
1915
}
1916
break;
1917
1918
case CopCommonInstruction::mfcn:
1919
{
1920
StallUntilGTEComplete();
1921
1922
const u32 value = GTE::ReadRegister(static_cast<u32>(inst.r.rd.GetValue()));
1923
WriteRegDelayed(inst.r.rt, value);
1924
1925
if constexpr (pgxp_mode >= PGXPMode::Memory)
1926
PGXP::CPU_MFC2(inst, value);
1927
}
1928
break;
1929
1930
case CopCommonInstruction::mtcn:
1931
{
1932
const u32 value = ReadReg(inst.r.rt);
1933
GTE::WriteRegister(static_cast<u32>(inst.r.rd.GetValue()), value);
1934
1935
if constexpr (pgxp_mode >= PGXPMode::Memory)
1936
PGXP::CPU_MTC2(inst, value);
1937
}
1938
break;
1939
1940
default:
1941
[[unlikely]] ERROR_LOG("Unhandled instruction at {:08X}: {:08X}", g_state.current_instruction_pc,
1942
inst.bits);
1943
break;
1944
}
1945
}
1946
else
1947
{
1948
StallUntilGTEComplete();
1949
GTE::ExecuteInstruction(inst.bits);
1950
}
1951
}
1952
break;
1953
1954
case InstructionOp::lwc2:
1955
{
1956
if (!g_state.cop0_regs.sr.CE2) [[unlikely]]
1957
{
1958
WARNING_LOG("Coprocessor 2 not enabled");
1959
RaiseException(Exception::CpU);
1960
return;
1961
}
1962
1963
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
1964
u32 value;
1965
if (!ReadMemoryWord(addr, &value))
1966
return;
1967
1968
GTE::WriteRegister(ZeroExtend32(static_cast<u8>(inst.i.rt.GetValue())), value);
1969
1970
if constexpr (pgxp_mode >= PGXPMode::Memory)
1971
PGXP::CPU_LWC2(inst, addr, value);
1972
}
1973
break;
1974
1975
case InstructionOp::swc2:
1976
{
1977
if (!g_state.cop0_regs.sr.CE2) [[unlikely]]
1978
{
1979
WARNING_LOG("Coprocessor 2 not enabled");
1980
RaiseException(Exception::CpU);
1981
return;
1982
}
1983
1984
StallUntilGTEComplete();
1985
1986
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
1987
const u32 value = GTE::ReadRegister(ZeroExtend32(static_cast<u8>(inst.i.rt.GetValue())));
1988
WriteMemoryWord(addr, value);
1989
1990
if constexpr (pgxp_mode >= PGXPMode::Memory)
1991
PGXP::CPU_SWC2(inst, addr, value);
1992
}
1993
break;
1994
1995
// swc0/lwc0/cop1/cop3 are essentially no-ops
1996
case InstructionOp::cop1:
1997
case InstructionOp::cop3:
1998
case InstructionOp::lwc0:
1999
case InstructionOp::lwc1:
2000
case InstructionOp::lwc3:
2001
case InstructionOp::swc0:
2002
case InstructionOp::swc1:
2003
case InstructionOp::swc3:
2004
{
2005
}
2006
break;
2007
2008
// everything else is reserved/invalid
2009
[[unlikely]]
2010
default:
2011
{
2012
u32 ram_value;
2013
if (SafeReadInstruction(g_state.current_instruction_pc, &ram_value) &&
2014
ram_value != g_state.current_instruction.bits) [[unlikely]]
2015
{
2016
ERROR_LOG("Stale icache at 0x{:08X} - ICache: {:08X} RAM: {:08X}", g_state.current_instruction_pc,
2017
g_state.current_instruction.bits, ram_value);
2018
g_state.current_instruction.bits = ram_value;
2019
goto restart_instruction;
2020
}
2021
2022
RaiseException(Exception::RI);
2023
}
2024
break;
2025
}
2026
}
2027
2028
void CPU::DispatchInterrupt()
2029
{
2030
// The GTE is a co-processor, therefore it executes the instruction even if we're servicing an exception.
2031
// The exception handlers should recognize this and increment the PC if the EPC was a cop2 instruction.
2032
SafeReadInstruction(g_state.pc, &g_state.next_instruction.bits);
2033
if (g_state.next_instruction.op == InstructionOp::cop2 && !g_state.next_instruction.cop.IsCommonInstruction())
2034
{
2035
StallUntilGTEComplete();
2036
GTE::ExecuteInstruction(g_state.next_instruction.bits);
2037
}
2038
2039
// Interrupt raising occurs before the start of the instruction.
2040
RaiseException(
2041
Cop0Registers::CAUSE::MakeValueForException(Exception::INT, g_state.next_instruction_is_branch_delay_slot,
2042
g_state.branch_was_taken, g_state.next_instruction.cop.cop_n),
2043
g_state.pc);
2044
2045
// Fix up downcount, the pending IRQ set it to zero.
2046
TimingEvents::UpdateCPUDowncount();
2047
}
2048
2049
CPUExecutionMode CPU::GetCurrentExecutionMode()
2050
{
2051
return s_current_execution_mode;
2052
}
2053
2054
bool CPU::UpdateDebugDispatcherFlag()
2055
{
2056
const bool has_any_breakpoints = (HasAnyBreakpoints() || s_break_type == ExecutionBreakType::SingleStep);
2057
2058
const auto& dcic = g_state.cop0_regs.dcic;
2059
const bool has_cop0_breakpoints = dcic.super_master_enable_1 && dcic.super_master_enable_2 &&
2060
dcic.execution_breakpoint_enable && IsCop0ExecutionBreakpointUnmasked();
2061
2062
const bool use_debug_dispatcher =
2063
has_any_breakpoints || has_cop0_breakpoints || s_trace_to_log ||
2064
(g_settings.cpu_execution_mode == CPUExecutionMode::Interpreter && g_settings.bios_tty_logging);
2065
if (use_debug_dispatcher == g_state.using_debug_dispatcher)
2066
return false;
2067
2068
DEV_LOG("{} debug dispatcher", use_debug_dispatcher ? "Now using" : "No longer using");
2069
g_state.using_debug_dispatcher = use_debug_dispatcher;
2070
return true;
2071
}
2072
2073
void CPU::CheckForExecutionModeChange()
2074
{
2075
// Currently, any breakpoints require the interpreter.
2076
const CPUExecutionMode new_execution_mode =
2077
(g_state.using_debug_dispatcher ? CPUExecutionMode::Interpreter : g_settings.cpu_execution_mode);
2078
if (s_current_execution_mode == new_execution_mode) [[likely]]
2079
{
2080
DebugAssert(g_state.using_interpreter == (s_current_execution_mode == CPUExecutionMode::Interpreter));
2081
return;
2082
}
2083
2084
WARNING_LOG("Execution mode changed from {} to {}", Settings::GetCPUExecutionModeName(s_current_execution_mode),
2085
Settings::GetCPUExecutionModeName(new_execution_mode));
2086
2087
// Clear bus error flag, it can get set in the rec and we don't want to fire it later in the int.
2088
g_state.bus_error = false;
2089
2090
const bool new_interpreter = (new_execution_mode == CPUExecutionMode::Interpreter);
2091
if (g_state.using_interpreter != new_interpreter)
2092
{
2093
// Have to clear out the icache too, only the tags are valid in the recs.
2094
ClearICache();
2095
2096
if (new_interpreter)
2097
{
2098
// Switching to interpreter. Set up the pipeline.
2099
// We'll also need to fetch the next instruction to execute.
2100
if (!SafeReadInstruction(g_state.pc, &g_state.next_instruction.bits)) [[unlikely]]
2101
{
2102
g_state.next_instruction.bits = 0;
2103
ERROR_LOG("Failed to read current instruction from 0x{:08X}", g_state.pc);
2104
}
2105
2106
g_state.npc = g_state.pc + sizeof(Instruction);
2107
}
2108
else
2109
{
2110
// Switching to recompiler. We can't start a rec block in a branch delay slot, so we need to execute the
2111
// instruction if we're currently in one.
2112
if (g_state.next_instruction_is_branch_delay_slot) [[unlikely]]
2113
{
2114
while (g_state.next_instruction_is_branch_delay_slot)
2115
{
2116
WARNING_LOG("EXECMODE: Executing instruction at 0x{:08X} because it is in a branch delay slot.", g_state.pc);
2117
if (fastjmp_set(&s_jmp_buf) == 0)
2118
{
2119
s_break_type = ExecutionBreakType::ExecuteOneInstruction;
2120
g_state.using_debug_dispatcher = true;
2121
ExecuteInterpreter();
2122
}
2123
}
2124
2125
// Need to restart the whole process again, because the branch slot could change the debug flag.
2126
UpdateDebugDispatcherFlag();
2127
CheckForExecutionModeChange();
2128
return;
2129
}
2130
}
2131
}
2132
2133
s_current_execution_mode = new_execution_mode;
2134
g_state.using_interpreter = new_interpreter;
2135
2136
// Wipe out code cache when switching modes.
2137
if (!new_interpreter)
2138
CPU::CodeCache::Reset();
2139
}
2140
2141
[[noreturn]] void CPU::ExitExecution()
2142
{
2143
// can't exit while running events without messing things up
2144
DebugAssert(!TimingEvents::IsRunningEvents());
2145
fastjmp_jmp(&s_jmp_buf, 1);
2146
}
2147
2148
bool CPU::HasAnyBreakpoints()
2149
{
2150
return (GetBreakpointList(BreakpointType::Execute).size() + GetBreakpointList(BreakpointType::Read).size() +
2151
GetBreakpointList(BreakpointType::Write).size()) > 0;
2152
}
2153
2154
ALWAYS_INLINE CPU::BreakpointList& CPU::GetBreakpointList(BreakpointType type)
2155
{
2156
return s_breakpoints[static_cast<size_t>(type)];
2157
}
2158
2159
const char* CPU::GetBreakpointTypeName(BreakpointType type)
2160
{
2161
static constexpr std::array<const char*, static_cast<u32>(BreakpointType::Count)> names = {{
2162
"Execute",
2163
"Read",
2164
"Write",
2165
}};
2166
return names[static_cast<size_t>(type)];
2167
}
2168
2169
bool CPU::HasBreakpointAtAddress(BreakpointType type, VirtualMemoryAddress address)
2170
{
2171
for (Breakpoint& bp : GetBreakpointList(type))
2172
{
2173
if (bp.enabled && (bp.address & 0x0FFFFFFFu) == (address & 0x0FFFFFFFu))
2174
{
2175
bp.hit_count++;
2176
return true;
2177
}
2178
}
2179
2180
return false;
2181
}
2182
2183
CPU::BreakpointList CPU::CopyBreakpointList(bool include_auto_clear, bool include_callbacks)
2184
{
2185
BreakpointList bps;
2186
2187
size_t total = 0;
2188
for (const BreakpointList& bplist : s_breakpoints)
2189
total += bplist.size();
2190
2191
bps.reserve(total);
2192
2193
for (const BreakpointList& bplist : s_breakpoints)
2194
{
2195
for (const Breakpoint& bp : bplist)
2196
{
2197
if (bp.callback && !include_callbacks)
2198
continue;
2199
if (bp.auto_clear && !include_auto_clear)
2200
continue;
2201
2202
bps.push_back(bp);
2203
}
2204
}
2205
2206
return bps;
2207
}
2208
2209
bool CPU::AddBreakpoint(BreakpointType type, VirtualMemoryAddress address, bool auto_clear, bool enabled)
2210
{
2211
if (HasBreakpointAtAddress(type, address))
2212
return false;
2213
2214
INFO_LOG("Adding {} breakpoint at {:08X}, auto clear = {}", GetBreakpointTypeName(type), address,
2215
static_cast<unsigned>(auto_clear));
2216
2217
Breakpoint bp{address, nullptr, auto_clear ? 0 : s_breakpoint_counter++, 0, type, auto_clear, enabled};
2218
GetBreakpointList(type).push_back(std::move(bp));
2219
if (UpdateDebugDispatcherFlag())
2220
System::InterruptExecution();
2221
2222
if (!auto_clear)
2223
Host::ReportDebuggerMessage(fmt::format("Added breakpoint at 0x{:08X}.", address));
2224
2225
return true;
2226
}
2227
2228
bool CPU::AddBreakpointWithCallback(BreakpointType type, VirtualMemoryAddress address, BreakpointCallback callback)
2229
{
2230
if (HasBreakpointAtAddress(type, address))
2231
return false;
2232
2233
INFO_LOG("Adding {} breakpoint with callback at {:08X}", GetBreakpointTypeName(type), address);
2234
2235
Breakpoint bp{address, callback, 0, 0, type, false, true};
2236
GetBreakpointList(type).push_back(std::move(bp));
2237
if (UpdateDebugDispatcherFlag())
2238
System::InterruptExecution();
2239
return true;
2240
}
2241
2242
bool CPU::SetBreakpointEnabled(BreakpointType type, VirtualMemoryAddress address, bool enabled)
2243
{
2244
BreakpointList& bplist = GetBreakpointList(type);
2245
auto it =
2246
std::find_if(bplist.begin(), bplist.end(), [address](const Breakpoint& bp) { return bp.address == address; });
2247
if (it == bplist.end())
2248
return false;
2249
2250
Host::ReportDebuggerMessage(fmt::format("{} {} breakpoint at 0x{:08X}.", enabled ? "Enabled" : "Disabled",
2251
GetBreakpointTypeName(type), address));
2252
it->enabled = enabled;
2253
2254
if (UpdateDebugDispatcherFlag())
2255
System::InterruptExecution();
2256
2257
if (address == s_last_breakpoint_check_pc && !enabled)
2258
s_last_breakpoint_check_pc = INVALID_BREAKPOINT_PC;
2259
2260
return true;
2261
}
2262
2263
bool CPU::RemoveBreakpoint(BreakpointType type, VirtualMemoryAddress address)
2264
{
2265
BreakpointList& bplist = GetBreakpointList(type);
2266
auto it =
2267
std::find_if(bplist.begin(), bplist.end(), [address](const Breakpoint& bp) { return bp.address == address; });
2268
if (it == bplist.end())
2269
return false;
2270
2271
Host::ReportDebuggerMessage(fmt::format("Removed {} breakpoint at 0x{:08X}.", GetBreakpointTypeName(type), address));
2272
2273
bplist.erase(it);
2274
if (UpdateDebugDispatcherFlag())
2275
System::InterruptExecution();
2276
2277
if (address == s_last_breakpoint_check_pc)
2278
s_last_breakpoint_check_pc = INVALID_BREAKPOINT_PC;
2279
2280
return true;
2281
}
2282
2283
void CPU::ClearBreakpoints()
2284
{
2285
for (BreakpointList& bplist : s_breakpoints)
2286
bplist.clear();
2287
s_breakpoint_counter = 0;
2288
s_last_breakpoint_check_pc = INVALID_BREAKPOINT_PC;
2289
if (UpdateDebugDispatcherFlag())
2290
System::InterruptExecution();
2291
}
2292
2293
bool CPU::AddStepOverBreakpoint()
2294
{
2295
u32 bp_pc = g_state.pc;
2296
2297
Instruction inst;
2298
if (!SafeReadInstruction(bp_pc, &inst.bits))
2299
return false;
2300
2301
bp_pc += sizeof(Instruction);
2302
2303
if (!IsCallInstruction(inst))
2304
{
2305
Host::ReportDebuggerMessage(fmt::format("0x{:08X} is not a call instruction.", g_state.pc));
2306
return false;
2307
}
2308
2309
if (!SafeReadInstruction(bp_pc, &inst.bits))
2310
return false;
2311
2312
if (IsBranchInstruction(inst))
2313
{
2314
Host::ReportDebuggerMessage(fmt::format("Can't step over double branch at 0x{:08X}", g_state.pc));
2315
return false;
2316
}
2317
2318
// skip the delay slot
2319
bp_pc += sizeof(Instruction);
2320
2321
Host::ReportDebuggerMessage(fmt::format("Stepping over to 0x{:08X}.", bp_pc));
2322
2323
return AddBreakpoint(BreakpointType::Execute, bp_pc, true);
2324
}
2325
2326
bool CPU::AddStepOutBreakpoint(u32 max_instructions_to_search)
2327
{
2328
// find the branch-to-ra instruction.
2329
u32 ret_pc = g_state.pc;
2330
for (u32 i = 0; i < max_instructions_to_search; i++)
2331
{
2332
ret_pc += sizeof(Instruction);
2333
2334
Instruction inst;
2335
if (!SafeReadInstruction(ret_pc, &inst.bits))
2336
{
2337
Host::ReportDebuggerMessage(
2338
fmt::format("Instruction read failed at {:08X} while searching for function end.", ret_pc));
2339
return false;
2340
}
2341
2342
if (IsReturnInstruction(inst))
2343
{
2344
Host::ReportDebuggerMessage(fmt::format("Stepping out to 0x{:08X}.", ret_pc));
2345
return AddBreakpoint(BreakpointType::Execute, ret_pc, true);
2346
}
2347
}
2348
2349
Host::ReportDebuggerMessage(fmt::format("No return instruction found after {} instructions for step-out at {:08X}.",
2350
max_instructions_to_search, g_state.pc));
2351
2352
return false;
2353
}
2354
2355
ALWAYS_INLINE_RELEASE bool CPU::CheckBreakpointList(BreakpointType type, VirtualMemoryAddress address)
2356
{
2357
BreakpointList& bplist = GetBreakpointList(type);
2358
size_t count = bplist.size();
2359
if (count == 0) [[likely]]
2360
return false;
2361
2362
for (size_t i = 0; i < count;)
2363
{
2364
Breakpoint& bp = bplist[i];
2365
if (!bp.enabled || (bp.address & 0x0FFFFFFFu) != (address & 0x0FFFFFFFu))
2366
{
2367
i++;
2368
continue;
2369
}
2370
2371
bp.hit_count++;
2372
2373
const u32 pc = g_state.pc;
2374
2375
if (bp.callback)
2376
{
2377
// if callback returns false, the bp is no longer recorded
2378
if (!bp.callback(BreakpointType::Execute, pc, address))
2379
{
2380
bplist.erase(bplist.begin() + i);
2381
count--;
2382
UpdateDebugDispatcherFlag();
2383
}
2384
else
2385
{
2386
i++;
2387
}
2388
}
2389
else
2390
{
2391
System::PauseSystem(true);
2392
2393
TinyString msg;
2394
if (bp.auto_clear)
2395
{
2396
msg.format("Stopped execution at 0x{:08X}.", pc);
2397
Host::ReportDebuggerMessage(msg);
2398
bplist.erase(bplist.begin() + i);
2399
count--;
2400
UpdateDebugDispatcherFlag();
2401
}
2402
else
2403
{
2404
msg.format("Hit {} breakpoint {} at 0x{:08X}, Hit Count {}.", GetBreakpointTypeName(type), bp.number, address,
2405
bp.hit_count);
2406
Host::ReportDebuggerMessage(msg);
2407
i++;
2408
}
2409
2410
return true;
2411
}
2412
}
2413
2414
return false;
2415
}
2416
2417
ALWAYS_INLINE_RELEASE void CPU::ExecutionBreakpointCheck()
2418
{
2419
if (s_breakpoints[static_cast<u32>(BreakpointType::Execute)].empty()) [[likely]]
2420
return;
2421
2422
const u32 pc = g_state.pc;
2423
if (pc == s_last_breakpoint_check_pc || s_break_type == ExecutionBreakType::ExecuteOneInstruction) [[unlikely]]
2424
{
2425
// we don't want to trigger the same breakpoint which just paused us repeatedly.
2426
return;
2427
}
2428
2429
s_last_breakpoint_check_pc = pc;
2430
2431
if (CheckBreakpointList(BreakpointType::Execute, pc)) [[unlikely]]
2432
{
2433
s_break_type = ExecutionBreakType::None;
2434
ExitExecution();
2435
}
2436
}
2437
2438
template<MemoryAccessType type>
2439
ALWAYS_INLINE_RELEASE void CPU::MemoryBreakpointCheck(VirtualMemoryAddress address)
2440
{
2441
const BreakpointType bptype = (type == MemoryAccessType::Read) ? BreakpointType::Read : BreakpointType::Write;
2442
if (CheckBreakpointList(bptype, address)) [[unlikely]]
2443
s_break_type = ExecutionBreakType::Breakpoint;
2444
}
2445
2446
template<PGXPMode pgxp_mode, bool debug>
2447
[[noreturn]] void CPU::ExecuteImpl()
2448
{
2449
if (g_state.pending_ticks >= g_state.downcount)
2450
TimingEvents::RunEvents();
2451
2452
for (;;)
2453
{
2454
do
2455
{
2456
if constexpr (debug)
2457
{
2458
Cop0ExecutionBreakpointCheck();
2459
ExecutionBreakpointCheck();
2460
}
2461
2462
g_state.pending_ticks++;
2463
2464
// now executing the instruction we previously fetched
2465
g_state.current_instruction.bits = g_state.next_instruction.bits;
2466
g_state.current_instruction_pc = g_state.pc;
2467
g_state.current_instruction_in_branch_delay_slot = g_state.next_instruction_is_branch_delay_slot;
2468
g_state.current_instruction_was_branch_taken = g_state.branch_was_taken;
2469
g_state.next_instruction_is_branch_delay_slot = false;
2470
g_state.branch_was_taken = false;
2471
2472
// fetch the next instruction - even if this fails, it'll still refetch on the flush so we can continue
2473
if (!FetchInstruction())
2474
continue;
2475
2476
// trace functionality
2477
if constexpr (debug)
2478
{
2479
if (s_trace_to_log)
2480
LogInstruction(g_state.current_instruction.bits, g_state.current_instruction_pc, true);
2481
2482
// handle all mirrors of the syscall trampoline. will catch 200000A0 etc, but those aren't fetchable anyway
2483
const u32 masked_pc = (g_state.current_instruction_pc & KSEG_MASK);
2484
if (masked_pc == 0xA0) [[unlikely]]
2485
HandleA0Syscall();
2486
else if (masked_pc == 0xB0) [[unlikely]]
2487
HandleB0Syscall();
2488
}
2489
2490
#if 0 // GTE flag test debugging
2491
if (g_state.m_current_instruction_pc == 0x8002cdf4)
2492
{
2493
if (g_state.m_regs.v1 != g_state.m_regs.v0)
2494
printf("Got %08X Expected? %08X\n", g_state.m_regs.v1, g_state.m_regs.v0);
2495
}
2496
#endif
2497
2498
// execute the instruction we previously fetched
2499
ExecuteInstruction<pgxp_mode, debug>();
2500
2501
// next load delay
2502
UpdateLoadDelay();
2503
2504
if constexpr (debug)
2505
{
2506
if (s_break_type != ExecutionBreakType::None) [[unlikely]]
2507
{
2508
const ExecutionBreakType break_type = std::exchange(s_break_type, ExecutionBreakType::None);
2509
if (break_type >= ExecutionBreakType::SingleStep)
2510
System::PauseSystem(true);
2511
2512
UpdateDebugDispatcherFlag();
2513
ExitExecution();
2514
}
2515
}
2516
} while (g_state.pending_ticks < g_state.downcount);
2517
2518
TimingEvents::RunEvents();
2519
}
2520
}
2521
2522
void CPU::ExecuteInterpreter()
2523
{
2524
if (g_state.using_debug_dispatcher)
2525
{
2526
if (g_settings.gpu_pgxp_enable)
2527
{
2528
if (g_settings.gpu_pgxp_cpu)
2529
ExecuteImpl<PGXPMode::CPU, true>();
2530
else
2531
ExecuteImpl<PGXPMode::Memory, true>();
2532
}
2533
else
2534
{
2535
ExecuteImpl<PGXPMode::Disabled, true>();
2536
}
2537
}
2538
else
2539
{
2540
if (g_settings.gpu_pgxp_enable)
2541
{
2542
if (g_settings.gpu_pgxp_cpu)
2543
ExecuteImpl<PGXPMode::CPU, false>();
2544
else
2545
ExecuteImpl<PGXPMode::Memory, false>();
2546
}
2547
else
2548
{
2549
ExecuteImpl<PGXPMode::Disabled, false>();
2550
}
2551
}
2552
}
2553
2554
fastjmp_buf* CPU::GetExecutionJmpBuf()
2555
{
2556
return &s_jmp_buf;
2557
}
2558
2559
void CPU::Execute()
2560
{
2561
CheckForExecutionModeChange();
2562
2563
if (fastjmp_set(&s_jmp_buf) != 0)
2564
return;
2565
2566
if (g_state.using_interpreter)
2567
ExecuteInterpreter();
2568
else
2569
CodeCache::Execute();
2570
}
2571
2572
void CPU::SetSingleStepFlag()
2573
{
2574
s_break_type = ExecutionBreakType::SingleStep;
2575
if (UpdateDebugDispatcherFlag())
2576
System::InterruptExecution();
2577
}
2578
2579
template<PGXPMode pgxp_mode>
2580
void CPU::CodeCache::InterpretCachedBlock(const Block* block)
2581
{
2582
// set up the state so we've already fetched the instruction
2583
DebugAssert(g_state.pc == block->pc);
2584
g_state.npc = block->pc + 4;
2585
g_state.exception_raised = false;
2586
2587
const Instruction* instruction = block->Instructions();
2588
const Instruction* end_instruction = instruction + block->size;
2589
const CodeCache::InstructionInfo* info = block->InstructionsInfo();
2590
2591
do
2592
{
2593
g_state.pending_ticks++;
2594
2595
// now executing the instruction we previously fetched
2596
g_state.current_instruction.bits = instruction->bits;
2597
g_state.current_instruction_pc = g_state.pc;
2598
g_state.current_instruction_in_branch_delay_slot = info->is_branch_delay_slot; // TODO: let int set it instead
2599
g_state.current_instruction_was_branch_taken = g_state.branch_was_taken;
2600
g_state.branch_was_taken = false;
2601
2602
// update pc
2603
g_state.pc = g_state.npc;
2604
g_state.npc += 4;
2605
2606
// execute the instruction we previously fetched
2607
ExecuteInstruction<pgxp_mode, false>();
2608
2609
// next load delay
2610
UpdateLoadDelay();
2611
2612
if (g_state.exception_raised)
2613
break;
2614
2615
instruction++;
2616
info++;
2617
} while (instruction != end_instruction);
2618
2619
// cleanup so the interpreter can kick in if needed
2620
g_state.next_instruction_is_branch_delay_slot = false;
2621
}
2622
2623
template void CPU::CodeCache::InterpretCachedBlock<PGXPMode::Disabled>(const Block* block);
2624
template void CPU::CodeCache::InterpretCachedBlock<PGXPMode::Memory>(const Block* block);
2625
template void CPU::CodeCache::InterpretCachedBlock<PGXPMode::CPU>(const Block* block);
2626
2627
template<PGXPMode pgxp_mode>
2628
void CPU::CodeCache::InterpretUncachedBlock()
2629
{
2630
g_state.npc = g_state.pc;
2631
g_state.exception_raised = false;
2632
g_state.bus_error = false;
2633
if (!FetchInstructionForInterpreterFallback())
2634
return;
2635
2636
// At this point, pc contains the last address executed (in the previous block). The instruction has not been fetched
2637
// yet. pc shouldn't be updated until the fetch occurs, that way the exception occurs in the delay slot.
2638
bool in_branch_delay_slot = false;
2639
for (;;)
2640
{
2641
g_state.pending_ticks++;
2642
2643
// now executing the instruction we previously fetched
2644
g_state.current_instruction.bits = g_state.next_instruction.bits;
2645
g_state.current_instruction_pc = g_state.pc;
2646
g_state.current_instruction_in_branch_delay_slot = g_state.next_instruction_is_branch_delay_slot;
2647
g_state.current_instruction_was_branch_taken = g_state.branch_was_taken;
2648
g_state.next_instruction_is_branch_delay_slot = false;
2649
g_state.branch_was_taken = false;
2650
2651
// Fetch the next instruction, except if we're in a branch delay slot. The "fetch" is done in the next block.
2652
const bool branch = IsBranchInstruction(g_state.current_instruction);
2653
if (!g_state.current_instruction_in_branch_delay_slot || branch)
2654
{
2655
if (!FetchInstructionForInterpreterFallback())
2656
break;
2657
}
2658
else
2659
{
2660
g_state.pc = g_state.npc;
2661
}
2662
2663
// execute the instruction we previously fetched
2664
ExecuteInstruction<pgxp_mode, false>();
2665
2666
// next load delay
2667
UpdateLoadDelay();
2668
2669
if (g_state.exception_raised || (!branch && in_branch_delay_slot) ||
2670
IsExitBlockInstruction(g_state.current_instruction))
2671
{
2672
break;
2673
}
2674
else if ((g_state.current_instruction.bits & 0xFFC0FFFFu) == 0x40806000u && HasPendingInterrupt())
2675
{
2676
// mtc0 rt, sr - Jackie Chan Stuntmaster, MTV Sports games.
2677
// Pain in the ass games trigger a software interrupt by writing to SR.Im.
2678
break;
2679
}
2680
2681
in_branch_delay_slot = branch;
2682
}
2683
}
2684
2685
template void CPU::CodeCache::InterpretUncachedBlock<PGXPMode::Disabled>();
2686
template void CPU::CodeCache::InterpretUncachedBlock<PGXPMode::Memory>();
2687
template void CPU::CodeCache::InterpretUncachedBlock<PGXPMode::CPU>();
2688
2689
bool CPU::RecompilerThunks::InterpretInstruction()
2690
{
2691
g_state.exception_raised = false;
2692
g_state.bus_error = false;
2693
ExecuteInstruction<PGXPMode::Disabled, false>();
2694
return g_state.exception_raised;
2695
}
2696
2697
bool CPU::RecompilerThunks::InterpretInstructionPGXP()
2698
{
2699
g_state.exception_raised = false;
2700
g_state.bus_error = false;
2701
ExecuteInstruction<PGXPMode::Memory, false>();
2702
return g_state.exception_raised;
2703
}
2704
2705
ALWAYS_INLINE_RELEASE Bus::MemoryReadHandler CPU::GetMemoryReadHandler(VirtualMemoryAddress address,
2706
MemoryAccessSize size)
2707
{
2708
Bus::MemoryReadHandler* base =
2709
Bus::OffsetHandlerArray<Bus::MemoryReadHandler>(g_state.memory_handlers, size, MemoryAccessType::Read);
2710
return base[address >> Bus::MEMORY_LUT_PAGE_SHIFT];
2711
}
2712
2713
ALWAYS_INLINE_RELEASE Bus::MemoryWriteHandler CPU::GetMemoryWriteHandler(VirtualMemoryAddress address,
2714
MemoryAccessSize size)
2715
{
2716
Bus::MemoryWriteHandler* base =
2717
Bus::OffsetHandlerArray<Bus::MemoryWriteHandler>(g_state.memory_handlers, size, MemoryAccessType::Write);
2718
return base[address >> Bus::MEMORY_LUT_PAGE_SHIFT];
2719
}
2720
2721
void CPU::UpdateMemoryPointers()
2722
{
2723
g_state.memory_handlers = Bus::GetMemoryHandlers(g_state.cop0_regs.sr.Isc, g_state.cop0_regs.sr.Swc);
2724
g_state.fastmem_base = Bus::GetFastmemBase(g_state.cop0_regs.sr.Isc);
2725
}
2726
2727
template<bool add_ticks, bool icache_read, u32 word_count, bool raise_exceptions>
2728
ALWAYS_INLINE_RELEASE bool CPU::DoInstructionRead(PhysicalMemoryAddress address, u32* data)
2729
{
2730
using namespace Bus;
2731
2732
// We can shortcut around VirtualAddressToPhysical() here because we're never going to be
2733
// calling with an out-of-range address.
2734
DebugAssert(VirtualAddressToPhysical(address) == (address & KSEG_MASK));
2735
address &= KSEG_MASK;
2736
2737
if (address < RAM_MIRROR_END)
2738
{
2739
std::memcpy(data, &g_ram[address & g_ram_mask], sizeof(u32) * word_count);
2740
if constexpr (add_ticks)
2741
g_state.pending_ticks += (icache_read ? 1 : RAM_READ_TICKS) * word_count;
2742
2743
return true;
2744
}
2745
else if (address >= BIOS_BASE && address < (BIOS_BASE + BIOS_SIZE))
2746
{
2747
std::memcpy(data, &g_bios[(address - BIOS_BASE) & BIOS_MASK], sizeof(u32) * word_count);
2748
if constexpr (add_ticks)
2749
g_state.pending_ticks += g_bios_access_time[static_cast<u32>(MemoryAccessSize::Word)] * word_count;
2750
2751
return true;
2752
}
2753
else if (address >= EXP1_BASE && address < (EXP1_BASE + EXP1_SIZE))
2754
{
2755
g_pio_device->CodeReadHandler(address & EXP1_MASK, data, word_count);
2756
if constexpr (add_ticks)
2757
g_state.pending_ticks += g_exp1_access_time[static_cast<u32>(MemoryAccessSize::Word)] * word_count;
2758
2759
return true;
2760
}
2761
else [[unlikely]]
2762
{
2763
if (raise_exceptions)
2764
{
2765
g_state.cop0_regs.BadVaddr = address;
2766
RaiseException(Cop0Registers::CAUSE::MakeValueForException(Exception::IBE, false, false, 0), address);
2767
}
2768
2769
std::memset(data, 0, sizeof(u32) * word_count);
2770
return false;
2771
}
2772
}
2773
2774
TickCount CPU::GetInstructionReadTicks(VirtualMemoryAddress address)
2775
{
2776
using namespace Bus;
2777
2778
DebugAssert(VirtualAddressToPhysical(address) == (address & KSEG_MASK));
2779
address &= KSEG_MASK;
2780
2781
if (address < RAM_MIRROR_END)
2782
{
2783
return RAM_READ_TICKS;
2784
}
2785
else if (address >= BIOS_BASE && address < (BIOS_BASE + BIOS_MIRROR_SIZE))
2786
{
2787
return g_bios_access_time[static_cast<u32>(MemoryAccessSize::Word)];
2788
}
2789
else
2790
{
2791
return 0;
2792
}
2793
}
2794
2795
TickCount CPU::GetICacheFillTicks(VirtualMemoryAddress address)
2796
{
2797
using namespace Bus;
2798
2799
DebugAssert(VirtualAddressToPhysical(address) == (address & KSEG_MASK));
2800
address &= KSEG_MASK;
2801
2802
if (address < RAM_MIRROR_END)
2803
{
2804
return 1 * ((ICACHE_LINE_SIZE - (address & (ICACHE_LINE_SIZE - 1))) / sizeof(u32));
2805
}
2806
else if (address >= BIOS_BASE && address < (BIOS_BASE + BIOS_MIRROR_SIZE))
2807
{
2808
return g_bios_access_time[static_cast<u32>(MemoryAccessSize::Word)] *
2809
((ICACHE_LINE_SIZE - (address & (ICACHE_LINE_SIZE - 1))) / sizeof(u32));
2810
}
2811
else
2812
{
2813
return 0;
2814
}
2815
}
2816
2817
void CPU::CheckAndUpdateICacheTags(u32 line_count)
2818
{
2819
VirtualMemoryAddress current_pc = g_state.pc & ICACHE_TAG_ADDRESS_MASK;
2820
2821
TickCount ticks = 0;
2822
TickCount cached_ticks_per_line = GetICacheFillTicks(current_pc);
2823
for (u32 i = 0; i < line_count; i++, current_pc += ICACHE_LINE_SIZE)
2824
{
2825
const u32 line = GetICacheLine(current_pc);
2826
if (g_state.icache_tags[line] != current_pc)
2827
{
2828
g_state.icache_tags[line] = current_pc;
2829
ticks += cached_ticks_per_line;
2830
}
2831
}
2832
2833
g_state.pending_ticks += ticks;
2834
}
2835
2836
u32 CPU::FillICache(VirtualMemoryAddress address)
2837
{
2838
const u32 line = GetICacheLine(address);
2839
const u32 line_word_offset = GetICacheLineWordOffset(address);
2840
u32* const line_data = g_state.icache_data.data() + (line * ICACHE_WORDS_PER_LINE);
2841
u32* const offset_line_data = line_data + line_word_offset;
2842
u32 line_tag;
2843
switch (line_word_offset)
2844
{
2845
case 0:
2846
DoInstructionRead<true, true, 4, false>(address & ~(ICACHE_LINE_SIZE - 1u), offset_line_data);
2847
line_tag = GetICacheTagForAddress(address);
2848
break;
2849
case 1:
2850
DoInstructionRead<true, true, 3, false>(address & (~(ICACHE_LINE_SIZE - 1u) | 0x4), offset_line_data);
2851
line_tag = GetICacheTagForAddress(address) | 0x1;
2852
break;
2853
case 2:
2854
DoInstructionRead<true, true, 2, false>(address & (~(ICACHE_LINE_SIZE - 1u) | 0x8), offset_line_data);
2855
line_tag = GetICacheTagForAddress(address) | 0x3;
2856
break;
2857
case 3:
2858
default:
2859
DoInstructionRead<true, true, 1, false>(address & (~(ICACHE_LINE_SIZE - 1u) | 0xC), offset_line_data);
2860
line_tag = GetICacheTagForAddress(address) | 0x7;
2861
break;
2862
}
2863
2864
g_state.icache_tags[line] = line_tag;
2865
return offset_line_data[0];
2866
}
2867
2868
void CPU::ClearICache()
2869
{
2870
std::memset(g_state.icache_data.data(), 0, ICACHE_SIZE);
2871
g_state.icache_tags.fill(ICACHE_INVALID_BITS);
2872
}
2873
2874
namespace CPU {
2875
ALWAYS_INLINE_RELEASE static u32 ReadICache(VirtualMemoryAddress address)
2876
{
2877
const u32 line = GetICacheLine(address);
2878
const u32 line_word_offset = GetICacheLineWordOffset(address);
2879
const u32* const line_data = g_state.icache_data.data() + (line * ICACHE_WORDS_PER_LINE);
2880
return line_data[line_word_offset];
2881
}
2882
} // namespace CPU
2883
2884
ALWAYS_INLINE_RELEASE bool CPU::FetchInstruction()
2885
{
2886
DebugAssert(Common::IsAlignedPow2(g_state.npc, 4));
2887
2888
const PhysicalMemoryAddress address = g_state.npc;
2889
switch (address >> 29)
2890
{
2891
case 0x00: // KUSEG 0M-512M
2892
case 0x04: // KSEG0 - physical memory cached
2893
{
2894
#if 0
2895
DoInstructionRead<true, false, 1, false>(address, &g_state.next_instruction.bits);
2896
#else
2897
if (CompareICacheTag(address))
2898
g_state.next_instruction.bits = ReadICache(address);
2899
else
2900
g_state.next_instruction.bits = FillICache(address);
2901
#endif
2902
}
2903
break;
2904
2905
case 0x05: // KSEG1 - physical memory uncached
2906
{
2907
if (!DoInstructionRead<true, false, 1, true>(address, &g_state.next_instruction.bits))
2908
return false;
2909
}
2910
break;
2911
2912
case 0x01: // KUSEG 512M-1024M
2913
case 0x02: // KUSEG 1024M-1536M
2914
case 0x03: // KUSEG 1536M-2048M
2915
case 0x06: // KSEG2
2916
case 0x07: // KSEG2
2917
default:
2918
{
2919
CPU::RaiseException(Cop0Registers::CAUSE::MakeValueForException(Exception::IBE, false, false, 0), address);
2920
return false;
2921
}
2922
}
2923
2924
g_state.pc = g_state.npc;
2925
g_state.npc += sizeof(g_state.next_instruction.bits);
2926
return true;
2927
}
2928
2929
bool CPU::FetchInstructionForInterpreterFallback()
2930
{
2931
if (!Common::IsAlignedPow2(g_state.npc, 4)) [[unlikely]]
2932
{
2933
// The BadVaddr and EPC must be set to the fetching address, not the instruction about to execute.
2934
g_state.cop0_regs.BadVaddr = g_state.npc;
2935
RaiseException(Cop0Registers::CAUSE::MakeValueForException(Exception::AdEL, false, false, 0), g_state.npc);
2936
return false;
2937
}
2938
2939
const PhysicalMemoryAddress address = g_state.npc;
2940
switch (address >> 29)
2941
{
2942
case 0x00: // KUSEG 0M-512M
2943
case 0x04: // KSEG0 - physical memory cached
2944
case 0x05: // KSEG1 - physical memory uncached
2945
{
2946
// We don't use the icache when doing interpreter fallbacks, because it's probably stale.
2947
if (!DoInstructionRead<false, false, 1, true>(address, &g_state.next_instruction.bits)) [[unlikely]]
2948
return false;
2949
}
2950
break;
2951
2952
case 0x01: // KUSEG 512M-1024M
2953
case 0x02: // KUSEG 1024M-1536M
2954
case 0x03: // KUSEG 1536M-2048M
2955
case 0x06: // KSEG2
2956
case 0x07: // KSEG2
2957
default:
2958
{
2959
CPU::RaiseException(Cop0Registers::CAUSE::MakeValueForException(Exception::IBE,
2960
g_state.current_instruction_in_branch_delay_slot,
2961
g_state.current_instruction_was_branch_taken, 0),
2962
address);
2963
return false;
2964
}
2965
}
2966
2967
g_state.pc = g_state.npc;
2968
g_state.npc += sizeof(g_state.next_instruction.bits);
2969
return true;
2970
}
2971
2972
bool CPU::SafeReadInstruction(VirtualMemoryAddress addr, u32* value)
2973
{
2974
switch (addr >> 29)
2975
{
2976
case 0x00: // KUSEG 0M-512M
2977
case 0x04: // KSEG0 - physical memory cached
2978
case 0x05: // KSEG1 - physical memory uncached
2979
{
2980
// TODO: Check icache.
2981
return DoInstructionRead<false, false, 1, false>(addr, value);
2982
}
2983
2984
case 0x01: // KUSEG 512M-1024M
2985
case 0x02: // KUSEG 1024M-1536M
2986
case 0x03: // KUSEG 1536M-2048M
2987
case 0x06: // KSEG2
2988
case 0x07: // KSEG2
2989
default:
2990
{
2991
return false;
2992
}
2993
}
2994
}
2995
2996
template<MemoryAccessType type, MemoryAccessSize size>
2997
ALWAYS_INLINE bool CPU::DoSafeMemoryAccess(VirtualMemoryAddress address, u32& value)
2998
{
2999
using namespace Bus;
3000
3001
switch (address >> 29)
3002
{
3003
case 0x00: // KUSEG 0M-512M
3004
case 0x04: // KSEG0 - physical memory cached
3005
{
3006
if ((address & SCRATCHPAD_ADDR_MASK) == SCRATCHPAD_ADDR)
3007
{
3008
const u32 offset = address & SCRATCHPAD_OFFSET_MASK;
3009
3010
if constexpr (type == MemoryAccessType::Read)
3011
{
3012
if constexpr (size == MemoryAccessSize::Byte)
3013
{
3014
value = CPU::g_state.scratchpad[offset];
3015
}
3016
else if constexpr (size == MemoryAccessSize::HalfWord)
3017
{
3018
u16 temp;
3019
std::memcpy(&temp, &CPU::g_state.scratchpad[offset], sizeof(u16));
3020
value = ZeroExtend32(temp);
3021
}
3022
else if constexpr (size == MemoryAccessSize::Word)
3023
{
3024
std::memcpy(&value, &CPU::g_state.scratchpad[offset], sizeof(u32));
3025
}
3026
}
3027
else
3028
{
3029
if constexpr (size == MemoryAccessSize::Byte)
3030
{
3031
CPU::g_state.scratchpad[offset] = Truncate8(value);
3032
}
3033
else if constexpr (size == MemoryAccessSize::HalfWord)
3034
{
3035
std::memcpy(&CPU::g_state.scratchpad[offset], &value, sizeof(u16));
3036
}
3037
else if constexpr (size == MemoryAccessSize::Word)
3038
{
3039
std::memcpy(&CPU::g_state.scratchpad[offset], &value, sizeof(u32));
3040
}
3041
}
3042
3043
return true;
3044
}
3045
3046
address &= KSEG_MASK;
3047
}
3048
break;
3049
3050
case 0x01: // KUSEG 512M-1024M
3051
case 0x02: // KUSEG 1024M-1536M
3052
case 0x03: // KUSEG 1536M-2048M
3053
case 0x06: // KSEG2
3054
case 0x07: // KSEG2
3055
{
3056
// Above 512mb raises an exception.
3057
return false;
3058
}
3059
3060
case 0x05: // KSEG1 - physical memory uncached
3061
{
3062
address &= KSEG_MASK;
3063
}
3064
break;
3065
}
3066
3067
if (address < RAM_MIRROR_END)
3068
{
3069
const u32 offset = address & g_ram_mask;
3070
if constexpr (type == MemoryAccessType::Read)
3071
{
3072
if constexpr (size == MemoryAccessSize::Byte)
3073
{
3074
value = g_unprotected_ram[offset];
3075
}
3076
else if constexpr (size == MemoryAccessSize::HalfWord)
3077
{
3078
u16 temp;
3079
std::memcpy(&temp, &g_unprotected_ram[offset], sizeof(temp));
3080
value = ZeroExtend32(temp);
3081
}
3082
else if constexpr (size == MemoryAccessSize::Word)
3083
{
3084
std::memcpy(&value, &g_unprotected_ram[offset], sizeof(u32));
3085
}
3086
}
3087
else
3088
{
3089
const u32 page_index = offset >> HOST_PAGE_SHIFT;
3090
3091
if constexpr (size == MemoryAccessSize::Byte)
3092
{
3093
if (g_unprotected_ram[offset] != Truncate8(value))
3094
{
3095
g_unprotected_ram[offset] = Truncate8(value);
3096
if (g_ram_code_bits[page_index])
3097
CPU::CodeCache::InvalidateBlocksWithPageIndex(page_index);
3098
}
3099
}
3100
else if constexpr (size == MemoryAccessSize::HalfWord)
3101
{
3102
const u16 new_value = Truncate16(value);
3103
u16 old_value;
3104
std::memcpy(&old_value, &g_unprotected_ram[offset], sizeof(old_value));
3105
if (old_value != new_value)
3106
{
3107
std::memcpy(&g_unprotected_ram[offset], &new_value, sizeof(u16));
3108
if (g_ram_code_bits[page_index])
3109
CPU::CodeCache::InvalidateBlocksWithPageIndex(page_index);
3110
}
3111
}
3112
else if constexpr (size == MemoryAccessSize::Word)
3113
{
3114
u32 old_value;
3115
std::memcpy(&old_value, &g_unprotected_ram[offset], sizeof(u32));
3116
if (old_value != value)
3117
{
3118
std::memcpy(&g_unprotected_ram[offset], &value, sizeof(u32));
3119
if (g_ram_code_bits[page_index])
3120
CPU::CodeCache::InvalidateBlocksWithPageIndex(page_index);
3121
}
3122
}
3123
}
3124
3125
return true;
3126
}
3127
if constexpr (type == MemoryAccessType::Read)
3128
{
3129
if (address >= BIOS_BASE && address < (BIOS_BASE + BIOS_SIZE))
3130
{
3131
const u32 offset = (address & BIOS_MASK);
3132
if constexpr (size == MemoryAccessSize::Byte)
3133
{
3134
value = ZeroExtend32(g_bios[offset]);
3135
}
3136
else if constexpr (size == MemoryAccessSize::HalfWord)
3137
{
3138
u16 halfword;
3139
std::memcpy(&halfword, &g_bios[offset], sizeof(u16));
3140
value = ZeroExtend32(halfword);
3141
}
3142
else
3143
{
3144
std::memcpy(&value, &g_bios[offset], sizeof(u32));
3145
}
3146
3147
return true;
3148
}
3149
}
3150
return false;
3151
}
3152
3153
bool CPU::SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value)
3154
{
3155
u32 temp = 0;
3156
if (!DoSafeMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Byte>(addr, temp))
3157
return false;
3158
3159
*value = Truncate8(temp);
3160
return true;
3161
}
3162
3163
bool CPU::SafeReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value)
3164
{
3165
if ((addr & 1) == 0)
3166
{
3167
u32 temp = 0;
3168
if (!DoSafeMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::HalfWord>(addr, temp))
3169
return false;
3170
3171
*value = Truncate16(temp);
3172
return true;
3173
}
3174
3175
u8 low, high;
3176
if (!SafeReadMemoryByte(addr, &low) || !SafeReadMemoryByte(addr + 1, &high))
3177
return false;
3178
3179
*value = (ZeroExtend16(high) << 8) | ZeroExtend16(low);
3180
return true;
3181
}
3182
3183
bool CPU::SafeReadMemoryWord(VirtualMemoryAddress addr, u32* value)
3184
{
3185
if ((addr & 3) == 0)
3186
return DoSafeMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(addr, *value);
3187
3188
u16 low, high;
3189
if (!SafeReadMemoryHalfWord(addr, &low) || !SafeReadMemoryHalfWord(addr + 2, &high))
3190
return false;
3191
3192
*value = (ZeroExtend32(high) << 16) | ZeroExtend32(low);
3193
return true;
3194
}
3195
3196
bool CPU::SafeReadMemoryCString(VirtualMemoryAddress addr, SmallStringBase* value, u32 max_length /*= 1024*/)
3197
{
3198
value->clear();
3199
3200
u8 ch;
3201
while (SafeReadMemoryByte(addr, &ch))
3202
{
3203
if (ch == 0)
3204
return true;
3205
3206
value->push_back(ch);
3207
if (value->length() >= max_length)
3208
return true;
3209
3210
addr++;
3211
}
3212
3213
value->clear();
3214
return false;
3215
}
3216
3217
bool CPU::SafeWriteMemoryByte(VirtualMemoryAddress addr, u8 value)
3218
{
3219
u32 temp = ZeroExtend32(value);
3220
return DoSafeMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte>(addr, temp);
3221
}
3222
3223
bool CPU::SafeWriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value)
3224
{
3225
if ((addr & 1) == 0)
3226
{
3227
u32 temp = ZeroExtend32(value);
3228
return DoSafeMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(addr, temp);
3229
}
3230
3231
return SafeWriteMemoryByte(addr, Truncate8(value)) && SafeWriteMemoryByte(addr + 1, Truncate8(value >> 8));
3232
}
3233
3234
bool CPU::SafeWriteMemoryWord(VirtualMemoryAddress addr, u32 value)
3235
{
3236
if ((addr & 3) == 0)
3237
return DoSafeMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(addr, value);
3238
3239
return SafeWriteMemoryHalfWord(addr, Truncate16(value)) && SafeWriteMemoryHalfWord(addr + 2, Truncate16(value >> 16));
3240
}
3241
3242
bool CPU::SafeReadMemoryBytes(VirtualMemoryAddress addr, void* data, u32 length)
3243
{
3244
using namespace Bus;
3245
3246
const u32 seg = (addr >> 29);
3247
if ((seg != 0 && seg != 4 && seg != 5) || (((addr + length) & KSEG_MASK) >= RAM_MIRROR_END) ||
3248
(((addr & g_ram_mask) + length) > g_ram_size))
3249
{
3250
u8* ptr = static_cast<u8*>(data);
3251
u8* const ptr_end = ptr + length;
3252
while (ptr != ptr_end)
3253
{
3254
if (!SafeReadMemoryByte(addr++, ptr++))
3255
return false;
3256
}
3257
3258
return true;
3259
}
3260
3261
// Fast path: all in RAM, no wraparound.
3262
std::memcpy(data, &g_ram[addr & g_ram_mask], length);
3263
return true;
3264
}
3265
3266
bool CPU::SafeWriteMemoryBytes(VirtualMemoryAddress addr, const void* data, u32 length)
3267
{
3268
using namespace Bus;
3269
3270
const u32 seg = (addr >> 29);
3271
if ((seg != 0 && seg != 4 && seg != 5) || (((addr + length) & KSEG_MASK) >= RAM_MIRROR_END) ||
3272
(((addr & g_ram_mask) + length) > g_ram_size))
3273
{
3274
const u8* ptr = static_cast<const u8*>(data);
3275
const u8* const ptr_end = ptr + length;
3276
while (ptr != ptr_end)
3277
{
3278
if (!SafeWriteMemoryByte(addr++, *(ptr++)))
3279
return false;
3280
}
3281
3282
return true;
3283
}
3284
3285
// Fast path: all in RAM, no wraparound.
3286
std::memcpy(&g_ram[addr & g_ram_mask], data, length);
3287
return true;
3288
}
3289
3290
bool CPU::SafeWriteMemoryBytes(VirtualMemoryAddress addr, const std::span<const u8> data)
3291
{
3292
return SafeWriteMemoryBytes(addr, data.data(), static_cast<u32>(data.size()));
3293
}
3294
3295
bool CPU::SafeZeroMemoryBytes(VirtualMemoryAddress addr, u32 length)
3296
{
3297
using namespace Bus;
3298
3299
const u32 seg = (addr >> 29);
3300
if ((seg != 0 && seg != 4 && seg != 5) || (((addr + length) & KSEG_MASK) >= RAM_MIRROR_END) ||
3301
(((addr & g_ram_mask) + length) > g_ram_size))
3302
{
3303
while ((addr & 3u) != 0 && length > 0)
3304
{
3305
if (!CPU::SafeWriteMemoryByte(addr, 0)) [[unlikely]]
3306
return false;
3307
3308
addr++;
3309
length--;
3310
}
3311
while (length >= 4)
3312
{
3313
if (!CPU::SafeWriteMemoryWord(addr, 0)) [[unlikely]]
3314
return false;
3315
3316
addr += 4;
3317
length -= 4;
3318
}
3319
while (length > 0)
3320
{
3321
if (!CPU::SafeWriteMemoryByte(addr, 0)) [[unlikely]]
3322
return false;
3323
3324
addr++;
3325
length--;
3326
}
3327
3328
return true;
3329
}
3330
3331
// Fast path: all in RAM, no wraparound.
3332
std::memset(&g_ram[addr & g_ram_mask], 0, length);
3333
return true;
3334
}
3335
3336
void* CPU::GetDirectReadMemoryPointer(VirtualMemoryAddress address, MemoryAccessSize size, TickCount* read_ticks)
3337
{
3338
using namespace Bus;
3339
3340
const u32 seg = (address >> 29);
3341
if (seg != 0 && seg != 4 && seg != 5)
3342
return nullptr;
3343
3344
const PhysicalMemoryAddress paddr = VirtualAddressToPhysical(address);
3345
if (paddr < RAM_MIRROR_END)
3346
{
3347
if (read_ticks)
3348
*read_ticks = RAM_READ_TICKS;
3349
3350
return &g_ram[paddr & g_ram_mask];
3351
}
3352
3353
if ((paddr & SCRATCHPAD_ADDR_MASK) == SCRATCHPAD_ADDR)
3354
{
3355
if (read_ticks)
3356
*read_ticks = 0;
3357
3358
return &g_state.scratchpad[paddr & SCRATCHPAD_OFFSET_MASK];
3359
}
3360
3361
if (paddr >= BIOS_BASE && paddr < (BIOS_BASE + BIOS_SIZE))
3362
{
3363
if (read_ticks)
3364
*read_ticks = g_bios_access_time[static_cast<u32>(size)];
3365
3366
return &g_bios[paddr & BIOS_MASK];
3367
}
3368
3369
return nullptr;
3370
}
3371
3372
void* CPU::GetDirectWriteMemoryPointer(VirtualMemoryAddress address, MemoryAccessSize size)
3373
{
3374
using namespace Bus;
3375
3376
const u32 seg = (address >> 29);
3377
if (seg != 0 && seg != 4 && seg != 5)
3378
return nullptr;
3379
3380
const PhysicalMemoryAddress paddr = address & KSEG_MASK;
3381
3382
if (paddr < RAM_MIRROR_END)
3383
return &g_ram[paddr & g_ram_mask];
3384
3385
if ((paddr & SCRATCHPAD_ADDR_MASK) == SCRATCHPAD_ADDR)
3386
return &g_state.scratchpad[paddr & SCRATCHPAD_OFFSET_MASK];
3387
3388
return nullptr;
3389
}
3390
3391
template<MemoryAccessType type, MemoryAccessSize size>
3392
ALWAYS_INLINE_RELEASE bool CPU::DoAlignmentCheck(VirtualMemoryAddress address)
3393
{
3394
if constexpr (size == MemoryAccessSize::HalfWord)
3395
{
3396
if (Common::IsAlignedPow2(address, 2))
3397
return true;
3398
}
3399
else if constexpr (size == MemoryAccessSize::Word)
3400
{
3401
if (Common::IsAlignedPow2(address, 4))
3402
return true;
3403
}
3404
else
3405
{
3406
return true;
3407
}
3408
3409
g_state.cop0_regs.BadVaddr = address;
3410
RaiseException(type == MemoryAccessType::Read ? Exception::AdEL : Exception::AdES);
3411
return false;
3412
}
3413
3414
#if 0
3415
static void MemoryBreakpoint(MemoryAccessType type, MemoryAccessSize size, VirtualMemoryAddress addr, u32 value)
3416
{
3417
static constexpr const char* sizes[3] = { "byte", "halfword", "word" };
3418
static constexpr const char* types[2] = { "read", "write" };
3419
3420
const u32 cycle = TimingEvents::GetGlobalTickCounter() + CPU::g_state.pending_ticks;
3421
if (cycle == 3301006373)
3422
__debugbreak();
3423
3424
#if 0
3425
static std::FILE* fp = nullptr;
3426
if (!fp)
3427
fp = std::fopen("D:\\memory.txt", "wb");
3428
if (fp)
3429
{
3430
std::fprintf(fp, "%u %s %s %08X %08X\n", cycle, types[static_cast<u32>(type)], sizes[static_cast<u32>(size)], addr, value);
3431
std::fflush(fp);
3432
}
3433
#endif
3434
3435
#if 0
3436
if (type == MemoryAccessType::Read && addr == 0x1F000084)
3437
__debugbreak();
3438
#endif
3439
#if 0
3440
if (type == MemoryAccessType::Write && addr == 0x000000B0 /*&& value == 0x3C080000*/)
3441
__debugbreak();
3442
#endif
3443
3444
#if 0 // TODO: MEMBP
3445
if (type == MemoryAccessType::Write && address == 0x80113028)
3446
{
3447
if ((TimingEvents::GetGlobalTickCounter() + CPU::g_state.pending_ticks) == 5051485)
3448
__debugbreak();
3449
3450
Log_WarningPrintf("VAL %08X @ %u", value, (TimingEvents::GetGlobalTickCounter() + CPU::g_state.pending_ticks));
3451
}
3452
#endif
3453
}
3454
#define MEMORY_BREAKPOINT(type, size, addr, value) MemoryBreakpoint((type), (size), (addr), (value))
3455
#else
3456
#define MEMORY_BREAKPOINT(type, size, addr, value)
3457
#endif
3458
3459
bool CPU::ReadMemoryByte(VirtualMemoryAddress addr, u8* value)
3460
{
3461
*value = Truncate8(GetMemoryReadHandler(addr, MemoryAccessSize::Byte)(addr));
3462
if (g_state.bus_error) [[unlikely]]
3463
{
3464
g_state.bus_error = false;
3465
RaiseException(Exception::DBE);
3466
return false;
3467
}
3468
3469
MEMORY_BREAKPOINT(MemoryAccessType::Read, MemoryAccessSize::Byte, addr, *value);
3470
return true;
3471
}
3472
3473
bool CPU::ReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value)
3474
{
3475
if (!DoAlignmentCheck<MemoryAccessType::Read, MemoryAccessSize::HalfWord>(addr))
3476
return false;
3477
3478
*value = Truncate16(GetMemoryReadHandler(addr, MemoryAccessSize::HalfWord)(addr));
3479
if (g_state.bus_error) [[unlikely]]
3480
{
3481
g_state.bus_error = false;
3482
RaiseException(Exception::DBE);
3483
return false;
3484
}
3485
3486
MEMORY_BREAKPOINT(MemoryAccessType::Read, MemoryAccessSize::HalfWord, addr, *value);
3487
return true;
3488
}
3489
3490
bool CPU::ReadMemoryWord(VirtualMemoryAddress addr, u32* value)
3491
{
3492
if (!DoAlignmentCheck<MemoryAccessType::Read, MemoryAccessSize::Word>(addr))
3493
return false;
3494
3495
*value = GetMemoryReadHandler(addr, MemoryAccessSize::Word)(addr);
3496
if (g_state.bus_error) [[unlikely]]
3497
{
3498
g_state.bus_error = false;
3499
RaiseException(Exception::DBE);
3500
return false;
3501
}
3502
3503
MEMORY_BREAKPOINT(MemoryAccessType::Read, MemoryAccessSize::Word, addr, *value);
3504
return true;
3505
}
3506
3507
bool CPU::WriteMemoryByte(VirtualMemoryAddress addr, u32 value)
3508
{
3509
MEMORY_BREAKPOINT(MemoryAccessType::Write, MemoryAccessSize::Byte, addr, value);
3510
3511
GetMemoryWriteHandler(addr, MemoryAccessSize::Byte)(addr, value);
3512
if (g_state.bus_error) [[unlikely]]
3513
{
3514
g_state.bus_error = false;
3515
RaiseException(Exception::DBE);
3516
return false;
3517
}
3518
3519
return true;
3520
}
3521
3522
bool CPU::WriteMemoryHalfWord(VirtualMemoryAddress addr, u32 value)
3523
{
3524
MEMORY_BREAKPOINT(MemoryAccessType::Write, MemoryAccessSize::HalfWord, addr, value);
3525
3526
if (!DoAlignmentCheck<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(addr))
3527
return false;
3528
3529
GetMemoryWriteHandler(addr, MemoryAccessSize::HalfWord)(addr, value);
3530
if (g_state.bus_error) [[unlikely]]
3531
{
3532
g_state.bus_error = false;
3533
RaiseException(Exception::DBE);
3534
return false;
3535
}
3536
3537
return true;
3538
}
3539
3540
bool CPU::WriteMemoryWord(VirtualMemoryAddress addr, u32 value)
3541
{
3542
MEMORY_BREAKPOINT(MemoryAccessType::Write, MemoryAccessSize::Word, addr, value);
3543
3544
if (!DoAlignmentCheck<MemoryAccessType::Write, MemoryAccessSize::Word>(addr))
3545
return false;
3546
3547
GetMemoryWriteHandler(addr, MemoryAccessSize::Word)(addr, value);
3548
if (g_state.bus_error) [[unlikely]]
3549
{
3550
g_state.bus_error = false;
3551
RaiseException(Exception::DBE);
3552
return false;
3553
}
3554
3555
return true;
3556
}
3557
3558
u64 CPU::RecompilerThunks::ReadMemoryByte(u32 address)
3559
{
3560
const u32 value = GetMemoryReadHandler(address, MemoryAccessSize::Byte)(address);
3561
if (g_state.bus_error) [[unlikely]]
3562
{
3563
g_state.bus_error = false;
3564
return static_cast<u64>(-static_cast<s64>(Exception::DBE));
3565
}
3566
3567
MEMORY_BREAKPOINT(MemoryAccessType::Read, MemoryAccessSize::Byte, address, value);
3568
return ZeroExtend64(value);
3569
}
3570
3571
u64 CPU::RecompilerThunks::ReadMemoryHalfWord(u32 address)
3572
{
3573
if (!Common::IsAlignedPow2(address, 2)) [[unlikely]]
3574
{
3575
g_state.cop0_regs.BadVaddr = address;
3576
return static_cast<u64>(-static_cast<s64>(Exception::AdEL));
3577
}
3578
3579
const u32 value = GetMemoryReadHandler(address, MemoryAccessSize::HalfWord)(address);
3580
if (g_state.bus_error) [[unlikely]]
3581
{
3582
g_state.bus_error = false;
3583
return static_cast<u64>(-static_cast<s64>(Exception::DBE));
3584
}
3585
3586
MEMORY_BREAKPOINT(MemoryAccessType::Read, MemoryAccessSize::HalfWord, address, value);
3587
return ZeroExtend64(value);
3588
}
3589
3590
u64 CPU::RecompilerThunks::ReadMemoryWord(u32 address)
3591
{
3592
if (!Common::IsAlignedPow2(address, 4)) [[unlikely]]
3593
{
3594
g_state.cop0_regs.BadVaddr = address;
3595
return static_cast<u64>(-static_cast<s64>(Exception::AdEL));
3596
}
3597
3598
const u32 value = GetMemoryReadHandler(address, MemoryAccessSize::Word)(address);
3599
if (g_state.bus_error) [[unlikely]]
3600
{
3601
g_state.bus_error = false;
3602
return static_cast<u64>(-static_cast<s64>(Exception::DBE));
3603
}
3604
3605
MEMORY_BREAKPOINT(MemoryAccessType::Read, MemoryAccessSize::Word, address, value);
3606
return ZeroExtend64(value);
3607
}
3608
3609
u32 CPU::RecompilerThunks::WriteMemoryByte(u32 address, u32 value)
3610
{
3611
MEMORY_BREAKPOINT(MemoryAccessType::Write, MemoryAccessSize::Byte, address, value);
3612
3613
GetMemoryWriteHandler(address, MemoryAccessSize::Byte)(address, value);
3614
if (g_state.bus_error) [[unlikely]]
3615
{
3616
g_state.bus_error = false;
3617
return static_cast<u32>(Exception::DBE);
3618
}
3619
3620
return 0;
3621
}
3622
3623
u32 CPU::RecompilerThunks::WriteMemoryHalfWord(u32 address, u32 value)
3624
{
3625
MEMORY_BREAKPOINT(MemoryAccessType::Write, MemoryAccessSize::HalfWord, address, value);
3626
3627
if (!Common::IsAlignedPow2(address, 2)) [[unlikely]]
3628
{
3629
g_state.cop0_regs.BadVaddr = address;
3630
return static_cast<u32>(Exception::AdES);
3631
}
3632
3633
GetMemoryWriteHandler(address, MemoryAccessSize::HalfWord)(address, value);
3634
if (g_state.bus_error) [[unlikely]]
3635
{
3636
g_state.bus_error = false;
3637
return static_cast<u32>(Exception::DBE);
3638
}
3639
3640
return 0;
3641
}
3642
3643
u32 CPU::RecompilerThunks::WriteMemoryWord(u32 address, u32 value)
3644
{
3645
MEMORY_BREAKPOINT(MemoryAccessType::Write, MemoryAccessSize::Word, address, value);
3646
3647
if (!Common::IsAlignedPow2(address, 4)) [[unlikely]]
3648
{
3649
g_state.cop0_regs.BadVaddr = address;
3650
return static_cast<u32>(Exception::AdES);
3651
}
3652
3653
GetMemoryWriteHandler(address, MemoryAccessSize::Word)(address, value);
3654
if (g_state.bus_error) [[unlikely]]
3655
{
3656
g_state.bus_error = false;
3657
return static_cast<u32>(Exception::DBE);
3658
}
3659
3660
return 0;
3661
}
3662
3663
u32 CPU::RecompilerThunks::UncheckedReadMemoryByte(u32 address)
3664
{
3665
const u32 value = GetMemoryReadHandler(address, MemoryAccessSize::Byte)(address);
3666
MEMORY_BREAKPOINT(MemoryAccessType::Read, MemoryAccessSize::Byte, address, value);
3667
return value;
3668
}
3669
3670
u32 CPU::RecompilerThunks::UncheckedReadMemoryHalfWord(u32 address)
3671
{
3672
const u32 value = GetMemoryReadHandler(address, MemoryAccessSize::HalfWord)(address);
3673
MEMORY_BREAKPOINT(MemoryAccessType::Read, MemoryAccessSize::HalfWord, address, value);
3674
return value;
3675
}
3676
3677
u32 CPU::RecompilerThunks::UncheckedReadMemoryWord(u32 address)
3678
{
3679
const u32 value = GetMemoryReadHandler(address, MemoryAccessSize::Word)(address);
3680
MEMORY_BREAKPOINT(MemoryAccessType::Read, MemoryAccessSize::Word, address, value);
3681
return value;
3682
}
3683
3684
void CPU::RecompilerThunks::UncheckedWriteMemoryByte(u32 address, u32 value)
3685
{
3686
MEMORY_BREAKPOINT(MemoryAccessType::Write, MemoryAccessSize::Byte, address, value);
3687
GetMemoryWriteHandler(address, MemoryAccessSize::Byte)(address, value);
3688
}
3689
3690
void CPU::RecompilerThunks::UncheckedWriteMemoryHalfWord(u32 address, u32 value)
3691
{
3692
MEMORY_BREAKPOINT(MemoryAccessType::Write, MemoryAccessSize::HalfWord, address, value);
3693
GetMemoryWriteHandler(address, MemoryAccessSize::HalfWord)(address, value);
3694
}
3695
3696
void CPU::RecompilerThunks::UncheckedWriteMemoryWord(u32 address, u32 value)
3697
{
3698
MEMORY_BREAKPOINT(MemoryAccessType::Write, MemoryAccessSize::Word, address, value);
3699
GetMemoryWriteHandler(address, MemoryAccessSize::Word)(address, value);
3700
}
3701
3702
#undef MEMORY_BREAKPOINT
3703
3704