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