diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h index 55d2a076c5..97213f6448 100644 --- a/Source/Core/Common/CommonPaths.h +++ b/Source/Core/Common/CommonPaths.h @@ -90,7 +90,8 @@ #define WII_SYSCONF "SYSCONF" // Files in the directory returned by GetUserPath(D_DUMP_IDX) -#define RAM_DUMP "ram.raw" +#define MEM1_DUMP "mem1.raw" +#define MEM2_DUMP "mem2.raw" #define ARAM_DUMP "aram.raw" #define FAKEVMEM_DUMP "fakevmem.raw" diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp index 53f242ecf0..72d441906e 100644 --- a/Source/Core/Common/FileUtil.cpp +++ b/Source/Core/Common/FileUtil.cpp @@ -794,7 +794,8 @@ static void RebuildUserDirectories(unsigned int dir_index) s_user_paths[F_DEBUGGERCONFIG_IDX] = s_user_paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; s_user_paths[F_LOGGERCONFIG_IDX] = s_user_paths[D_CONFIG_IDX] + LOGGER_CONFIG; s_user_paths[F_MAINLOG_IDX] = s_user_paths[D_LOGS_IDX] + MAIN_LOG; - s_user_paths[F_RAMDUMP_IDX] = s_user_paths[D_DUMP_IDX] + RAM_DUMP; + s_user_paths[F_MEM1DUMP_IDX] = s_user_paths[D_DUMP_IDX] + MEM1_DUMP; + s_user_paths[F_MEM2DUMP_IDX] = s_user_paths[D_DUMP_IDX] + MEM2_DUMP; s_user_paths[F_ARAMDUMP_IDX] = s_user_paths[D_DUMP_IDX] + ARAM_DUMP; s_user_paths[F_FAKEVMEMDUMP_IDX] = s_user_paths[D_DUMP_IDX] + FAKEVMEM_DUMP; s_user_paths[F_GCSRAM_IDX] = s_user_paths[D_GCUSER_IDX] + GC_SRAM; @@ -830,7 +831,8 @@ static void RebuildUserDirectories(unsigned int dir_index) s_user_paths[D_DUMPTEXTURES_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; s_user_paths[D_DUMPDSP_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_DSP_DIR DIR_SEP; s_user_paths[D_DUMPSSL_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_SSL_DIR DIR_SEP; - s_user_paths[F_RAMDUMP_IDX] = s_user_paths[D_DUMP_IDX] + RAM_DUMP; + s_user_paths[F_MEM1DUMP_IDX] = s_user_paths[D_DUMP_IDX] + MEM1_DUMP; + s_user_paths[F_MEM2DUMP_IDX] = s_user_paths[D_DUMP_IDX] + MEM2_DUMP; s_user_paths[F_ARAMDUMP_IDX] = s_user_paths[D_DUMP_IDX] + ARAM_DUMP; s_user_paths[F_FAKEVMEMDUMP_IDX] = s_user_paths[D_DUMP_IDX] + FAKEVMEM_DUMP; break; diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index 68d6921cdb..dadee2ae23 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -59,7 +59,8 @@ enum F_DEBUGGERCONFIG_IDX, F_LOGGERCONFIG_IDX, F_MAINLOG_IDX, - F_RAMDUMP_IDX, + F_MEM1DUMP_IDX, + F_MEM2DUMP_IDX, F_ARAMDUMP_IDX, F_FAKEVMEMDUMP_IDX, F_GCSRAM_IDX, diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 37d30e94ff..fa77ed20a1 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -71,6 +71,7 @@ add_library(core HLE/HLE_Misc.cpp HLE/HLE_OS.cpp HLE/HLE_VarArgs.cpp + HW/AddressSpace.cpp HW/AudioInterface.cpp HW/CPU.cpp HW/DSP.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index e9a088e422..132c29a5dd 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -109,6 +109,7 @@ + @@ -373,6 +374,7 @@ + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index f7aa17158e..29357418da 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -919,6 +919,9 @@ HW %28Flipper/Hollywood%29\Wiimote\Common + + HW %28Flipper/Hollywood%29 + @@ -1635,6 +1638,9 @@ HW %28Flipper/Hollywood%29\Wiimote\Common + + HW %28Flipper/Hollywood%29 + diff --git a/Source/Core/Core/HW/AddressSpace.cpp b/Source/Core/Core/HW/AddressSpace.cpp new file mode 100644 index 0000000000..5e46ab766f --- /dev/null +++ b/Source/Core/Core/HW/AddressSpace.cpp @@ -0,0 +1,415 @@ +// Copyright 2011 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/HW/AddressSpace.h" + +#include + +#include "Common/BitUtils.h" +#include "Core/ConfigManager.h" +#include "Core/HW/DSP.h" +#include "Core/HW/Memmap.h" +#include "Core/PowerPC/MMU.h" + +namespace AddressSpace +{ +u16 Accessors::ReadU16(u32 address) const +{ + u32 result = ReadU8(address); + result = result << 8 | ReadU8(address + 1); + return result; +} + +void Accessors::WriteU16(u32 address, u16 value) +{ + WriteU8(address, value & 0xff); + WriteU8(address + 1, (value >> 8) & 0xff); +} + +u32 Accessors::ReadU32(u32 address) const +{ + u32 result = ReadU16(address); + result = result << 16 | ReadU16(address + 2); + return result; +} + +void Accessors::WriteU32(u32 address, u32 value) +{ + WriteU16(address, value & 0xffff); + WriteU16(address + 2, (value >> 16) & 0xffff); +} + +u64 Accessors::ReadU64(u32 address) const +{ + u64 result = ReadU32(address); + result = result << 32 | ReadU32(address + 4); + return result; +} + +void Accessors::WriteU64(u32 address, u64 value) +{ + WriteU32(address, value & 0xffffffff); + WriteU32(address + 4, (value >> 32) & 0xffffffff); +} + +float Accessors::ReadF32(u32 address) const +{ + return Common::BitCast(ReadU32(address)); +} + +Accessors::iterator Accessors::begin() const +{ + return nullptr; +} + +Accessors::iterator Accessors::end() const +{ + return nullptr; +} + +std::optional Accessors::Search(u32 haystack_start, u8* needle_start, u32 needle_size, + bool forwards) const +{ + return std::nullopt; +} + +Accessors::~Accessors() +{ +} + +struct EffectiveAddressSpaceAccessors : Accessors +{ + bool IsValidAddress(u32 address) const override { return PowerPC::HostIsRAMAddress(address); } + u8 ReadU8(u32 address) const override { return PowerPC::HostRead_U8(address); } + void WriteU8(u32 address, u8 value) override { PowerPC::HostWrite_U8(address, value); } + u16 ReadU16(u32 address) const override { return PowerPC::HostRead_U16(address); } + void WriteU16(u32 address, u16 value) override { PowerPC::HostWrite_U16(address, value); } + u32 ReadU32(u32 address) const override { return PowerPC::HostRead_U32(address); } + void WriteU32(u32 address, u32 value) override { PowerPC::HostWrite_U32(address, value); } + u64 ReadU64(u32 address) const override { return PowerPC::HostRead_U64(address); } + void WriteU64(u32 address, u64 value) override { PowerPC::HostWrite_U64(address, value); } + float ReadF32(u32 address) const override { return PowerPC::HostRead_F32(address); }; + + bool Matches(u32 haystack_start, u8* needle_start, u32 needle_size) const + { + u32 page_base = haystack_start & 0xfffff000; + u32 offset = haystack_start & 0x0000fff; + do + { + if (!PowerPC::HostIsRAMAddress(page_base)) + { + return false; + } + auto page_physical_address = PowerPC::GetTranslatedAddress(page_base); + if (!page_physical_address.has_value()) + { + return false; + } + + // For now, limit to only mem1 and mem2 regions + // GetPointer can get confused by the locked dcache region that dolphin pins at 0xe0000000 + u32 memory_area = (*page_physical_address) >> 24; + if ((memory_area != 0x00) && (memory_area != 0x01)) + { + return false; + } + + u8* page_ptr = Memory::GetPointer(*page_physical_address); + if (page_ptr == nullptr) + { + return false; + } + + u32 chunk_size = std::min(0x1000 - offset, needle_size); + if (memcmp(needle_start, page_ptr + offset, chunk_size) != 0) + { + return false; + } + needle_size -= chunk_size; + needle_start += chunk_size; + offset = 0; + page_base = page_base + 0x1000; + } while (needle_size != 0 && page_base != 0); + return (needle_size == 0); + } + + std::optional Search(u32 haystack_start, u8* needle_start, u32 needle_size, + bool forward) const override + { + u32 haystack_address = haystack_start; + // For forward=true, search incrementally (step +1) until it wraps back to 0x00000000 + // For forward=false, search decrementally (step -1) until it wraps back to 0xfffff000 + // Any page that doesn't translate is completely skipped. + const u32 haystack_page_limit = forward ? 0x00000000 : 0xfffff000; + const u32 haystack_offset_limit = forward ? 0x000 : 0xfff; + const u32 haystack_page_change = forward ? 0x1000 : -0x1000; + const u32 haystack_offset_change = forward ? 1 : -1; + do + { + if (PowerPC::HostIsRAMAddress(haystack_address)) + { + do + { + if (Matches(haystack_address, needle_start, needle_size)) + { + return std::optional(haystack_address); + } + haystack_address += haystack_offset_change; + } while ((haystack_address & 0xfff) != haystack_offset_limit); + } + else + { + haystack_address = (haystack_address + haystack_page_change) & 0xfffff000; + } + } while ((haystack_address & 0xfffff000) != haystack_page_limit); + return std::nullopt; + } +}; + +struct AuxiliaryAddressSpaceAccessors : Accessors +{ + static constexpr u32 aram_base_address = 0; + bool IsValidAddress(u32 address) const override + { + return !SConfig::GetInstance().bWii && (address - aram_base_address) < GetSize(); + } + u8 ReadU8(u32 address) const override + { + const u8* base = DSP::GetARAMPtr(); + return base[address]; + } + + void WriteU8(u32 address, u8 value) override + { + u8* base = DSP::GetARAMPtr(); + base[address] = value; + } + + iterator begin() const override { return DSP::GetARAMPtr(); } + + iterator end() const override { return DSP::GetARAMPtr() + GetSize(); } + + std::optional Search(u32 haystack_offset, u8* needle_start, u32 needle_size, + bool forward) const override + { + if (!IsValidAddress(haystack_offset)) + { + return std::nullopt; + } + + const u8* result; + + if (forward) + { + result = + std::search(begin() + haystack_offset, end(), needle_start, needle_start + needle_size); + } + else + { + // using reverse iterator will also search the element in reverse + auto reverse_end = std::make_reverse_iterator(begin() + needle_size - 1); + auto it = std::search(std::make_reverse_iterator(begin() + haystack_offset + needle_size - 1), + reverse_end, std::make_reverse_iterator(needle_start + needle_size), + std::make_reverse_iterator(needle_start)); + result = (it == reverse_end) ? end() : (&(*it) - needle_size + 1); + } + if (result == end()) + { + return std::nullopt; + } + + return std::optional(result - begin()); + } + +private: + static u32 GetSize() { return 16 * 1024 * 1024; } +}; + +struct AccessorMapping +{ + u32 base; + Accessors* accessors; +}; + +struct CompositeAddressSpaceAccessors : Accessors +{ + CompositeAddressSpaceAccessors(std::initializer_list accessors) + : m_accessor_mappings(accessors.begin(), accessors.end()) + { + } + + bool IsValidAddress(u32 address) const override + { + return FindAppropriateAccessor(address) != m_accessor_mappings.end(); + } + + u8 ReadU8(u32 address) const override + { + auto it = FindAppropriateAccessor(address); + if (it == m_accessor_mappings.end()) + { + return 0; + } + return it->accessors->ReadU8(address); + } + + void WriteU8(u32 address, u8 value) override + { + auto it = FindAppropriateAccessor(address); + if (it == m_accessor_mappings.end()) + { + return; + } + return it->accessors->WriteU8(address, value); + } + + std::optional Search(u32 haystack_offset, u8* needle_start, u32 needle_size, + bool forward) const override + { + for (const AccessorMapping& mapping : m_accessor_mappings) + { + u32 mapping_offset = haystack_offset - mapping.base; + if (!mapping.accessors->IsValidAddress(mapping_offset)) + { + continue; + } + auto result = mapping.accessors->Search(mapping_offset, needle_start, needle_size, forward); + if (result.has_value()) + { + return std::optional(*result + mapping.base); + } + } + return std::nullopt; + } + +private: + std::vector m_accessor_mappings; + std::vector::iterator FindAppropriateAccessor(u32 address) + { + return std::find_if(m_accessor_mappings.begin(), m_accessor_mappings.end(), + [address](const AccessorMapping& a) { + return a.accessors->IsValidAddress(address - a.base); + }); + } + std::vector::const_iterator FindAppropriateAccessor(u32 address) const + { + return std::find_if(m_accessor_mappings.begin(), m_accessor_mappings.end(), + [address](const AccessorMapping& a) { + return a.accessors->IsValidAddress(address - a.base); + }); + } +}; + +struct SmallBlockAccessors : Accessors +{ + SmallBlockAccessors(u8** alloc_base, u32 size) : alloc_base(alloc_base), size(size) {} + + bool IsValidAddress(u32 address) const override + { + return (*alloc_base != nullptr) && (address < size); + } + u8 ReadU8(u32 address) const override { return (*alloc_base)[address]; } + + void WriteU8(u32 address, u8 value) override { (*alloc_base)[address] = value; } + + iterator begin() const override { return *alloc_base; } + + iterator end() const override + { + return (*alloc_base == nullptr) ? nullptr : (*alloc_base + size); + } + + std::optional Search(u32 haystack_offset, u8* needle_start, u32 needle_size, + bool forward) const override + { + if (!IsValidAddress(haystack_offset) || !IsValidAddress(haystack_offset + needle_size - 1)) + { + return std::nullopt; + } + + const u8* result; + if (forward) + { + result = + std::search(begin() + haystack_offset, end(), needle_start, needle_start + needle_size); + } + else + { + // using reverse iterator will also search the element in reverse + auto reverse_end = std::make_reverse_iterator(begin() + needle_size - 1); + auto it = std::search(std::make_reverse_iterator(begin() + haystack_offset + needle_size - 1), + reverse_end, std::make_reverse_iterator(needle_start + needle_size), + std::make_reverse_iterator(needle_start)); + result = (it == reverse_end) ? end() : (&(*it) - needle_size + 1); + } + if (result == end()) + { + return std::nullopt; + } + + return std::optional(result - begin()); + } + +private: + u8** alloc_base; + u32 size; +}; + +struct NullAccessors : Accessors +{ + bool IsValidAddress(u32 address) const override { return false; } + u8 ReadU8(u32 address) const override { return 0; } + void WriteU8(u32 address, u8 value) override {} +}; + +static EffectiveAddressSpaceAccessors s_effective_address_space_accessors; +static AuxiliaryAddressSpaceAccessors s_auxiliary_address_space_accessors; +static SmallBlockAccessors s_mem1_address_space_accessors{&Memory::m_pRAM, Memory::REALRAM_SIZE}; +static SmallBlockAccessors s_mem2_address_space_accessors{&Memory::m_pEXRAM, Memory::EXRAM_SIZE}; +static SmallBlockAccessors s_fake_address_space_accessors{&Memory::m_pFakeVMEM, + Memory::FAKEVMEM_SIZE}; +static CompositeAddressSpaceAccessors s_physical_address_space_accessors_gcn{ + {0x00000000, &s_mem1_address_space_accessors}}; +static CompositeAddressSpaceAccessors s_physical_address_space_accessors_wii{ + {0x00000000, &s_mem1_address_space_accessors}, {0x10000000, &s_mem2_address_space_accessors}}; +static NullAccessors s_null_accessors; + +Accessors* GetAccessors(Type address_space) +{ + // default to effective + switch (address_space) + { + case Type::Effective: + return &s_effective_address_space_accessors; + case Type::Physical: + if (SConfig::GetInstance().bWii) + { + return &s_physical_address_space_accessors_wii; + } + else + { + return &s_physical_address_space_accessors_gcn; + } + case Type::Mem1: + return &s_mem1_address_space_accessors; + case Type::Mem2: + if (SConfig::GetInstance().bWii) + { + return &s_mem2_address_space_accessors; + } + break; + case Type::Auxiliary: + if (!SConfig::GetInstance().bWii) + { + return &s_auxiliary_address_space_accessors; + } + break; + case Type::Fake: + return &s_fake_address_space_accessors; + } + + return &s_null_accessors; +} + +} // namespace AddressSpace diff --git a/Source/Core/Core/HW/AddressSpace.h b/Source/Core/Core/HW/AddressSpace.h new file mode 100644 index 0000000000..42724df053 --- /dev/null +++ b/Source/Core/Core/HW/AddressSpace.h @@ -0,0 +1,50 @@ +// Copyright 2011 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "Core/Core.h" + +namespace AddressSpace +{ +enum class Type +{ + Effective, + Auxiliary, + Physical, + Mem1, + Mem2, + Fake, +}; + +struct Accessors +{ + using iterator = const u8*; + + virtual bool IsValidAddress(u32 address) const = 0; + virtual u8 ReadU8(u32 address) const = 0; + virtual void WriteU8(u32 address, u8 value) = 0; + + // overrideable naive implementations of below are defined + virtual u16 ReadU16(u32 address) const; + virtual void WriteU16(u32 address, u16 value); + virtual u32 ReadU32(u32 address) const; + virtual void WriteU32(u32 address, u32 value); + virtual u64 ReadU64(u32 address) const; + virtual void WriteU64(u32 address, u64 value); + virtual float ReadF32(u32 address) const; + + virtual iterator begin() const; + virtual iterator end() const; + + virtual std::optional Search(u32 haystack_offset, u8* needle_start, u32 needle_size, + bool forward) const; + virtual ~Accessors(); +}; + +Accessors* GetAccessors(Type address_space); + +} // namespace AddressSpace diff --git a/Source/Core/Core/PowerPC/MMU.cpp b/Source/Core/Core/PowerPC/MMU.cpp index f92c39315b..40ab5e2169 100644 --- a/Source/Core/Core/PowerPC/MMU.cpp +++ b/Source/Core/Core/PowerPC/MMU.cpp @@ -1302,4 +1302,14 @@ static TranslateAddressResult TranslateAddress(u32 address) return TranslatePageAddress(address, flag); } -} // namespace +std::optional GetTranslatedAddress(u32 address) +{ + auto result = TranslateAddress(address); + if (!result.Success()) + { + return std::nullopt; + } + return std::optional(result.address); +} + +} // namespace PowerPC diff --git a/Source/Core/Core/PowerPC/MMU.h b/Source/Core/Core/PowerPC/MMU.h index f0f5ec506f..48d97ca606 100644 --- a/Source/Core/Core/PowerPC/MMU.h +++ b/Source/Core/Core/PowerPC/MMU.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "Common/CommonTypes.h" @@ -118,4 +119,6 @@ inline bool TranslateBatAddess(const BatTable& bat_table, u32* address) *address = (bat_result & BAT_RESULT_MASK) | (*address & (BAT_PAGE_SIZE - 1)); return true; } + +std::optional GetTranslatedAddress(u32 address); } // namespace PowerPC diff --git a/Source/Core/DolphinQt/Debugger/MemoryViewWidget.cpp b/Source/Core/DolphinQt/Debugger/MemoryViewWidget.cpp index d9041aaceb..4284f60bc9 100644 --- a/Source/Core/DolphinQt/Debugger/MemoryViewWidget.cpp +++ b/Source/Core/DolphinQt/Debugger/MemoryViewWidget.cpp @@ -16,9 +16,7 @@ #include "Core/Core.h" #include "Core/PowerPC/BreakPoints.h" -#include "Core/PowerPC/MMU.h" #include "Core/PowerPC/PowerPC.h" - #include "DolphinQt/Resources.h" #include "DolphinQt/Settings.h" @@ -74,6 +72,8 @@ void MemoryViewWidget::Update() setRowHeight(0, 24); + const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space); + // Calculate (roughly) how many rows will fit in our table int rows = std::round((height() / static_cast(rowHeight(0))) - 0.25); @@ -101,7 +101,7 @@ void MemoryViewWidget::Update() if (addr == m_address) addr_item->setSelected(true); - if (Core::GetState() != Core::State::Paused || !PowerPC::HostIsRAMAddress(addr)) + if (Core::GetState() != Core::State::Paused || !accessors->IsValidAddress(addr)) { for (int c = 2; c < columnCount(); c++) { @@ -115,14 +115,16 @@ void MemoryViewWidget::Update() continue; } - auto* description_item = - new QTableWidgetItem(QString::fromStdString(PowerPC::debug_interface.GetDescription(addr))); + if (m_address_space == AddressSpace::Type::Effective) + { + auto* description_item = new QTableWidgetItem( + QString::fromStdString(PowerPC::debug_interface.GetDescription(addr))); - description_item->setForeground(Qt::blue); - description_item->setFlags(Qt::ItemIsEnabled); - - setItem(i, columnCount() - 1, description_item); + description_item->setForeground(Qt::blue); + description_item->setFlags(Qt::ItemIsEnabled); + setItem(i, columnCount() - 1, description_item); + } bool row_breakpoint = true; auto update_values = [&](auto value_to_string) { @@ -132,14 +134,18 @@ void MemoryViewWidget::Update() hex_item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); const u32 address = addr + c * (16 / GetColumnCount(m_type)); - if (PowerPC::memchecks.OverlapsMemcheck(address, 16 / GetColumnCount(m_type))) + if (m_address_space == AddressSpace::Type::Effective && + PowerPC::memchecks.OverlapsMemcheck(address, 16 / GetColumnCount(m_type))) + { hex_item->setBackground(Qt::red); + } else + { row_breakpoint = false; - + } setItem(i, 2 + c, hex_item); - if (PowerPC::HostIsRAMAddress(address)) + if (accessors->IsValidAddress(address)) { hex_item->setText(value_to_string(address)); hex_item->setData(Qt::UserRole, address); @@ -151,35 +157,35 @@ void MemoryViewWidget::Update() } } }; - switch (m_type) { case Type::U8: - update_values([](u32 address) { - const u8 value = PowerPC::HostRead_U8(address); + update_values([&accessors](u32 address) { + const u8 value = accessors->ReadU8(address); return QStringLiteral("%1").arg(value, 2, 16, QLatin1Char('0')); }); break; case Type::ASCII: - update_values([](u32 address) { - const char value = PowerPC::HostRead_U8(address); + update_values([&accessors](u32 address) { + const char value = accessors->ReadU8(address); return std::isprint(value) ? QString{QChar::fromLatin1(value)} : QStringLiteral("."); }); break; case Type::U16: - update_values([](u32 address) { - const u16 value = PowerPC::HostRead_U16(address); + update_values([&accessors](u32 address) { + const u16 value = accessors->ReadU16(address); return QStringLiteral("%1").arg(value, 4, 16, QLatin1Char('0')); }); break; case Type::U32: - update_values([](u32 address) { - const u32 value = PowerPC::HostRead_U32(address); + update_values([&accessors](u32 address) { + const u32 value = accessors->ReadU32(address); return QStringLiteral("%1").arg(value, 8, 16, QLatin1Char('0')); }); break; case Type::Float32: - update_values([](u32 address) { return QString::number(PowerPC::HostRead_F32(address)); }); + update_values( + [&accessors](u32 address) { return QString::number(accessors->ReadF32(address)); }); break; } @@ -202,6 +208,22 @@ void MemoryViewWidget::Update() update(); } +void MemoryViewWidget::SetAddressSpace(AddressSpace::Type address_space) +{ + if (m_address_space == address_space) + { + return; + } + + m_address_space = address_space; + Update(); +} + +AddressSpace::Type MemoryViewWidget::GetAddressSpace() const +{ + return m_address_space; +} + void MemoryViewWidget::SetType(Type type) { if (m_type == type) @@ -273,21 +295,24 @@ void MemoryViewWidget::ToggleRowBreakpoint(bool row) const u32 addr = row ? GetContextAddress() & 0xFFFFFFF0 : GetContextAddress(); const auto length = row ? 16 : (16 / GetColumnCount(m_type)); - if (!PowerPC::memchecks.OverlapsMemcheck(addr, length)) + if (m_address_space == AddressSpace::Type::Effective) { - check.start_address = addr; - check.end_address = check.start_address + length - 1; - check.is_ranged = length > 0; - check.is_break_on_read = (m_bp_type == BPType::ReadOnly || m_bp_type == BPType::ReadWrite); - check.is_break_on_write = (m_bp_type == BPType::WriteOnly || m_bp_type == BPType::ReadWrite); - check.log_on_hit = m_do_log; - check.break_on_hit = true; + if (!PowerPC::memchecks.OverlapsMemcheck(addr, length)) + { + check.start_address = addr; + check.end_address = check.start_address + length - 1; + check.is_ranged = length > 0; + check.is_break_on_read = (m_bp_type == BPType::ReadOnly || m_bp_type == BPType::ReadWrite); + check.is_break_on_write = (m_bp_type == BPType::WriteOnly || m_bp_type == BPType::ReadWrite); + check.log_on_hit = m_do_log; + check.break_on_hit = true; - PowerPC::memchecks.Add(check); - } - else - { - PowerPC::memchecks.Remove(addr); + PowerPC::memchecks.Add(check); + } + else + { + PowerPC::memchecks.Remove(addr); + } } emit BreakpointsChanged(); @@ -348,7 +373,8 @@ void MemoryViewWidget::OnCopyHex() const auto length = 16 / GetColumnCount(m_type); - u64 value = PowerPC::HostRead_U64(addr); + const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space); + u64 value = accessors->ReadU64(addr); QApplication::clipboard()->setText( QStringLiteral("%1").arg(value, length * 2, 16, QLatin1Char('0')).left(length * 2)); @@ -362,8 +388,9 @@ void MemoryViewWidget::OnContextMenu() auto* copy_hex = menu->addAction(tr("Copy Hex"), this, &MemoryViewWidget::OnCopyHex); + const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space); copy_hex->setEnabled(Core::GetState() != Core::State::Uninitialized && - PowerPC::HostIsRAMAddress(GetContextAddress())); + accessors->IsValidAddress(GetContextAddress())); menu->addSeparator(); diff --git a/Source/Core/DolphinQt/Debugger/MemoryViewWidget.h b/Source/Core/DolphinQt/Debugger/MemoryViewWidget.h index 4b2b6958ea..5537139f37 100644 --- a/Source/Core/DolphinQt/Debugger/MemoryViewWidget.h +++ b/Source/Core/DolphinQt/Debugger/MemoryViewWidget.h @@ -7,6 +7,7 @@ #include #include "Common/CommonTypes.h" +#include "Core/HW/AddressSpace.h" class MemoryViewWidget : public QTableWidget { @@ -34,6 +35,8 @@ public: void ToggleBreakpoint(); void ToggleRowBreakpoint(bool row); + void SetAddressSpace(AddressSpace::Type address_space); + AddressSpace::Type GetAddressSpace() const; void SetType(Type type); void SetBPType(BPType type); void SetAddress(u32 address); @@ -56,6 +59,7 @@ private: void OnCopyAddress(); void OnCopyHex(); + AddressSpace::Type m_address_space = AddressSpace::Type::Effective; Type m_type = Type::U8; BPType m_bp_type = BPType::ReadWrite; bool m_do_log = true; diff --git a/Source/Core/DolphinQt/Debugger/MemoryWidget.cpp b/Source/Core/DolphinQt/Debugger/MemoryWidget.cpp index ec3f3ebf57..5030a8f98e 100644 --- a/Source/Core/DolphinQt/Debugger/MemoryWidget.cpp +++ b/Source/Core/DolphinQt/Debugger/MemoryWidget.cpp @@ -4,6 +4,7 @@ #include "DolphinQt/Debugger/MemoryWidget.h" +#include #include #include @@ -22,9 +23,6 @@ #include "Common/File.h" #include "Common/FileUtil.h" #include "Core/ConfigManager.h" -#include "Core/HW/DSP.h" -#include "Core/HW/Memmap.h" -#include "Core/PowerPC/PowerPC.h" #include "DolphinQt/Debugger/MemoryViewWidget.h" #include "DolphinQt/QtUtils/ModalMessageBox.h" #include "DolphinQt/Settings.h" @@ -60,6 +58,7 @@ MemoryWidget::MemoryWidget(QWidget* parent) : QDockWidget(parent) ConnectWidgets(); Update(); + OnAddressSpaceChanged(); OnTypeChanged(); } @@ -94,6 +93,7 @@ void MemoryWidget::CreateWidgets() // Dump m_dump_mram = new QPushButton(tr("Dump &MRAM")); m_dump_exram = new QPushButton(tr("Dump &ExRAM")); + m_dump_aram = new QPushButton(tr("Dump &ARAM")); m_dump_fake_vmem = new QPushButton(tr("Dump &FakeVMEM")); // Search Options @@ -112,6 +112,25 @@ void MemoryWidget::CreateWidgets() search_layout->addWidget(m_result_label); search_layout->setSpacing(1); + // Address Space + auto* address_space_group = new QGroupBox(tr("Address Space")); + auto* address_space_layout = new QVBoxLayout; + address_space_group->setLayout(address_space_layout); + + // i18n: "Effective" addresses are the addresses used directly by the CPU and may be subject to + // translation via the MMU to physical addresses. + m_address_space_effective = new QRadioButton(tr("Effective")); + // i18n: The "Auxiliary" address space is the address space of ARAM (Auxiliary RAM). + m_address_space_auxiliary = new QRadioButton(tr("Auxiliary")); + // i18n: The "Physical" address space is the address space that reflects how devices (e.g. RAM) is + // physically wired up. + m_address_space_physical = new QRadioButton(tr("Physical")); + + address_space_layout->addWidget(m_address_space_effective); + address_space_layout->addWidget(m_address_space_auxiliary); + address_space_layout->addWidget(m_address_space_physical); + address_space_layout->setSpacing(1); + // Data Type auto* datatype_group = new QGroupBox(tr("Data Type")); auto* datatype_layout = new QVBoxLayout; @@ -170,8 +189,10 @@ void MemoryWidget::CreateWidgets() sidebar_layout->addItem(new QSpacerItem(1, 32)); sidebar_layout->addWidget(m_dump_mram); sidebar_layout->addWidget(m_dump_exram); + sidebar_layout->addWidget(m_dump_aram); sidebar_layout->addWidget(m_dump_fake_vmem); sidebar_layout->addWidget(search_group); + sidebar_layout->addWidget(address_space_group); sidebar_layout->addWidget(datatype_group); sidebar_layout->addWidget(bp_group); sidebar_layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding)); @@ -208,11 +229,18 @@ void MemoryWidget::ConnectWidgets() connect(m_dump_mram, &QPushButton::pressed, this, &MemoryWidget::OnDumpMRAM); connect(m_dump_exram, &QPushButton::pressed, this, &MemoryWidget::OnDumpExRAM); + connect(m_dump_aram, &QPushButton::pressed, this, &MemoryWidget::OnDumpARAM); connect(m_dump_fake_vmem, &QPushButton::pressed, this, &MemoryWidget::OnDumpFakeVMEM); connect(m_find_next, &QPushButton::pressed, this, &MemoryWidget::OnFindNextValue); connect(m_find_previous, &QPushButton::pressed, this, &MemoryWidget::OnFindPreviousValue); + for (auto* radio : + {m_address_space_effective, m_address_space_auxiliary, m_address_space_physical}) + { + connect(radio, &QRadioButton::toggled, this, &MemoryWidget::OnAddressSpaceChanged); + } + for (auto* radio : {m_type_u8, m_type_u16, m_type_u32, m_type_ascii, m_type_float}) connect(radio, &QRadioButton::toggled, this, &MemoryWidget::OnTypeChanged); @@ -247,6 +275,17 @@ void MemoryWidget::LoadSettings() m_find_ascii->setChecked(search_ascii); m_find_hex->setChecked(search_hex); + const bool address_space_effective = + settings.value(QStringLiteral("memorywidget/addrspace_effective"), true).toBool(); + const bool address_space_auxiliary = + settings.value(QStringLiteral("memorywidget/addrspace_auxiliary"), false).toBool(); + const bool address_space_physical = + settings.value(QStringLiteral("memorywidget/addrspace_physical"), false).toBool(); + + m_address_space_effective->setChecked(address_space_effective); + m_address_space_auxiliary->setChecked(address_space_auxiliary); + m_address_space_physical->setChecked(address_space_physical); + const bool type_u8 = settings.value(QStringLiteral("memorywidget/typeu8"), true).toBool(); const bool type_u16 = settings.value(QStringLiteral("memorywidget/typeu16"), false).toBool(); const bool type_u32 = settings.value(QStringLiteral("memorywidget/typeu32"), false).toBool(); @@ -284,6 +323,13 @@ void MemoryWidget::SaveSettings() settings.setValue(QStringLiteral("memorywidget/searchascii"), m_find_ascii->isChecked()); settings.setValue(QStringLiteral("memorywidget/searchhex"), m_find_hex->isChecked()); + settings.setValue(QStringLiteral("memorywidget/addrspace_effective"), + m_address_space_effective->isChecked()); + settings.setValue(QStringLiteral("memorywidget/addrspace_auxiliary"), + m_address_space_auxiliary->isChecked()); + settings.setValue(QStringLiteral("memorywidget/addrspace_physical"), + m_address_space_physical->isChecked()); + settings.setValue(QStringLiteral("memorywidget/typeu8"), m_type_u8->isChecked()); settings.setValue(QStringLiteral("memorywidget/typeu16"), m_type_u16->isChecked()); settings.setValue(QStringLiteral("memorywidget/typeu32"), m_type_u32->isChecked()); @@ -296,6 +342,22 @@ void MemoryWidget::SaveSettings() settings.setValue(QStringLiteral("memorywidget/bplog"), m_bp_log_check->isChecked()); } +void MemoryWidget::OnAddressSpaceChanged() +{ + AddressSpace::Type space; + + if (m_address_space_effective->isChecked()) + space = AddressSpace::Type::Effective; + else if (m_address_space_auxiliary->isChecked()) + space = AddressSpace::Type::Auxiliary; + else + space = AddressSpace::Type::Physical; + + m_memory_view->SetAddressSpace(space); + + SaveSettings(); +} + void MemoryWidget::OnTypeChanged() { MemoryViewWidget::Type type; @@ -411,12 +473,14 @@ void MemoryWidget::OnSetValue() return; } + AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_memory_view->GetAddressSpace()); + if (m_find_ascii->isChecked()) { const QByteArray bytes = m_data_edit->text().toUtf8(); for (char c : bytes) - PowerPC::HostWrite_U8(static_cast(c), addr++); + accessors->WriteU8(static_cast(c), addr++); } else { @@ -431,18 +495,18 @@ void MemoryWidget::OnSetValue() if (value == static_cast(value)) { - PowerPC::HostWrite_U8(static_cast(value), addr); + accessors->WriteU8(static_cast(value), addr); } else if (value == static_cast(value)) { - PowerPC::HostWrite_U16(static_cast(value), addr); + accessors->WriteU16(static_cast(value), addr); } else if (value == static_cast(value)) { - PowerPC::HostWrite_U32(static_cast(value), addr); + accessors->WriteU32(static_cast(value), addr); } else - PowerPC::HostWrite_U64(value, addr); + accessors->WriteU64(value, addr); } Update(); @@ -473,24 +537,30 @@ static void DumpArray(const std::string& filename, const u8* data, size_t length void MemoryWidget::OnDumpMRAM() { - DumpArray(File::GetUserPath(F_RAMDUMP_IDX), Memory::m_pRAM, Memory::REALRAM_SIZE); + AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(AddressSpace::Type::Mem1); + DumpArray(File::GetUserPath(F_MEM1DUMP_IDX), accessors->begin(), + std::distance(accessors->begin(), accessors->end())); } void MemoryWidget::OnDumpExRAM() { - if (SConfig::GetInstance().bWii) - { - DumpArray(File::GetUserPath(F_ARAMDUMP_IDX), Memory::m_pEXRAM, Memory::EXRAM_SIZE); - } - else - { - DumpArray(File::GetUserPath(F_ARAMDUMP_IDX), DSP::GetARAMPtr(), DSP::ARAM_SIZE); - } + AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(AddressSpace::Type::Mem2); + DumpArray(File::GetUserPath(F_MEM2DUMP_IDX), accessors->begin(), + std::distance(accessors->begin(), accessors->end())); +} + +void MemoryWidget::OnDumpARAM() +{ + AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(AddressSpace::Type::Auxiliary); + DumpArray(File::GetUserPath(F_ARAMDUMP_IDX), accessors->begin(), + std::distance(accessors->begin(), accessors->end())); } void MemoryWidget::OnDumpFakeVMEM() { - DumpArray(File::GetUserPath(F_FAKEVMEMDUMP_IDX), Memory::m_pFakeVMEM, Memory::FAKEVMEM_SIZE); + AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(AddressSpace::Type::Fake); + DumpArray(File::GetUserPath(F_FAKEVMEMDUMP_IDX), accessors->begin(), + std::distance(accessors->begin(), accessors->end())); } std::vector MemoryWidget::GetValueData() const @@ -537,62 +607,24 @@ void MemoryWidget::FindValue(bool next) m_result_label->setText(tr("No Value Given")); return; } - - const u8* ram_ptr = nullptr; - std::size_t ram_size = 0; - u32 base_address = 0; - - if (m_type_u16->isChecked()) - { - ram_ptr = DSP::GetARAMPtr(); - ram_size = DSP::ARAM_SIZE; - base_address = 0x0c005000; - } - else if (Memory::m_pRAM) - { - ram_ptr = Memory::m_pRAM; - ram_size = Memory::REALRAM_SIZE; - base_address = 0x80000000; - } - else - { - m_result_label->setText(tr("Memory Not Ready")); - return; - } - u32 addr = 0; if (!m_search_address->text().isEmpty()) - addr = m_search_address->text().toUInt(nullptr, 16) + 1; - - if (addr >= base_address) - addr -= base_address; - - if (addr >= ram_size - search_for.size()) { - m_result_label->setText(tr("Address Out of Range")); - return; + // skip the quoted address so we don't potentially refind the last result + addr = m_search_address->text().toUInt(nullptr, 16) + (next ? 1 : -1); } - const u8* ptr; - const u8* end; + AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_memory_view->GetAddressSpace()); - if (next) - { - end = &ram_ptr[ram_size - search_for.size() + 1]; - ptr = std::search(&ram_ptr[addr], end, search_for.begin(), search_for.end()); - } - else - { - end = &ram_ptr[addr + search_for.size() - 1]; - ptr = std::find_end(ram_ptr, end, search_for.begin(), search_for.end()); - } + auto found_addr = + accessors->Search(addr, search_for.data(), static_cast(search_for.size()), next); - if (ptr != end) + if (found_addr.has_value()) { m_result_label->setText(tr("Match Found")); - u32 offset = static_cast(ptr - ram_ptr) + base_address; + u32 offset = *found_addr; m_search_address->setText(QStringLiteral("%1").arg(offset, 8, 16, QLatin1Char('0'))); diff --git a/Source/Core/DolphinQt/Debugger/MemoryWidget.h b/Source/Core/DolphinQt/Debugger/MemoryWidget.h index 5d4cb0b603..77c10b88e7 100644 --- a/Source/Core/DolphinQt/Debugger/MemoryWidget.h +++ b/Source/Core/DolphinQt/Debugger/MemoryWidget.h @@ -9,6 +9,7 @@ #include #include "Common/CommonTypes.h" +#include "Core/HW/AddressSpace.h" class MemoryViewWidget; class QCheckBox; @@ -38,6 +39,7 @@ private: void LoadSettings(); void SaveSettings(); + void OnAddressSpaceChanged(); void OnTypeChanged(); void OnBPLogChanged(); void OnBPTypeChanged(); @@ -51,6 +53,7 @@ private: void OnDumpMRAM(); void OnDumpExRAM(); + void OnDumpARAM(); void OnDumpFakeVMEM(); std::vector GetValueData() const; @@ -66,6 +69,7 @@ private: QPushButton* m_set_value; QPushButton* m_dump_mram; QPushButton* m_dump_exram; + QPushButton* m_dump_aram; QPushButton* m_dump_fake_vmem; // Search @@ -75,6 +79,11 @@ private: QRadioButton* m_find_hex; QLabel* m_result_label; + // Address Spaces + QRadioButton* m_address_space_physical; + QRadioButton* m_address_space_effective; + QRadioButton* m_address_space_auxiliary; + // Datatypes QRadioButton* m_type_u8; QRadioButton* m_type_u16;