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.h (7458B)


      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 #pragma once
      5 
      6 #include "cpu_types.h"
      7 #include "gte_types.h"
      8 #include "types.h"
      9 
     10 #include "common/bitfield.h"
     11 
     12 #include <array>
     13 #include <optional>
     14 #include <string>
     15 #include <vector>
     16 
     17 class StateWrapper;
     18 
     19 namespace CPU {
     20 
     21 enum : VirtualMemoryAddress
     22 {
     23   RESET_VECTOR = UINT32_C(0xBFC00000)
     24 };
     25 enum : PhysicalMemoryAddress
     26 {
     27   SCRATCHPAD_ADDR = UINT32_C(0x1F800000),
     28   SCRATCHPAD_ADDR_MASK = UINT32_C(0x7FFFFC00),
     29   SCRATCHPAD_OFFSET_MASK = UINT32_C(0x000003FF),
     30   SCRATCHPAD_SIZE = UINT32_C(0x00000400),
     31   ICACHE_SIZE = UINT32_C(0x00001000),
     32   ICACHE_SLOTS = ICACHE_SIZE / sizeof(u32),
     33   ICACHE_LINE_SIZE = 16,
     34   ICACHE_LINES = ICACHE_SIZE / ICACHE_LINE_SIZE,
     35   ICACHE_SLOTS_PER_LINE = ICACHE_SLOTS / ICACHE_LINES,
     36   ICACHE_TAG_ADDRESS_MASK = 0xFFFFFFF0u,
     37   ICACHE_INVALID_BITS = 0x0Fu,
     38 };
     39 
     40 union CacheControl
     41 {
     42   u32 bits;
     43 
     44   BitField<u32, bool, 0, 1> lock_mode;
     45   BitField<u32, bool, 1, 1> invalidate_mode;
     46   BitField<u32, bool, 2, 1> tag_test_mode;
     47   BitField<u32, bool, 3, 1> dcache_scratchpad;
     48   BitField<u32, bool, 7, 1> dcache_enable;
     49   BitField<u32, u8, 8, 2> icache_fill_size; // actually dcache? icache always fills to 16 bytes
     50   BitField<u32, bool, 11, 1> icache_enable;
     51 };
     52 
     53 struct PGXPValue
     54 {
     55   float x;
     56   float y;
     57   float z;
     58   u32 value;
     59   u32 flags;
     60 
     61   ALWAYS_INLINE void Validate(u32 psxval) { flags = (value == psxval) ? flags : 0; }
     62 
     63   ALWAYS_INLINE float GetValidX(u32 psxval) const
     64   {
     65     return (flags & 1) ? x : static_cast<float>(static_cast<s16>(psxval));
     66   }
     67   ALWAYS_INLINE float GetValidY(u32 psxval) const
     68   {
     69     return (flags & 2) ? y : static_cast<float>(static_cast<s16>(psxval >> 16));
     70   }
     71 };
     72 
     73 struct State
     74 {
     75   // ticks the CPU has executed
     76   u32 downcount = 0;
     77   u32 pending_ticks = 0;
     78   u32 gte_completion_tick = 0;
     79 
     80   Registers regs = {};
     81   Cop0Registers cop0_regs = {};
     82 
     83   u32 pc;  // at execution time: the address of the next instruction to execute (already fetched)
     84   u32 npc; // at execution time: the address of the next instruction to fetch
     85 
     86   // address of the instruction currently being executed
     87   Instruction current_instruction = {};
     88   u32 current_instruction_pc = 0;
     89   bool current_instruction_in_branch_delay_slot = false;
     90   bool current_instruction_was_branch_taken = false;
     91   bool next_instruction_is_branch_delay_slot = false;
     92   bool branch_was_taken = false;
     93   bool exception_raised = false;
     94   bool bus_error = false;
     95 
     96   // load delays
     97   Reg load_delay_reg = Reg::count;
     98   Reg next_load_delay_reg = Reg::count;
     99   u32 load_delay_value = 0;
    100   u32 next_load_delay_value = 0;
    101 
    102   Instruction next_instruction = {};
    103   CacheControl cache_control{0};
    104 
    105   // GTE registers are stored here so we can access them on ARM with a single instruction
    106   GTE::Regs gte_regs = {};
    107 
    108   // 2 bytes of padding here on x64
    109   bool using_interpreter = false;
    110   bool using_debug_dispatcher = false;
    111 
    112   void* fastmem_base = nullptr;
    113   void** memory_handlers = nullptr;
    114 
    115   PGXPValue pgxp_gpr[static_cast<u8>(Reg::count)] = {};
    116   PGXPValue pgxp_cop0[32] = {};
    117   PGXPValue pgxp_gte[64] = {};
    118 
    119   std::array<u32, ICACHE_LINES> icache_tags = {};
    120   std::array<u8, ICACHE_SIZE> icache_data = {};
    121 
    122   std::array<u8, SCRATCHPAD_SIZE> scratchpad = {};
    123 
    124   static constexpr u32 GPRRegisterOffset(u32 index) { return OFFSETOF(State, regs.r) + (sizeof(u32) * index); }
    125   static constexpr u32 GTERegisterOffset(u32 index) { return OFFSETOF(State, gte_regs.r32) + (sizeof(u32) * index); }
    126 };
    127 
    128 ALIGN_TO_CACHE_LINE extern State g_state;
    129 
    130 void Initialize();
    131 void Shutdown();
    132 void Reset();
    133 bool DoState(StateWrapper& sw);
    134 void ClearICache();
    135 bool UpdateDebugDispatcherFlag();
    136 void UpdateMemoryPointers();
    137 void ExecutionModeChanged();
    138 
    139 /// Executes interpreter loop.
    140 void Execute();
    141 
    142 // Forces an early exit from the CPU dispatcher.
    143 [[noreturn]] void ExitExecution();
    144 
    145 ALWAYS_INLINE static Registers& GetRegs()
    146 {
    147   return g_state.regs;
    148 }
    149 
    150 ALWAYS_INLINE static u32 GetPendingTicks()
    151 {
    152   return g_state.pending_ticks;
    153 }
    154 ALWAYS_INLINE static void ResetPendingTicks()
    155 {
    156   g_state.gte_completion_tick =
    157     (g_state.pending_ticks < g_state.gte_completion_tick) ? (g_state.gte_completion_tick - g_state.pending_ticks) : 0;
    158   g_state.pending_ticks = 0;
    159 }
    160 ALWAYS_INLINE static void AddPendingTicks(TickCount ticks)
    161 {
    162   g_state.pending_ticks += static_cast<u32>(ticks);
    163 }
    164 
    165 // state helpers
    166 ALWAYS_INLINE static bool InUserMode()
    167 {
    168   return g_state.cop0_regs.sr.KUc;
    169 }
    170 ALWAYS_INLINE static bool InKernelMode()
    171 {
    172   return !g_state.cop0_regs.sr.KUc;
    173 }
    174 
    175 // Memory reads variants which do not raise exceptions.
    176 // These methods do not support writing to MMIO addresses with side effects, and are
    177 // thus safe to call from the UI thread in debuggers, for example.
    178 bool SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value);
    179 bool SafeReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value);
    180 bool SafeReadMemoryWord(VirtualMemoryAddress addr, u32* value);
    181 bool SafeReadMemoryCString(VirtualMemoryAddress addr, std::string* value, u32 max_length = 1024);
    182 bool SafeReadMemoryBytes(VirtualMemoryAddress addr, void* data, u32 length);
    183 bool SafeWriteMemoryByte(VirtualMemoryAddress addr, u8 value);
    184 bool SafeWriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value);
    185 bool SafeWriteMemoryWord(VirtualMemoryAddress addr, u32 value);
    186 bool SafeWriteMemoryBytes(VirtualMemoryAddress addr, const void* data, u32 length);
    187 
    188 // External IRQs
    189 void SetIRQRequest(bool state);
    190 
    191 void DisassembleAndPrint(u32 addr);
    192 void DisassembleAndLog(u32 addr);
    193 void DisassembleAndPrint(u32 addr, u32 instructions_before, u32 instructions_after);
    194 
    195 // Write to CPU execution log file.
    196 void WriteToExecutionLog(const char* format, ...) printflike(1, 2);
    197 
    198 // Trace Routines
    199 bool IsTraceEnabled();
    200 void StartTrace();
    201 void StopTrace();
    202 
    203 // Breakpoint types - execute => breakpoint, read/write => watchpoints
    204 enum class BreakpointType : u8
    205 {
    206   Execute,
    207   Read,
    208   Write,
    209   Count
    210 };
    211 
    212 // Breakpoint callback - if the callback returns false, the breakpoint will be removed.
    213 using BreakpointCallback = bool (*)(BreakpointType type, VirtualMemoryAddress pc, VirtualMemoryAddress memaddr);
    214 
    215 struct Breakpoint
    216 {
    217   VirtualMemoryAddress address;
    218   BreakpointCallback callback;
    219   u32 number;
    220   u32 hit_count;
    221   BreakpointType type;
    222   bool auto_clear;
    223   bool enabled;
    224 };
    225 
    226 using BreakpointList = std::vector<Breakpoint>;
    227 
    228 // Breakpoints
    229 const char* GetBreakpointTypeName(BreakpointType type);
    230 bool HasAnyBreakpoints();
    231 bool HasBreakpointAtAddress(BreakpointType type, VirtualMemoryAddress address);
    232 BreakpointList CopyBreakpointList(bool include_auto_clear = false, bool include_callbacks = false);
    233 bool AddBreakpoint(BreakpointType type, VirtualMemoryAddress address, bool auto_clear = false, bool enabled = true);
    234 bool AddBreakpointWithCallback(BreakpointType type, VirtualMemoryAddress address, BreakpointCallback callback);
    235 bool RemoveBreakpoint(BreakpointType type, VirtualMemoryAddress address);
    236 void ClearBreakpoints();
    237 bool AddStepOverBreakpoint();
    238 bool AddStepOutBreakpoint(u32 max_instructions_to_search = 1000);
    239 void SetSingleStepFlag();
    240 
    241 extern bool TRACE_EXECUTION;
    242 
    243 // Debug register introspection
    244 struct DebuggerRegisterListEntry
    245 {
    246   const char* name;
    247   u32* value_ptr;
    248 };
    249 
    250 static constexpr u32 NUM_DEBUGGER_REGISTER_LIST_ENTRIES = 103;
    251 extern const std::array<DebuggerRegisterListEntry, NUM_DEBUGGER_REGISTER_LIST_ENTRIES> g_debugger_register_list;
    252 
    253 } // namespace CPU