Merge pull request #7980 from booto/memview-wtf

Debugger/Memory: Add support for address spaces
This commit is contained in:
Connor McLaughlin 2019-05-03 11:49:02 +10:00 committed by GitHub
commit a5af11bef1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 668 additions and 105 deletions

View File

@ -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"

View File

@ -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;

View File

@ -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,

View File

@ -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

View File

@ -109,6 +109,7 @@
<ClCompile Include="HLE\HLE_OS.cpp" />
<ClCompile Include="HLE\HLE_VarArgs.cpp" />
<ClCompile Include="HotkeyManager.cpp" />
<ClCompile Include="HW\AddressSpace.cpp" />
<ClCompile Include="HW\AudioInterface.cpp" />
<ClCompile Include="HW\CPU.cpp" />
<ClCompile Include="HW\DSP.cpp" />
@ -373,6 +374,7 @@
<ClInclude Include="HLE\HLE_VarArgs.h" />
<ClInclude Include="Host.h" />
<ClInclude Include="HotkeyManager.h" />
<ClInclude Include="HW\AddressSpace.h" />
<ClInclude Include="HW\AudioInterface.h" />
<ClInclude Include="HW\CPU.h" />
<ClInclude Include="HW\DSP.h" />

View File

@ -919,6 +919,9 @@
<ClCompile Include="HW\WiimoteCommon\DataReport.cpp">
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Common</Filter>
</ClCompile>
<ClCompile Include="HW\AddressSpace.cpp">
<Filter>HW %28Flipper/Hollywood%29</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="BootManager.h" />
@ -1635,6 +1638,9 @@
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Common</Filter>
</ClInclude>
<ClInclude Include="PowerPC\ConditionRegister.h" />
<ClInclude Include="HW\AddressSpace.h">
<Filter>HW %28Flipper/Hollywood%29</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />

View File

@ -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 <algorithm>
#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<float>(ReadU32(address));
}
Accessors::iterator Accessors::begin() const
{
return nullptr;
}
Accessors::iterator Accessors::end() const
{
return nullptr;
}
std::optional<u32> 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<u32> 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<u32>(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<u32> 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<u32>(result - begin());
}
private:
static u32 GetSize() { return 16 * 1024 * 1024; }
};
struct AccessorMapping
{
u32 base;
Accessors* accessors;
};
struct CompositeAddressSpaceAccessors : Accessors
{
CompositeAddressSpaceAccessors(std::initializer_list<AccessorMapping> 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<u32> 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<u32>(*result + mapping.base);
}
}
return std::nullopt;
}
private:
std::vector<AccessorMapping> m_accessor_mappings;
std::vector<AccessorMapping>::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<AccessorMapping>::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<u32> 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<u32>(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

View File

@ -0,0 +1,50 @@
// Copyright 2011 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <optional>
#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<u32> Search(u32 haystack_offset, u8* needle_start, u32 needle_size,
bool forward) const;
virtual ~Accessors();
};
Accessors* GetAccessors(Type address_space);
} // namespace AddressSpace

View File

@ -1302,4 +1302,14 @@ static TranslateAddressResult TranslateAddress(u32 address)
return TranslatePageAddress(address, flag);
}
} // namespace
std::optional<u32> GetTranslatedAddress(u32 address)
{
auto result = TranslateAddress<XCheckTLBFlag::NoException>(address);
if (!result.Success())
{
return std::nullopt;
}
return std::optional<u32>(result.address);
}
} // namespace PowerPC

View File

@ -6,6 +6,7 @@
#include <array>
#include <cstddef>
#include <optional>
#include <string>
#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<u32> GetTranslatedAddress(u32 address);
} // namespace PowerPC

View File

@ -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<float>(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();

View File

@ -7,6 +7,7 @@
#include <QTableWidget>
#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;

View File

@ -4,6 +4,7 @@
#include "DolphinQt/Debugger/MemoryWidget.h"
#include <optional>
#include <string>
#include <QCheckBox>
@ -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<u8>(c), addr++);
accessors->WriteU8(static_cast<u8>(c), addr++);
}
else
{
@ -431,18 +495,18 @@ void MemoryWidget::OnSetValue()
if (value == static_cast<u8>(value))
{
PowerPC::HostWrite_U8(static_cast<u8>(value), addr);
accessors->WriteU8(static_cast<u8>(value), addr);
}
else if (value == static_cast<u16>(value))
{
PowerPC::HostWrite_U16(static_cast<u16>(value), addr);
accessors->WriteU16(static_cast<u16>(value), addr);
}
else if (value == static_cast<u32>(value))
{
PowerPC::HostWrite_U32(static_cast<u32>(value), addr);
accessors->WriteU32(static_cast<u32>(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<u8> 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<u32>(search_for.size()), next);
if (ptr != end)
if (found_addr.has_value())
{
m_result_label->setText(tr("Match Found"));
u32 offset = static_cast<u32>(ptr - ram_ptr) + base_address;
u32 offset = *found_addr;
m_search_address->setText(QStringLiteral("%1").arg(offset, 8, 16, QLatin1Char('0')));

View File

@ -9,6 +9,7 @@
#include <QDockWidget>
#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<u8> 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;