Debugger: Implement CtrlMemorySearch

This commit is contained in:
Ty Lamontagne 2021-11-01 22:49:13 -04:00 committed by refractionpcsx2
parent f5d0542f06
commit c863876dee
8 changed files with 659 additions and 3 deletions

View File

@ -962,6 +962,7 @@ set(pcsx2GuiSources
gui/Debugger/CtrlDisassemblyView.cpp
gui/Debugger/CtrlRegisterList.cpp
gui/Debugger/CtrlMemView.cpp
gui/Debugger/CtrlMemSearch.cpp
gui/Debugger/DebuggerLists.cpp
gui/Debugger/DisassemblyDialog.cpp
gui/Debugger/DebugEvents.cpp
@ -1021,6 +1022,7 @@ set(pcsx2GuiHeaders
gui/Debugger/BreakpointWindow.h
gui/Debugger/CtrlDisassemblyView.h
gui/Debugger/CtrlMemView.h
gui/Debugger/CtrlMemSearch.h
gui/Debugger/CtrlRegisterList.h
gui/Debugger/DebugEvents.h
gui/Debugger/DebuggerLists.h
@ -1230,7 +1232,8 @@ set(pcsx2UtilitiesSources
# Utilities headers
set(pcsx2UtilitiesHeaders
Utilities/AsciiFile.h)
Utilities/AsciiFile.h
Utilities/BitCast.h)
# Windows sources

27
pcsx2/Utilities/BitCast.h Normal file
View File

@ -0,0 +1,27 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
// An implementation of bit_cast until we get c++20
template<class T2, class T1>
T2 bit_cast(const T1& src)
{
static_assert(sizeof(T2) == sizeof(T1), "bit_cast: types must be equal size");
static_assert(std::is_pod<T1>::value, "bit_cast: source must be POD");
static_assert(std::is_pod<T2>::value, "bit_cast: destination must be POD");
T2 dst;
memcpy(&dst, &src, sizeof(T2));
return dst;
}

View File

@ -0,0 +1,501 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "CtrlMemSearch.h"
#include "DebugTools/Debug.h"
#include "gui/AppConfig.h"
#include "Utilities/BitCast.h"
#include "../Patch.h" // Required for SwapEndian :(
#include "BreakpointWindow.h"
#include "DebugEvents.h"
#include "DisassemblyDialog.h"
#include <wchar.h>
#include <wx/clipbrd.h>
wxDEFINE_EVENT(pxEvt_SearchFinished, wxCommandEvent);
wxBEGIN_EVENT_TABLE(CtrlMemSearch, wxWindow)
EVT_SET_FOCUS(CtrlMemSearch::focusEvent)
EVT_KILL_FOCUS(CtrlMemSearch::focusEvent)
wxEND_EVENT_TABLE()
enum MemoryViewMenuIdentifiers {
ID_MEMVIEW_GOTOINDISASM = 1,
ID_MEMVIEW_GOTOADDRESS,
ID_MEMVIEW_COPYADDRESS,
ID_MEMVIEW_FOLLOWADDRESS,
ID_MEMVIEW_DISPLAYVALUE_8,
ID_MEMVIEW_DISPLAYVALUE_16,
ID_MEMVIEW_DISPLAYVALUE_32,
ID_MEMVIEW_DISPLAYVALUE_64,
ID_MEMVIEW_DISPLAYVALUE_128,
ID_MEMVIEW_COPYVALUE_8,
ID_MEMVIEW_COPYVALUE_16,
ID_MEMVIEW_COPYVALUE_32,
ID_MEMVIEW_COPYVALUE_64,
ID_MEMVIEW_COPYVALUE_128,
ID_MEMVIEW_ALIGNWINDOW,
};
// Kind of lame, but it works.
const wxString wxStringTypes[] =
{
"Byte",
"2 Bytes",
"4 Bytes",
"8 Bytes",
"Float",
"Double",
"String"};
const wxArrayString SearchTypes = wxArrayString(7, wxStringTypes);
CtrlMemSearch::CtrlMemSearch(wxWindow* parent, DebugInterface* _cpu)
: wxWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
, cpu(_cpu)
{
m_SearchThread.reset(new SearchThread(this, _cpu));
m_SearchThread->SetName("Debugger Memory Search");
Bind(pxEvt_SearchFinished, &CtrlMemSearch::onSearchFinished, this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
txtSearch = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1));
btnSearch = new wxButton(this, wxID_ANY, L"Search", wxDefaultPosition);
Bind(wxEVT_BUTTON, &CtrlMemSearch::Search, this, btnSearch->GetId());
cmbSearchType = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1), 0, NULL, wxCB_READONLY);
cmbSearchType->Set(SearchTypes);
cmbSearchType->SetSelection(0);
Bind(wxEVT_COMBOBOX, &CtrlMemSearch::onSearchTypeChanged, this, cmbSearchType->GetId());
chkHexadecimal = new wxCheckBox(this, wxID_ANY, L"Hex");
chkHexadecimal->SetValue(true);
radBigEndian = new wxRadioButton(this, wxID_ANY, L"Big Endian", wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
radLittleEndian = new wxRadioButton(this, wxID_ANY, L"Little Endian");
radLittleEndian->SetValue(true);
txtMemoryStart = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1));
txtMemoryStart->SetValue(L"0x0");
txtMemoryEnd = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1));
if (_cpu->getCpuType() == BREAKPOINT_EE)
txtMemoryEnd->SetValue(L"0x200000");
else
txtMemoryEnd->SetValue(L"0x20000");
lblSearchHits = new wxStaticText(this, wxID_ANY, L"Hits: 0");
lblSearchHitSelected = new wxStaticText(this, wxID_ANY, L"Selected: 0");
btnNext = new wxButton(this, wxID_ANY, L"Next", wxDefaultPosition);
btnNext->Enable(false);
Bind(wxEVT_BUTTON, &CtrlMemSearch::onSearchNext, this, btnNext->GetId());
btnPrev = new wxButton(this, wxID_ANY, L"Prev", wxDefaultPosition);
btnPrev->Enable(false);
Bind(wxEVT_BUTTON, &CtrlMemSearch::onSearchPrev, this, btnPrev->GetId());
// GUI design
// Search box and search button
wxBoxSizer* searchSizer = new wxBoxSizer(wxHORIZONTAL);
searchSizer->Add(txtSearch, 0);
searchSizer->Add(btnSearch, 0, wxLEFT, 5);
mainSizer->Add(searchSizer, 0, wxUP | wxLEFT | wxRIGHT, 5);
// Search type combo box and hexadecimal checkbox
wxBoxSizer* searchTypeSizer = new wxBoxSizer(wxHORIZONTAL);
searchTypeSizer->Add(cmbSearchType, 0, wxEXPAND);
searchTypeSizer->Add(chkHexadecimal, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 5);
mainSizer->Add(searchTypeSizer, 0, wxUP | wxLEFT | wxRIGHT, 5);
// Big / Little Endianess radio button
wxBoxSizer* endianessSizer = new wxBoxSizer(wxHORIZONTAL);
endianessSizer->Add(radLittleEndian, 0, wxALIGN_CENTER_VERTICAL);
endianessSizer->Add(radBigEndian, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 5);
mainSizer->Add(endianessSizer, 0, wxUP | wxLEFT | wxRIGHT, 5);
// Address range type text boxes
wxFlexGridSizer* addrRangeSizer = new wxFlexGridSizer(2, 2, 5);
addrRangeSizer->Add(new wxStaticText(this, wxID_ANY, L"Start Address"), 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
addrRangeSizer->Add(txtMemoryStart, 0, wxEXPAND);
addrRangeSizer->Add(new wxStaticText(this, wxID_ANY, L"End Address"), 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
addrRangeSizer->Add(txtMemoryEnd, 0, wxEXPAND);
mainSizer->Add(addrRangeSizer, 0, wxUP | wxLEFT | wxRIGHT, 5);
wxBoxSizer* resultSizer = new wxBoxSizer(wxHORIZONTAL);
resultSizer->Add(lblSearchHits, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
resultSizer->Add(lblSearchHitSelected, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
resultSizer->Add(btnPrev, 0, wxRIGHT, 5);
resultSizer->Add(btnNext, 0, wxRIGHT, 5);
mainSizer->Add(resultSizer, 0, wxUP | wxLEFT | wxRIGHT, 5);
this->SetSizer(mainSizer);
SetDoubleBuffered(true);
}
void CtrlMemSearch::postEvent(wxEventType type, wxString text)
{
wxCommandEvent event(type, GetId());
event.SetEventObject(this);
event.SetClientData(cpu);
event.SetString(text);
wxPostEvent(this, event);
}
void CtrlMemSearch::postEvent(wxEventType type, int value)
{
wxCommandEvent event(type, GetId());
event.SetEventObject(this);
event.SetClientData(cpu);
event.SetInt(value);
wxPostEvent(this, event);
}
// Set up the thread with out UI params
// Then execute the thread
void CtrlMemSearch::Search(wxCommandEvent& evt)
{
if (!m_searchResultsMutex.try_lock())
{
// Uh, if it doesn't lock then means we somehow tried to search while
// the search was still running.
return;
}
m_searchResults.clear();
m_searchResultsMutex.unlock();
lblSearchHits->SetLabelText(L"Hits: 0");
btnNext->Enable(false);
btnPrev->Enable(false);
u64 startAddress = 0;
u64 endAddress = 0;
if (!txtMemoryStart->GetValue().ToULong(&startAddress, 16))
{
wxMessageBox(L"Invalid start address", L"Error", wxOK | wxICON_ERROR);
return;
}
if (!txtMemoryEnd->GetValue().ToULong(&endAddress, 16))
{
wxMessageBox(L"Invalid end address", L"Error", wxOK | wxICON_ERROR);
return;
}
m_SearchThread->m_start = startAddress;
m_SearchThread->m_end = endAddress;
m_SearchThread->m_type = static_cast<SEARCHTYPE>(cmbSearchType->GetSelection());
// Prepare the search value for the thread
wxString searchString = txtSearch->GetValue();
if (cmbSearchType->GetSelection() == SEARCHTYPE::STRING)
{
m_SearchThread->m_value_string = searchString;
}
else if (chkHexadecimal->IsChecked())
{
u64 searchValue = 0;
if (!searchString.ToULong(&searchValue, 16))
{
wxMessageBox(L"Invalid hexadecimal value", L"Error", wxOK | wxICON_ERROR);
return;
}
m_SearchThread->m_value = radBigEndian->GetValue() ? SwapEndian(searchValue, SEARCHTYPEBITS[m_SearchThread->m_type]) : searchValue;
}
else
{
// According to my logic, we end up here if we aren't searching for a string
// Aren't in hexadecimal mode.
// Let's check if we're searching for a signed or unsigned value
// The thread will handle the interpretation for us.
m_SearchThread->m_signed = txtSearch->GetValue().First('-');
// Now we can convert the string to a number
if (cmbSearchType->GetSelection() == SEARCHTYPE::FLOAT || cmbSearchType->GetSelection() == SEARCHTYPE::DOUBLE)
{
// We're searching for a float or double, so we need to convert the string to a double
double searchValue = 0;
if (!searchString.ToDouble(&searchValue))
{
wxMessageBox(L"Invalid floating point value", L"Error", wxOK | wxICON_ERROR);
return;
}
if (m_SearchThread->m_type == SEARCHTYPE::FLOAT)
{
const u32 u32SearchValue = bit_cast<u32, float>(searchValue);
m_SearchThread->m_value = u32SearchValue;
}
else
{
const u64 u64SearchValue = bit_cast<u64, double>(searchValue);
m_SearchThread->m_value = u64SearchValue;
}
}
else
{
// We're searching for either a BYTE, WORD, DWORD or QWORD
u64 searchValue = 0;
if (!searchString.ToULong(&searchValue, 10))
{
wxMessageBox(L"Invalid decimal search value", L"Error", wxOK | wxICON_ERROR);
return;
}
// We don't need to bitcast here, because the thread already has the correct value type of u64
m_SearchThread->m_value = radBigEndian->GetValue() ? SwapEndian(searchValue, SEARCHTYPEBITS[m_SearchThread->m_type]) : searchValue;
}
}
m_SearchThread->Start();
};
void CtrlMemSearch::onSearchFinished(wxCommandEvent& evt)
{
// We're done searching, so let's update the UI
m_searchResultsMutex.lock();
lblSearchHits->SetLabelText(wxString::Format(L"Hits: %d", m_searchResults.size()));
// Enable the buttons only if we have results
// -1 indicates we haven't jumped to a result yet
m_searchIter = -1;
lblSearchHitSelected->SetLabelText(L"Cur: 0");
btnNext->Enable(m_searchResults.size() > 0);
btnPrev->Enable(m_searchResults.size() > 0);
m_searchResultsMutex.unlock();
this->Layout(); // This makes sure the search hit count label doesn't get cut off
}
void CtrlMemSearch::onSearchNext(wxCommandEvent& evt)
{
m_searchIter++;
if (m_searchIter == m_searchResults.size())
{
m_searchIter = 0;
}
lblSearchHitSelected->SetLabelText(wxString::Format(L"Cur: %d",m_searchIter + 1));
this->Layout();
postEvent(debEVT_GOTOINMEMORYVIEW, m_searchResults[m_searchIter]);
}
void CtrlMemSearch::onSearchPrev(wxCommandEvent& evt)
{
m_searchIter--;
// Unsigned underflow
if (m_searchIter > m_searchResults.size())
{
m_searchIter = m_searchResults.size() - 1;
}
lblSearchHitSelected->SetLabelText(wxString::Format(L"Cur: %d",m_searchIter + 1));
this->Layout();
postEvent(debEVT_GOTOINMEMORYVIEW, m_searchResults[m_searchIter]);
}
void CtrlMemSearch::onSearchTypeChanged(wxCommandEvent& evt)
{
// Update the search value text box to match the search type
switch (cmbSearchType->GetSelection())
{
case SEARCHTYPE::BYTE:
case SEARCHTYPE::WORD:
case SEARCHTYPE::DWORD:
case SEARCHTYPE::QWORD:
radBigEndian->Enable(true);
radLittleEndian->Enable(true);
chkHexadecimal->Enable(false);
break;
case SEARCHTYPE::FLOAT:
case SEARCHTYPE::DOUBLE:
radBigEndian->Enable(false);
radLittleEndian->Enable(false);
chkHexadecimal->Enable(false);
chkHexadecimal->SetValue(false);
break;
case SEARCHTYPE::STRING:
radBigEndian->Enable(true);
radLittleEndian->Enable(true);
chkHexadecimal->Enable(false);
chkHexadecimal->SetValue(false);
break;
}
}
// -- SearchThread
// There might be a prettier way to read from the vtlb
template <class T>
void memSearch(std::vector<u32>& out, u32 start, u32 end, T value, DebugInterface* cpu)
{
for (u32 addr = start; addr < end; addr += sizeof(T))
{
if (!cpu->isValidAddress(addr))
{
continue;
}
T val = 0;
if (sizeof(T) == 1)
{
val = cpu->read8(addr);
}
else if (sizeof(T) == 2)
{
val = cpu->read16(addr);
}
else if (sizeof(T) == 4)
{
if (std::is_same<T, float>::value)
{
float fpVal = bit_cast<float, u32>(value);
// Love floating point
if (bit_cast<float, u32>(cpu->read32(addr)) < fpVal + 0.001f && bit_cast<float, u32>(cpu->read32(addr)) > fpVal - 0.001f)
{
out.push_back(addr);
}
continue;
}
val = cpu->read32(addr);
}
else if (sizeof(T) == 8)
{
if (std::is_same<T, double>::value)
{
double dbVal = bit_cast<double, u64>(value);
if (bit_cast<double, u64>(cpu->read64(addr)) < dbVal + 0.001 && bit_cast<double, u64>(cpu->read64(addr)) > dbVal - 0.001)
{
out.push_back(addr);
}
continue;
}
val = cpu->read64(addr);
}
if (val == value)
{
out.push_back(addr);
}
}
}
void SearchThread::ExecuteTaskInThread()
{
// First, make sure we can lock the mutex
if (!m_parent->m_searchResultsMutex.try_lock())
{
// We couldn't lock the mutex, this thread either didn't unlock, or we've somehow raced
// with the main UI thread ?
assert("Failed to lock our memory search mutex?");
return;
}
switch (m_type)
{
case SEARCHTYPE::BYTE:
if (m_signed)
{
memSearch<s8>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
}
else
{
memSearch<u8>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
}
break;
case SEARCHTYPE::WORD:
if (m_signed)
{
memSearch<s16>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
}
else
{
memSearch<u16>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
}
break;
case SEARCHTYPE::DWORD:
if (m_signed)
{
memSearch<s32>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
}
else
{
memSearch<u32>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
}
break;
case SEARCHTYPE::QWORD:
if (m_signed)
{
memSearch<s64>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
}
else
{
memSearch<u64>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
}
break;
case SEARCHTYPE::FLOAT:
memSearch<float>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
break;
case SEARCHTYPE::DOUBLE:
memSearch<double>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
break;
case SEARCHTYPE::STRING:
for (u32 addr = m_start; addr < m_end; addr++)
{
// There is no easy way to do this with the vtable.
bool match = true;
for (u32 stringIndex = 0; stringIndex < m_value_string.size(); stringIndex++)
{
if ((char)m_cpu->read8(addr + stringIndex) != m_value_string[stringIndex])
{
match = false;
break;
}
}
if (match)
{
m_parent->m_searchResults.push_back(addr);
addr += m_value_string.size() - 1;
}
}
break;
}
m_parent->m_searchResultsMutex.unlock();
wxCommandEvent done(pxEvt_SearchFinished);
m_parent->GetEventHandler()->AddPendingEvent(done);
}

View File

@ -0,0 +1,102 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <wx/wx.h>
#include "common/PersistentThread.h"
#include "DebugTools/DebugInterface.h"
#include "DebugTools/DisassemblyManager.h"
enum SEARCHTYPE
{
BYTE,
WORD,
DWORD,
QWORD,
FLOAT,
DOUBLE,
STRING,
};
class CtrlMemSearch;
class SearchThread : public Threading::pxThread
{
CtrlMemSearch* m_parent;
DebugInterface* m_cpu;
public:
virtual ~SearchThread()
{
try
{
pxThread::Cancel();
}
DESTRUCTOR_CATCHALL
}
SearchThread(CtrlMemSearch* parent, DebugInterface* cpu) { m_parent = parent; m_cpu = cpu; };
SEARCHTYPE m_type;
// Only guaranteed to be valid if m_type == STRING
std::string m_value_string;
u64 m_value;
bool m_signed;
u64 m_start,m_end;
protected:
void ExecuteTaskInThread();
};
class CtrlMemSearch : public wxWindow
{
public:
CtrlMemSearch(wxWindow* parent, DebugInterface* _cpu);
std::vector<u32> m_searchResults;
std::mutex m_searchResultsMutex;
wxDECLARE_EVENT_TABLE();
private:
void setRowSize(int bytesInRow);
void postEvent(wxEventType type, wxString text);
void postEvent(wxEventType type, int value);
void onPopupClick(wxCommandEvent& evt);
void focusEvent(wxFocusEvent& evt) { Refresh(); };
void onSearchFinished(wxCommandEvent& evt);
void onSearchNext(wxCommandEvent& evt);
void onSearchPrev(wxCommandEvent& evt);
void onSearchTypeChanged(wxCommandEvent& evt);
void Search(wxCommandEvent& evt);
const u8 SEARCHTYPEBITS[8] = {8,16,32,64,32,64,0};
DebugInterface* cpu;
wxFont font, underlineFont;
wxTextCtrl* txtSearch;
wxButton* btnSearch;
wxComboBox* cmbSearchType;
wxCheckBox* chkHexadecimal;
wxTextCtrl* txtMemoryStart;
wxTextCtrl* txtMemoryEnd;
wxRadioButton* radBigEndian;
wxRadioButton* radLittleEndian;
wxStaticText* lblSearchHits;
wxStaticText* lblSearchHitSelected;
wxButton* btnNext;
wxButton* btnPrev;
u64 m_searchIter = -1;
std::unique_ptr<SearchThread> m_SearchThread;
wxMenu menu;
};

View File

@ -90,7 +90,10 @@ CpuTabPage::CpuTabPage(wxWindow* parent, DebugInterface* _cpu)
disassembly = new CtrlDisassemblyView(this, cpu);
registerList = new CtrlRegisterList(leftTabs, cpu);
functionList = new wxListBox(leftTabs, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxBORDER_NONE | wxLB_SORT);
memory = new CtrlMemView(bottomTabs, cpu);
wxPanel* memoryPanel = new wxPanel(bottomTabs);
memory = new CtrlMemView(memoryPanel, cpu);
memorySearch = new CtrlMemSearch(memoryPanel, cpu);
// create register list and disassembly section
wxBoxSizer* middleSizer = new wxBoxSizer(wxHORIZONTAL);
@ -110,8 +113,17 @@ CpuTabPage::CpuTabPage(wxWindow* parent, DebugInterface* _cpu)
middleSizer->Add(disassembly, 2, wxEXPAND);
mainSizer->Add(middleSizer, 3, wxEXPAND | wxBOTTOM, 3);
wxBoxSizer *memorySizer = new wxBoxSizer(wxHORIZONTAL);
memorySizer->Add(memory, 1, wxEXPAND);
// Unfortuneately hacky, probably cause I'm bad at wxWidgets
// A max width of 360 ensures that the memory view will never be blocked by the memory search
memorySearch->SetMaxSize(wxSize(360, -1));
memorySizer->Add(memorySearch, 1, wxEXPAND);
memoryPanel->SetSizer(memorySizer);
// create bottom section
bottomTabs->AddPage(memory, L"Memory");
bottomTabs->AddPage(memoryPanel, L"Memory");
breakpointList = new BreakpointList(bottomTabs, cpu, disassembly);
bottomTabs->AddPage(breakpointList, L"Breakpoints");

View File

@ -21,6 +21,7 @@
#include "CtrlDisassemblyView.h"
#include "CtrlRegisterList.h"
#include "CtrlMemView.h"
#include "CtrlMemSearch.h"
#include "DebugEvents.h"
#include "DebuggerLists.h"
#include "gui/MSWstuff.h"
@ -49,6 +50,7 @@ public:
CtrlDisassemblyView* getDisassembly() { return disassembly; };
CtrlRegisterList* getRegisterList() { return registerList; };
CtrlMemView* getMemoryView() { return memory; };
CtrlMemSearch* getMemorySearch() { return memorySearch; };
wxNotebook* getBottomTabs() { return bottomTabs; };
void update();
void showMemoryView() { setBottomTabPage(memory); };
@ -68,6 +70,7 @@ private:
CtrlRegisterList* registerList;
wxListBox* functionList;
CtrlMemView* memory;
CtrlMemSearch* memorySearch;
wxNotebook* bottomTabs;
wxNotebook* leftTabs;
BreakpointList* breakpointList;

View File

@ -310,6 +310,7 @@
<ClCompile Include="gui\Debugger\BreakpointWindow.cpp" />
<ClCompile Include="gui\Debugger\CtrlDisassemblyView.cpp" />
<ClCompile Include="gui\Debugger\CtrlMemView.cpp" />
<ClCompile Include="gui\Debugger\CtrlMemSearch.cpp" />
<ClCompile Include="gui\Debugger\CtrlRegisterList.cpp" />
<ClCompile Include="gui\Debugger\DebugEvents.cpp" />
<ClCompile Include="gui\Debugger\DebuggerLists.cpp" />
@ -753,6 +754,7 @@
<ClInclude Include="gui\Debugger\BreakpointWindow.h" />
<ClInclude Include="gui\Debugger\CtrlDisassemblyView.h" />
<ClInclude Include="gui\Debugger\CtrlMemView.h" />
<ClInclude Include="gui\Debugger\CtrlMemSearch.h" />
<ClInclude Include="gui\Debugger\CtrlRegisterList.h" />
<ClInclude Include="gui\Debugger\DebugEvents.h" />
<ClInclude Include="gui\Debugger\DebuggerLists.h" />

View File

@ -941,6 +941,9 @@
<ClCompile Include="gui\Debugger\CtrlMemView.cpp">
<Filter>AppHost\Debugger</Filter>
</ClCompile>
<ClCompile Include="gui\Debugger\CtrlMemSearch.cpp">
<Filter>AppHost\Debugger</Filter>
</ClCompile>
<ClCompile Include="DebugTools\ExpressionParser.cpp">
<Filter>System\Ps2\Debug</Filter>
</ClCompile>
@ -2037,6 +2040,9 @@
<ClInclude Include="gui\Debugger\CtrlMemView.h">
<Filter>AppHost\Debugger</Filter>
</ClInclude>
<ClInclude Include="gui\Debugger\CtrlMemSearch.h">
<Filter>AppHost\Debugger</Filter>
</ClInclude>
<ClInclude Include="DebugTools\ExpressionParser.h">
<Filter>System\Ps2\Debug</Filter>
</ClInclude>