1 // Copyright 2003 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4
5 #include "Core/PowerPC/MMU.h"
6
7 #include <cstddef>
8 #include <cstring>
9 #include <string>
10
11 #include "Common/BitUtils.h"
12 #include "Common/CommonTypes.h"
13
14 #include "Core/ConfigManager.h"
15 #include "Core/HW/CPU.h"
16 #include "Core/HW/GPFifo.h"
17 #include "Core/HW/MMIO.h"
18 #include "Core/HW/Memmap.h"
19 #include "Core/PowerPC/JitInterface.h"
20 #include "Core/PowerPC/PowerPC.h"
21
22 #include "VideoCommon/VideoBackendBase.h"
23
24 #ifdef USE_GDBSTUB
25 #include "Core/PowerPC/GDBStub.h"
26 #endif
27
28 namespace PowerPC
29 {
30 constexpr size_t HW_PAGE_SIZE = 4096;
31 constexpr u32 HW_PAGE_INDEX_SHIFT = 12;
32 constexpr u32 HW_PAGE_INDEX_MASK = 0x3f;
33
34 // EFB RE
35 /*
36 GXPeekZ
37 80322de8: rlwinm r0, r3, 2, 14, 29 (0003fffc) a = x << 2 & 0x3fffc
38 80322dec: oris r0, r0, 0xC800 a |= 0xc8000000
39 80322df0: rlwinm r3, r0, 0, 20, 9 (ffc00fff) x = a & 0xffc00fff
40 80322df4: rlwinm r0, r4, 12, 4, 19 (0ffff000) a = (y << 12) & 0x0ffff000;
41 80322df8: or r0, r3, r0 a |= x;
42 80322dfc: rlwinm r0, r0, 0, 10, 7 (ff3fffff) a &= 0xff3fffff
43 80322e00: oris r3, r0, 0x0040 x = a | 0x00400000
44 80322e04: lwz r0, 0 (r3) r0 = *r3
45 80322e08: stw r0, 0 (r5) z =
46 80322e0c: blr
47 */
48
49 // =================================
50 // From Memmap.cpp
51 // ----------------
52
53 // Overloaded byteswap functions, for use within the templated functions below.
bswap(u8 val)54 inline u8 bswap(u8 val)
55 {
56 return val;
57 }
bswap(s8 val)58 inline s8 bswap(s8 val)
59 {
60 return val;
61 }
bswap(u16 val)62 inline u16 bswap(u16 val)
63 {
64 return Common::swap16(val);
65 }
bswap(s16 val)66 inline s16 bswap(s16 val)
67 {
68 return Common::swap16(val);
69 }
bswap(u32 val)70 inline u32 bswap(u32 val)
71 {
72 return Common::swap32(val);
73 }
bswap(u64 val)74 inline u64 bswap(u64 val)
75 {
76 return Common::swap64(val);
77 }
78 // =================
79
80 enum class XCheckTLBFlag
81 {
82 NoException,
83 Read,
84 Write,
85 Opcode,
86 OpcodeNoException
87 };
88
IsOpcodeFlag(XCheckTLBFlag flag)89 static bool IsOpcodeFlag(XCheckTLBFlag flag)
90 {
91 return flag == XCheckTLBFlag::Opcode || flag == XCheckTLBFlag::OpcodeNoException;
92 }
93
IsNoExceptionFlag(XCheckTLBFlag flag)94 static bool IsNoExceptionFlag(XCheckTLBFlag flag)
95 {
96 return flag == XCheckTLBFlag::NoException || flag == XCheckTLBFlag::OpcodeNoException;
97 }
98
99 struct TranslateAddressResult
100 {
101 enum
102 {
103 BAT_TRANSLATED,
104 PAGE_TABLE_TRANSLATED,
105 DIRECT_STORE_SEGMENT,
106 PAGE_FAULT
107 } result;
108 u32 address;
SuccessPowerPC::TranslateAddressResult109 bool Success() const { return result <= PAGE_TABLE_TRANSLATED; }
110 };
111 template <const XCheckTLBFlag flag>
112 static TranslateAddressResult TranslateAddress(u32 address);
113
114 // Nasty but necessary. Super Mario Galaxy pointer relies on this stuff.
EFB_Read(const u32 addr)115 static u32 EFB_Read(const u32 addr)
116 {
117 u32 var = 0;
118 // Convert address to coordinates. It's possible that this should be done
119 // differently depending on color depth, especially regarding PeekColor.
120 const u32 x = (addr & 0xfff) >> 2;
121 const u32 y = (addr >> 12) & 0x3ff;
122
123 if (addr & 0x00800000)
124 {
125 ERROR_LOG(MEMMAP, "Unimplemented Z+Color EFB read @ 0x%08x", addr);
126 }
127 else if (addr & 0x00400000)
128 {
129 var = g_video_backend->Video_AccessEFB(EFBAccessType::PeekZ, x, y, 0);
130 DEBUG_LOG(MEMMAP, "EFB Z Read @ %u, %u\t= 0x%08x", x, y, var);
131 }
132 else
133 {
134 var = g_video_backend->Video_AccessEFB(EFBAccessType::PeekColor, x, y, 0);
135 DEBUG_LOG(MEMMAP, "EFB Color Read @ %u, %u\t= 0x%08x", x, y, var);
136 }
137
138 return var;
139 }
140
EFB_Write(u32 data,u32 addr)141 static void EFB_Write(u32 data, u32 addr)
142 {
143 const u32 x = (addr & 0xfff) >> 2;
144 const u32 y = (addr >> 12) & 0x3ff;
145
146 if (addr & 0x00800000)
147 {
148 // It's possible to do a z-tested write to EFB by writing a 64bit value to this address range.
149 // Not much is known, but let's at least get some loging.
150 ERROR_LOG(MEMMAP, "Unimplemented Z+Color EFB write. %08x @ 0x%08x", data, addr);
151 }
152 else if (addr & 0x00400000)
153 {
154 g_video_backend->Video_AccessEFB(EFBAccessType::PokeZ, x, y, data);
155 DEBUG_LOG(MEMMAP, "EFB Z Write %08x @ %u, %u", data, x, y);
156 }
157 else
158 {
159 g_video_backend->Video_AccessEFB(EFBAccessType::PokeColor, x, y, data);
160 DEBUG_LOG(MEMMAP, "EFB Color Write %08x @ %u, %u", data, x, y);
161 }
162 }
163
164 BatTable ibat_table;
165 BatTable dbat_table;
166
167 static void GenerateDSIException(u32 effective_address, bool write);
168
169 template <XCheckTLBFlag flag, typename T, bool never_translate = false>
ReadFromHardware(u32 em_address)170 static T ReadFromHardware(u32 em_address)
171 {
172 if (!never_translate && MSR.DR)
173 {
174 auto translated_addr = TranslateAddress<flag>(em_address);
175 if (!translated_addr.Success())
176 {
177 if (flag == XCheckTLBFlag::Read)
178 GenerateDSIException(em_address, false);
179 return 0;
180 }
181 if ((em_address & (HW_PAGE_SIZE - 1)) > HW_PAGE_SIZE - sizeof(T))
182 {
183 // This could be unaligned down to the byte level... hopefully this is rare, so doing it this
184 // way isn't too terrible.
185 // TODO: floats on non-word-aligned boundaries should technically cause alignment exceptions.
186 // Note that "word" means 32-bit, so paired singles or doubles might still be 32-bit aligned!
187 u32 em_address_next_page = (em_address + sizeof(T) - 1) & ~(HW_PAGE_SIZE - 1);
188 auto addr_next_page = TranslateAddress<flag>(em_address_next_page);
189 if (!addr_next_page.Success())
190 {
191 if (flag == XCheckTLBFlag::Read)
192 GenerateDSIException(em_address_next_page, false);
193 return 0;
194 }
195 T var = 0;
196 u32 addr_translated = translated_addr.address;
197 for (u32 addr = em_address; addr < em_address + sizeof(T); addr++, addr_translated++)
198 {
199 if (addr == em_address_next_page)
200 addr_translated = addr_next_page.address;
201 var = (var << 8) | ReadFromHardware<flag, u8, true>(addr_translated);
202 }
203 return var;
204 }
205 em_address = translated_addr.address;
206 }
207
208 // TODO: Make sure these are safe for unaligned addresses.
209
210 if ((em_address & 0xF8000000) == 0x00000000)
211 {
212 // Handle RAM; the masking intentionally discards bits (essentially creating
213 // mirrors of memory).
214 // TODO: Only the first GetRamSizeReal() is supposed to be backed by actual memory.
215 T value;
216 std::memcpy(&value, &Memory::m_pRAM[em_address & Memory::GetRamMask()], sizeof(T));
217 return bswap(value);
218 }
219
220 if (Memory::m_pEXRAM && (em_address >> 28) == 0x1 &&
221 (em_address & 0x0FFFFFFF) < Memory::GetExRamSizeReal())
222 {
223 T value;
224 std::memcpy(&value, &Memory::m_pEXRAM[em_address & 0x0FFFFFFF], sizeof(T));
225 return bswap(value);
226 }
227
228 // Locked L1 technically doesn't have a fixed address, but games all use 0xE0000000.
229 if ((em_address >> 28) == 0xE && (em_address < (0xE0000000 + Memory::GetL1CacheSize())))
230 {
231 T value;
232 std::memcpy(&value, &Memory::m_pL1Cache[em_address & 0x0FFFFFFF], sizeof(T));
233 return bswap(value);
234 }
235 // In Fake-VMEM mode, we need to map the memory somewhere into
236 // physical memory for BAT translation to work; we currently use
237 // [0x7E000000, 0x80000000).
238 if (Memory::m_pFakeVMEM && ((em_address & 0xFE000000) == 0x7E000000))
239 {
240 T value;
241 std::memcpy(&value, &Memory::m_pFakeVMEM[em_address & Memory::GetRamMask()], sizeof(T));
242 return bswap(value);
243 }
244
245 if (flag == XCheckTLBFlag::Read && (em_address & 0xF8000000) == 0x08000000)
246 {
247 if (em_address < 0x0c000000)
248 return EFB_Read(em_address);
249 else
250 return (T)Memory::mmio_mapping->Read<typename std::make_unsigned<T>::type>(em_address);
251 }
252
253 PanicAlert("Unable to resolve read address %x PC %x", em_address, PC);
254 return 0;
255 }
256
257 template <XCheckTLBFlag flag, typename T, bool never_translate = false>
WriteToHardware(u32 em_address,const T data)258 static void WriteToHardware(u32 em_address, const T data)
259 {
260 if (!never_translate && MSR.DR)
261 {
262 auto translated_addr = TranslateAddress<flag>(em_address);
263 if (!translated_addr.Success())
264 {
265 if (flag == XCheckTLBFlag::Write)
266 GenerateDSIException(em_address, true);
267 return;
268 }
269 if ((em_address & (sizeof(T) - 1)) &&
270 (em_address & (HW_PAGE_SIZE - 1)) > HW_PAGE_SIZE - sizeof(T))
271 {
272 // This could be unaligned down to the byte level... hopefully this is rare, so doing it this
273 // way isn't too terrible.
274 // TODO: floats on non-word-aligned boundaries should technically cause alignment exceptions.
275 // Note that "word" means 32-bit, so paired singles or doubles might still be 32-bit aligned!
276 u32 em_address_next_page = (em_address + sizeof(T) - 1) & ~(HW_PAGE_SIZE - 1);
277 auto addr_next_page = TranslateAddress<flag>(em_address_next_page);
278 if (!addr_next_page.Success())
279 {
280 if (flag == XCheckTLBFlag::Write)
281 GenerateDSIException(em_address_next_page, true);
282 return;
283 }
284 T val = bswap(data);
285 u32 addr_translated = translated_addr.address;
286 for (size_t i = 0; i < sizeof(T); i++, addr_translated++)
287 {
288 if (em_address + i == em_address_next_page)
289 addr_translated = addr_next_page.address;
290 WriteToHardware<flag, u8, true>(addr_translated, static_cast<u8>(val >> (i * 8)));
291 }
292 return;
293 }
294 em_address = translated_addr.address;
295 }
296
297 // TODO: Make sure these are safe for unaligned addresses.
298
299 if ((em_address & 0xF8000000) == 0x00000000)
300 {
301 // Handle RAM; the masking intentionally discards bits (essentially creating
302 // mirrors of memory).
303 // TODO: Only the first GetRamSizeReal() is supposed to be backed by actual memory.
304 const T swapped_data = bswap(data);
305 std::memcpy(&Memory::m_pRAM[em_address & Memory::GetRamMask()], &swapped_data, sizeof(T));
306 return;
307 }
308
309 if (Memory::m_pEXRAM && (em_address >> 28) == 0x1 &&
310 (em_address & 0x0FFFFFFF) < Memory::GetExRamSizeReal())
311 {
312 const T swapped_data = bswap(data);
313 std::memcpy(&Memory::m_pEXRAM[em_address & 0x0FFFFFFF], &swapped_data, sizeof(T));
314 return;
315 }
316
317 // Locked L1 technically doesn't have a fixed address, but games all use 0xE0000000.
318 if ((em_address >> 28 == 0xE) && (em_address < (0xE0000000 + Memory::GetL1CacheSize())))
319 {
320 const T swapped_data = bswap(data);
321 std::memcpy(&Memory::m_pL1Cache[em_address & 0x0FFFFFFF], &swapped_data, sizeof(T));
322 return;
323 }
324
325 // In Fake-VMEM mode, we need to map the memory somewhere into
326 // physical memory for BAT translation to work; we currently use
327 // [0x7E000000, 0x80000000).
328 if (Memory::m_pFakeVMEM && ((em_address & 0xFE000000) == 0x7E000000))
329 {
330 const T swapped_data = bswap(data);
331 std::memcpy(&Memory::m_pFakeVMEM[em_address & Memory::GetRamMask()], &swapped_data, sizeof(T));
332 return;
333 }
334
335 // Check for a gather pipe write.
336 // Note that we must mask the address to correctly emulate certain games;
337 // Pac-Man World 3 in particular is affected by this.
338 if (flag == XCheckTLBFlag::Write && (em_address & 0xFFFFF000) == 0x0C008000)
339 {
340 switch (sizeof(T))
341 {
342 case 1:
343 GPFifo::Write8((u8)data);
344 return;
345 case 2:
346 GPFifo::Write16((u16)data);
347 return;
348 case 4:
349 GPFifo::Write32((u32)data);
350 return;
351 case 8:
352 GPFifo::Write64((u64)data);
353 return;
354 }
355 }
356
357 if (flag == XCheckTLBFlag::Write && (em_address & 0xF8000000) == 0x08000000)
358 {
359 if (em_address < 0x0c000000)
360 {
361 EFB_Write((u32)data, em_address);
362 return;
363 }
364 else
365 {
366 Memory::mmio_mapping->Write(em_address, data);
367 return;
368 }
369 }
370
371 PanicAlert("Unable to resolve write address %x PC %x", em_address, PC);
372 return;
373 }
374 // =====================
375
376 // =================================
377 /* These functions are primarily called by the Interpreter functions and are routed to the correct
378 location through ReadFromHardware and WriteToHardware */
379 // ----------------
380
381 static void GenerateISIException(u32 effective_address);
382
Read_Opcode(u32 address)383 u32 Read_Opcode(u32 address)
384 {
385 TryReadInstResult result = TryReadInstruction(address);
386 if (!result.valid)
387 {
388 GenerateISIException(address);
389 return 0;
390 }
391 return result.hex;
392 }
393
TryReadInstruction(u32 address)394 TryReadInstResult TryReadInstruction(u32 address)
395 {
396 bool from_bat = true;
397 if (MSR.IR)
398 {
399 auto tlb_addr = TranslateAddress<XCheckTLBFlag::Opcode>(address);
400 if (!tlb_addr.Success())
401 {
402 return TryReadInstResult{false, false, 0, 0};
403 }
404 else
405 {
406 address = tlb_addr.address;
407 from_bat = tlb_addr.result == TranslateAddressResult::BAT_TRANSLATED;
408 }
409 }
410
411 u32 hex;
412 // TODO: Refactor this. This icache implementation is totally wrong if used with the fake vmem.
413 if (Memory::m_pFakeVMEM && ((address & 0xFE000000) == 0x7E000000))
414 {
415 hex = Common::swap32(&Memory::m_pFakeVMEM[address & Memory::GetFakeVMemMask()]);
416 }
417 else
418 {
419 hex = PowerPC::ppcState.iCache.ReadInstruction(address);
420 }
421 return TryReadInstResult{true, from_bat, hex, address};
422 }
423
HostRead_Instruction(const u32 address)424 u32 HostRead_Instruction(const u32 address)
425 {
426 return ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32>(address);
427 }
428
Memcheck(u32 address,u32 var,bool write,size_t size)429 static void Memcheck(u32 address, u32 var, bool write, size_t size)
430 {
431 if (PowerPC::memchecks.HasAny())
432 {
433 TMemCheck* mc = PowerPC::memchecks.GetMemCheck(address, size);
434 if (mc)
435 {
436 if (CPU::IsStepping())
437 {
438 // Disable when stepping so that resume works.
439 return;
440 }
441 mc->num_hits++;
442 bool pause = mc->Action(&PowerPC::debug_interface, var, address, write, size, PC);
443 if (pause)
444 {
445 CPU::Break();
446 // Fake a DSI so that all the code that tests for it in order to skip
447 // the rest of the instruction will apply. (This means that
448 // watchpoints will stop the emulator before the offending load/store,
449 // not after like GDB does, but that's better anyway. Just need to
450 // make sure resuming after that works.)
451 // It doesn't matter if ReadFromHardware triggers its own DSI because
452 // we'll take it after resuming.
453 PowerPC::ppcState.Exceptions |= EXCEPTION_DSI | EXCEPTION_FAKE_MEMCHECK_HIT;
454 }
455 }
456 }
457 }
458
Read_U8(const u32 address)459 u8 Read_U8(const u32 address)
460 {
461 u8 var = ReadFromHardware<XCheckTLBFlag::Read, u8>(address);
462 Memcheck(address, var, false, 1);
463 return var;
464 }
465
Read_U16(const u32 address)466 u16 Read_U16(const u32 address)
467 {
468 u16 var = ReadFromHardware<XCheckTLBFlag::Read, u16>(address);
469 Memcheck(address, var, false, 2);
470 return var;
471 }
472
Read_U32(const u32 address)473 u32 Read_U32(const u32 address)
474 {
475 u32 var = ReadFromHardware<XCheckTLBFlag::Read, u32>(address);
476 Memcheck(address, var, false, 4);
477 return var;
478 }
479
Read_U64(const u32 address)480 u64 Read_U64(const u32 address)
481 {
482 u64 var = ReadFromHardware<XCheckTLBFlag::Read, u64>(address);
483 Memcheck(address, (u32)var, false, 8);
484 return var;
485 }
486
Read_F64(const u32 address)487 double Read_F64(const u32 address)
488 {
489 const u64 integral = Read_U64(address);
490
491 return Common::BitCast<double>(integral);
492 }
493
Read_F32(const u32 address)494 float Read_F32(const u32 address)
495 {
496 const u32 integral = Read_U32(address);
497
498 return Common::BitCast<float>(integral);
499 }
500
Read_U8_ZX(const u32 address)501 u32 Read_U8_ZX(const u32 address)
502 {
503 return Read_U8(address);
504 }
505
Read_U16_ZX(const u32 address)506 u32 Read_U16_ZX(const u32 address)
507 {
508 return Read_U16(address);
509 }
510
Write_U8(const u8 var,const u32 address)511 void Write_U8(const u8 var, const u32 address)
512 {
513 Memcheck(address, var, true, 1);
514 WriteToHardware<XCheckTLBFlag::Write, u8>(address, var);
515 }
516
Write_U16(const u16 var,const u32 address)517 void Write_U16(const u16 var, const u32 address)
518 {
519 Memcheck(address, var, true, 2);
520 WriteToHardware<XCheckTLBFlag::Write, u16>(address, var);
521 }
Write_U16_Swap(const u16 var,const u32 address)522 void Write_U16_Swap(const u16 var, const u32 address)
523 {
524 Memcheck(address, var, true, 2);
525 Write_U16(Common::swap16(var), address);
526 }
527
Write_U32(const u32 var,const u32 address)528 void Write_U32(const u32 var, const u32 address)
529 {
530 Memcheck(address, var, true, 4);
531 WriteToHardware<XCheckTLBFlag::Write, u32>(address, var);
532 }
Write_U32_Swap(const u32 var,const u32 address)533 void Write_U32_Swap(const u32 var, const u32 address)
534 {
535 Memcheck(address, var, true, 4);
536 Write_U32(Common::swap32(var), address);
537 }
538
Write_U64(const u64 var,const u32 address)539 void Write_U64(const u64 var, const u32 address)
540 {
541 Memcheck(address, (u32)var, true, 8);
542 WriteToHardware<XCheckTLBFlag::Write, u64>(address, var);
543 }
Write_U64_Swap(const u64 var,const u32 address)544 void Write_U64_Swap(const u64 var, const u32 address)
545 {
546 Memcheck(address, (u32)var, true, 8);
547 Write_U64(Common::swap64(var), address);
548 }
549
Write_F64(const double var,const u32 address)550 void Write_F64(const double var, const u32 address)
551 {
552 const u64 integral = Common::BitCast<u64>(var);
553
554 Write_U64(integral, address);
555 }
556
HostRead_U8(const u32 address)557 u8 HostRead_U8(const u32 address)
558 {
559 return ReadFromHardware<XCheckTLBFlag::NoException, u8>(address);
560 }
561
HostRead_U16(const u32 address)562 u16 HostRead_U16(const u32 address)
563 {
564 return ReadFromHardware<XCheckTLBFlag::NoException, u16>(address);
565 }
566
HostRead_U32(const u32 address)567 u32 HostRead_U32(const u32 address)
568 {
569 return ReadFromHardware<XCheckTLBFlag::NoException, u32>(address);
570 }
571
HostRead_U64(const u32 address)572 u64 HostRead_U64(const u32 address)
573 {
574 return ReadFromHardware<XCheckTLBFlag::NoException, u64>(address);
575 }
576
HostRead_F32(const u32 address)577 float HostRead_F32(const u32 address)
578 {
579 const u32 integral = HostRead_U32(address);
580
581 return Common::BitCast<float>(integral);
582 }
583
HostRead_F64(const u32 address)584 double HostRead_F64(const u32 address)
585 {
586 const u64 integral = HostRead_U64(address);
587
588 return Common::BitCast<double>(integral);
589 }
590
HostWrite_U8(const u8 var,const u32 address)591 void HostWrite_U8(const u8 var, const u32 address)
592 {
593 WriteToHardware<XCheckTLBFlag::NoException, u8>(address, var);
594 }
595
HostWrite_U16(const u16 var,const u32 address)596 void HostWrite_U16(const u16 var, const u32 address)
597 {
598 WriteToHardware<XCheckTLBFlag::NoException, u16>(address, var);
599 }
600
HostWrite_U32(const u32 var,const u32 address)601 void HostWrite_U32(const u32 var, const u32 address)
602 {
603 WriteToHardware<XCheckTLBFlag::NoException, u32>(address, var);
604 }
605
HostWrite_U64(const u64 var,const u32 address)606 void HostWrite_U64(const u64 var, const u32 address)
607 {
608 WriteToHardware<XCheckTLBFlag::NoException, u64>(address, var);
609 }
610
HostWrite_F32(const float var,const u32 address)611 void HostWrite_F32(const float var, const u32 address)
612 {
613 const u32 integral = Common::BitCast<u32>(var);
614
615 HostWrite_U32(integral, address);
616 }
617
HostWrite_F64(const double var,const u32 address)618 void HostWrite_F64(const double var, const u32 address)
619 {
620 const u64 integral = Common::BitCast<u64>(var);
621
622 HostWrite_U64(integral, address);
623 }
624
HostGetString(u32 address,size_t size)625 std::string HostGetString(u32 address, size_t size)
626 {
627 std::string s;
628 do
629 {
630 if (!HostIsRAMAddress(address))
631 break;
632 u8 res = HostRead_U8(address);
633 if (!res)
634 break;
635 s += static_cast<char>(res);
636 ++address;
637 } while (size == 0 || s.length() < size);
638 return s;
639 }
640
IsOptimizableRAMAddress(const u32 address)641 bool IsOptimizableRAMAddress(const u32 address)
642 {
643 if (PowerPC::memchecks.HasAny())
644 return false;
645
646 if (!MSR.DR)
647 return false;
648
649 // TODO: This API needs to take an access size
650 //
651 // We store whether an access can be optimized to an unchecked access
652 // in dbat_table.
653 u32 bat_result = dbat_table[address >> BAT_INDEX_SHIFT];
654 return (bat_result & BAT_PHYSICAL_BIT) != 0;
655 }
656
657 template <XCheckTLBFlag flag>
IsRAMAddress(u32 address,bool translate)658 static bool IsRAMAddress(u32 address, bool translate)
659 {
660 if (translate)
661 {
662 auto translate_address = TranslateAddress<flag>(address);
663 if (!translate_address.Success())
664 return false;
665 address = translate_address.address;
666 }
667
668 u32 segment = address >> 28;
669 if (segment == 0x0 && (address & 0x0FFFFFFF) < Memory::GetRamSizeReal())
670 return true;
671 else if (Memory::m_pEXRAM && segment == 0x1 &&
672 (address & 0x0FFFFFFF) < Memory::GetExRamSizeReal())
673 return true;
674 else if (Memory::m_pFakeVMEM && ((address & 0xFE000000) == 0x7E000000))
675 return true;
676 else if (segment == 0xE && (address < (0xE0000000 + Memory::GetL1CacheSize())))
677 return true;
678 return false;
679 }
680
HostIsRAMAddress(u32 address)681 bool HostIsRAMAddress(u32 address)
682 {
683 return IsRAMAddress<XCheckTLBFlag::NoException>(address, MSR.DR);
684 }
685
HostIsInstructionRAMAddress(u32 address)686 bool HostIsInstructionRAMAddress(u32 address)
687 {
688 // Instructions are always 32bit aligned.
689 return !(address & 3) && IsRAMAddress<XCheckTLBFlag::OpcodeNoException>(address, MSR.IR);
690 }
691
DMA_LCToMemory(const u32 mem_address,const u32 cache_address,const u32 num_blocks)692 void DMA_LCToMemory(const u32 mem_address, const u32 cache_address, const u32 num_blocks)
693 {
694 // TODO: It's not completely clear this is the right spot for this code;
695 // what would happen if, for example, the DVD drive tried to write to the EFB?
696 // TODO: This is terribly slow.
697 // TODO: Refactor.
698 // Avatar: The Last Airbender (GC) uses this for videos.
699 if ((mem_address & 0x0F000000) == 0x08000000)
700 {
701 for (u32 i = 0; i < 32 * num_blocks; i += 4)
702 {
703 const u32 data = Common::swap32(Memory::m_pL1Cache + ((cache_address + i) & 0x3FFFF));
704 EFB_Write(data, mem_address + i);
705 }
706 return;
707 }
708
709 // No known game uses this; here for completeness.
710 // TODO: Refactor.
711 if ((mem_address & 0x0F000000) == 0x0C000000)
712 {
713 for (u32 i = 0; i < 32 * num_blocks; i += 4)
714 {
715 const u32 data = Common::swap32(Memory::m_pL1Cache + ((cache_address + i) & 0x3FFFF));
716 Memory::mmio_mapping->Write(mem_address + i, data);
717 }
718 return;
719 }
720
721 const u8* src = Memory::m_pL1Cache + (cache_address & 0x3FFFF);
722 u8* dst = Memory::GetPointer(mem_address);
723 if (dst == nullptr)
724 return;
725
726 memcpy(dst, src, 32 * num_blocks);
727 }
728
DMA_MemoryToLC(const u32 cache_address,const u32 mem_address,const u32 num_blocks)729 void DMA_MemoryToLC(const u32 cache_address, const u32 mem_address, const u32 num_blocks)
730 {
731 const u8* src = Memory::GetPointer(mem_address);
732 u8* dst = Memory::m_pL1Cache + (cache_address & 0x3FFFF);
733
734 // No known game uses this; here for completeness.
735 // TODO: Refactor.
736 if ((mem_address & 0x0F000000) == 0x08000000)
737 {
738 for (u32 i = 0; i < 32 * num_blocks; i += 4)
739 {
740 const u32 data = Common::swap32(EFB_Read(mem_address + i));
741 std::memcpy(Memory::m_pL1Cache + ((cache_address + i) & 0x3FFFF), &data, sizeof(u32));
742 }
743 return;
744 }
745
746 // No known game uses this.
747 // TODO: Refactor.
748 if ((mem_address & 0x0F000000) == 0x0C000000)
749 {
750 for (u32 i = 0; i < 32 * num_blocks; i += 4)
751 {
752 const u32 data = Common::swap32(Memory::mmio_mapping->Read<u32>(mem_address + i));
753 std::memcpy(Memory::m_pL1Cache + ((cache_address + i) & 0x3FFFF), &data, sizeof(u32));
754 }
755 return;
756 }
757
758 if (src == nullptr)
759 return;
760
761 memcpy(dst, src, 32 * num_blocks);
762 }
763
ClearCacheLine(u32 address)764 void ClearCacheLine(u32 address)
765 {
766 DEBUG_ASSERT((address & 0x1F) == 0);
767 if (MSR.DR)
768 {
769 auto translated_address = TranslateAddress<XCheckTLBFlag::Write>(address);
770 if (translated_address.result == TranslateAddressResult::DIRECT_STORE_SEGMENT)
771 {
772 // dcbz to direct store segments is ignored. This is a little
773 // unintuitive, but this is consistent with both console and the PEM.
774 // Advance Game Port crashes if we don't emulate this correctly.
775 return;
776 }
777 if (translated_address.result == TranslateAddressResult::PAGE_FAULT)
778 {
779 // If translation fails, generate a DSI.
780 GenerateDSIException(address, true);
781 return;
782 }
783 address = translated_address.address;
784 }
785
786 // TODO: This isn't precisely correct for non-RAM regions, but the difference
787 // is unlikely to matter.
788 for (u32 i = 0; i < 32; i += 8)
789 WriteToHardware<XCheckTLBFlag::Write, u64, true>(address + i, 0);
790 }
791
IsOptimizableMMIOAccess(u32 address,u32 access_size)792 u32 IsOptimizableMMIOAccess(u32 address, u32 access_size)
793 {
794 if (PowerPC::memchecks.HasAny())
795 return 0;
796
797 if (!MSR.DR)
798 return 0;
799
800 // Translate address
801 // If we also optimize for TLB mappings, we'd have to clear the
802 // JitCache on each TLB invalidation.
803 if (!TranslateBatAddess(dbat_table, &address))
804 return 0;
805
806 // Check whether the address is an aligned address of an MMIO register.
807 const bool aligned = (address & ((access_size >> 3) - 1)) == 0;
808 if (!aligned || !MMIO::IsMMIOAddress(address))
809 return 0;
810
811 return address;
812 }
813
IsOptimizableGatherPipeWrite(u32 address)814 bool IsOptimizableGatherPipeWrite(u32 address)
815 {
816 if (PowerPC::memchecks.HasAny())
817 return false;
818
819 if (!MSR.DR)
820 return false;
821
822 // Translate address, only check BAT mapping.
823 // If we also optimize for TLB mappings, we'd have to clear the
824 // JitCache on each TLB invalidation.
825 if (!TranslateBatAddess(dbat_table, &address))
826 return false;
827
828 // Check whether the translated address equals the address in WPAR.
829 return address == 0x0C008000;
830 }
831
JitCache_TranslateAddress(u32 address)832 TranslateResult JitCache_TranslateAddress(u32 address)
833 {
834 if (!MSR.IR)
835 return TranslateResult{true, true, address};
836
837 // TODO: We shouldn't use FLAG_OPCODE if the caller is the debugger.
838 auto tlb_addr = TranslateAddress<XCheckTLBFlag::Opcode>(address);
839 if (!tlb_addr.Success())
840 {
841 return TranslateResult{false, false, 0};
842 }
843
844 bool from_bat = tlb_addr.result == TranslateAddressResult::BAT_TRANSLATED;
845 return TranslateResult{true, from_bat, tlb_addr.address};
846 }
847
848 // *********************************************************************************
849 // Warning: Test Area
850 //
851 // This code is for TESTING and it works in interpreter mode ONLY. Some games (like
852 // COD iirc) work thanks to this basic TLB emulation.
853 // It is just a small hack and we have never spend enough time to finalize it.
854 // Cheers PearPC!
855 //
856 // *********************************************************************************
857
858 /*
859 * PearPC
860 * ppc_mmu.cc
861 *
862 * Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net)
863 *
864 * This program is free software; you can redistribute it and/or modify
865 * it under the terms of the GNU General Public License version 2 as
866 * published by the Free Software Foundation.
867 *
868 * This program is distributed in the hope that it will be useful,
869 * but WITHOUT ANY WARRANTY; without even the implied warranty of
870 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
871 * GNU General Public License for more details.
872 *
873 * You should have received a copy of the GNU General Public License
874 * along with this program; if not, write to the Free Software
875 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
876 */
877
878 #define PPC_EXC_DSISR_PAGE (1 << 30)
879 #define PPC_EXC_DSISR_PROT (1 << 27)
880 #define PPC_EXC_DSISR_STORE (1 << 25)
881
882 #define SDR1_HTABORG(v) (((v) >> 16) & 0xffff)
883 #define SDR1_HTABMASK(v) ((v)&0x1ff)
884 #define SDR1_PAGETABLE_BASE(v) ((v)&0xffff)
885 #define SR_T (1 << 31)
886 #define SR_Ks (1 << 30)
887 #define SR_Kp (1 << 29)
888 #define SR_N (1 << 28)
889 #define SR_VSID(v) ((v)&0xffffff)
890 #define SR_BUID(v) (((v) >> 20) & 0x1ff)
891 #define SR_CNTRL_SPEC(v) ((v)&0xfffff)
892
893 #define EA_SR(v) (((v) >> 28) & 0xf)
894 #define EA_PageIndex(v) (((v) >> 12) & 0xffff)
895 #define EA_Offset(v) ((v)&0xfff)
896 #define EA_API(v) (((v) >> 22) & 0x3f)
897
898 #define PA_RPN(v) (((v) >> 12) & 0xfffff)
899 #define PA_Offset(v) ((v)&0xfff)
900
901 #define PTE1_V (1 << 31)
902 #define PTE1_VSID(v) (((v) >> 7) & 0xffffff)
903 #define PTE1_H (1 << 6)
904 #define PTE1_API(v) ((v)&0x3f)
905
906 #define PTE2_RPN(v) ((v)&0xfffff000)
907 #define PTE2_R (1 << 8)
908 #define PTE2_C (1 << 7)
909 #define PTE2_WIMG(v) (((v) >> 3) & 0xf)
910 #define PTE2_PP(v) ((v)&3)
911
912 // Hey! these duplicate a structure in Gekko.h
913 union UPTE1
914 {
915 struct
916 {
917 u32 API : 6;
918 u32 H : 1;
919 u32 VSID : 24;
920 u32 V : 1;
921 };
922 u32 Hex;
923 };
924
925 union UPTE2
926 {
927 struct
928 {
929 u32 PP : 2;
930 u32 : 1;
931 u32 WIMG : 4;
932 u32 C : 1;
933 u32 R : 1;
934 u32 : 3;
935 u32 RPN : 20;
936 };
937 u32 Hex;
938 };
939
GenerateDSIException(u32 effective_address,bool write)940 static void GenerateDSIException(u32 effective_address, bool write)
941 {
942 // DSI exceptions are only supported in MMU mode.
943 if (!SConfig::GetInstance().bMMU)
944 {
945 PanicAlert("Invalid %s 0x%08x, PC = 0x%08x ", write ? "write to" : "read from",
946 effective_address, PC);
947 return;
948 }
949
950 if (effective_address)
951 PowerPC::ppcState.spr[SPR_DSISR] = PPC_EXC_DSISR_PAGE | PPC_EXC_DSISR_STORE;
952 else
953 PowerPC::ppcState.spr[SPR_DSISR] = PPC_EXC_DSISR_PAGE;
954
955 PowerPC::ppcState.spr[SPR_DAR] = effective_address;
956
957 PowerPC::ppcState.Exceptions |= EXCEPTION_DSI;
958 }
959
GenerateISIException(u32 effective_address)960 static void GenerateISIException(u32 effective_address)
961 {
962 // Address of instruction could not be translated
963 NPC = effective_address;
964
965 PowerPC::ppcState.Exceptions |= EXCEPTION_ISI;
966 WARN_LOG(POWERPC, "ISI exception at 0x%08x", PC);
967 }
968
SDRUpdated()969 void SDRUpdated()
970 {
971 u32 htabmask = SDR1_HTABMASK(PowerPC::ppcState.spr[SPR_SDR]);
972 if (!Common::IsValidLowMask(htabmask))
973 {
974 return;
975 }
976 u32 htaborg = SDR1_HTABORG(PowerPC::ppcState.spr[SPR_SDR]);
977 if (htaborg & htabmask)
978 {
979 return;
980 }
981 PowerPC::ppcState.pagetable_base = htaborg << 16;
982 PowerPC::ppcState.pagetable_hashmask = ((htabmask << 10) | 0x3ff);
983 }
984
985 enum class TLBLookupResult
986 {
987 Found,
988 NotFound,
989 UpdateC
990 };
991
LookupTLBPageAddress(const XCheckTLBFlag flag,const u32 vpa,u32 * paddr)992 static TLBLookupResult LookupTLBPageAddress(const XCheckTLBFlag flag, const u32 vpa, u32* paddr)
993 {
994 const u32 tag = vpa >> HW_PAGE_INDEX_SHIFT;
995 TLBEntry& tlbe = ppcState.tlb[IsOpcodeFlag(flag)][tag & HW_PAGE_INDEX_MASK];
996
997 if (tlbe.tag[0] == tag)
998 {
999 // Check if C bit requires updating
1000 if (flag == XCheckTLBFlag::Write)
1001 {
1002 UPTE2 PTE2;
1003 PTE2.Hex = tlbe.pte[0];
1004 if (PTE2.C == 0)
1005 {
1006 PTE2.C = 1;
1007 tlbe.pte[0] = PTE2.Hex;
1008 return TLBLookupResult::UpdateC;
1009 }
1010 }
1011
1012 if (!IsNoExceptionFlag(flag))
1013 tlbe.recent = 0;
1014
1015 *paddr = tlbe.paddr[0] | (vpa & 0xfff);
1016
1017 return TLBLookupResult::Found;
1018 }
1019 if (tlbe.tag[1] == tag)
1020 {
1021 // Check if C bit requires updating
1022 if (flag == XCheckTLBFlag::Write)
1023 {
1024 UPTE2 PTE2;
1025 PTE2.Hex = tlbe.pte[1];
1026 if (PTE2.C == 0)
1027 {
1028 PTE2.C = 1;
1029 tlbe.pte[1] = PTE2.Hex;
1030 return TLBLookupResult::UpdateC;
1031 }
1032 }
1033
1034 if (!IsNoExceptionFlag(flag))
1035 tlbe.recent = 1;
1036
1037 *paddr = tlbe.paddr[1] | (vpa & 0xfff);
1038
1039 return TLBLookupResult::Found;
1040 }
1041 return TLBLookupResult::NotFound;
1042 }
1043
UpdateTLBEntry(const XCheckTLBFlag flag,UPTE2 PTE2,const u32 address)1044 static void UpdateTLBEntry(const XCheckTLBFlag flag, UPTE2 PTE2, const u32 address)
1045 {
1046 if (IsNoExceptionFlag(flag))
1047 return;
1048
1049 const int tag = address >> HW_PAGE_INDEX_SHIFT;
1050 TLBEntry& tlbe = ppcState.tlb[IsOpcodeFlag(flag)][tag & HW_PAGE_INDEX_MASK];
1051 const int index = tlbe.recent == 0 && tlbe.tag[0] != TLBEntry::INVALID_TAG;
1052 tlbe.recent = index;
1053 tlbe.paddr[index] = PTE2.RPN << HW_PAGE_INDEX_SHIFT;
1054 tlbe.pte[index] = PTE2.Hex;
1055 tlbe.tag[index] = tag;
1056 }
1057
InvalidateTLBEntry(u32 address)1058 void InvalidateTLBEntry(u32 address)
1059 {
1060 const u32 entry_index = (address >> HW_PAGE_INDEX_SHIFT) & HW_PAGE_INDEX_MASK;
1061
1062 TLBEntry& tlbe = ppcState.tlb[0][entry_index];
1063 tlbe.tag[0] = TLBEntry::INVALID_TAG;
1064 tlbe.tag[1] = TLBEntry::INVALID_TAG;
1065
1066 TLBEntry& tlbe_i = ppcState.tlb[1][entry_index];
1067 tlbe_i.tag[0] = TLBEntry::INVALID_TAG;
1068 tlbe_i.tag[1] = TLBEntry::INVALID_TAG;
1069 }
1070
1071 // Page Address Translation
TranslatePageAddress(const u32 address,const XCheckTLBFlag flag)1072 static TranslateAddressResult TranslatePageAddress(const u32 address, const XCheckTLBFlag flag)
1073 {
1074 // TLB cache
1075 // This catches 99%+ of lookups in practice, so the actual page table entry code below doesn't
1076 // benefit
1077 // much from optimization.
1078 u32 translatedAddress = 0;
1079 TLBLookupResult res = LookupTLBPageAddress(flag, address, &translatedAddress);
1080 if (res == TLBLookupResult::Found)
1081 return TranslateAddressResult{TranslateAddressResult::PAGE_TABLE_TRANSLATED, translatedAddress};
1082
1083 u32 sr = PowerPC::ppcState.sr[EA_SR(address)];
1084
1085 if (sr & 0x80000000)
1086 return TranslateAddressResult{TranslateAddressResult::DIRECT_STORE_SEGMENT, 0};
1087
1088 // TODO: Handle KS/KP segment register flags.
1089
1090 // No-execute segment register flag.
1091 if ((flag == XCheckTLBFlag::Opcode || flag == XCheckTLBFlag::OpcodeNoException) &&
1092 (sr & 0x10000000))
1093 {
1094 return TranslateAddressResult{TranslateAddressResult::PAGE_FAULT, 0};
1095 }
1096
1097 u32 offset = EA_Offset(address); // 12 bit
1098 u32 page_index = EA_PageIndex(address); // 16 bit
1099 u32 VSID = SR_VSID(sr); // 24 bit
1100 u32 api = EA_API(address); // 6 bit (part of page_index)
1101
1102 // hash function no 1 "xor" .360
1103 u32 hash = (VSID ^ page_index);
1104 u32 pte1 = Common::swap32((VSID << 7) | api | PTE1_V);
1105
1106 for (int hash_func = 0; hash_func < 2; hash_func++)
1107 {
1108 // hash function no 2 "not" .360
1109 if (hash_func == 1)
1110 {
1111 hash = ~hash;
1112 pte1 |= PTE1_H << 24;
1113 }
1114
1115 u32 pteg_addr =
1116 ((hash & PowerPC::ppcState.pagetable_hashmask) << 6) | PowerPC::ppcState.pagetable_base;
1117
1118 for (int i = 0; i < 8; i++, pteg_addr += 8)
1119 {
1120 u32 pteg = Common::swap32(Memory::Read_U32(pteg_addr));
1121
1122 if (pte1 == pteg)
1123 {
1124 UPTE2 PTE2;
1125 PTE2.Hex = Memory::Read_U32(pteg_addr + 4);
1126
1127 // set the access bits
1128 switch (flag)
1129 {
1130 case XCheckTLBFlag::NoException:
1131 case XCheckTLBFlag::OpcodeNoException:
1132 break;
1133 case XCheckTLBFlag::Read:
1134 PTE2.R = 1;
1135 break;
1136 case XCheckTLBFlag::Write:
1137 PTE2.R = 1;
1138 PTE2.C = 1;
1139 break;
1140 case XCheckTLBFlag::Opcode:
1141 PTE2.R = 1;
1142 break;
1143 }
1144
1145 if (!IsNoExceptionFlag(flag))
1146 {
1147 Memory::Write_U32(PTE2.Hex, pteg_addr + 4);
1148 }
1149
1150 // We already updated the TLB entry if this was caused by a C bit.
1151 if (res != TLBLookupResult::UpdateC)
1152 UpdateTLBEntry(flag, PTE2, address);
1153
1154 return TranslateAddressResult{TranslateAddressResult::PAGE_TABLE_TRANSLATED,
1155 (PTE2.RPN << 12) | offset};
1156 }
1157 }
1158 }
1159 return TranslateAddressResult{TranslateAddressResult::PAGE_FAULT, 0};
1160 }
1161
UpdateBATs(BatTable & bat_table,u32 base_spr)1162 static void UpdateBATs(BatTable& bat_table, u32 base_spr)
1163 {
1164 // TODO: Separate BATs for MSR.PR==0 and MSR.PR==1
1165 // TODO: Handle PP/WIMG settings.
1166 // TODO: Check how hardware reacts to overlapping BATs (including
1167 // BATs which should cause a DSI).
1168 // TODO: Check how hardware reacts to invalid BATs (bad mask etc).
1169 for (int i = 0; i < 4; ++i)
1170 {
1171 const u32 spr = base_spr + i * 2;
1172 const UReg_BAT_Up batu{ppcState.spr[spr]};
1173 const UReg_BAT_Lo batl{ppcState.spr[spr + 1]};
1174 if (batu.VS == 0 && batu.VP == 0)
1175 continue;
1176
1177 if ((batu.BEPI & batu.BL) != 0)
1178 {
1179 // With a valid BAT, the simplest way to match is
1180 // (input & ~BL_mask) == BEPI. For now, assume it's
1181 // implemented this way for invalid BATs as well.
1182 WARN_LOG(POWERPC, "Bad BAT setup: BEPI overlaps BL");
1183 continue;
1184 }
1185 if ((batl.BRPN & batu.BL) != 0)
1186 {
1187 // With a valid BAT, the simplest way to translate is
1188 // (input & BL_mask) | BRPN_address. For now, assume it's
1189 // implemented this way for invalid BATs as well.
1190 WARN_LOG(POWERPC, "Bad BAT setup: BPRN overlaps BL");
1191 }
1192 if (!Common::IsValidLowMask((u32)batu.BL))
1193 {
1194 // With a valid BAT, the simplest way of masking is
1195 // (input & ~BL_mask) for matching and (input & BL_mask) for
1196 // translation. For now, assume it's implemented this way for
1197 // invalid BATs as well.
1198 WARN_LOG(POWERPC, "Bad BAT setup: invalid mask in BL");
1199 }
1200 for (u32 j = 0; j <= batu.BL; ++j)
1201 {
1202 // Enumerate all bit-patterns which fit within the given mask.
1203 if ((j & batu.BL) == j)
1204 {
1205 // This bit is a little weird: if BRPN & j != 0, we end up with
1206 // a strange mapping. Need to check on hardware.
1207 u32 physical_address = (batl.BRPN | j) << BAT_INDEX_SHIFT;
1208 u32 virtual_address = (batu.BEPI | j) << BAT_INDEX_SHIFT;
1209
1210 // The bottom bit is whether the translation is valid; the second
1211 // bit from the bottom is whether we can use the fastmem arena.
1212 u32 valid_bit = BAT_MAPPED_BIT;
1213 if (Memory::m_pFakeVMEM && (physical_address & 0xFE000000) == 0x7E000000)
1214 valid_bit |= BAT_PHYSICAL_BIT;
1215 else if (physical_address < Memory::GetRamSizeReal())
1216 valid_bit |= BAT_PHYSICAL_BIT;
1217 else if (Memory::m_pEXRAM && physical_address >> 28 == 0x1 &&
1218 (physical_address & 0x0FFFFFFF) < Memory::GetExRamSizeReal())
1219 valid_bit |= BAT_PHYSICAL_BIT;
1220 else if (physical_address >> 28 == 0xE &&
1221 physical_address < 0xE0000000 + Memory::GetL1CacheSize())
1222 valid_bit |= BAT_PHYSICAL_BIT;
1223
1224 // Fastmem doesn't support memchecks, so disable it for all overlapping virtual pages.
1225 if (PowerPC::memchecks.OverlapsMemcheck(virtual_address, BAT_PAGE_SIZE))
1226 valid_bit &= ~BAT_PHYSICAL_BIT;
1227
1228 // (BEPI | j) == (BEPI & ~BL) | (j & BL).
1229 bat_table[virtual_address >> BAT_INDEX_SHIFT] = physical_address | valid_bit;
1230 }
1231 }
1232 }
1233 }
1234
UpdateFakeMMUBat(BatTable & bat_table,u32 start_addr)1235 static void UpdateFakeMMUBat(BatTable& bat_table, u32 start_addr)
1236 {
1237 for (u32 i = 0; i < (0x10000000 >> BAT_INDEX_SHIFT); ++i)
1238 {
1239 // Map from 0x4XXXXXXX or 0x7XXXXXXX to the range
1240 // [0x7E000000,0x80000000).
1241 u32 e_address = i + (start_addr >> BAT_INDEX_SHIFT);
1242 u32 p_address = 0x7E000000 | (i << BAT_INDEX_SHIFT & Memory::GetFakeVMemMask());
1243 u32 flags = BAT_MAPPED_BIT | BAT_PHYSICAL_BIT;
1244
1245 if (PowerPC::memchecks.OverlapsMemcheck(e_address << BAT_INDEX_SHIFT, BAT_PAGE_SIZE))
1246 flags &= ~BAT_PHYSICAL_BIT;
1247
1248 bat_table[e_address] = p_address | flags;
1249 }
1250 }
1251
DBATUpdated()1252 void DBATUpdated()
1253 {
1254 dbat_table = {};
1255 UpdateBATs(dbat_table, SPR_DBAT0U);
1256 bool extended_bats = SConfig::GetInstance().bWii && HID4.SBE;
1257 if (extended_bats)
1258 UpdateBATs(dbat_table, SPR_DBAT4U);
1259 if (Memory::m_pFakeVMEM)
1260 {
1261 // In Fake-MMU mode, insert some extra entries into the BAT tables.
1262 UpdateFakeMMUBat(dbat_table, 0x40000000);
1263 UpdateFakeMMUBat(dbat_table, 0x70000000);
1264 }
1265
1266 #ifndef _ARCH_32
1267 Memory::UpdateLogicalMemory(dbat_table);
1268 #endif
1269
1270 // IsOptimizable*Address and dcbz depends on the BAT mapping, so we need a flush here.
1271 JitInterface::ClearSafe();
1272 }
1273
IBATUpdated()1274 void IBATUpdated()
1275 {
1276 ibat_table = {};
1277 UpdateBATs(ibat_table, SPR_IBAT0U);
1278 bool extended_bats = SConfig::GetInstance().bWii && HID4.SBE;
1279 if (extended_bats)
1280 UpdateBATs(ibat_table, SPR_IBAT4U);
1281 if (Memory::m_pFakeVMEM)
1282 {
1283 // In Fake-MMU mode, insert some extra entries into the BAT tables.
1284 UpdateFakeMMUBat(ibat_table, 0x40000000);
1285 UpdateFakeMMUBat(ibat_table, 0x70000000);
1286 }
1287 JitInterface::ClearSafe();
1288 }
1289
1290 // Translate effective address using BAT or PAT. Returns 0 if the address cannot be translated.
1291 // Through the hardware looks up BAT and TLB in parallel, BAT is used first if available.
1292 // So we first check if there is a matching BAT entry, else we look for the TLB in
1293 // TranslatePageAddress().
1294 template <const XCheckTLBFlag flag>
TranslateAddress(u32 address)1295 static TranslateAddressResult TranslateAddress(u32 address)
1296 {
1297 if (TranslateBatAddess(IsOpcodeFlag(flag) ? ibat_table : dbat_table, &address))
1298 return TranslateAddressResult{TranslateAddressResult::BAT_TRANSLATED, address};
1299
1300 return TranslatePageAddress(address, flag);
1301 }
1302
GetTranslatedAddress(u32 address)1303 std::optional<u32> GetTranslatedAddress(u32 address)
1304 {
1305 auto result = TranslateAddress<XCheckTLBFlag::NoException>(address);
1306 if (!result.Success())
1307 {
1308 return std::nullopt;
1309 }
1310 return std::optional<u32>(result.address);
1311 }
1312
1313 } // namespace PowerPC
1314