mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-22 05:45:38 +00:00
Qt: Add edit function to debugger memory view
This commit is contained in:
parent
efb0952c23
commit
ce3d110ba1
|
@ -805,26 +805,31 @@ std::optional<Bus::MemoryRegion> Bus::GetMemoryRegionForAddress(PhysicalMemoryAd
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
static constexpr std::array<std::pair<PhysicalMemoryAddress, PhysicalMemoryAddress>,
|
||||
static constexpr std::array<std::tuple<PhysicalMemoryAddress, PhysicalMemoryAddress, bool>,
|
||||
static_cast<u32>(Bus::MemoryRegion::Count)>
|
||||
s_code_region_ranges = {{
|
||||
{0, Bus::RAM_2MB_SIZE},
|
||||
{Bus::RAM_2MB_SIZE, Bus::RAM_2MB_SIZE * 2},
|
||||
{Bus::RAM_2MB_SIZE * 2, Bus::RAM_2MB_SIZE * 3},
|
||||
{Bus::RAM_2MB_SIZE * 3, Bus::RAM_MIRROR_END},
|
||||
{Bus::EXP1_BASE, Bus::EXP1_BASE + Bus::EXP1_SIZE},
|
||||
{CPU::SCRATCHPAD_ADDR, CPU::SCRATCHPAD_ADDR + CPU::SCRATCHPAD_SIZE},
|
||||
{Bus::BIOS_BASE, Bus::BIOS_BASE + Bus::BIOS_SIZE},
|
||||
{0, Bus::RAM_2MB_SIZE, true},
|
||||
{Bus::RAM_2MB_SIZE, Bus::RAM_2MB_SIZE * 2, true},
|
||||
{Bus::RAM_2MB_SIZE * 2, Bus::RAM_2MB_SIZE * 3, true},
|
||||
{Bus::RAM_2MB_SIZE * 3, Bus::RAM_MIRROR_END, true},
|
||||
{Bus::EXP1_BASE, Bus::EXP1_BASE + Bus::EXP1_SIZE, false},
|
||||
{CPU::SCRATCHPAD_ADDR, CPU::SCRATCHPAD_ADDR + CPU::SCRATCHPAD_SIZE, true},
|
||||
{Bus::BIOS_BASE, Bus::BIOS_BASE + Bus::BIOS_SIZE, false},
|
||||
}};
|
||||
|
||||
PhysicalMemoryAddress Bus::GetMemoryRegionStart(MemoryRegion region)
|
||||
{
|
||||
return s_code_region_ranges[static_cast<u32>(region)].first;
|
||||
return std::get<0>(s_code_region_ranges[static_cast<u32>(region)]);
|
||||
}
|
||||
|
||||
PhysicalMemoryAddress Bus::GetMemoryRegionEnd(MemoryRegion region)
|
||||
{
|
||||
return s_code_region_ranges[static_cast<u32>(region)].second;
|
||||
return std::get<1>(s_code_region_ranges[static_cast<u32>(region)]);
|
||||
}
|
||||
|
||||
bool Bus::IsMemoryRegionWritable(MemoryRegion region)
|
||||
{
|
||||
return std::get<2>(s_code_region_ranges[static_cast<u32>(region)]);
|
||||
}
|
||||
|
||||
u8* Bus::GetMemoryRegionPointer(MemoryRegion region)
|
||||
|
|
|
@ -221,6 +221,7 @@ enum class MemoryRegion
|
|||
std::optional<MemoryRegion> GetMemoryRegionForAddress(PhysicalMemoryAddress address);
|
||||
PhysicalMemoryAddress GetMemoryRegionStart(MemoryRegion region);
|
||||
PhysicalMemoryAddress GetMemoryRegionEnd(MemoryRegion region);
|
||||
bool IsMemoryRegionWritable(MemoryRegion region);
|
||||
u8* GetMemoryRegionPointer(MemoryRegion region);
|
||||
std::optional<PhysicalMemoryAddress> SearchMemory(PhysicalMemoryAddress start_address, const u8* pattern,
|
||||
const u8* mask, u32 pattern_length);
|
||||
|
|
|
@ -547,7 +547,8 @@ void DebuggerWindow::setMemoryViewRegion(Bus::MemoryRegion region)
|
|||
|
||||
const PhysicalMemoryAddress start = Bus::GetMemoryRegionStart(region);
|
||||
const PhysicalMemoryAddress end = Bus::GetMemoryRegionEnd(region);
|
||||
m_ui.memoryView->setData(start, Bus::GetMemoryRegionPointer(region), end - start);
|
||||
m_ui.memoryView->setData(start, Bus::GetMemoryRegionPointer(region), end - start,
|
||||
Bus::IsMemoryRegionWritable(region));
|
||||
|
||||
#define SET_REGION_RADIO_BUTTON(name, rb_region) \
|
||||
do \
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
#include "memoryviewwidget.h"
|
||||
|
||||
#include <QtGui/QKeyEvent>
|
||||
#include <QtGui/QMouseEvent>
|
||||
#include <QtGui/QPainter>
|
||||
#include <QtWidgets/QScrollBar>
|
||||
#include <cstring>
|
||||
|
||||
MemoryViewWidget::MemoryViewWidget(QWidget* parent /* = nullptr */, size_t address_offset /* = 0 */,
|
||||
const void* data_ptr /* = nullptr */, size_t data_size /* = 0 */)
|
||||
void* data_ptr /* = nullptr */, size_t data_size /* = 0 */,
|
||||
bool data_editable /* = false */)
|
||||
: QAbstractScrollArea(parent)
|
||||
{
|
||||
m_bytes_per_line = 16;
|
||||
|
@ -15,7 +19,7 @@ MemoryViewWidget::MemoryViewWidget(QWidget* parent /* = nullptr */, size_t addre
|
|||
connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, &MemoryViewWidget::adjustContent);
|
||||
|
||||
if (data_ptr)
|
||||
setData(address_offset, data_ptr, data_size);
|
||||
setData(address_offset, data_ptr, data_size, data_editable);
|
||||
}
|
||||
|
||||
MemoryViewWidget::~MemoryViewWidget() = default;
|
||||
|
@ -42,11 +46,13 @@ void MemoryViewWidget::updateMetrics()
|
|||
m_char_height = fm.height();
|
||||
}
|
||||
|
||||
void MemoryViewWidget::setData(size_t address_offset, const void* data_ptr, size_t data_size)
|
||||
void MemoryViewWidget::setData(size_t address_offset, void* data_ptr, size_t data_size, bool data_editable)
|
||||
{
|
||||
m_data = data_ptr;
|
||||
m_data_size = data_size;
|
||||
m_data_editable = data_editable;
|
||||
m_address_offset = address_offset;
|
||||
m_selected_address = INVALID_SELECTED_ADDRESS;
|
||||
adjustContent();
|
||||
}
|
||||
|
||||
|
@ -84,18 +90,92 @@ void MemoryViewWidget::setFont(const QFont& font)
|
|||
updateMetrics();
|
||||
}
|
||||
|
||||
void MemoryViewWidget::resizeEvent(QResizeEvent*)
|
||||
void MemoryViewWidget::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
adjustContent();
|
||||
}
|
||||
|
||||
void MemoryViewWidget::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
if ((event->buttons() & Qt::LeftButton) != 0)
|
||||
updateSelectedByte(event->pos());
|
||||
|
||||
QAbstractScrollArea::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void MemoryViewWidget::mouseMoveEvent(QMouseEvent* event)
|
||||
{
|
||||
if ((event->buttons() & Qt::LeftButton) != 0)
|
||||
updateSelectedByte(event->pos());
|
||||
|
||||
QAbstractScrollArea::mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
void MemoryViewWidget::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
if (m_selected_address < m_data_size && m_data_editable)
|
||||
{
|
||||
const int key = event->key();
|
||||
if (key == Qt::Key_Backspace)
|
||||
{
|
||||
if (m_selected_address > 0)
|
||||
{
|
||||
m_selected_address--;
|
||||
m_editing_nibble = -1;
|
||||
viewport()->update();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const QString key_text = event->text();
|
||||
if (key_text.length() == 1)
|
||||
{
|
||||
const char ch = key_text[0].toLatin1();
|
||||
if (m_selection_was_ascii)
|
||||
{
|
||||
std::memcpy(static_cast<unsigned char*>(m_data) + m_selected_address, &ch, sizeof(unsigned char));
|
||||
m_selected_address = std::min(m_selected_address + 1, m_data_size - 1);
|
||||
viewport()->update();
|
||||
}
|
||||
else
|
||||
{
|
||||
int nibble = -1;
|
||||
if (ch >= 'a' && ch <= 'f')
|
||||
nibble = ch - 'a' + 0xa;
|
||||
else if (ch >= 'A' && ch <= 'F')
|
||||
nibble = ch - 'A' + 0xa;
|
||||
else if (ch >= '0' && ch <= '9')
|
||||
nibble = ch - '0';
|
||||
if (nibble >= 0)
|
||||
{
|
||||
m_editing_nibble++;
|
||||
|
||||
unsigned char* pdata = static_cast<unsigned char*>(m_data) + m_selected_address;
|
||||
*pdata = (*pdata & ~(0xf0 >> (m_editing_nibble * 4))) | (nibble << ((1 - m_editing_nibble) * 4));
|
||||
|
||||
if (m_editing_nibble == 1)
|
||||
{
|
||||
m_editing_nibble = -1;
|
||||
m_selected_address = std::min(m_selected_address + 1, m_data_size - 1);
|
||||
}
|
||||
|
||||
viewport()->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QAbstractScrollArea::keyPressEvent(event);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static bool RangesOverlap(T x1, T x2, T y1, T y2)
|
||||
{
|
||||
return (x2 >= y1 && x1 < y2);
|
||||
}
|
||||
|
||||
void MemoryViewWidget::paintEvent(QPaintEvent*)
|
||||
void MemoryViewWidget::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
QPainter painter(viewport());
|
||||
painter.setFont(font());
|
||||
|
@ -103,12 +183,15 @@ void MemoryViewWidget::paintEvent(QPaintEvent*)
|
|||
return;
|
||||
|
||||
const QColor highlight_color(100, 100, 0);
|
||||
const QColor selected_color = viewport()->palette().color(QPalette::Highlight);
|
||||
const QColor text_color = viewport()->palette().color(QPalette::WindowText);
|
||||
const QColor edited_color(255, 0, 0);
|
||||
const int offsetX = horizontalScrollBar()->value();
|
||||
|
||||
int y = m_char_height;
|
||||
QString address;
|
||||
|
||||
painter.setPen(viewport()->palette().color(QPalette::WindowText));
|
||||
painter.setPen(text_color);
|
||||
|
||||
y += m_char_height;
|
||||
|
||||
|
@ -162,10 +245,25 @@ void MemoryViewWidget::paintEvent(QPaintEvent*)
|
|||
{
|
||||
unsigned char value;
|
||||
std::memcpy(&value, static_cast<const unsigned char*>(m_data) + offset, sizeof(value));
|
||||
if (offset >= m_highlight_start && offset < m_highlight_end)
|
||||
painter.fillRect(x - m_char_width, y - m_char_height + 3, HEX_CHAR_WIDTH, m_char_height, highlight_color);
|
||||
if (m_selected_address == offset)
|
||||
painter.fillRect(x - m_char_width, y - m_char_height + 4, HEX_CHAR_WIDTH, m_char_height, selected_color);
|
||||
else if (offset >= m_highlight_start && offset < m_highlight_end)
|
||||
painter.fillRect(x - m_char_width, y - m_char_height + 4, HEX_CHAR_WIDTH, m_char_height, highlight_color);
|
||||
|
||||
if (m_selected_address != offset || m_editing_nibble != 0 || m_selection_was_ascii) [[likely]]
|
||||
{
|
||||
painter.drawText(x, y, QString::asprintf("%02X", value));
|
||||
}
|
||||
else
|
||||
{
|
||||
const QString high = QString::asprintf("%X", value >> 4);
|
||||
const QRect low_rc = painter.boundingRect(x, y, HEX_CHAR_WIDTH, m_char_height, 0, high);
|
||||
painter.setPen(edited_color);
|
||||
painter.drawText(x, y, high);
|
||||
painter.setPen(text_color);
|
||||
painter.drawText(x + low_rc.width(), y, QString::asprintf("%X", (value & 0xF)));
|
||||
}
|
||||
|
||||
x += HEX_CHAR_WIDTH;
|
||||
}
|
||||
y += m_char_height;
|
||||
|
@ -195,8 +293,10 @@ void MemoryViewWidget::paintEvent(QPaintEvent*)
|
|||
{
|
||||
unsigned char value;
|
||||
std::memcpy(&value, static_cast<const unsigned char*>(m_data) + offset, sizeof(value));
|
||||
if (offset >= m_highlight_start && offset < m_highlight_end)
|
||||
painter.fillRect(x, y - m_char_height + 3, 2 * m_char_width, m_char_height, highlight_color);
|
||||
if (m_selected_address == offset)
|
||||
painter.fillRect(x, y - m_char_height + 4, 2 * m_char_width, m_char_height, selected_color);
|
||||
else if (offset >= m_highlight_start && offset < m_highlight_end)
|
||||
painter.fillRect(x, y - m_char_height + 4, 2 * m_char_width, m_char_height, highlight_color);
|
||||
|
||||
if (!std::isprint(value))
|
||||
value = '.';
|
||||
|
@ -207,6 +307,57 @@ void MemoryViewWidget::paintEvent(QPaintEvent*)
|
|||
}
|
||||
}
|
||||
|
||||
void MemoryViewWidget::updateSelectedByte(const QPoint& pos)
|
||||
{
|
||||
const int xpos = pos.x() + horizontalScrollBar()->value();
|
||||
const int ypos = pos.y();
|
||||
|
||||
size_t new_selection = INVALID_SELECTED_ADDRESS;
|
||||
bool new_ascii = false;
|
||||
|
||||
// to left or above hex view
|
||||
const int addr_width = addressWidth();
|
||||
if (xpos >= addr_width && ypos >= m_char_height)
|
||||
{
|
||||
const int row = (ypos - m_char_height) / m_char_height;
|
||||
const size_t row_address = m_start_offset + (static_cast<unsigned>(row) * m_bytes_per_line);
|
||||
|
||||
// out of Y range
|
||||
if (row_address < m_end_offset)
|
||||
{
|
||||
// in hex view?
|
||||
const int hex_end = addr_width + hexWidth() + m_char_width;
|
||||
if (xpos < hex_end)
|
||||
{
|
||||
const int hex_char_width = 4 * m_char_width;
|
||||
const int hex_offset = (xpos - addr_width) / hex_char_width;
|
||||
new_selection = row_address + static_cast<size_t>(hex_offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
// in ascii view?
|
||||
const int ascii_char_width = 2 * m_char_width;
|
||||
const int ascii_end = hex_end + (m_bytes_per_line * ascii_char_width);
|
||||
|
||||
// might be offscreen again
|
||||
if (xpos < ascii_end)
|
||||
{
|
||||
const int ascii_offset = (xpos - hex_end) / ascii_char_width;
|
||||
new_selection = row_address + static_cast<size_t>(ascii_offset);
|
||||
new_ascii = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (new_selection != m_selected_address || new_ascii != m_selection_was_ascii)
|
||||
{
|
||||
m_selected_address = new_selection;
|
||||
m_selection_was_ascii = new_ascii;
|
||||
viewport()->update();
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryViewWidget::adjustContent()
|
||||
{
|
||||
if (!m_data)
|
||||
|
|
|
@ -8,13 +8,13 @@ class MemoryViewWidget : public QAbstractScrollArea
|
|||
public:
|
||||
Q_OBJECT
|
||||
public:
|
||||
MemoryViewWidget(QWidget* parent = nullptr, size_t address_offset = 0, const void* data_ptr = nullptr,
|
||||
size_t data_size = 0);
|
||||
MemoryViewWidget(QWidget* parent = nullptr, size_t address_offset = 0, void* data_ptr = nullptr, size_t data_size = 0,
|
||||
bool data_editable = false);
|
||||
~MemoryViewWidget();
|
||||
|
||||
size_t addressOffset() const { return m_address_offset; }
|
||||
|
||||
void setData(size_t address_offset, const void* data_ptr, size_t data_size);
|
||||
void setData(size_t address_offset, void* data_ptr, size_t data_size, bool data_editable);
|
||||
void setHighlightRange(size_t start, size_t end);
|
||||
void clearHighlightRange();
|
||||
void scrolltoOffset(size_t offset);
|
||||
|
@ -22,19 +22,25 @@ public:
|
|||
void setFont(const QFont& font);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent*);
|
||||
void resizeEvent(QResizeEvent*);
|
||||
void paintEvent(QPaintEvent* event);
|
||||
void resizeEvent(QResizeEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void keyPressEvent(QKeyEvent* event);
|
||||
|
||||
private Q_SLOTS:
|
||||
void adjustContent();
|
||||
|
||||
private:
|
||||
static constexpr size_t INVALID_SELECTED_ADDRESS = ~static_cast<size_t>(0);
|
||||
|
||||
int addressWidth() const;
|
||||
int hexWidth() const;
|
||||
int asciiWidth() const;
|
||||
void updateMetrics();
|
||||
void updateSelectedByte(const QPoint& pos);
|
||||
|
||||
const void* m_data;
|
||||
void* m_data;
|
||||
size_t m_data_size;
|
||||
size_t m_address_offset;
|
||||
|
||||
|
@ -44,6 +50,11 @@ private:
|
|||
size_t m_highlight_start = 0;
|
||||
size_t m_highlight_end = 0;
|
||||
|
||||
size_t m_selected_address = INVALID_SELECTED_ADDRESS;
|
||||
int m_editing_nibble = -1;
|
||||
bool m_selection_was_ascii = false;
|
||||
bool m_data_editable = false;
|
||||
|
||||
unsigned m_bytes_per_line;
|
||||
|
||||
int m_char_width;
|
||||
|
|
Loading…
Reference in a new issue