Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/core/cpu_core_private.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 "bus.h"
7
#include "cpu_core.h"
8
9
struct fastjmp_buf;
10
11
namespace CPU {
12
13
// Memory address mask used for fetching as well as loadstores (removes cached/uncached/user/kernel bits).
14
enum : PhysicalMemoryAddress
15
{
16
KSEG_MASK = 0x1FFFFFFF,
17
KUSEG_MASK = 0x7FFFFFFF,
18
};
19
20
void SetPC(u32 new_pc);
21
22
// exceptions
23
void RaiseException(Exception excode);
24
void RaiseException(u32 CAUSE_bits, u32 EPC);
25
void RaiseBreakException(u32 CAUSE_bits, u32 EPC, u32 instruction_bits);
26
27
ALWAYS_INLINE bool HasPendingInterrupt()
28
{
29
return g_state.cop0_regs.sr.IEc &&
30
(((g_state.cop0_regs.cause.bits & g_state.cop0_regs.sr.bits) & (UINT32_C(0xFF) << 8)) != 0);
31
}
32
33
ALWAYS_INLINE void CheckForPendingInterrupt()
34
{
35
if (HasPendingInterrupt())
36
g_state.downcount = 0;
37
}
38
39
void DispatchInterrupt();
40
41
// access to execution jump buffer, use with care!
42
fastjmp_buf* GetExecutionJmpBuf();
43
44
// icache stuff
45
ALWAYS_INLINE bool IsCachedAddress(VirtualMemoryAddress address)
46
{
47
// KUSEG, KSEG0
48
return (address >> 29) <= 4;
49
}
50
ALWAYS_INLINE u32 GetICacheLine(VirtualMemoryAddress address)
51
{
52
return ((address >> 4) & 0xFFu);
53
}
54
ALWAYS_INLINE u32 GetICacheLineOffset(VirtualMemoryAddress address)
55
{
56
return (address & (ICACHE_LINE_SIZE - 1));
57
}
58
ALWAYS_INLINE u32 GetICacheLineWordOffset(VirtualMemoryAddress address)
59
{
60
return (address >> 2) & 0x03u;
61
}
62
ALWAYS_INLINE u32 GetICacheTagForAddress(VirtualMemoryAddress address)
63
{
64
return (address & ICACHE_TAG_ADDRESS_MASK);
65
}
66
ALWAYS_INLINE u32 GetICacheFillTagForAddress(VirtualMemoryAddress address)
67
{
68
static const u32 invalid_bits[4] = {0, 1, 3, 7};
69
return GetICacheTagForAddress(address) | invalid_bits[(address >> 2) & 0x03u];
70
}
71
ALWAYS_INLINE u32 GetICacheTagMaskForAddress(VirtualMemoryAddress address)
72
{
73
static const u32 mask[4] = {ICACHE_TAG_ADDRESS_MASK | 1, ICACHE_TAG_ADDRESS_MASK | 2, ICACHE_TAG_ADDRESS_MASK | 4,
74
ICACHE_TAG_ADDRESS_MASK | 8};
75
return mask[(address >> 2) & 0x03u];
76
}
77
78
ALWAYS_INLINE bool CompareICacheTag(VirtualMemoryAddress address)
79
{
80
const u32 line = GetICacheLine(address);
81
return ((g_state.icache_tags[line] & GetICacheTagMaskForAddress(address)) == GetICacheTagForAddress(address));
82
}
83
84
TickCount GetInstructionReadTicks(VirtualMemoryAddress address);
85
TickCount GetICacheFillTicks(VirtualMemoryAddress address);
86
u32 FillICache(VirtualMemoryAddress address);
87
void CheckAndUpdateICacheTags(u32 line_count);
88
89
ALWAYS_INLINE Segment GetSegmentForAddress(VirtualMemoryAddress address)
90
{
91
switch ((address >> 29))
92
{
93
case 0x00: // KUSEG 0M-512M
94
case 0x01: // KUSEG 512M-1024M
95
case 0x02: // KUSEG 1024M-1536M
96
case 0x03: // KUSEG 1536M-2048M
97
return Segment::KUSEG;
98
99
case 0x04: // KSEG0 - physical memory cached
100
return Segment::KSEG0;
101
102
case 0x05: // KSEG1 - physical memory uncached
103
return Segment::KSEG1;
104
105
case 0x06: // KSEG2
106
case 0x07: // KSEG2
107
default:
108
return Segment::KSEG2;
109
}
110
}
111
112
ALWAYS_INLINE constexpr PhysicalMemoryAddress VirtualAddressToPhysical(VirtualMemoryAddress address)
113
{
114
// KUSEG goes to the first 2GB, others are only 512MB.
115
return (address & ((address & 0x80000000u) ? KSEG_MASK : KUSEG_MASK));
116
}
117
118
ALWAYS_INLINE VirtualMemoryAddress PhysicalAddressToVirtual(PhysicalMemoryAddress address, Segment segment)
119
{
120
static constexpr std::array<VirtualMemoryAddress, 4> bases = {{0x00000000, 0x80000000, 0xA0000000, 0xE0000000}};
121
return bases[static_cast<u32>(segment)] | address;
122
}
123
124
Bus::MemoryReadHandler GetMemoryReadHandler(VirtualMemoryAddress address, MemoryAccessSize size);
125
Bus::MemoryWriteHandler GetMemoryWriteHandler(VirtualMemoryAddress address, MemoryAccessSize size);
126
127
// memory access functions which return false if an exception was thrown.
128
bool SafeReadInstruction(VirtualMemoryAddress addr, u32* value);
129
void* GetDirectReadMemoryPointer(VirtualMemoryAddress address, MemoryAccessSize size, TickCount* read_ticks);
130
void* GetDirectWriteMemoryPointer(VirtualMemoryAddress address, MemoryAccessSize size);
131
132
ALWAYS_INLINE void AddGTETicks(TickCount ticks)
133
{
134
g_state.gte_completion_tick = g_state.pending_ticks + ticks + 1;
135
}
136
137
ALWAYS_INLINE void StallUntilGTEComplete()
138
{
139
g_state.pending_ticks =
140
(g_state.gte_completion_tick > g_state.pending_ticks) ? g_state.gte_completion_tick : g_state.pending_ticks;
141
}
142
143
ALWAYS_INLINE void AddMulDivTicks(TickCount ticks)
144
{
145
g_state.muldiv_completion_tick = g_state.pending_ticks + ticks;
146
}
147
148
ALWAYS_INLINE void StallUntilMulDivComplete()
149
{
150
g_state.pending_ticks =
151
(g_state.muldiv_completion_tick > g_state.pending_ticks) ? g_state.muldiv_completion_tick : g_state.pending_ticks;
152
}
153
154
ALWAYS_INLINE constexpr TickCount GetMultTicks(s32 rs)
155
{
156
// Subtract one because of the instruction cycle.
157
if (rs < 0)
158
return (rs >= -2048) ? (6 - 1) : ((rs >= -1048576) ? (9 - 1) : (13 - 1));
159
else
160
return (rs < 0x800) ? (6 - 1) : ((rs < 0x100000) ? (9 - 1) : (13 - 1));
161
}
162
163
ALWAYS_INLINE constexpr TickCount GetMultTicks(u32 rs)
164
{
165
return (rs < 0x800) ? (6 - 1) : ((rs < 0x100000) ? (9 - 1) : (13 - 1));
166
}
167
168
ALWAYS_INLINE constexpr TickCount GetDivTicks()
169
{
170
return (36 - 1);
171
}
172
173
// kernel call interception
174
void HandleA0Syscall();
175
void HandleB0Syscall();
176
177
#ifdef ENABLE_RECOMPILER
178
179
namespace RecompilerThunks {
180
181
//////////////////////////////////////////////////////////////////////////
182
// Trampolines for calling back from the JIT
183
// Needed because we can't cast member functions to void*...
184
// TODO: Abuse carry flag or something else for exception
185
//////////////////////////////////////////////////////////////////////////
186
bool InterpretInstruction();
187
bool InterpretInstructionPGXP();
188
189
// Memory access functions for the JIT - MSB is set on exception.
190
u64 ReadMemoryByte(u32 address);
191
u64 ReadMemoryHalfWord(u32 address);
192
u64 ReadMemoryWord(u32 address);
193
u32 WriteMemoryByte(u32 address, u32 value);
194
u32 WriteMemoryHalfWord(u32 address, u32 value);
195
u32 WriteMemoryWord(u32 address, u32 value);
196
197
// Unchecked memory access variants. No alignment or bus exceptions.
198
u32 UncheckedReadMemoryByte(u32 address);
199
u32 UncheckedReadMemoryHalfWord(u32 address);
200
u32 UncheckedReadMemoryWord(u32 address);
201
void UncheckedWriteMemoryByte(u32 address, u32 value);
202
void UncheckedWriteMemoryHalfWord(u32 address, u32 value);
203
void UncheckedWriteMemoryWord(u32 address, u32 value);
204
205
} // namespace RecompilerThunks
206
207
#endif
208
209
} // namespace CPU
210