Debugger: Implement little endian memory view support

This commit is contained in:
Ty Lamontagne 2024-07-15 11:29:31 -04:00 committed by lightningterror
parent 84fe413635
commit 951780b43d
2 changed files with 177 additions and 49 deletions

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: LGPL-3.0+ // SPDX-License-Identifier: LGPL-3.0+
#include "MemoryViewWidget.h" #include "MemoryViewWidget.h"
#include "common/Console.h"
#include "QtHost.h" #include "QtHost.h"
#include "QtUtils.h" #include "QtUtils.h"
@ -10,7 +11,6 @@
#include <QtWidgets/QInputDialog> #include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox> #include <QtWidgets/QMessageBox>
#include <QtGui/QClipboard> #include <QtGui/QClipboard>
#include <QtCore/QtEndian>
using namespace QtUtils; using namespace QtUtils;
@ -67,35 +67,28 @@ void MemoryViewTable::DrawTable(QPainter& painter, const QPalette& palette, s32
s32 valX = valuexAxis; s32 valX = valuexAxis;
segmentXAxis[0] = valX; segmentXAxis[0] = valX;
u32 currentSegmentAddress = currentRowAddress; u32 currentSegmentAddress = currentRowAddress;
for (int j = 0; j < 16; j++) for (int j = 0; j < 16 / (s32)displayType; j++)
{ {
const u32 currentByteAddress = currentRowAddress + j; valX += charWidth;
const u32 thisSegmentsStart = currentRowAddress + (j * (s32)displayType);
if (!(j % (s32)displayType))
{
valX += charWidth;
currentSegmentAddress = currentByteAddress;
}
segmentXAxis[j] = valX; segmentXAxis[j] = valX;
if ((selectedAddress & ~0xF) == currentRowAddress) if ((selectedAddress & ~0xF) == currentRowAddress)
{ {
if (selectedAddress == currentByteAddress) if (selectedAddress >= thisSegmentsStart && selectedAddress < (thisSegmentsStart + (s32)displayType))
{ // If the current byte and row we are drawing is selected { // If the current byte and row we are drawing is selected
if (!selectedText) if (!selectedText)
{ {
s32 charsIntoSegment = ((selectedAddress - thisSegmentsStart) * 2) + ((selectedNibbleHI ? 0 : 1) ^ littleEndian);
if (littleEndian)
charsIntoSegment = ((s32)displayType * 2) - charsIntoSegment - 1;
painter.setPen(QColor::fromRgb(205, 165, 0)); // SELECTED NIBBLE LINE COLOUR painter.setPen(QColor::fromRgb(205, 165, 0)); // SELECTED NIBBLE LINE COLOUR
const QPoint lineStart(valX + (selectedNibbleHI ? 0 : charWidth) + 1, y + (rowHeight * i)); const QPoint lineStart(valX + (charsIntoSegment * charWidth) + 1, y + (rowHeight * i));
painter.drawLine(lineStart, lineStart + QPoint(charWidth - 3, 0)); painter.drawLine(lineStart, lineStart + QPoint(charWidth - 3, 0));
} }
painter.setPen(QColor::fromRgb(0xaa, 0x22, 0x22)); // SELECTED BYTE COLOUR painter.setPen(QColor::fromRgb(0xaa, 0x22, 0x22)); // SELECTED BYTE COLOUR
} }
// If the current selected byte is in our current segment, highlight the entire segment
else if (displayType != MemoryViewType::BYTE &&
currentSegmentAddress <= selectedAddress && (selectedAddress <= (currentSegmentAddress + (s32)displayType - 1)))
{
painter.setPen(palette.highlight().color()); // SELECTED SEGMENT COLOUR
}
else else
painter.setPen(palette.text().color()); // Default colour painter.setPen(palette.text().color()); // Default colour
} }
@ -103,11 +96,34 @@ void MemoryViewTable::DrawTable(QPainter& painter, const QPalette& palette, s32
painter.setPen(palette.text().color()); // Default colour painter.setPen(palette.text().color()); // Default colour
bool valid; bool valid;
const u8 val = static_cast<u8>(m_cpu->read8(currentByteAddress, valid)); switch (displayType)
{
painter.drawText(valX, y + (rowHeight * i), valid ? FilledQStringFromValue(val, 16) : "??"); case MemoryViewType::BYTE:
{
valX += charWidth * 2; const u8 val = static_cast<u8>(m_cpu->read8(thisSegmentsStart, valid));
painter.drawText(valX, y + (rowHeight * i), valid ? FilledQStringFromValue(val, 16) : "??");
break;
}
case MemoryViewType::BYTEHW:
{
const u16 val = convertEndian<u16>(static_cast<u16>(m_cpu->read16(thisSegmentsStart, valid)));
painter.drawText(valX, y + (rowHeight * i), valid ? FilledQStringFromValue(val, 16) : "????");
break;
}
case MemoryViewType::WORD:
{
const u32 val = convertEndian<u32>(m_cpu->read32(thisSegmentsStart, valid));
painter.drawText(valX, y + (rowHeight * i), valid ? FilledQStringFromValue(val, 16) : "????????");
break;
}
case MemoryViewType::DWORD:
{
const u64 val = convertEndian<u64>(m_cpu->read64(thisSegmentsStart, valid));
painter.drawText(valX, y + (rowHeight * i), valid ? FilledQStringFromValue(val, 16) : "????????????????");
break;
}
}
valX += charWidth * 2 * (s32)displayType;
} }
// valX is our new X position after the hex values // valX is our new X position after the hex values
@ -146,12 +162,15 @@ void MemoryViewTable::SelectAt(QPoint pos)
const u32 selectedRow = (pos.y() - 2) / (rowHeight); const u32 selectedRow = (pos.y() - 2) / (rowHeight);
const s32 x = pos.x(); const s32 x = pos.x();
const s32 avgSegmentWidth = segmentXAxis[1] - segmentXAxis[0]; const s32 avgSegmentWidth = segmentXAxis[1] - segmentXAxis[0];
const u32 nibbleWidth = (avgSegmentWidth / (2 * (s32)displayType));
selectedAddress = (selectedRow * 0x10) + startAddress; selectedAddress = (selectedRow * 0x10) + startAddress;
if (x <= segmentXAxis[0]) if (x <= segmentXAxis[0])
{ {
selectedText = false;
// The user clicked before the first segment // The user clicked before the first segment
selectedText = false;
if (littleEndian)
selectedAddress += (s32)displayType - 1;
selectedNibbleHI = true; selectedNibbleHI = true;
} }
else if (x > valuexAxis && x < textXAxis) else if (x > valuexAxis && x < textXAxis)
@ -160,10 +179,13 @@ void MemoryViewTable::SelectAt(QPoint pos)
// The user clicked inside of the hexadecimal area // The user clicked inside of the hexadecimal area
for (s32 i = 0; i < 16; i++) for (s32 i = 0; i < 16; i++)
{ {
if (i == 15 || (x >= segmentXAxis[i] && x < (segmentXAxis[i + 1]))) if (i == ((16 / (s32)displayType) - 1) || (x >= segmentXAxis[i] && x < (segmentXAxis[i + 1])))
{ {
selectedAddress = selectedAddress + i; u32 indexInSegment = (x - segmentXAxis[i]) / nibbleWidth;
selectedNibbleHI = ((x - segmentXAxis[i]) < ((avgSegmentWidth / 2) - 2)); // Subtract 2 units, makes selecting nibbles feel more natural if (littleEndian)
indexInSegment = ((s32)displayType * 2) - indexInSegment - 1;
selectedAddress = selectedAddress + i * (s32)displayType + (indexInSegment / 2);
selectedNibbleHI = littleEndian ? indexInSegment & 1 : !(indexInSegment & 1);
break; break;
} }
} }
@ -185,13 +207,13 @@ u128 MemoryViewTable::GetSelectedSegment()
val.lo = m_cpu->read8(selectedAddress); val.lo = m_cpu->read8(selectedAddress);
break; break;
case MemoryViewType::BYTEHW: case MemoryViewType::BYTEHW:
val.lo = qToBigEndian((u16)m_cpu->read16(selectedAddress & ~1)); val.lo = convertEndian((u16)m_cpu->read16(selectedAddress & ~1));
break; break;
case MemoryViewType::WORD: case MemoryViewType::WORD:
val.lo = qToBigEndian(m_cpu->read32(selectedAddress & ~3)); val.lo = convertEndian(m_cpu->read32(selectedAddress & ~3));
break; break;
case MemoryViewType::DWORD: case MemoryViewType::DWORD:
val._u64[0] = qToBigEndian(m_cpu->read64(selectedAddress & ~7)); val._u64[0] = convertEndian(m_cpu->read64(selectedAddress & ~7));
break; break;
} }
return val; return val;
@ -210,7 +232,8 @@ void MemoryViewTable::InsertIntoSelectedHexView(u8 value)
}); });
} }
void MemoryViewTable::InsertAtCurrentSelection(const QString& text) { void MemoryViewTable::InsertAtCurrentSelection(const QString& text)
{
if (!m_cpu->isValidAddress(selectedAddress)) if (!m_cpu->isValidAddress(selectedAddress))
return; return;
@ -218,16 +241,89 @@ void MemoryViewTable::InsertAtCurrentSelection(const QString& text) {
// This approach prevents one from pasting on a nibble boundary, but that is almost always // This approach prevents one from pasting on a nibble boundary, but that is almost always
// user error, and we don't have an undo function in this view, so best to stay conservative. // user error, and we don't have an undo function in this view, so best to stay conservative.
QByteArray input = selectedText ? text.toUtf8() : QByteArray::fromHex(text.toUtf8()); QByteArray input = selectedText ? text.toUtf8() : QByteArray::fromHex(text.toUtf8());
Host::RunOnCPUThread([this, address = selectedAddress, cpu = m_cpu, inBytes = input] { Host::RunOnCPUThread([this, address = selectedAddress, cpu = m_cpu, inBytes = input] {
u32 currAddr = address;
for (int i = 0; i < inBytes.size(); i++) for (int i = 0; i < inBytes.size(); i++)
{ {
cpu->write8(address + i, inBytes[i]); cpu->write8(currAddr, inBytes[i]);
currAddr = nextAddress(currAddr);
QtHost::RunOnUIThread([this] { parent->update(); });
} }
QtHost::RunOnUIThread([this, inBytes] { UpdateSelectedAddress(selectedAddress + inBytes.size()); parent->update(); }); QtHost::RunOnUIThread([this, inBytes] { UpdateSelectedAddress(selectedAddress + inBytes.size()); parent->update(); });
}); });
} }
u32 MemoryViewTable::nextAddress(u32 addr)
{
if (!littleEndian)
{
return addr + 1;
}
else
{
if (selectedAddress % (s32)displayType == 0)
return addr + ((s32)displayType * 2 - 1);
else
return addr - 1;
}
}
u32 MemoryViewTable::prevAddress(u32 addr)
{
if (!littleEndian)
{
return addr - 1;
}
else
{
// It works
if ((addr & ((s32)displayType - 1)) == ((s32)displayType - 1))
return addr - ((s32)displayType * 2 - 1);
else
return selectedAddress + 1;
}
}
void MemoryViewTable::ForwardSelection()
{
if (!littleEndian)
{
if ((selectedNibbleHI = !selectedNibbleHI))
UpdateSelectedAddress(selectedAddress + 1);
}
else
{
if ((selectedNibbleHI = !selectedNibbleHI))
{
if (selectedAddress % (s32)displayType == 0)
UpdateSelectedAddress(selectedAddress + ((s32)displayType * 2 - 1));
else
UpdateSelectedAddress(selectedAddress - 1);
}
}
}
void MemoryViewTable::BackwardSelection()
{
if (!littleEndian)
{
if (!(selectedNibbleHI = !selectedNibbleHI))
UpdateSelectedAddress(selectedAddress - 1);
}
else
{
if (!(selectedNibbleHI = !selectedNibbleHI))
{
// It works
if ((selectedAddress & ((s32)displayType - 1)) == ((s32)displayType - 1))
UpdateSelectedAddress(selectedAddress - ((s32)displayType * 2 - 1));
else
UpdateSelectedAddress(selectedAddress + 1);
}
}
}
// We need both key and keychar because `key` is easy to use, but is case insensitive // We need both key and keychar because `key` is easy to use, but is case insensitive
bool MemoryViewTable::KeyPress(int key, QChar keychar) bool MemoryViewTable::KeyPress(int key, QChar keychar)
{ {
@ -255,16 +351,16 @@ bool MemoryViewTable::KeyPress(int key, QChar keychar)
case Qt::Key::Key_Escape: case Qt::Key::Key_Escape:
Host::RunOnCPUThread([this, address = selectedAddress, cpu = m_cpu] { Host::RunOnCPUThread([this, address = selectedAddress, cpu = m_cpu] {
cpu->write8(address, 0); cpu->write8(address, 0);
QtHost::RunOnUIThread([this] { UpdateSelectedAddress(selectedAddress - 1); parent->update(); }); QtHost::RunOnUIThread([this] {BackwardSelection(); parent->update(); });
}); });
pressHandled = true; pressHandled = true;
break; break;
case Qt::Key::Key_Right: case Qt::Key::Key_Right:
UpdateSelectedAddress(selectedAddress + 1); ForwardSelection();
pressHandled = true; pressHandled = true;
break; break;
case Qt::Key::Key_Left: case Qt::Key::Key_Left:
UpdateSelectedAddress(selectedAddress - 1); BackwardSelection();
pressHandled = true; pressHandled = true;
break; break;
default: default:
@ -282,9 +378,7 @@ bool MemoryViewTable::KeyPress(int key, QChar keychar)
if (pressHandled) if (pressHandled)
{ {
InsertIntoSelectedHexView(keyPressed); InsertIntoSelectedHexView(keyPressed);
// Increment to the next nibble or byte ForwardSelection();
if ((selectedNibbleHI = !selectedNibbleHI))
UpdateSelectedAddress(selectedAddress + 1);
} }
} }
@ -293,19 +387,15 @@ bool MemoryViewTable::KeyPress(int key, QChar keychar)
case Qt::Key::Key_Backspace: case Qt::Key::Key_Backspace:
case Qt::Key::Key_Escape: case Qt::Key::Key_Escape:
InsertIntoSelectedHexView(0); InsertIntoSelectedHexView(0);
// Move back a byte or nibble if it's backspace being pressed BackwardSelection();
if (!(selectedNibbleHI = !selectedNibbleHI))
UpdateSelectedAddress(selectedAddress - 1);
pressHandled = true; pressHandled = true;
break; break;
case Qt::Key::Key_Right: case Qt::Key::Key_Right:
if ((selectedNibbleHI = !selectedNibbleHI)) ForwardSelection();
UpdateSelectedAddress(selectedAddress + 1);
pressHandled = true; pressHandled = true;
break; break;
case Qt::Key::Key_Left: case Qt::Key::Key_Left:
if (!(selectedNibbleHI = !selectedNibbleHI)) BackwardSelection();
UpdateSelectedAddress(selectedAddress - 1);
pressHandled = true; pressHandled = true;
break; break;
default: default:
@ -403,6 +493,11 @@ void MemoryViewWidget::customMenuRequested(QPoint pos)
m_contextMenu->addSeparator(); m_contextMenu->addSeparator();
m_actionLittleEndian = new QAction(tr("Show as Little Endian"));
m_actionLittleEndian->setCheckable(true);
m_contextMenu->addAction(m_actionLittleEndian);
connect(m_actionLittleEndian, &QAction::triggered, this, [this]() { m_table.SetLittleEndian(m_actionLittleEndian->isChecked()); });
// View Types // View Types
m_actionBYTE = new QAction(tr("Show as 1 byte")); m_actionBYTE = new QAction(tr("Show as 1 byte"));
m_actionBYTE->setCheckable(true); m_actionBYTE->setCheckable(true);
@ -446,6 +541,8 @@ void MemoryViewWidget::customMenuRequested(QPoint pos)
m_contextMenu->addAction(action); m_contextMenu->addAction(action);
connect(action, &QAction::triggered, this, [this]() { contextPaste(); }); connect(action, &QAction::triggered, this, [this]() { contextPaste(); });
} }
m_actionLittleEndian->setChecked(m_table.GetLittleEndian());
const MemoryViewType currentViewType = m_table.GetViewType(); const MemoryViewType currentViewType = m_table.GetViewType();
m_actionBYTE->setChecked(currentViewType == MemoryViewType::BYTE); m_actionBYTE->setChecked(currentViewType == MemoryViewType::BYTE);

View File

@ -12,6 +12,7 @@
#include <QtWidgets/QMenu> #include <QtWidgets/QMenu>
#include <QtWidgets/QTabBar> #include <QtWidgets/QTabBar>
#include <QtGui/QPainter> #include <QtGui/QPainter>
#include <QtCore/QtEndian>
#include <vector> #include <vector>
@ -28,22 +29,39 @@ class MemoryViewTable
QWidget* parent; QWidget* parent;
DebugInterface* m_cpu; DebugInterface* m_cpu;
MemoryViewType displayType = MemoryViewType::BYTE; MemoryViewType displayType = MemoryViewType::BYTE;
bool littleEndian = true;
u32 rowCount; u32 rowCount;
u32 rowVisible; u32 rowVisible;
s32 rowHeight; s32 rowHeight;
// Stuff used for selection handling // Stuff used for selection handling
// This gets set every paint and depends on the window size / current display mode (1byte,2byte,etc) // This gets set every paint and depends on the window size / current display mode (1byte,2byte,etc)
s32 valuexAxis; // Where the hexadecimal view begins s32 valuexAxis; // Where the hexadecimal view begins
s32 textXAxis; // Where the text view begins s32 textXAxis; // Where the text view begins
s32 row1YAxis; // Where the first row starts s32 row1YAxis; // Where the first row starts
s32 segmentXAxis[16]; // Where the segments begin s32 segmentXAxis[16]; // Where the segments begin
bool selectedText = false; // Whether the user has clicked on text or hex bool selectedText = false; // Whether the user has clicked on text or hex
bool selectedNibbleHI = false; bool selectedNibbleHI = false;
void InsertIntoSelectedHexView(u8 value); void InsertIntoSelectedHexView(u8 value);
template <class T>
T convertEndian(T in)
{
if (littleEndian)
{
return in;
}
else
{
return qToBigEndian(in);
}
}
u32 nextAddress(u32 addr);
u32 prevAddress(u32 addr);
public: public:
MemoryViewTable(QWidget* parent) MemoryViewTable(QWidget* parent)
: parent(parent){}; : parent(parent){};
@ -60,6 +78,8 @@ public:
void SelectAt(QPoint pos); void SelectAt(QPoint pos);
u128 GetSelectedSegment(); u128 GetSelectedSegment();
void InsertAtCurrentSelection(const QString& text); void InsertAtCurrentSelection(const QString& text);
void ForwardSelection();
void BackwardSelection();
// Returns true if the keypress was handled // Returns true if the keypress was handled
bool KeyPress(int key, QChar keychar); bool KeyPress(int key, QChar keychar);
@ -72,6 +92,16 @@ public:
{ {
displayType = viewType; displayType = viewType;
} }
bool GetLittleEndian()
{
return littleEndian;
}
void SetLittleEndian(bool le)
{
littleEndian = le;
}
}; };
@ -111,6 +141,7 @@ private:
Ui::RegisterWidget ui; Ui::RegisterWidget ui;
QMenu* m_contextMenu = 0x0; QMenu* m_contextMenu = 0x0;
QAction* m_actionLittleEndian;
QAction* m_actionBYTE; QAction* m_actionBYTE;
QAction* m_actionBYTEHW; QAction* m_actionBYTEHW;
QAction* m_actionWORD; QAction* m_actionWORD;