2015-05-25 13:11:41 +02:00
|
|
|
// Copyright 2003 Dolphin Emulator Project
|
2021-07-05 03:22:19 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-26 14:59:08 +02:00
|
|
|
// Some of the code in this file was originally based on PearPC, though it has been modified since.
|
|
|
|
// We have been given permission by the author to re-license the code under GPLv2+.
|
|
|
|
/*
|
|
|
|
* PearPC
|
|
|
|
* ppc_mmu.cc
|
|
|
|
*
|
|
|
|
* Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net)
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
2018-05-17 17:09:55 -04:00
|
|
|
#include "Core/PowerPC/MMU.h"
|
|
|
|
|
2022-08-05 15:39:00 +02:00
|
|
|
#include <bit>
|
2017-02-03 11:42:40 -05:00
|
|
|
#include <cstddef>
|
2016-09-30 03:49:15 -04:00
|
|
|
#include <cstring>
|
2017-06-06 23:36:16 -04:00
|
|
|
#include <string>
|
2016-09-30 03:49:15 -04:00
|
|
|
|
2022-12-25 12:29:13 +01:00
|
|
|
#include "Common/Align.h"
|
2021-07-26 14:59:20 +02:00
|
|
|
#include "Common/Assert.h"
|
2017-06-22 13:04:00 +02:00
|
|
|
#include "Common/BitUtils.h"
|
2014-09-07 20:06:58 -05:00
|
|
|
#include "Common/CommonTypes.h"
|
2021-07-26 16:51:15 +02:00
|
|
|
#include "Common/Logging/Log.h"
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2014-09-09 00:24:49 -04:00
|
|
|
#include "Core/ConfigManager.h"
|
2023-03-12 18:48:23 +01:00
|
|
|
#include "Core/Core.h"
|
2015-04-23 01:22:35 -04:00
|
|
|
#include "Core/HW/CPU.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Core/HW/GPFifo.h"
|
|
|
|
#include "Core/HW/MMIO.h"
|
|
|
|
#include "Core/HW/Memmap.h"
|
2021-07-26 17:48:47 +02:00
|
|
|
#include "Core/HW/ProcessorInterface.h"
|
2021-10-01 09:56:26 -04:00
|
|
|
#include "Core/PowerPC/GDBStub.h"
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
#include "Core/PowerPC/JitInterface.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Core/PowerPC/PowerPC.h"
|
2022-01-07 04:37:07 +01:00
|
|
|
#include "Core/System.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
|
|
|
|
#include "VideoCommon/VideoBackendBase.h"
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2015-01-17 13:17:36 -08:00
|
|
|
namespace PowerPC
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2023-03-28 20:26:52 +02:00
|
|
|
MMU::MMU(Core::System& system, Memory::MemoryManager& memory, PowerPC::PowerPCManager& power_pc)
|
|
|
|
: m_system(system), m_memory(memory), m_power_pc(power_pc), m_ppc_state(power_pc.GetPPCState())
|
2023-03-12 20:31:10 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
MMU::~MMU() = default;
|
|
|
|
|
2009-07-11 10:18:25 +00:00
|
|
|
// Overloaded byteswap functions, for use within the templated functions below.
|
2023-03-12 20:31:10 +01:00
|
|
|
[[maybe_unused]] static u8 bswap(u8 val)
|
2014-12-11 14:12:20 -08:00
|
|
|
{
|
|
|
|
return val;
|
|
|
|
}
|
2023-03-12 20:31:10 +01:00
|
|
|
[[maybe_unused]] static s8 bswap(s8 val)
|
2014-12-11 14:12:20 -08:00
|
|
|
{
|
|
|
|
return val;
|
|
|
|
}
|
2023-03-12 20:31:10 +01:00
|
|
|
[[maybe_unused]] static u16 bswap(u16 val)
|
2014-12-11 14:12:20 -08:00
|
|
|
{
|
|
|
|
return Common::swap16(val);
|
|
|
|
}
|
2023-03-12 20:31:10 +01:00
|
|
|
[[maybe_unused]] static s16 bswap(s16 val)
|
2014-12-11 14:12:20 -08:00
|
|
|
{
|
|
|
|
return Common::swap16(val);
|
|
|
|
}
|
2023-03-12 20:31:10 +01:00
|
|
|
[[maybe_unused]] static u32 bswap(u32 val)
|
2014-12-11 14:12:20 -08:00
|
|
|
{
|
|
|
|
return Common::swap32(val);
|
|
|
|
}
|
2023-03-12 20:31:10 +01:00
|
|
|
[[maybe_unused]] static u64 bswap(u64 val)
|
2014-12-11 14:12:20 -08:00
|
|
|
{
|
|
|
|
return Common::swap64(val);
|
|
|
|
}
|
2009-07-11 10:18:25 +00:00
|
|
|
|
2016-06-25 18:57:16 -07:00
|
|
|
static bool IsOpcodeFlag(XCheckTLBFlag flag)
|
|
|
|
{
|
2018-03-25 19:48:15 -04:00
|
|
|
return flag == XCheckTLBFlag::Opcode || flag == XCheckTLBFlag::OpcodeNoException;
|
2016-06-25 18:57:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool IsNoExceptionFlag(XCheckTLBFlag flag)
|
|
|
|
{
|
2018-03-25 19:48:15 -04:00
|
|
|
return flag == XCheckTLBFlag::NoException || flag == XCheckTLBFlag::OpcodeNoException;
|
2016-06-25 18:57:16 -07:00
|
|
|
}
|
|
|
|
|
2010-01-17 21:28:39 +00:00
|
|
|
// Nasty but necessary. Super Mario Galaxy pointer relies on this stuff.
|
2014-05-25 16:30:04 -07:00
|
|
|
static u32 EFB_Read(const u32 addr)
|
2010-01-17 21:28:39 +00:00
|
|
|
{
|
|
|
|
u32 var = 0;
|
|
|
|
// Convert address to coordinates. It's possible that this should be done
|
2017-01-23 02:51:46 -05:00
|
|
|
// differently depending on color depth, especially regarding PeekColor.
|
2018-05-20 16:09:04 -04:00
|
|
|
const u32 x = (addr & 0xfff) >> 2;
|
|
|
|
const u32 y = (addr >> 12) & 0x3ff;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-09-09 12:31:00 +12:00
|
|
|
if (addr & 0x00800000)
|
|
|
|
{
|
2020-11-25 08:45:21 -05:00
|
|
|
ERROR_LOG_FMT(MEMMAP, "Unimplemented Z+Color EFB read @ {:#010x}", addr);
|
2015-09-09 12:31:00 +12:00
|
|
|
}
|
|
|
|
else if (addr & 0x00400000)
|
2014-08-30 17:23:13 -04:00
|
|
|
{
|
2017-01-23 02:51:46 -05:00
|
|
|
var = g_video_backend->Video_AccessEFB(EFBAccessType::PeekZ, x, y, 0);
|
2020-11-25 08:45:21 -05:00
|
|
|
DEBUG_LOG_FMT(MEMMAP, "EFB Z Read @ {}, {}\t= {:#010x}", x, y, var);
|
2014-08-30 17:23:13 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-01-23 02:51:46 -05:00
|
|
|
var = g_video_backend->Video_AccessEFB(EFBAccessType::PeekColor, x, y, 0);
|
2020-11-25 08:45:21 -05:00
|
|
|
DEBUG_LOG_FMT(MEMMAP, "EFB Color Read @ {}, {}\t= {:#010x}", x, y, var);
|
2010-01-17 21:28:39 +00:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2010-01-17 21:28:39 +00:00
|
|
|
return var;
|
|
|
|
}
|
|
|
|
|
2015-01-09 22:20:06 -08:00
|
|
|
static void EFB_Write(u32 data, u32 addr)
|
|
|
|
{
|
2018-05-20 16:09:04 -04:00
|
|
|
const u32 x = (addr & 0xfff) >> 2;
|
|
|
|
const u32 y = (addr >> 12) & 0x3ff;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-09-09 12:31:00 +12:00
|
|
|
if (addr & 0x00800000)
|
|
|
|
{
|
|
|
|
// It's possible to do a z-tested write to EFB by writing a 64bit value to this address range.
|
|
|
|
// Not much is known, but let's at least get some loging.
|
2020-11-25 08:45:21 -05:00
|
|
|
ERROR_LOG_FMT(MEMMAP, "Unimplemented Z+Color EFB write. {:08x} @ {:#010x}", data, addr);
|
2015-09-09 12:31:00 +12:00
|
|
|
}
|
|
|
|
else if (addr & 0x00400000)
|
2015-01-09 22:20:06 -08:00
|
|
|
{
|
2017-01-23 02:51:46 -05:00
|
|
|
g_video_backend->Video_AccessEFB(EFBAccessType::PokeZ, x, y, data);
|
2020-11-25 08:45:21 -05:00
|
|
|
DEBUG_LOG_FMT(MEMMAP, "EFB Z Write {:08x} @ {}, {}", data, x, y);
|
2015-01-09 22:20:06 -08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-01-23 02:51:46 -05:00
|
|
|
g_video_backend->Video_AccessEFB(EFBAccessType::PokeColor, x, y, data);
|
2020-11-25 08:45:21 -05:00
|
|
|
DEBUG_LOG_FMT(MEMMAP, "EFB Color Write {:08x} @ {}, {}", data, x, y);
|
2015-01-09 22:20:06 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
template <XCheckTLBFlag flag, typename T, bool never_translate>
|
|
|
|
T MMU::ReadFromHardware(u32 em_address)
|
2009-02-15 19:47:12 +00:00
|
|
|
{
|
2022-05-07 19:37:44 +02:00
|
|
|
const u32 em_address_start_page = em_address & ~HW_PAGE_MASK;
|
|
|
|
const u32 em_address_end_page = (em_address + sizeof(T) - 1) & ~HW_PAGE_MASK;
|
2022-05-07 14:21:30 +02:00
|
|
|
if (em_address_start_page != em_address_end_page)
|
|
|
|
{
|
|
|
|
// This could be unaligned down to the byte level... hopefully this is rare, so doing it this
|
|
|
|
// way isn't too terrible.
|
|
|
|
// TODO: floats on non-word-aligned boundaries should technically cause alignment exceptions.
|
|
|
|
// Note that "word" means 32-bit, so paired singles or doubles might still be 32-bit aligned!
|
|
|
|
u64 var = 0;
|
|
|
|
for (u32 i = 0; i < sizeof(T); ++i)
|
2023-03-07 04:02:48 +01:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
var = (var << 8) | ReadFromHardware<flag, u8, never_translate>(em_address + i);
|
2023-03-07 04:02:48 +01:00
|
|
|
}
|
2022-05-07 14:21:30 +02:00
|
|
|
return static_cast<T>(var);
|
|
|
|
}
|
|
|
|
|
2022-10-17 15:28:29 -04:00
|
|
|
bool wi = false;
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!never_translate && m_ppc_state.msr.DR)
|
2009-06-29 18:49:37 +00:00
|
|
|
{
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
auto translated_addr = TranslateAddress<flag>(em_address);
|
|
|
|
if (!translated_addr.Success())
|
2014-12-30 18:12:47 -08:00
|
|
|
{
|
2018-03-25 19:48:15 -04:00
|
|
|
if (flag == XCheckTLBFlag::Read)
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
GenerateDSIException(em_address, false);
|
|
|
|
return 0;
|
2015-01-01 17:56:14 -08:00
|
|
|
}
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
em_address = translated_addr.address;
|
2022-10-17 15:28:29 -04:00
|
|
|
wi = translated_addr.wi;
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
|
2022-05-07 14:32:45 +02:00
|
|
|
if (flag == XCheckTLBFlag::Read && (em_address & 0xF8000000) == 0x08000000)
|
|
|
|
{
|
|
|
|
if (em_address < 0x0c000000)
|
|
|
|
return EFB_Read(em_address);
|
|
|
|
else
|
2023-03-12 20:31:10 +01:00
|
|
|
return static_cast<T>(m_memory.GetMMIOMapping()->Read<std::make_unsigned_t<T>>(em_address));
|
2022-05-07 14:32:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Locked L1 technically doesn't have a fixed address, but games all use 0xE0000000.
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_memory.GetL1Cache() && (em_address >> 28) == 0xE &&
|
|
|
|
(em_address < (0xE0000000 + m_memory.GetL1CacheSize())))
|
2022-05-07 14:32:45 +02:00
|
|
|
{
|
|
|
|
T value;
|
2023-03-12 20:31:10 +01:00
|
|
|
std::memcpy(&value, &m_memory.GetL1Cache()[em_address & 0x0FFFFFFF], sizeof(T));
|
2022-05-07 14:32:45 +02:00
|
|
|
return bswap(value);
|
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_memory.GetRAM() && (em_address & 0xF8000000) == 0x00000000)
|
2016-09-09 01:16:26 +02:00
|
|
|
{
|
|
|
|
// Handle RAM; the masking intentionally discards bits (essentially creating
|
|
|
|
// mirrors of memory).
|
2017-04-17 19:21:28 -04:00
|
|
|
T value;
|
2023-03-12 20:31:10 +01:00
|
|
|
em_address &= m_memory.GetRamMask();
|
2022-10-17 15:28:29 -04:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!m_ppc_state.m_enable_dcache || wi)
|
2022-10-17 15:28:29 -04:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
std::memcpy(&value, &m_memory.GetRAM()[em_address], sizeof(T));
|
2022-10-17 15:28:29 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
m_ppc_state.dCache.Read(em_address, &value, sizeof(T),
|
|
|
|
HID0(m_ppc_state).DLOCK || flag != XCheckTLBFlag::Read);
|
2022-10-17 15:28:29 -04:00
|
|
|
}
|
|
|
|
|
2017-04-17 19:21:28 -04:00
|
|
|
return bswap(value);
|
2016-09-09 01:16:26 +02:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_memory.GetEXRAM() && (em_address >> 28) == 0x1 &&
|
|
|
|
(em_address & 0x0FFFFFFF) < m_memory.GetExRamSizeReal())
|
2016-09-09 01:16:26 +02:00
|
|
|
{
|
2017-04-17 19:21:28 -04:00
|
|
|
T value;
|
2022-10-17 15:28:29 -04:00
|
|
|
em_address &= 0x0FFFFFFF;
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!m_ppc_state.m_enable_dcache || wi)
|
2022-10-17 15:28:29 -04:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
std::memcpy(&value, &m_memory.GetEXRAM()[em_address], sizeof(T));
|
2022-10-17 15:28:29 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
m_ppc_state.dCache.Read(em_address + 0x10000000, &value, sizeof(T),
|
|
|
|
HID0(m_ppc_state).DLOCK || flag != XCheckTLBFlag::Read);
|
2022-10-17 15:28:29 -04:00
|
|
|
}
|
|
|
|
|
2017-04-17 19:21:28 -04:00
|
|
|
return bswap(value);
|
2016-09-09 01:16:26 +02:00
|
|
|
}
|
|
|
|
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
// In Fake-VMEM mode, we need to map the memory somewhere into
|
|
|
|
// physical memory for BAT translation to work; we currently use
|
|
|
|
// [0x7E000000, 0x80000000).
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_memory.GetFakeVMEM() && ((em_address & 0xFE000000) == 0x7E000000))
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
2017-04-17 19:21:28 -04:00
|
|
|
T value;
|
2023-03-12 20:31:10 +01:00
|
|
|
std::memcpy(&value, &m_memory.GetFakeVMEM()[em_address & m_memory.GetFakeVMemMask()],
|
|
|
|
sizeof(T));
|
2017-04-17 19:21:28 -04:00
|
|
|
return bswap(value);
|
2015-01-17 13:17:36 -08:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
PanicAlertFmt("Unable to resolve read address {:x} PC {:x}", em_address, m_ppc_state.pc);
|
|
|
|
if (m_system.IsPauseOnPanicMode())
|
2022-04-17 00:15:12 -07:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
m_system.GetCPU().Break();
|
|
|
|
m_ppc_state.Exceptions |= EXCEPTION_DSI | EXCEPTION_FAKE_MEMCHECK_HIT;
|
2022-04-17 00:15:12 -07:00
|
|
|
}
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
return 0;
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
template <XCheckTLBFlag flag, bool never_translate>
|
|
|
|
void MMU::WriteToHardware(u32 em_address, const u32 data, const u32 size)
|
2009-02-15 19:47:12 +00:00
|
|
|
{
|
2021-07-26 14:59:20 +02:00
|
|
|
DEBUG_ASSERT(size <= 4);
|
|
|
|
|
2022-05-07 19:37:44 +02:00
|
|
|
const u32 em_address_start_page = em_address & ~HW_PAGE_MASK;
|
|
|
|
const u32 em_address_end_page = (em_address + size - 1) & ~HW_PAGE_MASK;
|
2021-07-26 14:59:20 +02:00
|
|
|
if (em_address_start_page != em_address_end_page)
|
|
|
|
{
|
|
|
|
// The write crosses a page boundary. Break it up into two writes.
|
|
|
|
// TODO: floats on non-word-aligned boundaries should technically cause alignment exceptions.
|
|
|
|
// Note that "word" means 32-bit, so paired singles or doubles might still be 32-bit aligned!
|
|
|
|
const u32 first_half_size = em_address_end_page - em_address;
|
|
|
|
const u32 second_half_size = size - first_half_size;
|
2023-03-12 20:31:10 +01:00
|
|
|
WriteToHardware<flag, never_translate>(em_address, std::rotr(data, second_half_size * 8),
|
|
|
|
first_half_size);
|
|
|
|
WriteToHardware<flag, never_translate>(em_address_end_page, data, second_half_size);
|
2021-07-26 14:59:20 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-07-26 16:51:15 +02:00
|
|
|
bool wi = false;
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!never_translate && m_ppc_state.msr.DR)
|
2010-07-29 12:17:47 +00:00
|
|
|
{
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
auto translated_addr = TranslateAddress<flag>(em_address);
|
|
|
|
if (!translated_addr.Success())
|
2010-03-07 16:37:35 +00:00
|
|
|
{
|
2018-03-25 19:48:15 -04:00
|
|
|
if (flag == XCheckTLBFlag::Write)
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
GenerateDSIException(em_address, true);
|
|
|
|
return;
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
em_address = translated_addr.address;
|
2021-07-26 16:51:15 +02:00
|
|
|
wi = translated_addr.wi;
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
}
|
|
|
|
|
2022-07-21 15:20:31 -07:00
|
|
|
// Check for a gather pipe write (which are not implemented through the MMIO system).
|
2022-07-21 14:18:35 -07:00
|
|
|
//
|
|
|
|
// Note that we must mask the address to correctly emulate certain games; Pac-Man World 3
|
|
|
|
// in particular is affected by this. (See https://bugs.dolphin-emu.org/issues/8386)
|
|
|
|
//
|
2022-07-21 15:20:31 -07:00
|
|
|
// The PowerPC 750CL manual says (in section 9.4.2 Write Gather Pipe Operation on page 327):
|
|
|
|
// "A noncacheable store to an address with bits 0-26 matching WPAR[GB_ADDR] but with bits 27-31
|
|
|
|
// not all zero will result in incorrect data in the buffer." So, it's possible that in some cases
|
|
|
|
// writes which do not exactly match the masking behave differently, but Pac-Man World 3's writes
|
|
|
|
// happen to behave correctly.
|
2022-07-20 17:44:49 -07:00
|
|
|
if (flag == XCheckTLBFlag::Write &&
|
|
|
|
(em_address & 0xFFFFF000) == GPFifo::GATHER_PIPE_PHYSICAL_ADDRESS)
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
2021-07-26 14:59:20 +02:00
|
|
|
switch (size)
|
2015-01-17 13:17:36 -08:00
|
|
|
{
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
case 1:
|
2023-03-12 20:31:10 +01:00
|
|
|
m_system.GetGPFifo().Write8(static_cast<u8>(data));
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
return;
|
|
|
|
case 2:
|
2023-03-12 20:31:10 +01:00
|
|
|
m_system.GetGPFifo().Write16(static_cast<u16>(data));
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
return;
|
|
|
|
case 4:
|
2023-03-12 20:31:10 +01:00
|
|
|
m_system.GetGPFifo().Write32(data);
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
return;
|
2021-07-26 14:59:20 +02:00
|
|
|
default:
|
|
|
|
// Some kind of misaligned write. TODO: Does this match how the actual hardware handles it?
|
2023-03-12 20:31:10 +01:00
|
|
|
auto& gpfifo = m_system.GetGPFifo();
|
2021-07-26 14:59:20 +02:00
|
|
|
for (size_t i = size * 8; i > 0;)
|
|
|
|
{
|
|
|
|
i -= 8;
|
2023-01-05 05:18:24 +01:00
|
|
|
gpfifo.Write8(static_cast<u8>(data >> i));
|
2021-07-26 14:59:20 +02:00
|
|
|
}
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
return;
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
}
|
|
|
|
|
2018-03-25 19:48:15 -04:00
|
|
|
if (flag == XCheckTLBFlag::Write && (em_address & 0xF8000000) == 0x08000000)
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
|
|
|
if (em_address < 0x0c000000)
|
2015-01-17 13:17:36 -08:00
|
|
|
{
|
2021-07-26 14:59:20 +02:00
|
|
|
EFB_Write(data, em_address);
|
2015-01-17 13:17:36 -08:00
|
|
|
return;
|
|
|
|
}
|
2021-07-26 14:59:20 +02:00
|
|
|
|
|
|
|
switch (size)
|
2015-01-17 13:17:36 -08:00
|
|
|
{
|
2021-07-26 14:59:20 +02:00
|
|
|
case 1:
|
2023-03-12 20:31:10 +01:00
|
|
|
m_memory.GetMMIOMapping()->Write<u8>(em_address, static_cast<u8>(data));
|
2021-07-26 14:59:20 +02:00
|
|
|
return;
|
|
|
|
case 2:
|
2023-03-12 20:31:10 +01:00
|
|
|
m_memory.GetMMIOMapping()->Write<u16>(em_address, static_cast<u16>(data));
|
2021-07-26 14:59:20 +02:00
|
|
|
return;
|
|
|
|
case 4:
|
2023-03-12 20:31:10 +01:00
|
|
|
m_memory.GetMMIOMapping()->Write<u32>(em_address, data);
|
2021-07-26 14:59:20 +02:00
|
|
|
return;
|
|
|
|
default:
|
|
|
|
// Some kind of misaligned write. TODO: Does this match how the actual hardware handles it?
|
|
|
|
for (size_t i = size * 8; i > 0; em_address++)
|
|
|
|
{
|
|
|
|
i -= 8;
|
2023-03-12 20:31:10 +01:00
|
|
|
m_memory.GetMMIOMapping()->Write<u8>(em_address, static_cast<u8>(data >> i));
|
2021-07-26 14:59:20 +02:00
|
|
|
}
|
2015-01-17 13:17:36 -08:00
|
|
|
return;
|
|
|
|
}
|
2009-02-15 19:47:12 +00:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2022-08-05 15:39:00 +02:00
|
|
|
const u32 swapped_data = Common::swap32(std::rotr(data, size * 8));
|
2021-07-26 16:51:15 +02:00
|
|
|
|
|
|
|
// Locked L1 technically doesn't have a fixed address, but games all use 0xE0000000.
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_memory.GetL1Cache() && (em_address >> 28 == 0xE) &&
|
|
|
|
(em_address < (0xE0000000 + m_memory.GetL1CacheSize())))
|
2021-07-26 16:51:15 +02:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
std::memcpy(&m_memory.GetL1Cache()[em_address & 0x0FFFFFFF], &swapped_data, size);
|
2021-07-26 16:51:15 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wi && (size < 4 || (em_address & 0x3)))
|
|
|
|
{
|
|
|
|
// When a write to memory is performed in hardware, 64 bits of data are sent to the memory
|
|
|
|
// controller along with a mask. This mask is encoded using just two bits of data - one for
|
|
|
|
// the upper 32 bits and one for the lower 32 bits - which leads to some odd data duplication
|
|
|
|
// behavior for write-through/cache-inhibited writes with a start address or end address that
|
|
|
|
// isn't 32-bit aligned. See https://bugs.dolphin-emu.org/issues/12565 for details.
|
|
|
|
|
2021-07-26 17:48:47 +02:00
|
|
|
// TODO: This interrupt is supposed to have associated cause and address registers
|
|
|
|
// TODO: This should trigger the hwtest's interrupt handling, but it does not seem to
|
|
|
|
// (https://github.com/dolphin-emu/hwtests/pull/42)
|
2023-03-12 20:31:10 +01:00
|
|
|
m_system.GetProcessorInterface().SetInterrupt(ProcessorInterface::INT_CAUSE_PI);
|
2021-07-26 17:48:47 +02:00
|
|
|
|
2022-08-05 15:39:00 +02:00
|
|
|
const u32 rotated_data = std::rotr(data, ((em_address & 0x3) + size) * 8);
|
2021-07-26 16:51:15 +02:00
|
|
|
|
2022-12-25 12:29:13 +01:00
|
|
|
const u32 start_addr = Common::AlignDown(em_address, 8);
|
|
|
|
const u32 end_addr = Common::AlignUp(em_address + size, 8);
|
|
|
|
for (u32 addr = start_addr; addr != end_addr; addr += 8)
|
2021-07-26 16:51:15 +02:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
WriteToHardware<flag, true>(addr, rotated_data, 4);
|
|
|
|
WriteToHardware<flag, true>(addr + 4, rotated_data, 4);
|
2021-07-26 16:51:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_memory.GetRAM() && (em_address & 0xF8000000) == 0x00000000)
|
2021-07-26 16:51:15 +02:00
|
|
|
{
|
|
|
|
// Handle RAM; the masking intentionally discards bits (essentially creating
|
|
|
|
// mirrors of memory).
|
2023-03-12 20:31:10 +01:00
|
|
|
em_address &= m_memory.GetRamMask();
|
2022-10-17 15:28:29 -04:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_ppc_state.m_enable_dcache && !wi)
|
|
|
|
m_ppc_state.dCache.Write(em_address, &swapped_data, size, HID0(m_ppc_state).DLOCK);
|
2022-10-17 15:28:29 -04:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!m_ppc_state.m_enable_dcache || wi || flag != XCheckTLBFlag::Write)
|
|
|
|
std::memcpy(&m_memory.GetRAM()[em_address], &swapped_data, size);
|
2022-10-17 15:28:29 -04:00
|
|
|
|
2021-07-26 16:51:15 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_memory.GetEXRAM() && (em_address >> 28) == 0x1 &&
|
|
|
|
(em_address & 0x0FFFFFFF) < m_memory.GetExRamSizeReal())
|
2021-07-26 16:51:15 +02:00
|
|
|
{
|
2022-10-17 15:28:29 -04:00
|
|
|
em_address &= 0x0FFFFFFF;
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_ppc_state.m_enable_dcache && !wi)
|
2023-01-09 20:47:18 +01:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
m_ppc_state.dCache.Write(em_address + 0x10000000, &swapped_data, size,
|
|
|
|
HID0(m_ppc_state).DLOCK);
|
2023-01-09 20:47:18 +01:00
|
|
|
}
|
2022-10-17 15:28:29 -04:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!m_ppc_state.m_enable_dcache || wi || flag != XCheckTLBFlag::Write)
|
|
|
|
std::memcpy(&m_memory.GetEXRAM()[em_address], &swapped_data, size);
|
2022-10-17 15:28:29 -04:00
|
|
|
|
2021-07-26 16:51:15 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// In Fake-VMEM mode, we need to map the memory somewhere into
|
|
|
|
// physical memory for BAT translation to work; we currently use
|
|
|
|
// [0x7E000000, 0x80000000).
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_memory.GetFakeVMEM() && ((em_address & 0xFE000000) == 0x7E000000))
|
2021-07-26 16:51:15 +02:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
std::memcpy(&m_memory.GetFakeVMEM()[em_address & m_memory.GetFakeVMemMask()], &swapped_data,
|
|
|
|
size);
|
2021-07-26 16:51:15 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
PanicAlertFmt("Unable to resolve write address {:x} PC {:x}", em_address, m_ppc_state.pc);
|
|
|
|
if (m_system.IsPauseOnPanicMode())
|
2022-04-17 00:15:12 -07:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
m_system.GetCPU().Break();
|
|
|
|
m_ppc_state.Exceptions |= EXCEPTION_DSI | EXCEPTION_FAKE_MEMCHECK_HIT;
|
2022-04-17 00:15:12 -07:00
|
|
|
}
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
|
|
|
// =====================
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2009-01-18 09:51:09 +00:00
|
|
|
// =================================
|
2009-01-19 10:54:43 +00:00
|
|
|
/* These functions are primarily called by the Interpreter functions and are routed to the correct
|
2009-02-15 19:47:12 +00:00
|
|
|
location through ReadFromHardware and WriteToHardware */
|
2009-01-18 09:51:09 +00:00
|
|
|
// ----------------
|
2014-05-25 16:30:04 -07:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
u32 MMU::Read_Opcode(u32 address)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2015-01-17 13:17:36 -08:00
|
|
|
TryReadInstResult result = TryReadInstruction(address);
|
|
|
|
if (!result.valid)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2015-01-17 13:17:36 -08:00
|
|
|
GenerateISIException(address);
|
|
|
|
return 0;
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2015-01-17 13:17:36 -08:00
|
|
|
return result.hex;
|
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
TryReadInstResult MMU::TryReadInstruction(u32 address)
|
2015-01-17 13:17:36 -08:00
|
|
|
{
|
|
|
|
bool from_bat = true;
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_ppc_state.msr.IR)
|
2010-07-29 12:17:47 +00:00
|
|
|
{
|
2018-03-25 19:48:15 -04:00
|
|
|
auto tlb_addr = TranslateAddress<XCheckTLBFlag::Opcode>(address);
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
if (!tlb_addr.Success())
|
2010-07-29 12:17:47 +00:00
|
|
|
{
|
2017-01-22 16:23:56 +01:00
|
|
|
return TryReadInstResult{false, false, 0, 0};
|
2010-07-29 12:17:47 +00:00
|
|
|
}
|
|
|
|
else
|
2013-04-16 23:14:36 -04:00
|
|
|
{
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
address = tlb_addr.address;
|
2021-08-07 00:07:40 +02:00
|
|
|
from_bat = tlb_addr.result == TranslateAddressResultEnum::BAT_TRANSLATED;
|
2013-04-16 23:14:36 -04:00
|
|
|
}
|
2010-07-29 12:17:47 +00:00
|
|
|
}
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
|
|
|
|
u32 hex;
|
2016-08-24 08:12:05 +02:00
|
|
|
// TODO: Refactor this. This icache implementation is totally wrong if used with the fake vmem.
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_memory.GetFakeVMEM() && ((address & 0xFE000000) == 0x7E000000))
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
hex = Common::swap32(&m_memory.GetFakeVMEM()[address & m_memory.GetFakeVMemMask()]);
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
}
|
2015-01-17 13:17:36 -08:00
|
|
|
else
|
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
hex = m_ppc_state.iCache.ReadInstruction(address);
|
2015-01-17 13:17:36 -08:00
|
|
|
}
|
2017-01-22 16:23:56 +01:00
|
|
|
return TryReadInstResult{true, from_bat, hex, address};
|
2015-01-17 13:17:36 -08:00
|
|
|
}
|
2010-07-29 12:17:47 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
u32 MMU::HostRead_Instruction(const Core::CPUThreadGuard& guard, const u32 address)
|
2015-01-17 13:17:36 -08:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
return guard.GetSystem().GetMMU().ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32>(
|
|
|
|
address);
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<ReadResult<u32>> MMU::HostTryReadInstruction(const Core::CPUThreadGuard& guard,
|
|
|
|
const u32 address,
|
|
|
|
RequestedAddressSpace space)
|
2021-02-27 14:06:59 +01:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
if (!HostIsInstructionRAMAddress(guard, address, space))
|
2021-09-04 11:05:35 -07:00
|
|
|
return std::nullopt;
|
2021-02-27 14:06:59 +01:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
auto& mmu = guard.GetSystem().GetMMU();
|
2021-02-27 14:06:59 +01:00
|
|
|
switch (space)
|
|
|
|
{
|
|
|
|
case RequestedAddressSpace::Effective:
|
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
const u32 value = mmu.ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32>(address);
|
|
|
|
return ReadResult<u32>(!!mmu.m_ppc_state.msr.DR, value);
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
case RequestedAddressSpace::Physical:
|
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
const u32 value = mmu.ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32, true>(address);
|
2021-09-04 11:05:35 -07:00
|
|
|
return ReadResult<u32>(false, value);
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
case RequestedAddressSpace::Virtual:
|
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!mmu.m_ppc_state.msr.DR)
|
2021-09-04 11:05:35 -07:00
|
|
|
return std::nullopt;
|
2023-03-12 20:31:10 +01:00
|
|
|
const u32 value = mmu.ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32>(address);
|
2021-09-04 11:05:35 -07:00
|
|
|
return ReadResult<u32>(true, value);
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-27 23:54:27 -05:00
|
|
|
ASSERT(false);
|
2021-09-04 11:05:35 -07:00
|
|
|
return std::nullopt;
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::Memcheck(u32 address, u64 var, bool write, size_t size)
|
2015-01-01 13:02:33 -08:00
|
|
|
{
|
2023-03-28 20:26:52 +02:00
|
|
|
if (!m_power_pc.GetMemChecks().HasAny())
|
2021-08-31 11:30:55 -04:00
|
|
|
return;
|
|
|
|
|
2023-03-28 20:26:52 +02:00
|
|
|
TMemCheck* mc = m_power_pc.GetMemChecks().GetMemCheck(address, size);
|
2021-08-31 11:30:55 -04:00
|
|
|
if (mc == nullptr)
|
|
|
|
return;
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_system.GetCPU().IsStepping())
|
2015-01-01 13:02:33 -08:00
|
|
|
{
|
2021-08-31 11:30:55 -04:00
|
|
|
// Disable when stepping so that resume works.
|
|
|
|
return;
|
2015-01-01 13:02:33 -08:00
|
|
|
}
|
2021-08-31 11:30:55 -04:00
|
|
|
|
|
|
|
mc->num_hits++;
|
|
|
|
|
2023-03-28 20:26:52 +02:00
|
|
|
const bool pause = mc->Action(m_system, &m_power_pc.GetDebugInterface(), var, address, write,
|
|
|
|
size, m_ppc_state.pc);
|
2021-08-31 11:30:55 -04:00
|
|
|
if (!pause)
|
|
|
|
return;
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
m_system.GetCPU().Break();
|
2021-08-31 11:30:55 -04:00
|
|
|
|
2021-10-01 11:15:30 -04:00
|
|
|
if (GDBStub::IsActive())
|
|
|
|
GDBStub::TakeControl();
|
2021-09-30 23:36:11 -04:00
|
|
|
|
2021-08-31 11:30:55 -04:00
|
|
|
// Fake a DSI so that all the code that tests for it in order to skip
|
|
|
|
// the rest of the instruction will apply. (This means that
|
|
|
|
// watchpoints will stop the emulator before the offending load/store,
|
|
|
|
// not after like GDB does, but that's better anyway. Just need to
|
|
|
|
// make sure resuming after that works.)
|
|
|
|
// It doesn't matter if ReadFromHardware triggers its own DSI because
|
|
|
|
// we'll take it after resuming.
|
2023-03-12 20:31:10 +01:00
|
|
|
m_ppc_state.Exceptions |= EXCEPTION_DSI | EXCEPTION_FAKE_MEMCHECK_HIT;
|
2015-01-01 13:02:33 -08:00
|
|
|
}
|
2014-12-30 18:12:47 -08:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
u8 MMU::Read_U8(const u32 address)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
u8 var = ReadFromHardware<XCheckTLBFlag::Read, u8>(address);
|
|
|
|
Memcheck(address, var, false, 1);
|
2016-09-30 06:48:13 -04:00
|
|
|
return var;
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
u16 MMU::Read_U16(const u32 address)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
u16 var = ReadFromHardware<XCheckTLBFlag::Read, u16>(address);
|
|
|
|
Memcheck(address, var, false, 2);
|
2016-09-30 06:48:13 -04:00
|
|
|
return var;
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
u32 MMU::Read_U32(const u32 address)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
u32 var = ReadFromHardware<XCheckTLBFlag::Read, u32>(address);
|
|
|
|
Memcheck(address, var, false, 4);
|
2015-01-01 13:02:33 -08:00
|
|
|
return var;
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
u64 MMU::Read_U64(const u32 address)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
u64 var = ReadFromHardware<XCheckTLBFlag::Read, u64>(address);
|
|
|
|
Memcheck(address, var, false, 8);
|
2015-01-01 13:02:33 -08:00
|
|
|
return var;
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2021-02-27 14:06:59 +01:00
|
|
|
template <typename T>
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<ReadResult<T>> MMU::HostTryReadUX(const Core::CPUThreadGuard& guard,
|
|
|
|
const u32 address, RequestedAddressSpace space)
|
2021-02-27 14:06:59 +01:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
if (!HostIsRAMAddress(guard, address, space))
|
2021-09-04 11:05:35 -07:00
|
|
|
return std::nullopt;
|
2021-02-27 14:06:59 +01:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
auto& mmu = guard.GetSystem().GetMMU();
|
2021-02-27 14:06:59 +01:00
|
|
|
switch (space)
|
|
|
|
{
|
|
|
|
case RequestedAddressSpace::Effective:
|
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
T value = mmu.ReadFromHardware<XCheckTLBFlag::NoException, T>(address);
|
|
|
|
return ReadResult<T>(!!mmu.m_ppc_state.msr.DR, std::move(value));
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
case RequestedAddressSpace::Physical:
|
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
T value = mmu.ReadFromHardware<XCheckTLBFlag::NoException, T, true>(address);
|
2021-09-04 11:05:35 -07:00
|
|
|
return ReadResult<T>(false, std::move(value));
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
case RequestedAddressSpace::Virtual:
|
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!mmu.m_ppc_state.msr.DR)
|
2021-09-04 11:05:35 -07:00
|
|
|
return std::nullopt;
|
2023-03-12 20:31:10 +01:00
|
|
|
T value = mmu.ReadFromHardware<XCheckTLBFlag::NoException, T>(address);
|
2021-09-04 11:05:35 -07:00
|
|
|
return ReadResult<T>(true, std::move(value));
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-27 23:54:27 -05:00
|
|
|
ASSERT(false);
|
2021-09-04 11:05:35 -07:00
|
|
|
return std::nullopt;
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<ReadResult<u8>> MMU::HostTryReadU8(const Core::CPUThreadGuard& guard, u32 address,
|
|
|
|
RequestedAddressSpace space)
|
2021-02-27 14:06:59 +01:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
return HostTryReadUX<u8>(guard, address, space);
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<ReadResult<u16>> MMU::HostTryReadU16(const Core::CPUThreadGuard& guard, u32 address,
|
|
|
|
RequestedAddressSpace space)
|
2021-02-27 14:06:59 +01:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
return HostTryReadUX<u16>(guard, address, space);
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<ReadResult<u32>> MMU::HostTryReadU32(const Core::CPUThreadGuard& guard, u32 address,
|
|
|
|
RequestedAddressSpace space)
|
2021-02-27 14:06:59 +01:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
return HostTryReadUX<u32>(guard, address, space);
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<ReadResult<u64>> MMU::HostTryReadU64(const Core::CPUThreadGuard& guard, u32 address,
|
|
|
|
RequestedAddressSpace space)
|
2021-02-27 14:06:59 +01:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
return HostTryReadUX<u64>(guard, address, space);
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<ReadResult<float>> MMU::HostTryReadF32(const Core::CPUThreadGuard& guard, u32 address,
|
|
|
|
RequestedAddressSpace space)
|
2021-02-27 14:06:59 +01:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
const auto result = HostTryReadUX<u32>(guard, address, space);
|
2021-02-27 14:06:59 +01:00
|
|
|
if (!result)
|
2021-09-04 11:05:35 -07:00
|
|
|
return std::nullopt;
|
|
|
|
return ReadResult<float>(result->translated, Common::BitCast<float>(result->value));
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<ReadResult<double>> MMU::HostTryReadF64(const Core::CPUThreadGuard& guard,
|
|
|
|
u32 address, RequestedAddressSpace space)
|
2021-02-27 14:06:59 +01:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
const auto result = HostTryReadUX<u64>(guard, address, space);
|
2021-02-27 14:06:59 +01:00
|
|
|
if (!result)
|
2021-09-04 11:05:35 -07:00
|
|
|
return std::nullopt;
|
|
|
|
return ReadResult<double>(result->translated, Common::BitCast<double>(result->value));
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::Write_U8(const u32 var, const u32 address)
|
2009-10-07 08:50:40 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
Memcheck(address, var, true, 1);
|
|
|
|
WriteToHardware<XCheckTLBFlag::Write>(address, var, 1);
|
2009-10-07 08:50:40 +00:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::Write_U16(const u32 var, const u32 address)
|
2009-10-07 08:50:40 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
Memcheck(address, var, true, 2);
|
|
|
|
WriteToHardware<XCheckTLBFlag::Write>(address, var, 2);
|
2009-10-07 08:50:40 +00:00
|
|
|
}
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::Write_U16_Swap(const u32 var, const u32 address)
|
2014-08-30 17:23:13 -04:00
|
|
|
{
|
2021-07-26 14:59:20 +02:00
|
|
|
Write_U16((var & 0xFFFF0000) | Common::swap16(static_cast<u16>(var)), address);
|
2010-01-16 22:44:49 +00:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::Write_U32(const u32 var, const u32 address)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
Memcheck(address, var, true, 4);
|
|
|
|
WriteToHardware<XCheckTLBFlag::Write>(address, var, 4);
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::Write_U32_Swap(const u32 var, const u32 address)
|
2010-01-16 22:44:49 +00:00
|
|
|
{
|
2015-01-01 13:02:33 -08:00
|
|
|
Write_U32(Common::swap32(var), address);
|
2010-01-16 22:44:49 +00:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::Write_U64(const u64 var, const u32 address)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
Memcheck(address, var, true, 8);
|
|
|
|
WriteToHardware<XCheckTLBFlag::Write>(address, static_cast<u32>(var >> 32), 4);
|
|
|
|
WriteToHardware<XCheckTLBFlag::Write>(address + sizeof(u32), static_cast<u32>(var), 4);
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::Write_U64_Swap(const u64 var, const u32 address)
|
2014-08-30 17:23:13 -04:00
|
|
|
{
|
2015-01-01 13:02:33 -08:00
|
|
|
Write_U64(Common::swap64(var), address);
|
2010-01-16 22:44:49 +00:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
u8 MMU::HostRead_U8(const Core::CPUThreadGuard& guard, const u32 address)
|
2013-09-14 05:09:46 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
auto& mmu = guard.GetSystem().GetMMU();
|
|
|
|
return mmu.ReadFromHardware<XCheckTLBFlag::NoException, u8>(address);
|
2013-09-14 05:09:46 +00:00
|
|
|
}
|
2015-01-17 13:17:36 -08:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
u16 MMU::HostRead_U16(const Core::CPUThreadGuard& guard, const u32 address)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
auto& mmu = guard.GetSystem().GetMMU();
|
|
|
|
return mmu.ReadFromHardware<XCheckTLBFlag::NoException, u16>(address);
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
u32 MMU::HostRead_U32(const Core::CPUThreadGuard& guard, const u32 address)
|
2015-01-17 13:17:36 -08:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
auto& mmu = guard.GetSystem().GetMMU();
|
|
|
|
return mmu.ReadFromHardware<XCheckTLBFlag::NoException, u32>(address);
|
2015-01-17 13:17:36 -08:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
u64 MMU::HostRead_U64(const Core::CPUThreadGuard& guard, const u32 address)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
auto& mmu = guard.GetSystem().GetMMU();
|
|
|
|
return mmu.ReadFromHardware<XCheckTLBFlag::NoException, u64>(address);
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
float MMU::HostRead_F32(const Core::CPUThreadGuard& guard, const u32 address)
|
2016-12-09 17:16:47 +00:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
const u32 integral = HostRead_U32(guard, address);
|
2016-12-09 17:16:47 +00:00
|
|
|
|
2018-05-18 15:59:56 -04:00
|
|
|
return Common::BitCast<float>(integral);
|
2016-12-09 17:16:47 +00:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
double MMU::HostRead_F64(const Core::CPUThreadGuard& guard, const u32 address)
|
2016-12-09 17:16:47 +00:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
const u64 integral = HostRead_U64(guard, address);
|
2016-12-09 17:16:47 +00:00
|
|
|
|
2018-05-18 15:59:56 -04:00
|
|
|
return Common::BitCast<double>(integral);
|
2016-12-09 17:16:47 +00:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::HostWrite_U8(const Core::CPUThreadGuard& guard, const u32 var, const u32 address)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
auto& mmu = guard.GetSystem().GetMMU();
|
|
|
|
mmu.WriteToHardware<XCheckTLBFlag::NoException>(address, var, 1);
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2013-04-16 23:14:36 -04:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::HostWrite_U16(const Core::CPUThreadGuard& guard, const u32 var, const u32 address)
|
2015-01-17 13:17:36 -08:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
auto& mmu = guard.GetSystem().GetMMU();
|
|
|
|
mmu.WriteToHardware<XCheckTLBFlag::NoException>(address, var, 2);
|
2015-01-17 13:17:36 -08:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::HostWrite_U32(const Core::CPUThreadGuard& guard, const u32 var, const u32 address)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
auto& mmu = guard.GetSystem().GetMMU();
|
|
|
|
mmu.WriteToHardware<XCheckTLBFlag::NoException>(address, var, 4);
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::HostWrite_U64(const Core::CPUThreadGuard& guard, const u64 var, const u32 address)
|
2015-01-17 13:17:36 -08:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
auto& mmu = guard.GetSystem().GetMMU();
|
|
|
|
mmu.WriteToHardware<XCheckTLBFlag::NoException>(address, static_cast<u32>(var >> 32), 4);
|
|
|
|
mmu.WriteToHardware<XCheckTLBFlag::NoException>(address + sizeof(u32), static_cast<u32>(var), 4);
|
2015-01-17 13:17:36 -08:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::HostWrite_F32(const Core::CPUThreadGuard& guard, const float var, const u32 address)
|
2016-12-09 17:16:47 +00:00
|
|
|
{
|
2018-05-18 15:59:56 -04:00
|
|
|
const u32 integral = Common::BitCast<u32>(var);
|
2016-12-09 17:16:47 +00:00
|
|
|
|
2023-02-12 11:07:11 +01:00
|
|
|
HostWrite_U32(guard, integral, address);
|
2016-12-09 17:16:47 +00:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::HostWrite_F64(const Core::CPUThreadGuard& guard, const double var, const u32 address)
|
2016-12-09 17:16:47 +00:00
|
|
|
{
|
2018-05-18 15:59:56 -04:00
|
|
|
const u64 integral = Common::BitCast<u64>(var);
|
2016-12-09 17:16:47 +00:00
|
|
|
|
2023-02-12 11:07:11 +01:00
|
|
|
HostWrite_U64(guard, integral, address);
|
2016-12-09 17:16:47 +00:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<WriteResult> MMU::HostTryWriteUX(const Core::CPUThreadGuard& guard, const u32 var,
|
|
|
|
const u32 address, const u32 size,
|
|
|
|
RequestedAddressSpace space)
|
2021-02-27 16:07:41 +01:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
if (!HostIsRAMAddress(guard, address, space))
|
2021-09-04 11:05:35 -07:00
|
|
|
return std::nullopt;
|
2021-02-27 16:07:41 +01:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
auto& mmu = guard.GetSystem().GetMMU();
|
2021-02-27 16:07:41 +01:00
|
|
|
switch (space)
|
|
|
|
{
|
|
|
|
case RequestedAddressSpace::Effective:
|
2023-03-12 20:31:10 +01:00
|
|
|
mmu.WriteToHardware<XCheckTLBFlag::NoException>(address, var, size);
|
|
|
|
return WriteResult(!!mmu.m_ppc_state.msr.DR);
|
2021-02-27 16:07:41 +01:00
|
|
|
case RequestedAddressSpace::Physical:
|
2023-03-12 20:31:10 +01:00
|
|
|
mmu.WriteToHardware<XCheckTLBFlag::NoException, true>(address, var, size);
|
2021-09-04 11:05:35 -07:00
|
|
|
return WriteResult(false);
|
2021-02-27 16:07:41 +01:00
|
|
|
case RequestedAddressSpace::Virtual:
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!mmu.m_ppc_state.msr.DR)
|
2021-09-04 11:05:35 -07:00
|
|
|
return std::nullopt;
|
2023-03-12 20:31:10 +01:00
|
|
|
mmu.WriteToHardware<XCheckTLBFlag::NoException>(address, var, size);
|
2021-09-04 11:05:35 -07:00
|
|
|
return WriteResult(true);
|
2021-02-27 16:07:41 +01:00
|
|
|
}
|
|
|
|
|
2022-07-27 23:54:27 -05:00
|
|
|
ASSERT(false);
|
2021-09-04 11:05:35 -07:00
|
|
|
return std::nullopt;
|
2021-02-27 16:07:41 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<WriteResult> MMU::HostTryWriteU8(const Core::CPUThreadGuard& guard, const u32 var,
|
|
|
|
const u32 address, RequestedAddressSpace space)
|
2021-02-27 16:07:41 +01:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
return HostTryWriteUX(guard, var, address, 1, space);
|
2021-02-27 16:07:41 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<WriteResult> MMU::HostTryWriteU16(const Core::CPUThreadGuard& guard, const u32 var,
|
|
|
|
const u32 address, RequestedAddressSpace space)
|
2021-02-27 16:07:41 +01:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
return HostTryWriteUX(guard, var, address, 2, space);
|
2021-02-27 16:07:41 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<WriteResult> MMU::HostTryWriteU32(const Core::CPUThreadGuard& guard, const u32 var,
|
|
|
|
const u32 address, RequestedAddressSpace space)
|
2021-02-27 16:07:41 +01:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
return HostTryWriteUX(guard, var, address, 4, space);
|
2021-02-27 16:07:41 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<WriteResult> MMU::HostTryWriteU64(const Core::CPUThreadGuard& guard, const u64 var,
|
|
|
|
const u32 address, RequestedAddressSpace space)
|
2021-02-27 16:07:41 +01:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
const auto result = HostTryWriteUX(guard, static_cast<u32>(var >> 32), address, 4, space);
|
2021-07-26 14:59:20 +02:00
|
|
|
if (!result)
|
|
|
|
return result;
|
|
|
|
|
2023-02-12 11:07:11 +01:00
|
|
|
return HostTryWriteUX(guard, static_cast<u32>(var), address + 4, 4, space);
|
2021-02-27 16:07:41 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<WriteResult> MMU::HostTryWriteF32(const Core::CPUThreadGuard& guard, const float var,
|
|
|
|
const u32 address, RequestedAddressSpace space)
|
2021-02-27 16:07:41 +01:00
|
|
|
{
|
|
|
|
const u32 integral = Common::BitCast<u32>(var);
|
2023-02-12 11:07:11 +01:00
|
|
|
return HostTryWriteU32(guard, integral, address, space);
|
2021-02-27 16:07:41 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<WriteResult> MMU::HostTryWriteF64(const Core::CPUThreadGuard& guard, const double var,
|
|
|
|
const u32 address, RequestedAddressSpace space)
|
2021-02-27 16:07:41 +01:00
|
|
|
{
|
|
|
|
const u64 integral = Common::BitCast<u64>(var);
|
2023-02-12 11:07:11 +01:00
|
|
|
return HostTryWriteU64(guard, integral, address, space);
|
2021-02-27 16:07:41 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::string MMU::HostGetString(const Core::CPUThreadGuard& guard, u32 address, size_t size)
|
2015-01-17 13:17:36 -08:00
|
|
|
{
|
|
|
|
std::string s;
|
|
|
|
do
|
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
if (!HostIsRAMAddress(guard, address))
|
2015-01-17 13:17:36 -08:00
|
|
|
break;
|
2023-02-12 11:07:11 +01:00
|
|
|
u8 res = HostRead_U8(guard, address);
|
2015-01-17 13:17:36 -08:00
|
|
|
if (!res)
|
|
|
|
break;
|
2015-09-26 19:08:15 -07:00
|
|
|
s += static_cast<char>(res);
|
2015-01-17 13:17:36 -08:00
|
|
|
++address;
|
|
|
|
} while (size == 0 || s.length() < size);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<ReadResult<std::string>> MMU::HostTryReadString(const Core::CPUThreadGuard& guard,
|
|
|
|
u32 address, size_t size,
|
|
|
|
RequestedAddressSpace space)
|
2021-02-27 14:06:59 +01:00
|
|
|
{
|
2023-02-12 11:07:11 +01:00
|
|
|
auto c = HostTryReadU8(guard, address, space);
|
2021-02-27 14:06:59 +01:00
|
|
|
if (!c)
|
2021-09-04 11:05:35 -07:00
|
|
|
return std::nullopt;
|
|
|
|
if (c->value == 0)
|
|
|
|
return ReadResult<std::string>(c->translated, "");
|
2021-02-27 14:06:59 +01:00
|
|
|
|
|
|
|
std::string s;
|
2021-09-04 11:05:35 -07:00
|
|
|
s += static_cast<char>(c->value);
|
2021-02-27 14:06:59 +01:00
|
|
|
while (size == 0 || s.length() < size)
|
|
|
|
{
|
|
|
|
++address;
|
2023-02-12 11:07:11 +01:00
|
|
|
const auto res = HostTryReadU8(guard, address, space);
|
2021-09-04 11:05:35 -07:00
|
|
|
if (!res || res->value == 0)
|
2021-02-27 14:06:59 +01:00
|
|
|
break;
|
2021-09-04 11:05:35 -07:00
|
|
|
s += static_cast<char>(res->value);
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
2021-09-04 11:05:35 -07:00
|
|
|
return ReadResult<std::string>(c->translated, std::move(s));
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
bool MMU::IsOptimizableRAMAddress(const u32 address) const
|
2015-01-17 13:17:36 -08:00
|
|
|
{
|
2023-03-28 20:26:52 +02:00
|
|
|
if (m_power_pc.GetMemChecks().HasAny())
|
2016-09-09 14:10:48 -04:00
|
|
|
return false;
|
2015-04-23 00:41:36 -04:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!m_ppc_state.msr.DR)
|
2015-01-17 13:17:36 -08:00
|
|
|
return false;
|
|
|
|
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
// TODO: This API needs to take an access size
|
|
|
|
//
|
|
|
|
// We store whether an access can be optimized to an unchecked access
|
|
|
|
// in dbat_table.
|
2023-03-12 20:31:10 +01:00
|
|
|
u32 bat_result = m_dbat_table[address >> BAT_INDEX_SHIFT];
|
2017-03-04 14:55:38 +01:00
|
|
|
return (bat_result & BAT_PHYSICAL_BIT) != 0;
|
2015-01-17 13:17:36 -08:00
|
|
|
}
|
|
|
|
|
2017-01-23 21:05:11 +00:00
|
|
|
template <XCheckTLBFlag flag>
|
2023-03-12 20:31:10 +01:00
|
|
|
bool MMU::IsRAMAddress(u32 address, bool translate)
|
2015-01-17 13:17:36 -08:00
|
|
|
{
|
2017-01-23 21:05:11 +00:00
|
|
|
if (translate)
|
2015-01-17 13:17:36 -08:00
|
|
|
{
|
2017-01-23 21:05:11 +00:00
|
|
|
auto translate_address = TranslateAddress<flag>(address);
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
if (!translate_address.Success())
|
2015-01-17 13:17:36 -08:00
|
|
|
return false;
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
address = translate_address.address;
|
2015-01-17 13:17:36 -08:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-01-23 21:05:11 +00:00
|
|
|
u32 segment = address >> 28;
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_memory.GetRAM() && segment == 0x0 && (address & 0x0FFFFFFF) < m_memory.GetRamSizeReal())
|
2021-02-24 19:28:37 +04:00
|
|
|
{
|
2015-01-17 13:17:36 -08:00
|
|
|
return true;
|
2021-02-24 19:28:37 +04:00
|
|
|
}
|
2023-03-12 20:31:10 +01:00
|
|
|
else if (m_memory.GetEXRAM() && segment == 0x1 &&
|
|
|
|
(address & 0x0FFFFFFF) < m_memory.GetExRamSizeReal())
|
2021-02-24 19:28:37 +04:00
|
|
|
{
|
2015-01-17 13:17:36 -08:00
|
|
|
return true;
|
2021-02-24 19:28:37 +04:00
|
|
|
}
|
2023-03-12 20:31:10 +01:00
|
|
|
else if (m_memory.GetFakeVMEM() && ((address & 0xFE000000) == 0x7E000000))
|
2021-02-24 19:28:37 +04:00
|
|
|
{
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
return true;
|
2021-02-24 19:28:37 +04:00
|
|
|
}
|
2023-03-12 20:31:10 +01:00
|
|
|
else if (m_memory.GetL1Cache() && segment == 0xE &&
|
|
|
|
(address < (0xE0000000 + m_memory.GetL1CacheSize())))
|
2021-02-24 19:28:37 +04:00
|
|
|
{
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
return true;
|
2021-02-24 19:28:37 +04:00
|
|
|
}
|
2015-01-17 13:17:36 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
bool MMU::HostIsRAMAddress(const Core::CPUThreadGuard& guard, u32 address,
|
|
|
|
RequestedAddressSpace space)
|
2017-01-23 21:05:11 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
auto& mmu = guard.GetSystem().GetMMU();
|
2021-02-27 14:06:59 +01:00
|
|
|
switch (space)
|
|
|
|
{
|
|
|
|
case RequestedAddressSpace::Effective:
|
2023-03-12 20:31:10 +01:00
|
|
|
return mmu.IsRAMAddress<XCheckTLBFlag::NoException>(address, mmu.m_ppc_state.msr.DR);
|
2021-02-27 14:06:59 +01:00
|
|
|
case RequestedAddressSpace::Physical:
|
2023-03-12 20:31:10 +01:00
|
|
|
return mmu.IsRAMAddress<XCheckTLBFlag::NoException>(address, false);
|
2021-02-27 14:06:59 +01:00
|
|
|
case RequestedAddressSpace::Virtual:
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!mmu.m_ppc_state.msr.DR)
|
2021-02-27 14:06:59 +01:00
|
|
|
return false;
|
2023-03-12 20:31:10 +01:00
|
|
|
return mmu.IsRAMAddress<XCheckTLBFlag::NoException>(address, true);
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
|
2022-07-27 23:54:27 -05:00
|
|
|
ASSERT(false);
|
2021-02-27 14:06:59 +01:00
|
|
|
return false;
|
2017-01-23 21:05:11 +00:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
bool MMU::HostIsInstructionRAMAddress(const Core::CPUThreadGuard& guard, u32 address,
|
|
|
|
RequestedAddressSpace space)
|
2017-01-23 21:05:11 +00:00
|
|
|
{
|
|
|
|
// Instructions are always 32bit aligned.
|
2021-07-09 05:48:17 +02:00
|
|
|
if (address & 3)
|
2021-02-27 14:06:59 +01:00
|
|
|
return false;
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
auto& mmu = guard.GetSystem().GetMMU();
|
2021-02-27 14:06:59 +01:00
|
|
|
switch (space)
|
|
|
|
{
|
|
|
|
case RequestedAddressSpace::Effective:
|
2023-03-12 20:31:10 +01:00
|
|
|
return mmu.IsRAMAddress<XCheckTLBFlag::OpcodeNoException>(address, mmu.m_ppc_state.msr.IR);
|
2021-02-27 14:06:59 +01:00
|
|
|
case RequestedAddressSpace::Physical:
|
2023-03-12 20:31:10 +01:00
|
|
|
return mmu.IsRAMAddress<XCheckTLBFlag::OpcodeNoException>(address, false);
|
2021-02-27 14:06:59 +01:00
|
|
|
case RequestedAddressSpace::Virtual:
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!mmu.m_ppc_state.msr.IR)
|
2021-02-27 14:06:59 +01:00
|
|
|
return false;
|
2023-03-12 20:31:10 +01:00
|
|
|
return mmu.IsRAMAddress<XCheckTLBFlag::OpcodeNoException>(address, true);
|
2021-02-27 14:06:59 +01:00
|
|
|
}
|
|
|
|
|
2022-07-27 23:54:27 -05:00
|
|
|
ASSERT(false);
|
2021-02-27 14:06:59 +01:00
|
|
|
return false;
|
2017-01-23 21:05:11 +00:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::DMA_LCToMemory(const u32 mem_address, const u32 cache_address, const u32 num_blocks)
|
2015-01-09 22:20:06 -08:00
|
|
|
{
|
|
|
|
// TODO: It's not completely clear this is the right spot for this code;
|
|
|
|
// what would happen if, for example, the DVD drive tried to write to the EFB?
|
|
|
|
// TODO: This is terribly slow.
|
|
|
|
// TODO: Refactor.
|
|
|
|
// Avatar: The Last Airbender (GC) uses this for videos.
|
2018-05-25 12:43:05 -04:00
|
|
|
if ((mem_address & 0x0F000000) == 0x08000000)
|
2015-01-09 22:20:06 -08:00
|
|
|
{
|
2018-05-25 12:43:05 -04:00
|
|
|
for (u32 i = 0; i < 32 * num_blocks; i += 4)
|
2015-01-09 22:20:06 -08:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
const u32 data = Common::swap32(m_memory.GetL1Cache() + ((cache_address + i) & 0x3FFFF));
|
2018-05-25 12:43:05 -04:00
|
|
|
EFB_Write(data, mem_address + i);
|
2015-01-09 22:20:06 -08:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-01-09 22:20:06 -08:00
|
|
|
// No known game uses this; here for completeness.
|
|
|
|
// TODO: Refactor.
|
2018-05-25 12:43:05 -04:00
|
|
|
if ((mem_address & 0x0F000000) == 0x0C000000)
|
2015-01-09 22:20:06 -08:00
|
|
|
{
|
2018-05-25 12:43:05 -04:00
|
|
|
for (u32 i = 0; i < 32 * num_blocks; i += 4)
|
2015-01-09 22:20:06 -08:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
const u32 data = Common::swap32(m_memory.GetL1Cache() + ((cache_address + i) & 0x3FFFF));
|
|
|
|
m_memory.GetMMIOMapping()->Write(mem_address + i, data);
|
2015-01-09 22:20:06 -08:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
const u8* src = m_memory.GetL1Cache() + (cache_address & 0x3FFFF);
|
|
|
|
u8* dst = m_memory.GetPointer(mem_address);
|
2015-01-09 22:20:06 -08:00
|
|
|
if (dst == nullptr)
|
|
|
|
return;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-05-25 12:43:05 -04:00
|
|
|
memcpy(dst, src, 32 * num_blocks);
|
2015-01-09 22:20:06 -08:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::DMA_MemoryToLC(const u32 cache_address, const u32 mem_address, const u32 num_blocks)
|
2015-01-09 22:20:06 -08:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
const u8* src = m_memory.GetPointer(mem_address);
|
|
|
|
u8* dst = m_memory.GetL1Cache() + (cache_address & 0x3FFFF);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-01-09 22:20:06 -08:00
|
|
|
// No known game uses this; here for completeness.
|
|
|
|
// TODO: Refactor.
|
2018-05-25 12:43:05 -04:00
|
|
|
if ((mem_address & 0x0F000000) == 0x08000000)
|
2015-01-09 22:20:06 -08:00
|
|
|
{
|
2018-05-25 12:43:05 -04:00
|
|
|
for (u32 i = 0; i < 32 * num_blocks; i += 4)
|
2015-01-09 22:20:06 -08:00
|
|
|
{
|
2018-05-25 12:43:05 -04:00
|
|
|
const u32 data = Common::swap32(EFB_Read(mem_address + i));
|
2023-03-12 20:31:10 +01:00
|
|
|
std::memcpy(m_memory.GetL1Cache() + ((cache_address + i) & 0x3FFFF), &data, sizeof(u32));
|
2015-01-09 22:20:06 -08:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-01-09 22:20:06 -08:00
|
|
|
// No known game uses this.
|
|
|
|
// TODO: Refactor.
|
2018-05-25 12:43:05 -04:00
|
|
|
if ((mem_address & 0x0F000000) == 0x0C000000)
|
2015-01-09 22:20:06 -08:00
|
|
|
{
|
2018-05-25 12:43:05 -04:00
|
|
|
for (u32 i = 0; i < 32 * num_blocks; i += 4)
|
2015-01-09 22:20:06 -08:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
const u32 data = Common::swap32(m_memory.GetMMIOMapping()->Read<u32>(mem_address + i));
|
|
|
|
std::memcpy(m_memory.GetL1Cache() + ((cache_address + i) & 0x3FFFF), &data, sizeof(u32));
|
2015-01-09 22:20:06 -08:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-01-09 22:20:06 -08:00
|
|
|
if (src == nullptr)
|
|
|
|
return;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-05-25 12:43:05 -04:00
|
|
|
memcpy(dst, src, 32 * num_blocks);
|
2015-01-09 22:20:06 -08:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
static bool TranslateBatAddress(const BatTable& bat_table, u32* address, bool* wi)
|
|
|
|
{
|
|
|
|
u32 bat_result = bat_table[*address >> BAT_INDEX_SHIFT];
|
|
|
|
if ((bat_result & BAT_MAPPED_BIT) == 0)
|
|
|
|
return false;
|
|
|
|
*address = (bat_result & BAT_RESULT_MASK) | (*address & (BAT_PAGE_SIZE - 1));
|
|
|
|
*wi = (bat_result & BAT_WI_BIT) != 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MMU::ClearDCacheLine(u32 address)
|
2015-01-17 13:17:36 -08:00
|
|
|
{
|
2018-03-16 12:57:36 -04:00
|
|
|
DEBUG_ASSERT((address & 0x1F) == 0);
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_ppc_state.msr.DR)
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
2018-03-25 19:48:15 -04:00
|
|
|
auto translated_address = TranslateAddress<XCheckTLBFlag::Write>(address);
|
2021-08-07 00:07:40 +02:00
|
|
|
if (translated_address.result == TranslateAddressResultEnum::DIRECT_STORE_SEGMENT)
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
|
|
|
// dcbz to direct store segments is ignored. This is a little
|
|
|
|
// unintuitive, but this is consistent with both console and the PEM.
|
|
|
|
// Advance Game Port crashes if we don't emulate this correctly.
|
|
|
|
return;
|
|
|
|
}
|
2021-08-07 00:07:40 +02:00
|
|
|
if (translated_address.result == TranslateAddressResultEnum::PAGE_FAULT)
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
|
|
|
// If translation fails, generate a DSI.
|
|
|
|
GenerateDSIException(address, true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
address = translated_address.address;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: This isn't precisely correct for non-RAM regions, but the difference
|
|
|
|
// is unlikely to matter.
|
2021-07-26 14:59:20 +02:00
|
|
|
for (u32 i = 0; i < 32; i += 4)
|
2023-03-12 20:31:10 +01:00
|
|
|
WriteToHardware<XCheckTLBFlag::Write, true>(address + i, 0, 4);
|
2015-01-17 13:17:36 -08:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::StoreDCacheLine(u32 address)
|
2022-10-17 15:28:29 -04:00
|
|
|
{
|
|
|
|
address &= ~0x1F;
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_ppc_state.msr.DR)
|
2022-10-17 15:28:29 -04:00
|
|
|
{
|
|
|
|
auto translated_address = TranslateAddress<XCheckTLBFlag::Write>(address);
|
|
|
|
if (translated_address.result == TranslateAddressResultEnum::DIRECT_STORE_SEGMENT)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (translated_address.result == TranslateAddressResultEnum::PAGE_FAULT)
|
|
|
|
{
|
|
|
|
// If translation fails, generate a DSI.
|
|
|
|
GenerateDSIException(address, true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
address = translated_address.address;
|
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_ppc_state.m_enable_dcache)
|
|
|
|
m_ppc_state.dCache.Store(address);
|
2022-10-17 15:28:29 -04:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::InvalidateDCacheLine(u32 address)
|
2022-10-17 15:28:29 -04:00
|
|
|
{
|
|
|
|
address &= ~0x1F;
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_ppc_state.msr.DR)
|
2022-10-17 15:28:29 -04:00
|
|
|
{
|
|
|
|
auto translated_address = TranslateAddress<XCheckTLBFlag::Write>(address);
|
|
|
|
if (translated_address.result == TranslateAddressResultEnum::DIRECT_STORE_SEGMENT)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (translated_address.result == TranslateAddressResultEnum::PAGE_FAULT)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
address = translated_address.address;
|
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_ppc_state.m_enable_dcache)
|
|
|
|
m_ppc_state.dCache.Invalidate(address);
|
2022-10-17 15:28:29 -04:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::FlushDCacheLine(u32 address)
|
2022-10-17 15:28:29 -04:00
|
|
|
{
|
|
|
|
address &= ~0x1F;
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_ppc_state.msr.DR)
|
2022-10-17 15:28:29 -04:00
|
|
|
{
|
|
|
|
auto translated_address = TranslateAddress<XCheckTLBFlag::Write>(address);
|
|
|
|
if (translated_address.result == TranslateAddressResultEnum::DIRECT_STORE_SEGMENT)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (translated_address.result == TranslateAddressResultEnum::PAGE_FAULT)
|
|
|
|
{
|
|
|
|
// If translation fails, generate a DSI.
|
|
|
|
GenerateDSIException(address, true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
address = translated_address.address;
|
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_ppc_state.m_enable_dcache)
|
|
|
|
m_ppc_state.dCache.Flush(address);
|
2022-10-17 15:28:29 -04:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::TouchDCacheLine(u32 address, bool store)
|
2022-10-17 15:28:29 -04:00
|
|
|
{
|
|
|
|
address &= ~0x1F;
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_ppc_state.msr.DR)
|
2022-10-17 15:28:29 -04:00
|
|
|
{
|
|
|
|
auto translated_address = TranslateAddress<XCheckTLBFlag::Write>(address);
|
|
|
|
if (translated_address.result == TranslateAddressResultEnum::DIRECT_STORE_SEGMENT)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (translated_address.result == TranslateAddressResultEnum::PAGE_FAULT)
|
|
|
|
{
|
|
|
|
// If translation fails, generate a DSI.
|
|
|
|
GenerateDSIException(address, true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
address = translated_address.address;
|
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_ppc_state.m_enable_dcache)
|
|
|
|
m_ppc_state.dCache.Touch(address, store);
|
2022-10-17 15:28:29 -04:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
u32 MMU::IsOptimizableMMIOAccess(u32 address, u32 access_size) const
|
2015-02-11 18:01:47 -08:00
|
|
|
{
|
2023-03-28 20:26:52 +02:00
|
|
|
if (m_power_pc.GetMemChecks().HasAny())
|
2016-09-09 14:10:48 -04:00
|
|
|
return 0;
|
2015-04-23 00:41:36 -04:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!m_ppc_state.msr.DR)
|
2015-02-11 18:01:47 -08:00
|
|
|
return 0;
|
|
|
|
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
// Translate address
|
2016-09-03 14:53:22 +02:00
|
|
|
// If we also optimize for TLB mappings, we'd have to clear the
|
|
|
|
// JitCache on each TLB invalidation.
|
2021-07-04 20:47:04 +02:00
|
|
|
bool wi = false;
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!TranslateBatAddress(m_dbat_table, &address, &wi))
|
2016-09-03 14:53:22 +02:00
|
|
|
return 0;
|
2015-02-11 18:01:47 -08:00
|
|
|
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
// Check whether the address is an aligned address of an MMIO register.
|
2018-05-25 12:43:05 -04:00
|
|
|
const bool aligned = (address & ((access_size >> 3) - 1)) == 0;
|
2016-09-03 14:53:22 +02:00
|
|
|
if (!aligned || !MMIO::IsMMIOAddress(address))
|
2015-02-11 18:01:47 -08:00
|
|
|
return 0;
|
2016-09-03 14:53:22 +02:00
|
|
|
|
|
|
|
return address;
|
2015-02-11 18:01:47 -08:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
bool MMU::IsOptimizableGatherPipeWrite(u32 address) const
|
2015-02-21 08:50:50 -08:00
|
|
|
{
|
2023-03-28 20:26:52 +02:00
|
|
|
if (m_power_pc.GetMemChecks().HasAny())
|
2017-01-21 21:16:41 -05:00
|
|
|
return false;
|
2015-04-23 00:41:36 -04:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!m_ppc_state.msr.DR)
|
2017-01-21 21:16:41 -05:00
|
|
|
return false;
|
2015-02-21 08:50:50 -08:00
|
|
|
|
2016-09-03 14:53:22 +02:00
|
|
|
// Translate address, only check BAT mapping.
|
|
|
|
// If we also optimize for TLB mappings, we'd have to clear the
|
|
|
|
// JitCache on each TLB invalidation.
|
2021-07-04 20:47:04 +02:00
|
|
|
bool wi = false;
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!TranslateBatAddress(m_dbat_table, &address, &wi))
|
2017-01-21 21:16:41 -05:00
|
|
|
return false;
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
|
|
|
|
// Check whether the translated address equals the address in WPAR.
|
2022-07-20 17:44:49 -07:00
|
|
|
return address == GPFifo::GATHER_PIPE_PHYSICAL_ADDRESS;
|
2015-02-21 08:50:50 -08:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
TranslateResult MMU::JitCache_TranslateAddress(u32 address)
|
2016-06-25 18:57:16 -07:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!m_ppc_state.msr.IR)
|
2021-08-09 01:19:22 +02:00
|
|
|
return TranslateResult{address};
|
2016-06-25 18:57:16 -07:00
|
|
|
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
// TODO: We shouldn't use FLAG_OPCODE if the caller is the debugger.
|
2021-08-09 01:19:22 +02:00
|
|
|
const auto tlb_addr = TranslateAddress<XCheckTLBFlag::Opcode>(address);
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
if (!tlb_addr.Success())
|
2021-08-09 01:19:22 +02:00
|
|
|
return TranslateResult{};
|
2016-06-25 18:57:16 -07:00
|
|
|
|
2021-08-09 01:19:22 +02:00
|
|
|
const bool from_bat = tlb_addr.result == TranslateAddressResultEnum::BAT_TRANSLATED;
|
|
|
|
return TranslateResult{from_bat, tlb_addr.address};
|
2016-06-25 18:57:16 -07:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::GenerateDSIException(u32 effective_address, bool write)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2015-01-01 13:02:33 -08:00
|
|
|
// DSI exceptions are only supported in MMU mode.
|
2023-03-12 20:31:10 +01:00
|
|
|
if (!m_system.IsMMUMode())
|
2015-01-01 13:02:33 -08:00
|
|
|
{
|
2020-11-25 08:45:21 -05:00
|
|
|
PanicAlertFmt("Invalid {} {:#010x}, PC = {:#010x}", write ? "write to" : "read from",
|
2023-03-12 20:31:10 +01:00
|
|
|
effective_address, m_ppc_state.pc);
|
|
|
|
if (m_system.IsPauseOnPanicMode())
|
2022-04-17 00:15:12 -07:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
m_system.GetCPU().Break();
|
|
|
|
m_ppc_state.Exceptions |= EXCEPTION_DSI | EXCEPTION_FAKE_MEMCHECK_HIT;
|
2022-04-17 00:15:12 -07:00
|
|
|
}
|
2015-01-01 13:02:33 -08:00
|
|
|
return;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-08-31 11:13:42 -04:00
|
|
|
constexpr u32 dsisr_page = 1U << 30;
|
|
|
|
constexpr u32 dsisr_store = 1U << 25;
|
|
|
|
|
|
|
|
if (effective_address != 0)
|
2023-03-12 20:31:10 +01:00
|
|
|
m_ppc_state.spr[SPR_DSISR] = dsisr_page | dsisr_store;
|
2009-01-18 09:51:09 +00:00
|
|
|
else
|
2023-03-12 20:31:10 +01:00
|
|
|
m_ppc_state.spr[SPR_DSISR] = dsisr_page;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
m_ppc_state.spr[SPR_DAR] = effective_address;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
m_ppc_state.Exceptions |= EXCEPTION_DSI;
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::GenerateISIException(u32 effective_address)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2010-07-29 12:17:47 +00:00
|
|
|
// Address of instruction could not be translated
|
2023-03-12 20:31:10 +01:00
|
|
|
m_ppc_state.npc = effective_address;
|
2010-07-29 12:17:47 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
m_ppc_state.Exceptions |= EXCEPTION_ISI;
|
|
|
|
WARN_LOG_FMT(POWERPC, "ISI exception at {:#010x}", m_ppc_state.pc);
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::SDRUpdated()
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
const auto sdr = UReg_SDR1{m_ppc_state.spr[SPR_SDR]};
|
2021-08-31 10:20:54 -04:00
|
|
|
const u32 htabmask = sdr.htabmask;
|
|
|
|
|
2017-06-22 13:04:00 +02:00
|
|
|
if (!Common::IsValidLowMask(htabmask))
|
2021-04-06 01:35:56 +02:00
|
|
|
WARN_LOG_FMT(POWERPC, "Invalid HTABMASK: 0b{:032b}", htabmask);
|
|
|
|
|
|
|
|
// While 6xx_pem.pdf §7.6.1.1 mentions that the number of trailing zeros in HTABORG
|
|
|
|
// must be equal to the number of trailing ones in the mask (i.e. HTABORG must be
|
|
|
|
// properly aligned), this is actually not a hard requirement. Real hardware will just OR
|
|
|
|
// the base address anyway. Ignoring SDR changes would lead to incorrect emulation.
|
2021-08-31 10:20:54 -04:00
|
|
|
const u32 htaborg = sdr.htaborg;
|
|
|
|
if ((htaborg & htabmask) != 0)
|
2021-04-06 01:35:56 +02:00
|
|
|
WARN_LOG_FMT(POWERPC, "Invalid HTABORG: htaborg=0x{:08x} htabmask=0x{:08x}", htaborg, htabmask);
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
m_ppc_state.pagetable_base = htaborg << 16;
|
|
|
|
m_ppc_state.pagetable_hashmask = ((htabmask << 10) | 0x3ff);
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2009-02-23 06:15:48 +00:00
|
|
|
|
2018-03-25 19:44:14 -04:00
|
|
|
enum class TLBLookupResult
|
2015-01-02 19:22:39 -08:00
|
|
|
{
|
2018-03-25 19:44:14 -04:00
|
|
|
Found,
|
|
|
|
NotFound,
|
|
|
|
UpdateC
|
2015-01-02 19:22:39 -08:00
|
|
|
};
|
2010-07-29 12:17:47 +00:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
static TLBLookupResult LookupTLBPageAddress(PowerPC::PowerPCState& ppc_state,
|
|
|
|
const XCheckTLBFlag flag, const u32 vpa, u32* paddr,
|
2021-07-04 20:47:04 +02:00
|
|
|
bool* wi)
|
2010-07-29 12:17:47 +00:00
|
|
|
{
|
2017-02-04 19:03:19 -05:00
|
|
|
const u32 tag = vpa >> HW_PAGE_INDEX_SHIFT;
|
2023-03-12 20:31:10 +01:00
|
|
|
TLBEntry& tlbe = ppc_state.tlb[IsOpcodeFlag(flag)][tag & HW_PAGE_INDEX_MASK];
|
2017-02-04 19:03:19 -05:00
|
|
|
|
|
|
|
if (tlbe.tag[0] == tag)
|
2010-07-29 12:17:47 +00:00
|
|
|
{
|
2021-08-31 09:43:37 -04:00
|
|
|
UPTE_Hi pte2(tlbe.pte[0]);
|
2021-07-04 20:47:04 +02:00
|
|
|
|
2014-12-05 14:29:13 +11:00
|
|
|
// Check if C bit requires updating
|
2018-03-25 19:48:15 -04:00
|
|
|
if (flag == XCheckTLBFlag::Write)
|
2014-12-05 14:29:13 +11:00
|
|
|
{
|
2021-08-31 09:43:37 -04:00
|
|
|
if (pte2.C == 0)
|
2014-12-06 10:28:34 +11:00
|
|
|
{
|
2021-08-31 09:43:37 -04:00
|
|
|
pte2.C = 1;
|
|
|
|
tlbe.pte[0] = pte2.Hex;
|
2018-03-25 19:44:14 -04:00
|
|
|
return TLBLookupResult::UpdateC;
|
2014-12-06 10:28:34 +11:00
|
|
|
}
|
2014-12-05 14:29:13 +11:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-06-25 18:57:16 -07:00
|
|
|
if (!IsNoExceptionFlag(flag))
|
2017-02-04 19:03:19 -05:00
|
|
|
tlbe.recent = 0;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-02-04 19:03:19 -05:00
|
|
|
*paddr = tlbe.paddr[0] | (vpa & 0xfff);
|
2021-08-31 09:43:37 -04:00
|
|
|
*wi = (pte2.WIMG & 0b1100) != 0;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-03-25 19:44:14 -04:00
|
|
|
return TLBLookupResult::Found;
|
2014-12-05 19:56:45 +11:00
|
|
|
}
|
2017-02-04 19:03:19 -05:00
|
|
|
if (tlbe.tag[1] == tag)
|
2014-12-05 19:56:45 +11:00
|
|
|
{
|
2021-08-31 09:43:37 -04:00
|
|
|
UPTE_Hi pte2(tlbe.pte[1]);
|
2021-07-04 20:47:04 +02:00
|
|
|
|
2014-12-05 14:29:13 +11:00
|
|
|
// Check if C bit requires updating
|
2018-03-25 19:48:15 -04:00
|
|
|
if (flag == XCheckTLBFlag::Write)
|
2014-12-05 14:29:13 +11:00
|
|
|
{
|
2021-08-31 09:43:37 -04:00
|
|
|
if (pte2.C == 0)
|
2014-12-06 10:28:34 +11:00
|
|
|
{
|
2021-08-31 09:43:37 -04:00
|
|
|
pte2.C = 1;
|
|
|
|
tlbe.pte[1] = pte2.Hex;
|
2018-03-25 19:44:14 -04:00
|
|
|
return TLBLookupResult::UpdateC;
|
2014-12-06 10:28:34 +11:00
|
|
|
}
|
2014-12-05 14:29:13 +11:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-06-25 18:57:16 -07:00
|
|
|
if (!IsNoExceptionFlag(flag))
|
2017-02-04 19:03:19 -05:00
|
|
|
tlbe.recent = 1;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-02-04 19:03:19 -05:00
|
|
|
*paddr = tlbe.paddr[1] | (vpa & 0xfff);
|
2021-08-31 09:43:37 -04:00
|
|
|
*wi = (pte2.WIMG & 0b1100) != 0;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-03-25 19:44:14 -04:00
|
|
|
return TLBLookupResult::Found;
|
2010-07-29 12:17:47 +00:00
|
|
|
}
|
2018-03-25 19:44:14 -04:00
|
|
|
return TLBLookupResult::NotFound;
|
2010-07-29 12:17:47 +00:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
static void UpdateTLBEntry(PowerPC::PowerPCState& ppc_state, const XCheckTLBFlag flag, UPTE_Hi pte2,
|
|
|
|
const u32 address)
|
2010-07-29 12:17:47 +00:00
|
|
|
{
|
2016-06-25 18:57:16 -07:00
|
|
|
if (IsNoExceptionFlag(flag))
|
2014-10-13 18:00:45 +11:00
|
|
|
return;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-08-31 09:55:13 -04:00
|
|
|
const u32 tag = address >> HW_PAGE_INDEX_SHIFT;
|
2023-03-12 20:31:10 +01:00
|
|
|
TLBEntry& tlbe = ppc_state.tlb[IsOpcodeFlag(flag)][tag & HW_PAGE_INDEX_MASK];
|
2021-08-31 09:55:13 -04:00
|
|
|
const u32 index = tlbe.recent == 0 && tlbe.tag[0] != TLBEntry::INVALID_TAG;
|
2017-02-04 19:03:19 -05:00
|
|
|
tlbe.recent = index;
|
2021-08-31 09:43:37 -04:00
|
|
|
tlbe.paddr[index] = pte2.RPN << HW_PAGE_INDEX_SHIFT;
|
|
|
|
tlbe.pte[index] = pte2.Hex;
|
2017-02-04 19:03:19 -05:00
|
|
|
tlbe.tag[index] = tag;
|
2010-07-29 12:17:47 +00:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::InvalidateTLBEntry(u32 address)
|
2010-07-29 12:17:47 +00:00
|
|
|
{
|
2017-02-04 19:03:19 -05:00
|
|
|
const u32 entry_index = (address >> HW_PAGE_INDEX_SHIFT) & HW_PAGE_INDEX_MASK;
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
m_ppc_state.tlb[0][entry_index].Invalidate();
|
|
|
|
m_ppc_state.tlb[1][entry_index].Invalidate();
|
2010-07-29 12:17:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Page Address Translation
|
2023-03-12 20:31:10 +01:00
|
|
|
MMU::TranslateAddressResult MMU::TranslatePageAddress(const EffectiveAddress address,
|
|
|
|
const XCheckTLBFlag flag, bool* wi)
|
2010-07-29 12:17:47 +00:00
|
|
|
{
|
|
|
|
// TLB cache
|
2015-01-01 14:35:17 -08:00
|
|
|
// This catches 99%+ of lookups in practice, so the actual page table entry code below doesn't
|
2021-07-04 20:47:04 +02:00
|
|
|
// benefit much from optimization.
|
2021-08-31 10:52:49 -04:00
|
|
|
u32 translated_address = 0;
|
2023-03-12 20:31:10 +01:00
|
|
|
const TLBLookupResult res =
|
|
|
|
LookupTLBPageAddress(m_ppc_state, flag, address.Hex, &translated_address, wi);
|
2018-03-25 19:44:14 -04:00
|
|
|
if (res == TLBLookupResult::Found)
|
2021-08-31 10:52:49 -04:00
|
|
|
{
|
2021-08-07 00:07:40 +02:00
|
|
|
return TranslateAddressResult{TranslateAddressResultEnum::PAGE_TABLE_TRANSLATED,
|
2021-08-31 10:52:49 -04:00
|
|
|
translated_address};
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
const auto sr = UReg_SR{m_ppc_state.sr[address.SR]};
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-08-31 10:52:49 -04:00
|
|
|
if (sr.T != 0)
|
2021-08-07 00:07:40 +02:00
|
|
|
return TranslateAddressResult{TranslateAddressResultEnum::DIRECT_STORE_SEGMENT, 0};
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
|
|
|
|
// TODO: Handle KS/KP segment register flags.
|
|
|
|
|
|
|
|
// No-execute segment register flag.
|
2021-08-31 10:52:49 -04:00
|
|
|
if ((flag == XCheckTLBFlag::Opcode || flag == XCheckTLBFlag::OpcodeNoException) && sr.N != 0)
|
2018-03-25 19:48:15 -04:00
|
|
|
{
|
2021-08-07 00:07:40 +02:00
|
|
|
return TranslateAddressResult{TranslateAddressResultEnum::PAGE_FAULT, 0};
|
2018-03-25 19:48:15 -04:00
|
|
|
}
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
|
2021-08-31 11:13:42 -04:00
|
|
|
const u32 offset = address.offset; // 12 bit
|
|
|
|
const u32 page_index = address.page_index; // 16 bit
|
|
|
|
const u32 VSID = sr.VSID; // 24 bit
|
|
|
|
const u32 api = address.API; // 6 bit (part of page_index)
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2009-01-18 09:51:09 +00:00
|
|
|
// hash function no 1 "xor" .360
|
2014-12-06 10:28:34 +11:00
|
|
|
u32 hash = (VSID ^ page_index);
|
2021-08-31 10:52:49 -04:00
|
|
|
|
|
|
|
UPTE_Lo pte1;
|
|
|
|
pte1.VSID = VSID;
|
|
|
|
pte1.API = api;
|
|
|
|
pte1.V = 1;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2014-12-06 10:28:34 +11:00
|
|
|
for (int hash_func = 0; hash_func < 2; hash_func++)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2015-01-02 20:34:47 -08:00
|
|
|
// hash function no 2 "not" .360
|
2014-12-06 10:28:34 +11:00
|
|
|
if (hash_func == 1)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2014-12-06 10:28:34 +11:00
|
|
|
hash = ~hash;
|
2021-08-31 10:52:49 -04:00
|
|
|
pte1.H = 1;
|
2014-12-06 10:28:34 +11:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
u32 pteg_addr = ((hash & m_ppc_state.pagetable_hashmask) << 6) | m_ppc_state.pagetable_base;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-01-02 20:34:47 -08:00
|
|
|
for (int i = 0; i < 8; i++, pteg_addr += 8)
|
2014-12-06 10:28:34 +11:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
const u32 pteg = m_memory.Read_U32(pteg_addr);
|
2017-04-17 19:21:28 -04:00
|
|
|
|
2021-08-31 10:52:49 -04:00
|
|
|
if (pte1.Hex == pteg)
|
2009-01-18 09:51:09 +00:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
UPTE_Hi pte2(m_memory.Read_U32(pteg_addr + 4));
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-01-02 20:34:47 -08:00
|
|
|
// set the access bits
|
2015-01-03 10:22:36 -08:00
|
|
|
switch (flag)
|
2010-01-15 14:39:27 +00:00
|
|
|
{
|
2018-03-25 19:48:15 -04:00
|
|
|
case XCheckTLBFlag::NoException:
|
|
|
|
case XCheckTLBFlag::OpcodeNoException:
|
2015-01-02 20:34:47 -08:00
|
|
|
break;
|
2018-03-25 19:48:15 -04:00
|
|
|
case XCheckTLBFlag::Read:
|
2021-08-31 09:43:37 -04:00
|
|
|
pte2.R = 1;
|
2015-01-02 20:34:47 -08:00
|
|
|
break;
|
2018-03-25 19:48:15 -04:00
|
|
|
case XCheckTLBFlag::Write:
|
2021-08-31 09:43:37 -04:00
|
|
|
pte2.R = 1;
|
|
|
|
pte2.C = 1;
|
2015-01-02 20:34:47 -08:00
|
|
|
break;
|
2018-03-25 19:48:15 -04:00
|
|
|
case XCheckTLBFlag::Opcode:
|
2021-08-31 09:43:37 -04:00
|
|
|
pte2.R = 1;
|
2016-06-24 10:43:46 +02:00
|
|
|
break;
|
2014-12-06 10:28:34 +11:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-06-25 18:57:16 -07:00
|
|
|
if (!IsNoExceptionFlag(flag))
|
2017-04-17 19:21:28 -04:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
m_memory.Write_U32(pte2.Hex, pteg_addr + 4);
|
2017-04-17 19:21:28 -04:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-01-02 20:34:47 -08:00
|
|
|
// We already updated the TLB entry if this was caused by a C bit.
|
2018-03-25 19:44:14 -04:00
|
|
|
if (res != TLBLookupResult::UpdateC)
|
2023-03-12 20:31:10 +01:00
|
|
|
UpdateTLBEntry(m_ppc_state, flag, pte2, address.Hex);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-08-31 09:43:37 -04:00
|
|
|
*wi = (pte2.WIMG & 0b1100) != 0;
|
2021-07-04 20:47:04 +02:00
|
|
|
|
2021-08-07 00:07:40 +02:00
|
|
|
return TranslateAddressResult{TranslateAddressResultEnum::PAGE_TABLE_TRANSLATED,
|
2021-08-31 09:43:37 -04:00
|
|
|
(pte2.RPN << 12) | offset};
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2021-08-07 00:07:40 +02:00
|
|
|
return TranslateAddressResult{TranslateAddressResultEnum::PAGE_FAULT, 0};
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::UpdateBATs(BatTable& bat_table, u32 base_spr)
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
|
|
|
// TODO: Separate BATs for MSR.PR==0 and MSR.PR==1
|
2021-07-04 20:47:04 +02:00
|
|
|
// TODO: Handle PP settings.
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
// TODO: Check how hardware reacts to overlapping BATs (including
|
|
|
|
// BATs which should cause a DSI).
|
|
|
|
// TODO: Check how hardware reacts to invalid BATs (bad mask etc).
|
|
|
|
for (int i = 0; i < 4; ++i)
|
|
|
|
{
|
2018-06-03 12:34:03 -04:00
|
|
|
const u32 spr = base_spr + i * 2;
|
2023-03-12 20:31:10 +01:00
|
|
|
const UReg_BAT_Up batu{m_ppc_state.spr[spr]};
|
|
|
|
const UReg_BAT_Lo batl{m_ppc_state.spr[spr + 1]};
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
if (batu.VS == 0 && batu.VP == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if ((batu.BEPI & batu.BL) != 0)
|
|
|
|
{
|
|
|
|
// With a valid BAT, the simplest way to match is
|
|
|
|
// (input & ~BL_mask) == BEPI. For now, assume it's
|
|
|
|
// implemented this way for invalid BATs as well.
|
2020-11-25 08:45:21 -05:00
|
|
|
WARN_LOG_FMT(POWERPC, "Bad BAT setup: BEPI overlaps BL");
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((batl.BRPN & batu.BL) != 0)
|
|
|
|
{
|
|
|
|
// With a valid BAT, the simplest way to translate is
|
|
|
|
// (input & BL_mask) | BRPN_address. For now, assume it's
|
|
|
|
// implemented this way for invalid BATs as well.
|
2020-11-25 08:45:21 -05:00
|
|
|
WARN_LOG_FMT(POWERPC, "Bad BAT setup: BPRN overlaps BL");
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
}
|
2017-06-22 13:04:00 +02:00
|
|
|
if (!Common::IsValidLowMask((u32)batu.BL))
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
|
|
|
// With a valid BAT, the simplest way of masking is
|
|
|
|
// (input & ~BL_mask) for matching and (input & BL_mask) for
|
|
|
|
// translation. For now, assume it's implemented this way for
|
|
|
|
// invalid BATs as well.
|
2020-11-25 08:45:21 -05:00
|
|
|
WARN_LOG_FMT(POWERPC, "Bad BAT setup: invalid mask in BL");
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
}
|
2016-08-23 22:39:09 +02:00
|
|
|
for (u32 j = 0; j <= batu.BL; ++j)
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
|
|
|
// Enumerate all bit-patterns which fit within the given mask.
|
|
|
|
if ((j & batu.BL) == j)
|
|
|
|
{
|
|
|
|
// This bit is a little weird: if BRPN & j != 0, we end up with
|
|
|
|
// a strange mapping. Need to check on hardware.
|
2017-03-04 14:55:38 +01:00
|
|
|
u32 physical_address = (batl.BRPN | j) << BAT_INDEX_SHIFT;
|
|
|
|
u32 virtual_address = (batu.BEPI | j) << BAT_INDEX_SHIFT;
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
|
2021-07-04 20:47:04 +02:00
|
|
|
// BAT_MAPPED_BIT is whether the translation is valid
|
|
|
|
// BAT_PHYSICAL_BIT is whether we can use the fastmem arena
|
|
|
|
// BAT_WI_BIT is whether either W or I (of WIMG) is set
|
2017-03-04 14:55:38 +01:00
|
|
|
u32 valid_bit = BAT_MAPPED_BIT;
|
2021-07-04 20:47:04 +02:00
|
|
|
|
|
|
|
const bool wi = (batl.WIMG & 0b1100) != 0;
|
|
|
|
if (wi)
|
|
|
|
valid_bit |= BAT_WI_BIT;
|
|
|
|
|
|
|
|
// Enable fastmem mappings for cached memory. There are quirks related to uncached memory
|
|
|
|
// that fastmem doesn't emulate properly (though no normal games are known to rely on them).
|
|
|
|
if (!wi)
|
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
if (m_memory.GetFakeVMEM() && (physical_address & 0xFE000000) == 0x7E000000)
|
2021-07-04 20:47:04 +02:00
|
|
|
{
|
|
|
|
valid_bit |= BAT_PHYSICAL_BIT;
|
|
|
|
}
|
2023-03-12 20:31:10 +01:00
|
|
|
else if (physical_address < m_memory.GetRamSizeReal())
|
2021-07-04 20:47:04 +02:00
|
|
|
{
|
|
|
|
valid_bit |= BAT_PHYSICAL_BIT;
|
|
|
|
}
|
2023-03-12 20:31:10 +01:00
|
|
|
else if (m_memory.GetEXRAM() && physical_address >> 28 == 0x1 &&
|
|
|
|
(physical_address & 0x0FFFFFFF) < m_memory.GetExRamSizeReal())
|
2021-07-04 20:47:04 +02:00
|
|
|
{
|
|
|
|
valid_bit |= BAT_PHYSICAL_BIT;
|
|
|
|
}
|
|
|
|
else if (physical_address >> 28 == 0xE &&
|
2023-03-12 20:31:10 +01:00
|
|
|
physical_address < 0xE0000000 + m_memory.GetL1CacheSize())
|
2021-07-04 20:47:04 +02:00
|
|
|
{
|
|
|
|
valid_bit |= BAT_PHYSICAL_BIT;
|
|
|
|
}
|
|
|
|
}
|
2017-03-04 14:55:38 +01:00
|
|
|
|
2017-02-28 17:32:17 -05:00
|
|
|
// Fastmem doesn't support memchecks, so disable it for all overlapping virtual pages.
|
2023-03-28 20:26:52 +02:00
|
|
|
if (m_power_pc.GetMemChecks().OverlapsMemcheck(virtual_address, BAT_PAGE_SIZE))
|
2017-03-04 14:55:38 +01:00
|
|
|
valid_bit &= ~BAT_PHYSICAL_BIT;
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
|
|
|
|
// (BEPI | j) == (BEPI & ~BL) | (j & BL).
|
2017-03-04 14:55:38 +01:00
|
|
|
bat_table[virtual_address >> BAT_INDEX_SHIFT] = physical_address | valid_bit;
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::UpdateFakeMMUBat(BatTable& bat_table, u32 start_addr)
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
2016-08-24 08:12:05 +02:00
|
|
|
for (u32 i = 0; i < (0x10000000 >> BAT_INDEX_SHIFT); ++i)
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
|
|
|
// Map from 0x4XXXXXXX or 0x7XXXXXXX to the range
|
|
|
|
// [0x7E000000,0x80000000).
|
|
|
|
u32 e_address = i + (start_addr >> BAT_INDEX_SHIFT);
|
2023-03-12 20:31:10 +01:00
|
|
|
u32 p_address = 0x7E000000 | (i << BAT_INDEX_SHIFT & m_memory.GetFakeVMemMask());
|
2017-03-04 14:55:38 +01:00
|
|
|
u32 flags = BAT_MAPPED_BIT | BAT_PHYSICAL_BIT;
|
|
|
|
|
2023-03-28 20:26:52 +02:00
|
|
|
if (m_power_pc.GetMemChecks().OverlapsMemcheck(e_address << BAT_INDEX_SHIFT, BAT_PAGE_SIZE))
|
2017-03-04 14:55:38 +01:00
|
|
|
flags &= ~BAT_PHYSICAL_BIT;
|
|
|
|
|
|
|
|
bat_table[e_address] = p_address | flags;
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::DBATUpdated()
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
m_dbat_table = {};
|
|
|
|
UpdateBATs(m_dbat_table, SPR_DBAT0U);
|
|
|
|
bool extended_bats = SConfig::GetInstance().bWii && HID4(m_ppc_state).SBE;
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
if (extended_bats)
|
2023-03-12 20:31:10 +01:00
|
|
|
UpdateBATs(m_dbat_table, SPR_DBAT4U);
|
|
|
|
if (m_memory.GetFakeVMEM())
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
|
|
|
// In Fake-MMU mode, insert some extra entries into the BAT tables.
|
2023-03-12 20:31:10 +01:00
|
|
|
UpdateFakeMMUBat(m_dbat_table, 0x40000000);
|
|
|
|
UpdateFakeMMUBat(m_dbat_table, 0x70000000);
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
}
|
2016-10-01 17:38:09 +02:00
|
|
|
|
|
|
|
#ifndef _ARCH_32
|
2023-03-12 20:31:10 +01:00
|
|
|
m_memory.UpdateLogicalMemory(m_dbat_table);
|
2016-10-01 17:38:09 +02:00
|
|
|
#endif
|
2016-09-03 14:53:22 +02:00
|
|
|
|
|
|
|
// IsOptimizable*Address and dcbz depends on the BAT mapping, so we need a flush here.
|
2023-03-12 20:31:10 +01:00
|
|
|
m_system.GetJitInterface().ClearSafe();
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void MMU::IBATUpdated()
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
2023-03-12 20:31:10 +01:00
|
|
|
m_ibat_table = {};
|
|
|
|
UpdateBATs(m_ibat_table, SPR_IBAT0U);
|
|
|
|
bool extended_bats = SConfig::GetInstance().bWii && HID4(m_ppc_state).SBE;
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
if (extended_bats)
|
2023-03-12 20:31:10 +01:00
|
|
|
UpdateBATs(m_ibat_table, SPR_IBAT4U);
|
|
|
|
if (m_memory.GetFakeVMEM())
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
{
|
|
|
|
// In Fake-MMU mode, insert some extra entries into the BAT tables.
|
2023-03-12 20:31:10 +01:00
|
|
|
UpdateFakeMMUBat(m_ibat_table, 0x40000000);
|
|
|
|
UpdateFakeMMUBat(m_ibat_table, 0x70000000);
|
Support for dynamic BAT modification (dynamic-bat).
Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.
This change can be logically divided into three parts. The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.). This does
nothing by itself, but it's necessary for the other parts.
The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.
The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration. We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.
One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
2016-06-25 18:58:09 -07:00
|
|
|
}
|
2023-03-12 20:31:10 +01:00
|
|
|
m_system.GetJitInterface().ClearSafe();
|
2009-01-18 09:51:09 +00:00
|
|
|
}
|
2010-01-15 14:39:27 +00:00
|
|
|
|
2010-09-11 02:34:51 +00:00
|
|
|
// Translate effective address using BAT or PAT. Returns 0 if the address cannot be translated.
|
2016-09-03 14:53:22 +02:00
|
|
|
// Through the hardware looks up BAT and TLB in parallel, BAT is used first if available.
|
|
|
|
// So we first check if there is a matching BAT entry, else we look for the TLB in
|
|
|
|
// TranslatePageAddress().
|
2015-01-03 10:22:36 -08:00
|
|
|
template <const XCheckTLBFlag flag>
|
2023-03-12 20:31:10 +01:00
|
|
|
MMU::TranslateAddressResult MMU::TranslateAddress(u32 address)
|
2010-07-29 12:17:47 +00:00
|
|
|
{
|
2021-07-04 20:47:04 +02:00
|
|
|
bool wi = false;
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
if (TranslateBatAddress(IsOpcodeFlag(flag) ? m_ibat_table : m_dbat_table, &address, &wi))
|
2021-08-07 00:07:40 +02:00
|
|
|
return TranslateAddressResult{TranslateAddressResultEnum::BAT_TRANSLATED, address, wi};
|
2017-03-04 14:55:38 +01:00
|
|
|
|
2021-08-31 11:13:42 -04:00
|
|
|
return TranslatePageAddress(EffectiveAddress{address}, flag, &wi);
|
2010-07-29 12:17:47 +00:00
|
|
|
}
|
2014-12-30 18:12:47 -08:00
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
std::optional<u32> MMU::GetTranslatedAddress(u32 address)
|
2019-04-11 01:50:52 -04:00
|
|
|
{
|
|
|
|
auto result = TranslateAddress<XCheckTLBFlag::NoException>(address);
|
|
|
|
if (!result.Success())
|
|
|
|
{
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
return std::optional<u32>(result.address);
|
|
|
|
}
|
|
|
|
|
2023-03-12 20:31:10 +01:00
|
|
|
void ClearDCacheLineFromJit64(MMU& mmu, u32 address)
|
|
|
|
{
|
|
|
|
mmu.ClearDCacheLine(address);
|
|
|
|
}
|
|
|
|
u32 ReadU8ZXFromJit64(MMU& mmu, u32 address)
|
|
|
|
{
|
|
|
|
return mmu.Read_U8(address);
|
|
|
|
}
|
|
|
|
u32 ReadU16ZXFromJit64(MMU& mmu, u32 address)
|
|
|
|
{
|
|
|
|
return mmu.Read_U16(address);
|
|
|
|
}
|
|
|
|
u32 ReadU32FromJit64(MMU& mmu, u32 address)
|
|
|
|
{
|
|
|
|
return mmu.Read_U32(address);
|
|
|
|
}
|
|
|
|
u64 ReadU64FromJit64(MMU& mmu, u32 address)
|
|
|
|
{
|
|
|
|
return mmu.Read_U64(address);
|
|
|
|
}
|
|
|
|
void WriteU8FromJit64(MMU& mmu, u32 var, u32 address)
|
|
|
|
{
|
|
|
|
mmu.Write_U8(var, address);
|
|
|
|
}
|
|
|
|
void WriteU16FromJit64(MMU& mmu, u32 var, u32 address)
|
|
|
|
{
|
|
|
|
mmu.Write_U16(var, address);
|
|
|
|
}
|
|
|
|
void WriteU32FromJit64(MMU& mmu, u32 var, u32 address)
|
|
|
|
{
|
|
|
|
mmu.Write_U32(var, address);
|
|
|
|
}
|
|
|
|
void WriteU64FromJit64(MMU& mmu, u64 var, u32 address)
|
|
|
|
{
|
|
|
|
mmu.Write_U64(var, address);
|
|
|
|
}
|
|
|
|
void WriteU16SwapFromJit64(MMU& mmu, u32 var, u32 address)
|
|
|
|
{
|
|
|
|
mmu.Write_U16_Swap(var, address);
|
|
|
|
}
|
|
|
|
void WriteU32SwapFromJit64(MMU& mmu, u32 var, u32 address)
|
|
|
|
{
|
|
|
|
mmu.Write_U32_Swap(var, address);
|
|
|
|
}
|
|
|
|
void WriteU64SwapFromJit64(MMU& mmu, u64 var, u32 address)
|
|
|
|
{
|
|
|
|
mmu.Write_U64_Swap(var, address);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClearDCacheLineFromJitArm64(u32 address, MMU& mmu)
|
|
|
|
{
|
|
|
|
mmu.ClearDCacheLine(address);
|
|
|
|
}
|
|
|
|
u8 ReadU8FromJitArm64(u32 address, MMU& mmu)
|
|
|
|
{
|
|
|
|
return mmu.Read_U8(address);
|
|
|
|
}
|
|
|
|
u16 ReadU16FromJitArm64(u32 address, MMU& mmu)
|
|
|
|
{
|
|
|
|
return mmu.Read_U16(address);
|
|
|
|
}
|
|
|
|
u32 ReadU32FromJitArm64(u32 address, MMU& mmu)
|
|
|
|
{
|
|
|
|
return mmu.Read_U32(address);
|
|
|
|
}
|
|
|
|
u64 ReadU64FromJitArm64(u32 address, MMU& mmu)
|
|
|
|
{
|
|
|
|
return mmu.Read_U64(address);
|
|
|
|
}
|
|
|
|
void WriteU8FromJitArm64(u32 var, u32 address, MMU& mmu)
|
|
|
|
{
|
|
|
|
mmu.Write_U8(var, address);
|
|
|
|
}
|
|
|
|
void WriteU16FromJitArm64(u32 var, u32 address, MMU& mmu)
|
|
|
|
{
|
|
|
|
mmu.Write_U16(var, address);
|
|
|
|
}
|
|
|
|
void WriteU32FromJitArm64(u32 var, u32 address, MMU& mmu)
|
|
|
|
{
|
|
|
|
mmu.Write_U32(var, address);
|
|
|
|
}
|
|
|
|
void WriteU64FromJitArm64(u64 var, u32 address, MMU& mmu)
|
|
|
|
{
|
|
|
|
mmu.Write_U64(var, address);
|
|
|
|
}
|
|
|
|
void WriteU16SwapFromJitArm64(u32 var, u32 address, MMU& mmu)
|
|
|
|
{
|
|
|
|
mmu.Write_U16_Swap(var, address);
|
|
|
|
}
|
|
|
|
void WriteU32SwapFromJitArm64(u32 var, u32 address, MMU& mmu)
|
|
|
|
{
|
|
|
|
mmu.Write_U32_Swap(var, address);
|
|
|
|
}
|
|
|
|
void WriteU64SwapFromJitArm64(u64 var, u32 address, MMU& mmu)
|
|
|
|
{
|
|
|
|
mmu.Write_U64_Swap(var, address);
|
|
|
|
}
|
2019-04-11 01:50:52 -04:00
|
|
|
} // namespace PowerPC
|