duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

cpu_core.cpp (106738B)


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