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;