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