Skip to content

Commit

Permalink
Qt/Debugger: Highlight changed bytes in memory view
Browse files Browse the repository at this point in the history
  • Loading branch information
stenzek committed Sep 7, 2024
1 parent ac5a2a1 commit 71ec59b
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 11 deletions.
14 changes: 10 additions & 4 deletions src/duckstation-qt/debuggerwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ void DebuggerWindow::onPauseActionToggled(bool paused)
{
if (!paused)
{
m_registers_model->saveCurrentValues();
saveCurrentState();
setUIEnabled(false, true);
}

Expand Down Expand Up @@ -203,7 +203,7 @@ void DebuggerWindow::onBreakpointListContextMenuRequested()
void DebuggerWindow::onStepIntoActionTriggered()
{
Assert(System::IsPaused());
m_registers_model->saveCurrentValues();
saveCurrentState();
g_emu_thread->singleStepCPU();
}

Expand All @@ -217,7 +217,7 @@ void DebuggerWindow::onStepOverActionTriggered()
}

// unpause to let it run to the breakpoint
m_registers_model->saveCurrentValues();
saveCurrentState();
g_emu_thread->setSystemPaused(false);
}

Expand All @@ -231,7 +231,7 @@ void DebuggerWindow::onStepOutActionTriggered()
}

// unpause to let it run to the breakpoint
m_registers_model->saveCurrentValues();
saveCurrentState();
g_emu_thread->setSystemPaused(false);
}

Expand Down Expand Up @@ -538,6 +538,12 @@ void DebuggerWindow::setUIEnabled(bool enabled, bool allow_pause)
m_ui.memoryRegionBIOS->setEnabled(enabled);
}

void DebuggerWindow::saveCurrentState()
{
m_registers_model->saveCurrentValues();
m_ui.memoryView->saveCurrentData();
}

void DebuggerWindow::setMemoryViewRegion(Bus::MemoryRegion region)
{
if (m_active_memory_region == region)
Expand Down
1 change: 1 addition & 0 deletions src/duckstation-qt/debuggerwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ private Q_SLOTS:
void disconnectSignals();
void createModels();
void setUIEnabled(bool enabled, bool allow_pause);
void saveCurrentState();
void setMemoryViewRegion(Bus::MemoryRegion region);
void toggleBreakpoint(VirtualMemoryAddress address);
void clearBreakpoints();
Expand Down
74 changes: 69 additions & 5 deletions src/duckstation-qt/memoryviewwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ void MemoryViewWidget::setData(size_t address_offset, void* data_ptr, size_t dat
m_data_editable = data_editable;
m_address_offset = address_offset;
m_selected_address = INVALID_SELECTED_ADDRESS;
m_last_data_start_offset = 0;
m_last_data.clear();
adjustContent();
saveCurrentData();
}

void MemoryViewWidget::setHighlightRange(size_t start, size_t end)
Expand Down Expand Up @@ -133,6 +136,7 @@ void MemoryViewWidget::keyPressEvent(QKeyEvent* event)
const char ch = key_text[0].toLatin1();
if (m_selection_was_ascii)
{
expandCurrentDataToInclude(m_selected_address);
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();
Expand All @@ -150,6 +154,8 @@ void MemoryViewWidget::keyPressEvent(QKeyEvent* event)
{
m_editing_nibble++;

expandCurrentDataToInclude(m_selected_address);

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

Expand Down Expand Up @@ -185,7 +191,7 @@ void MemoryViewWidget::paintEvent(QPaintEvent* event)
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 QColor edited_color(240, 30, 30);
const int offsetX = horizontalScrollBar()->value();

int y = m_char_height;
Expand Down Expand Up @@ -252,7 +258,18 @@ void MemoryViewWidget::paintEvent(QPaintEvent* event)

if (m_selected_address != offset || m_editing_nibble != 0 || m_selection_was_ascii) [[likely]]
{
painter.drawText(x, y, QString::asprintf("%02X", value));
const QString text = QString::asprintf("%02X", value);
const size_t view_offset = offset - m_last_data_start_offset; // may underflow, but unsigned so it's okay
if (view_offset < m_last_data.size() && value != m_last_data[view_offset])
{
painter.setPen(edited_color);
painter.drawText(x, y, text);
painter.setPen(text_color);
}
else
{
painter.drawText(x, y, text);
}
}
else
{
Expand Down Expand Up @@ -298,9 +315,18 @@ void MemoryViewWidget::paintEvent(QPaintEvent* event)
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 = '.';
painter.drawText(x, y, static_cast<QChar>(value));
const QChar print_char = std::isprint(value) ? static_cast<QChar>(value) : static_cast<QChar>('.');
const size_t view_offset = offset - m_last_data_start_offset; // may underflow, but unsigned so it's okay
if (view_offset < m_last_data.size() && value != m_last_data[view_offset])
{
painter.setPen(edited_color);
painter.drawText(x, y, print_char);
painter.setPen(text_color);
}
else
{
painter.drawText(x, y, print_char);
}
x += 2 * m_char_width;
}
y += m_char_height;
Expand Down Expand Up @@ -354,10 +380,48 @@ void MemoryViewWidget::updateSelectedByte(const QPoint& pos)
{
m_selected_address = new_selection;
m_selection_was_ascii = new_ascii;
m_editing_nibble = -1;
viewport()->update();
}
}

void MemoryViewWidget::expandCurrentDataToInclude(size_t offset)
{
offset = std::min(offset, m_data_size - 1);
if (offset < m_last_data_start_offset)
{
const size_t add_bytes = m_last_data_start_offset - offset;
const size_t old_size = m_last_data.size();
m_last_data.resize(old_size + add_bytes);
std::memmove(&m_last_data[add_bytes], &m_last_data[0], old_size);
std::memcpy(&m_last_data[0], static_cast<const u8*>(m_data) + offset, add_bytes);
m_last_data_start_offset = offset;
}
else if (offset >= (m_last_data_start_offset + m_last_data.size()))
{
const size_t new_size = m_last_data_start_offset + offset + 1;
const size_t old_size = m_last_data.size();
m_last_data.resize(new_size);
std::memcpy(&m_last_data[old_size], static_cast<const u8*>(m_data) + m_last_data_start_offset + old_size,
new_size - old_size);
}
}

void MemoryViewWidget::saveCurrentData()
{
if (!m_data)
{
m_last_data.clear();
return;
}

const size_t size = m_end_offset - m_start_offset;
m_last_data_start_offset = m_start_offset;
m_last_data.resize(size);
std::memcpy(m_last_data.data(), static_cast<const u8*>(m_data) + m_start_offset, size);
viewport()->update();
}

void MemoryViewWidget::adjustContent()
{
if (!m_data)
Expand Down
15 changes: 13 additions & 2 deletions src/duckstation-qt/memoryviewwidget.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#pragma once

#include "common/types.h"

#include <QtWidgets/QAbstractScrollArea>
#include <vector>

// Based on https://stackoverflow.com/questions/46375673/how-can-realize-my-own-memory-viewer-by-qt

Expand Down Expand Up @@ -28,6 +32,9 @@ class MemoryViewWidget : public QAbstractScrollArea
void mouseMoveEvent(QMouseEvent* event);
void keyPressEvent(QKeyEvent* event);

public Q_SLOTS:
void saveCurrentData();

private Q_SLOTS:
void adjustContent();

Expand All @@ -39,6 +46,7 @@ private Q_SLOTS:
int asciiWidth() const;
void updateMetrics();
void updateSelectedByte(const QPoint& pos);
void expandCurrentDataToInclude(size_t offset);

void* m_data;
size_t m_data_size;
Expand All @@ -51,14 +59,17 @@ private Q_SLOTS:
size_t m_highlight_end = 0;

size_t m_selected_address = INVALID_SELECTED_ADDRESS;
int m_editing_nibble = -1;
s32 m_editing_nibble = -1;
bool m_selection_was_ascii = false;
bool m_data_editable = false;

unsigned m_bytes_per_line;
u32 m_bytes_per_line;

int m_char_width;
int m_char_height;

int m_rows_visible;

std::vector<u8> m_last_data;
size_t m_last_data_start_offset = 0;
};

0 comments on commit 71ec59b

Please sign in to comment.