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