Merge pull request #7980 from booto/memview-wtf
Debugger/Memory: Add support for address spaces
This commit is contained in:
commit
a5af11bef1
|
@ -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"
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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')));
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue