mirror of https://github.com/PCSX2/pcsx2.git
Debugger: Implement little endian memory view support
This commit is contained in:
parent
84fe413635
commit
951780b43d
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
|
||||
#include "MemoryViewWidget.h"
|
||||
#include "common/Console.h"
|
||||
|
||||
#include "QtHost.h"
|
||||
#include "QtUtils.h"
|
||||
|
@ -10,7 +11,6 @@
|
|||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtCore/QtEndian>
|
||||
|
||||
using namespace QtUtils;
|
||||
|
||||
|
@ -67,35 +67,28 @@ void MemoryViewTable::DrawTable(QPainter& painter, const QPalette& palette, s32
|
|||
s32 valX = valuexAxis;
|
||||
segmentXAxis[0] = valX;
|
||||
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;
|
||||
|
||||
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 (!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
|
||||
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.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
|
||||
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
|
||||
|
||||
bool valid;
|
||||
const u8 val = static_cast<u8>(m_cpu->read8(currentByteAddress, valid));
|
||||
|
||||
painter.drawText(valX, y + (rowHeight * i), valid ? FilledQStringFromValue(val, 16) : "??");
|
||||
|
||||
valX += charWidth * 2;
|
||||
switch (displayType)
|
||||
{
|
||||
case MemoryViewType::BYTE:
|
||||
{
|
||||
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
|
||||
|
@ -146,12 +162,15 @@ void MemoryViewTable::SelectAt(QPoint pos)
|
|||
const u32 selectedRow = (pos.y() - 2) / (rowHeight);
|
||||
const s32 x = pos.x();
|
||||
const s32 avgSegmentWidth = segmentXAxis[1] - segmentXAxis[0];
|
||||
const u32 nibbleWidth = (avgSegmentWidth / (2 * (s32)displayType));
|
||||
selectedAddress = (selectedRow * 0x10) + startAddress;
|
||||
|
||||
if (x <= segmentXAxis[0])
|
||||
{
|
||||
selectedText = false;
|
||||
// The user clicked before the first segment
|
||||
selectedText = false;
|
||||
if (littleEndian)
|
||||
selectedAddress += (s32)displayType - 1;
|
||||
selectedNibbleHI = true;
|
||||
}
|
||||
else if (x > valuexAxis && x < textXAxis)
|
||||
|
@ -160,10 +179,13 @@ void MemoryViewTable::SelectAt(QPoint pos)
|
|||
// The user clicked inside of the hexadecimal area
|
||||
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;
|
||||
selectedNibbleHI = ((x - segmentXAxis[i]) < ((avgSegmentWidth / 2) - 2)); // Subtract 2 units, makes selecting nibbles feel more natural
|
||||
u32 indexInSegment = (x - segmentXAxis[i]) / nibbleWidth;
|
||||
if (littleEndian)
|
||||
indexInSegment = ((s32)displayType * 2) - indexInSegment - 1;
|
||||
selectedAddress = selectedAddress + i * (s32)displayType + (indexInSegment / 2);
|
||||
selectedNibbleHI = littleEndian ? indexInSegment & 1 : !(indexInSegment & 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -185,13 +207,13 @@ u128 MemoryViewTable::GetSelectedSegment()
|
|||
val.lo = m_cpu->read8(selectedAddress);
|
||||
break;
|
||||
case MemoryViewType::BYTEHW:
|
||||
val.lo = qToBigEndian((u16)m_cpu->read16(selectedAddress & ~1));
|
||||
val.lo = convertEndian((u16)m_cpu->read16(selectedAddress & ~1));
|
||||
break;
|
||||
case MemoryViewType::WORD:
|
||||
val.lo = qToBigEndian(m_cpu->read32(selectedAddress & ~3));
|
||||
val.lo = convertEndian(m_cpu->read32(selectedAddress & ~3));
|
||||
break;
|
||||
case MemoryViewType::DWORD:
|
||||
val._u64[0] = qToBigEndian(m_cpu->read64(selectedAddress & ~7));
|
||||
val._u64[0] = convertEndian(m_cpu->read64(selectedAddress & ~7));
|
||||
break;
|
||||
}
|
||||
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))
|
||||
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
|
||||
// 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());
|
||||
|
||||
Host::RunOnCPUThread([this, address = selectedAddress, cpu = m_cpu, inBytes = input] {
|
||||
u32 currAddr = address;
|
||||
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(); });
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
bool MemoryViewTable::KeyPress(int key, QChar keychar)
|
||||
{
|
||||
|
@ -255,16 +351,16 @@ bool MemoryViewTable::KeyPress(int key, QChar keychar)
|
|||
case Qt::Key::Key_Escape:
|
||||
Host::RunOnCPUThread([this, address = selectedAddress, cpu = m_cpu] {
|
||||
cpu->write8(address, 0);
|
||||
QtHost::RunOnUIThread([this] { UpdateSelectedAddress(selectedAddress - 1); parent->update(); });
|
||||
QtHost::RunOnUIThread([this] {BackwardSelection(); parent->update(); });
|
||||
});
|
||||
pressHandled = true;
|
||||
break;
|
||||
case Qt::Key::Key_Right:
|
||||
UpdateSelectedAddress(selectedAddress + 1);
|
||||
ForwardSelection();
|
||||
pressHandled = true;
|
||||
break;
|
||||
case Qt::Key::Key_Left:
|
||||
UpdateSelectedAddress(selectedAddress - 1);
|
||||
BackwardSelection();
|
||||
pressHandled = true;
|
||||
break;
|
||||
default:
|
||||
|
@ -282,9 +378,7 @@ bool MemoryViewTable::KeyPress(int key, QChar keychar)
|
|||
if (pressHandled)
|
||||
{
|
||||
InsertIntoSelectedHexView(keyPressed);
|
||||
// Increment to the next nibble or byte
|
||||
if ((selectedNibbleHI = !selectedNibbleHI))
|
||||
UpdateSelectedAddress(selectedAddress + 1);
|
||||
ForwardSelection();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,19 +387,15 @@ bool MemoryViewTable::KeyPress(int key, QChar keychar)
|
|||
case Qt::Key::Key_Backspace:
|
||||
case Qt::Key::Key_Escape:
|
||||
InsertIntoSelectedHexView(0);
|
||||
// Move back a byte or nibble if it's backspace being pressed
|
||||
if (!(selectedNibbleHI = !selectedNibbleHI))
|
||||
UpdateSelectedAddress(selectedAddress - 1);
|
||||
BackwardSelection();
|
||||
pressHandled = true;
|
||||
break;
|
||||
case Qt::Key::Key_Right:
|
||||
if ((selectedNibbleHI = !selectedNibbleHI))
|
||||
UpdateSelectedAddress(selectedAddress + 1);
|
||||
ForwardSelection();
|
||||
pressHandled = true;
|
||||
break;
|
||||
case Qt::Key::Key_Left:
|
||||
if (!(selectedNibbleHI = !selectedNibbleHI))
|
||||
UpdateSelectedAddress(selectedAddress - 1);
|
||||
BackwardSelection();
|
||||
pressHandled = true;
|
||||
break;
|
||||
default:
|
||||
|
@ -403,6 +493,11 @@ void MemoryViewWidget::customMenuRequested(QPoint pos)
|
|||
|
||||
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
|
||||
m_actionBYTE = new QAction(tr("Show as 1 byte"));
|
||||
m_actionBYTE->setCheckable(true);
|
||||
|
@ -446,6 +541,8 @@ void MemoryViewWidget::customMenuRequested(QPoint pos)
|
|||
m_contextMenu->addAction(action);
|
||||
connect(action, &QAction::triggered, this, [this]() { contextPaste(); });
|
||||
}
|
||||
m_actionLittleEndian->setChecked(m_table.GetLittleEndian());
|
||||
|
||||
const MemoryViewType currentViewType = m_table.GetViewType();
|
||||
|
||||
m_actionBYTE->setChecked(currentViewType == MemoryViewType::BYTE);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QTabBar>
|
||||
#include <QtGui/QPainter>
|
||||
#include <QtCore/QtEndian>
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
@ -28,22 +29,39 @@ class MemoryViewTable
|
|||
QWidget* parent;
|
||||
DebugInterface* m_cpu;
|
||||
MemoryViewType displayType = MemoryViewType::BYTE;
|
||||
bool littleEndian = true;
|
||||
u32 rowCount;
|
||||
u32 rowVisible;
|
||||
s32 rowHeight;
|
||||
|
||||
// Stuff used for selection handling
|
||||
// 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 textXAxis; // Where the text view begins
|
||||
s32 row1YAxis; // Where the first row starts
|
||||
s32 segmentXAxis[16]; // Where the segments begin
|
||||
s32 valuexAxis; // Where the hexadecimal view begins
|
||||
s32 textXAxis; // Where the text view begins
|
||||
s32 row1YAxis; // Where the first row starts
|
||||
s32 segmentXAxis[16]; // Where the segments begin
|
||||
bool selectedText = false; // Whether the user has clicked on text or hex
|
||||
|
||||
|
||||
bool selectedNibbleHI = false;
|
||||
|
||||
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:
|
||||
MemoryViewTable(QWidget* parent)
|
||||
: parent(parent){};
|
||||
|
@ -60,6 +78,8 @@ public:
|
|||
void SelectAt(QPoint pos);
|
||||
u128 GetSelectedSegment();
|
||||
void InsertAtCurrentSelection(const QString& text);
|
||||
void ForwardSelection();
|
||||
void BackwardSelection();
|
||||
// Returns true if the keypress was handled
|
||||
bool KeyPress(int key, QChar keychar);
|
||||
|
||||
|
@ -72,6 +92,16 @@ public:
|
|||
{
|
||||
displayType = viewType;
|
||||
}
|
||||
|
||||
bool GetLittleEndian()
|
||||
{
|
||||
return littleEndian;
|
||||
}
|
||||
|
||||
void SetLittleEndian(bool le)
|
||||
{
|
||||
littleEndian = le;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -111,6 +141,7 @@ private:
|
|||
Ui::RegisterWidget ui;
|
||||
|
||||
QMenu* m_contextMenu = 0x0;
|
||||
QAction* m_actionLittleEndian;
|
||||
QAction* m_actionBYTE;
|
||||
QAction* m_actionBYTEHW;
|
||||
QAction* m_actionWORD;
|
||||
|
|
Loading…
Reference in New Issue