Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/core/bus.h
4214 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#pragma once
5
6
#include "types.h"
7
8
#include <array>
9
#include <bitset>
10
#include <optional>
11
#include <span>
12
#include <string_view>
13
14
class Error;
15
16
class StateWrapper;
17
18
namespace Bus {
19
20
enum : u32
21
{
22
RAM_BASE = 0x00000000,
23
RAM_2MB_SIZE = 0x200000,
24
RAM_2MB_MASK = RAM_2MB_SIZE - 1,
25
RAM_8MB_SIZE = 0x800000,
26
RAM_8MB_MASK = RAM_8MB_SIZE - 1,
27
RAM_MIRROR_END = 0x800000,
28
RAM_MIRROR_SIZE = 0x800000,
29
EXP1_BASE = 0x1F000000,
30
EXP1_SIZE = 0x800000,
31
EXP1_MASK = EXP1_SIZE - 1,
32
HW_BASE = 0x1F801000,
33
HW_SIZE = 0x1000,
34
MEMCTRL_BASE = 0x1F801000,
35
MEMCTRL_SIZE = 0x40,
36
MEMCTRL_MASK = MEMCTRL_SIZE - 1,
37
PAD_BASE = 0x1F801040,
38
PAD_SIZE = 0x10,
39
PAD_MASK = PAD_SIZE - 1,
40
SIO_BASE = 0x1F801050,
41
SIO_SIZE = 0x10,
42
SIO_MASK = SIO_SIZE - 1,
43
MEMCTRL2_BASE = 0x1F801060,
44
MEMCTRL2_SIZE = 0x10,
45
MEMCTRL2_MASK = MEMCTRL2_SIZE - 1,
46
INTC_BASE = 0x1F801070,
47
INTC_SIZE = 0x10,
48
INTERRUPT_CONTROLLER_MASK = INTC_SIZE - 1,
49
DMA_BASE = 0x1F801080,
50
DMA_SIZE = 0x80,
51
DMA_MASK = DMA_SIZE - 1,
52
TIMERS_BASE = 0x1F801100,
53
TIMERS_SIZE = 0x40,
54
TIMERS_MASK = TIMERS_SIZE - 1,
55
CDROM_BASE = 0x1F801800,
56
CDROM_SIZE = 0x10,
57
CDROM_MASK = CDROM_SIZE - 1,
58
GPU_BASE = 0x1F801810,
59
GPU_SIZE = 0x10,
60
GPU_MASK = GPU_SIZE - 1,
61
MDEC_BASE = 0x1F801820,
62
MDEC_SIZE = 0x10,
63
MDEC_MASK = MDEC_SIZE - 1,
64
SPU_BASE = 0x1F801C00,
65
SPU_SIZE = 0x400,
66
SPU_MASK = SPU_SIZE - 1,
67
SIO2_BASE = 0x1F808000,
68
SIO2_SIZE = 0x1000,
69
SIO2_MASK = SIO2_SIZE - 1,
70
EXP2_BASE = 0x1F802000,
71
EXP2_SIZE = 0x2000,
72
EXP2_MASK = EXP2_SIZE - 1,
73
EXP3_BASE = 0x1FA00000,
74
EXP3_SIZE = 0x200000,
75
EXP3_MASK = EXP3_SIZE - 1,
76
BIOS_BASE = 0x1FC00000,
77
BIOS_SIZE = 0x80000,
78
BIOS_MIRROR_SIZE = 0x400000,
79
BIOS_MASK = 0x7FFFF,
80
};
81
82
enum : u32
83
{
84
MEMCTRL_REG_COUNT = 9
85
};
86
87
enum : TickCount
88
{
89
RAM_READ_TICKS = 6
90
};
91
92
enum : u32
93
{
94
RAM_2MB_CODE_PAGE_COUNT = (RAM_2MB_SIZE + (MIN_HOST_PAGE_SIZE - 1)) / MIN_HOST_PAGE_SIZE,
95
RAM_8MB_CODE_PAGE_COUNT = (RAM_8MB_SIZE + (MIN_HOST_PAGE_SIZE - 1)) / MIN_HOST_PAGE_SIZE,
96
97
MEMORY_LUT_PAGE_SIZE = 4096,
98
MEMORY_LUT_PAGE_SHIFT = 12,
99
MEMORY_LUT_PAGE_MASK = MEMORY_LUT_PAGE_SIZE - 1,
100
MEMORY_LUT_SIZE = 0x100000, // 0x100000000 >> 12
101
MEMORY_LUT_SLOTS = MEMORY_LUT_SIZE * 3 * 2, // [size][read_write]
102
103
FASTMEM_LUT_PAGE_SIZE = 4096,
104
FASTMEM_LUT_PAGE_MASK = FASTMEM_LUT_PAGE_SIZE - 1,
105
FASTMEM_LUT_PAGE_SHIFT = 12,
106
FASTMEM_LUT_SIZE = 0x100000, // 0x100000000 >> 12
107
FASTMEM_LUT_SLOTS = FASTMEM_LUT_SIZE * 2, // [isc]
108
};
109
110
#ifdef ENABLE_MMAP_FASTMEM
111
// Fastmem region size is 4GB to cover the entire 32-bit address space.
112
inline constexpr size_t FASTMEM_ARENA_SIZE = UINT64_C(0x100000000);
113
#endif
114
115
bool AllocateMemory(bool export_shared_memory, Error* error);
116
void ReleaseMemory();
117
118
/// Frees and re-allocates the memory map for the process.
119
/// This should be called when shared memory exports are enabled.
120
bool ReallocateMemoryMap(bool export_shared_memory, Error* error);
121
122
/// Cleans up/deletes the shared memory object for this process.
123
/// Should be called when the process crashes, to avoid leaking.
124
void CleanupMemoryMap();
125
126
void Initialize();
127
void Shutdown();
128
void Reset();
129
bool DoState(StateWrapper& sw);
130
131
using MemoryReadHandler = u32 (*)(VirtualMemoryAddress address);
132
using MemoryWriteHandler = void (*)(VirtualMemoryAddress, u32);
133
134
void** GetMemoryHandlers(bool isolate_cache, bool swap_caches);
135
136
template<typename FP>
137
ALWAYS_INLINE_RELEASE FP* OffsetHandlerArray(void** handlers, MemoryAccessSize size, MemoryAccessType type)
138
{
139
return reinterpret_cast<FP*>(handlers +
140
(((static_cast<size_t>(size) * 2) + static_cast<size_t>(type)) * MEMORY_LUT_SIZE));
141
}
142
143
void* GetFastmemBase(bool isc);
144
void RemapFastmemViews();
145
bool CanUseFastmemForAddress(VirtualMemoryAddress address);
146
147
extern std::bitset<RAM_8MB_CODE_PAGE_COUNT> g_ram_code_bits;
148
extern u8* g_ram; // 2MB-8MB RAM
149
extern u8* g_unprotected_ram; // RAM without page protection, use for debugger access.
150
extern u32 g_ram_size; // Active size of RAM.
151
extern u32 g_ram_mapped_size; // Maximum mapped address for RAM, determined by RAM size register.
152
extern u32 g_ram_mask; // Active address bits for RAM.
153
extern u8* g_bios; // 512K BIOS ROM
154
extern std::array<TickCount, 3> g_exp1_access_time;
155
extern std::array<TickCount, 3> g_exp2_access_time;
156
extern std::array<TickCount, 3> g_bios_access_time;
157
extern std::array<TickCount, 3> g_cdrom_access_time;
158
extern std::array<TickCount, 3> g_spu_access_time;
159
160
/// Returns true if the address specified is writable (RAM).
161
ALWAYS_INLINE bool IsRAMAddress(PhysicalMemoryAddress address)
162
{
163
return address < RAM_MIRROR_END;
164
}
165
166
/// Returns the code page index for a RAM address.
167
ALWAYS_INLINE u32 GetRAMCodePageIndex(PhysicalMemoryAddress address)
168
{
169
return (address & g_ram_mask) >> HOST_PAGE_SHIFT;
170
}
171
172
/// Returns true if the specified page contains code.
173
ALWAYS_INLINE bool IsRAMCodePage(u32 index)
174
{
175
return g_ram_code_bits[index];
176
}
177
178
/// Flags a RAM region as code, so we know when to invalidate blocks.
179
void SetRAMCodePage(u32 index);
180
181
/// Unflags a RAM region as code, the code cache will no longer be notified when writes occur.
182
void ClearRAMCodePage(u32 index);
183
184
/// Clears all code bits for RAM regions.
185
void ClearRAMCodePageFlags();
186
187
/// Returns true if the specified address is in a code page.
188
bool IsCodePageAddress(PhysicalMemoryAddress address);
189
190
/// Returns true if the range specified overlaps with a code page.
191
bool HasCodePagesInRange(PhysicalMemoryAddress start_address, u32 size);
192
193
/// Returns the number of cycles stolen by DMA RAM access.
194
ALWAYS_INLINE TickCount GetDMARAMTickCount(u32 word_count)
195
{
196
// DMA is using DRAM Hyper Page mode, allowing it to access DRAM rows at 1 clock cycle per word (effectively around
197
// 17 clks per 16 words, due to required row address loading, probably plus some further minimal overload due to
198
// refresh cycles). This is making DMA much faster than CPU memory accesses (CPU DRAM access takes 1 opcode cycle
199
// plus 6 waitstates, ie. 7 cycles in total).
200
return static_cast<TickCount>(word_count + ((word_count + 15) / 16));
201
}
202
203
/// Returns a pointer to the cycle count for a non-RAM memory access.
204
const TickCount* GetMemoryAccessTimePtr(PhysicalMemoryAddress address, MemoryAccessSize size);
205
206
enum class MemoryRegion
207
{
208
RAM,
209
RAMMirror1,
210
RAMMirror2,
211
RAMMirror3,
212
EXP1,
213
Scratchpad,
214
BIOS,
215
Count
216
};
217
218
std::optional<MemoryRegion> GetMemoryRegionForAddress(PhysicalMemoryAddress address);
219
PhysicalMemoryAddress GetMemoryRegionStart(MemoryRegion region);
220
PhysicalMemoryAddress GetMemoryRegionEnd(MemoryRegion region);
221
bool IsMemoryRegionWritable(MemoryRegion region);
222
u8* GetMemoryRegionPointer(MemoryRegion region);
223
std::optional<PhysicalMemoryAddress> SearchMemory(PhysicalMemoryAddress start_address, const u8* pattern,
224
const u8* mask, u32 pattern_length);
225
226
// TTY Logging.
227
void AddTTYCharacter(char ch);
228
void AddTTYString(std::string_view str);
229
230
/// Injects a PS-EXE into memory at its specified load location. If set_pc is set, execution will be redirected.
231
bool InjectExecutable(std::span<const u8> buffer, bool set_pc, Error* error);
232
233
} // namespace Bus
234
235