First Commit

This commit is contained in:
2025-11-18 14:18:26 -07:00
parent 33eb6e3707
commit 27277ec342
6106 changed files with 3571167 additions and 0 deletions

View File

@@ -0,0 +1,467 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include "RegisterView.h"
#include "Debugger/JsonValueWrapper.h"
#include "QtUtils.h"
#include <QtGui/QMouseEvent>
#include <QtWidgets/QTabBar>
#include <QtWidgets/QStylePainter>
#include <QtWidgets/QStyleOptionTab>
#include <QtGui/QClipboard>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QProxyStyle>
#include <QtWidgets/QMessageBox>
#include <bit>
#define CAT_SHOW_FLOAT (categoryIndex == EECAT_FPR && m_showFPRFloat) || (categoryIndex == EECAT_VU0F && m_showVU0FFloat)
using namespace QtUtils;
RegisterView::RegisterView(const DebuggerViewParameters& parameters)
: DebuggerView(parameters, MONOSPACE_FONT)
{
this->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
ui.setupUi(this);
ui.registerTabs->setDrawBase(false);
connect(this, &RegisterView::customContextMenuRequested, this, &RegisterView::customMenuRequested);
connect(ui.registerTabs, &QTabBar::currentChanged, this, &RegisterView::tabCurrentChanged);
for (int i = 0; i < cpu().getRegisterCategoryCount(); i++)
{
ui.registerTabs->addTab(cpu().getRegisterCategoryName(i));
}
connect(ui.registerTabs, &QTabBar::currentChanged, [this]() { this->repaint(); });
receiveEvent<DebuggerEvents::Refresh>([this](const DebuggerEvents::Refresh& event) -> bool {
update();
return true;
});
}
RegisterView::~RegisterView()
{
}
void RegisterView::toJson(JsonValueWrapper& json)
{
DebuggerView::toJson(json);
json.value().AddMember("showVU0FFloat", m_showVU0FFloat, json.allocator());
json.value().AddMember("showFPRFloat", m_showFPRFloat, json.allocator());
}
bool RegisterView::fromJson(const JsonValueWrapper& json)
{
if (!DebuggerView::fromJson(json))
return false;
auto show_vu0f_float = json.value().FindMember("showVU0FFloat");
if (show_vu0f_float != json.value().MemberEnd() && show_vu0f_float->value.IsBool())
m_showVU0FFloat = show_vu0f_float->value.GetBool();
auto show_fpr_float = json.value().FindMember("showFPRFloat");
if (show_fpr_float != json.value().MemberEnd() && show_fpr_float->value.IsBool())
m_showFPRFloat = show_fpr_float->value.GetBool();
repaint();
return true;
}
void RegisterView::tabCurrentChanged(int cur)
{
m_rowStart = 0;
}
void RegisterView::paintEvent(QPaintEvent* event)
{
QPainter painter(this);
painter.setPen(this->palette().text().color());
m_renderStart = QPoint(0, ui.registerTabs->pos().y() + ui.registerTabs->size().height());
const QSize renderSize = QSize(this->size().width(), this->size().height() - ui.registerTabs->size().height());
m_rowHeight = painter.fontMetrics().height() + 2;
m_rowEnd = m_rowStart + (renderSize.height() / m_rowHeight) - 1; // Maybe move this to a onsize event
bool alternate = m_rowStart % 2;
const int categoryIndex = ui.registerTabs->currentIndex();
// Used for 128 bit and VU0f registers
const int titleStartX = m_renderStart.x() + (painter.fontMetrics().averageCharWidth() * 6);
m_fieldWidth = ((renderSize.width() - (painter.fontMetrics().averageCharWidth() * 6)) / 4);
m_fieldStartX[0] = titleStartX;
m_fieldStartX[1] = titleStartX + m_fieldWidth;
m_fieldStartX[2] = titleStartX + (m_fieldWidth * 2);
m_fieldStartX[3] = titleStartX + (m_fieldWidth * 3);
if (categoryIndex == EECAT_VU0F)
{
painter.fillRect(m_renderStart.x(), m_renderStart.y(), renderSize.width(), m_rowHeight, this->palette().highlight());
painter.drawText(m_fieldStartX[0], m_renderStart.y(), m_fieldWidth, m_rowHeight, Qt::AlignLeft, "W");
painter.drawText(m_fieldStartX[1], m_renderStart.y(), m_fieldWidth, m_rowHeight, Qt::AlignLeft, "Z");
painter.drawText(m_fieldStartX[2], m_renderStart.y(), m_fieldWidth, m_rowHeight, Qt::AlignLeft, "Y");
painter.drawText(m_fieldStartX[3], m_renderStart.y(), m_fieldWidth, m_rowHeight, Qt::AlignLeft, "X");
m_renderStart += QPoint(0, m_rowHeight); // Make room for VU0f titles
}
// Find the longest register name and calculate where to place our values
// off of that.
// Can probably constexpr the loop out as register names are known during runtime
int safeValueStartX = 0;
for (int i = 0; i < cpu().getRegisterCount(categoryIndex); i++)
{
const int registerNameWidth = strlen(cpu().getRegisterName(categoryIndex, i));
if (safeValueStartX < registerNameWidth)
{
safeValueStartX = registerNameWidth;
}
}
// Add a space between the value and name
safeValueStartX += 2;
// Convert to width in pixels
safeValueStartX *= painter.fontMetrics().averageCharWidth();
// Make it relative to where we start rendering
safeValueStartX += m_renderStart.x();
for (s32 i = 0; i < cpu().getRegisterCount(categoryIndex) - m_rowStart; i++)
{
const s32 registerIndex = i + m_rowStart;
const int yStart = (i * m_rowHeight) + m_renderStart.y();
painter.fillRect(m_renderStart.x(), yStart, renderSize.width(), m_rowHeight, alternate ? this->palette().base() : this->palette().alternateBase());
alternate = !alternate;
// Draw register name
painter.setPen(this->palette().text().color());
painter.drawText(m_renderStart.x() + painter.fontMetrics().averageCharWidth(), yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft, cpu().getRegisterName(categoryIndex, registerIndex));
if (cpu().getRegisterSize(categoryIndex) == 128)
{
const u128 curRegister = cpu().getRegister(categoryIndex, registerIndex);
int regIndex = 3;
for (int j = 0; j < 4; j++)
{
if (m_selectedRow == registerIndex && m_selected128Field == j)
painter.setPen(this->palette().highlight().color());
else
painter.setPen(this->palette().text().color());
if (categoryIndex == EECAT_VU0F && m_showVU0FFloat)
painter.drawText(m_fieldStartX[j], yStart, m_fieldWidth, m_rowHeight, Qt::AlignLeft,
painter.fontMetrics().elidedText(QString::number(std::bit_cast<float>(cpu().getRegister(categoryIndex, registerIndex)._u32[regIndex])), Qt::ElideRight, m_fieldWidth - painter.fontMetrics().averageCharWidth()));
else
painter.drawText(m_fieldStartX[j], yStart, m_fieldWidth, m_rowHeight,
Qt::AlignLeft, FilledQStringFromValue(curRegister._u32[regIndex], 16));
regIndex--;
}
painter.setPen(this->palette().text().color());
}
else
{
if (m_selectedRow == registerIndex)
painter.setPen(this->palette().highlight().color());
else
painter.setPen(this->palette().text().color());
if (categoryIndex == EECAT_FPR && m_showFPRFloat)
painter.drawText(safeValueStartX, yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft,
QString("%1").arg(QString::number(std::bit_cast<float>(cpu().getRegister(categoryIndex, registerIndex)._u32[0]))).toUpper());
else if (cpu().getRegisterSize(categoryIndex) == 64)
painter.drawText(safeValueStartX, yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft,
FilledQStringFromValue(cpu().getRegister(categoryIndex, registerIndex).lo, 16));
else
painter.drawText(safeValueStartX, yStart, renderSize.width(), m_rowHeight, Qt::AlignLeft,
FilledQStringFromValue(cpu().getRegister(categoryIndex, registerIndex)._u32[0], 16));
}
}
painter.end();
}
void RegisterView::mousePressEvent(QMouseEvent* event)
{
const int categoryIndex = ui.registerTabs->currentIndex();
m_selectedRow = static_cast<int>(((event->position().y() - m_renderStart.y()) / m_rowHeight)) + m_rowStart;
// For 128 bit types, support selecting segments
if (cpu().getRegisterSize(categoryIndex) == 128)
{
constexpr auto inRange = [](u32 low, u32 high, u32 val) {
return (low <= val && val <= high);
};
for (int i = 0; i < 4; i++)
{
if (inRange(m_fieldStartX[i], m_fieldStartX[i] + m_fieldWidth, event->position().x()))
{
m_selected128Field = i;
}
}
}
this->repaint();
}
void RegisterView::wheelEvent(QWheelEvent* event)
{
if (event->angleDelta().y() < 0 && m_rowEnd < cpu().getRegisterCount(ui.registerTabs->currentIndex()))
{
m_rowStart += 1;
}
else if (event->angleDelta().y() > 0 && m_rowStart > 0)
{
m_rowStart -= 1;
}
this->repaint();
}
void RegisterView::mouseDoubleClickEvent(QMouseEvent* event)
{
if (!cpu().isAlive())
return;
if (m_selectedRow > m_rowEnd) // Unsigned underflow; selectedRow will be > m_rowEnd (technically negative)
return;
const int categoryIndex = ui.registerTabs->currentIndex();
if (cpu().getRegisterSize(categoryIndex) == 128)
contextChangeSegment();
else
contextChangeValue();
}
void RegisterView::customMenuRequested(QPoint pos)
{
if (!cpu().isAlive())
return;
if (m_selectedRow > m_rowEnd) // Unsigned underflow; selectedRow will be > m_rowEnd (technically negative)
return;
QMenu* menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose);
const int categoryIndex = ui.registerTabs->currentIndex();
if (categoryIndex == EECAT_FPR)
{
QAction* action = menu->addAction(tr("Show as Float"));
action->setCheckable(true);
action->setChecked(m_showFPRFloat);
connect(action, &QAction::triggered, this, [this]() {
m_showFPRFloat = !m_showFPRFloat;
repaint();
});
menu->addSeparator();
}
if (categoryIndex == EECAT_VU0F)
{
QAction* action = menu->addAction(tr("Show as Float"));
action->setCheckable(true);
action->setChecked(m_showVU0FFloat);
connect(action, &QAction::triggered, this, [this]() {
m_showVU0FFloat = !m_showVU0FFloat;
repaint();
});
menu->addSeparator();
}
if (cpu().getRegisterSize(categoryIndex) == 128)
{
connect(menu->addAction(tr("Copy Top Half")), &QAction::triggered, this, &RegisterView::contextCopyTop);
connect(menu->addAction(tr("Copy Bottom Half")), &QAction::triggered, this, &RegisterView::contextCopyBottom);
connect(menu->addAction(tr("Copy Segment")), &QAction::triggered, this, &RegisterView::contextCopySegment);
}
else
{
connect(menu->addAction(tr("Copy Value")), &QAction::triggered, this, &RegisterView::contextCopyValue);
}
menu->addSeparator();
if (cpu().getRegisterSize(categoryIndex) == 128)
{
connect(menu->addAction(tr("Change Top Half")), &QAction::triggered,
this, &RegisterView::contextChangeTop);
connect(menu->addAction(tr("Change Bottom Half")), &QAction::triggered,
this, &RegisterView::contextChangeBottom);
connect(menu->addAction(tr("Change Segment")), &QAction::triggered,
this, &RegisterView::contextChangeSegment);
}
else
{
connect(menu->addAction(tr("Change Value")), &QAction::triggered,
this, &RegisterView::contextChangeValue);
}
menu->addSeparator();
createEventActions<DebuggerEvents::GoToAddress>(menu, [this]() {
return contextCreateGotoEvent();
});
menu->popup(this->mapToGlobal(pos));
}
void RegisterView::contextCopyValue()
{
const int categoryIndex = ui.registerTabs->currentIndex();
const u128 val = cpu().getRegister(categoryIndex, m_selectedRow);
if (CAT_SHOW_FLOAT)
QApplication::clipboard()->setText(QString("%1").arg(QString::number(std::bit_cast<float>(val._u32[0])).toUpper(), 16));
else
QApplication::clipboard()->setText(QString("%1").arg(QString::number(val._u64[0], 16).toUpper(), 16));
}
void RegisterView::contextCopyTop()
{
const int categoryIndex = ui.registerTabs->currentIndex();
const u128 val = cpu().getRegister(categoryIndex, m_selectedRow);
QApplication::clipboard()->setText(FilledQStringFromValue(val.hi, 16));
}
void RegisterView::contextCopyBottom()
{
const int categoryIndex = ui.registerTabs->currentIndex();
const u128 val = cpu().getRegister(categoryIndex, m_selectedRow);
QApplication::clipboard()->setText(FilledQStringFromValue(val.lo, 16));
}
void RegisterView::contextCopySegment()
{
const int categoryIndex = ui.registerTabs->currentIndex();
const u128 val = cpu().getRegister(categoryIndex, m_selectedRow);
if (CAT_SHOW_FLOAT)
QApplication::clipboard()->setText(FilledQStringFromValue(std::bit_cast<float>(val._u32[3 - m_selected128Field]), 10));
else
QApplication::clipboard()->setText(FilledQStringFromValue(val._u32[3 - m_selected128Field], 16));
}
bool RegisterView::contextFetchNewValue(u64& out, u64 currentValue, bool segment)
{
const int categoryIndex = ui.registerTabs->currentIndex();
const bool floatingPoint = CAT_SHOW_FLOAT && segment;
const int regSize = cpu().getRegisterSize(categoryIndex);
bool ok = false;
QString existingValue("%1");
if (!floatingPoint)
existingValue = existingValue.arg(currentValue, regSize == 64 ? 16 : 8, 16, QChar('0'));
else
existingValue = existingValue.arg(std::bit_cast<float>((u32)currentValue));
//: Changing the value in a CPU register (e.g. "Change t0")
QString input = QInputDialog::getText(this, tr("Change %1").arg(cpu().getRegisterName(categoryIndex, m_selectedRow)), "",
QLineEdit::Normal, existingValue, &ok);
if (!ok)
return false;
if (!floatingPoint) // Get input as hexadecimal
{
out = input.toULongLong(&ok, 16);
if (!ok)
{
QMessageBox::warning(this, tr("Invalid register value"), tr("Invalid hexadecimal register value."));
return false;
}
}
else
{
out = std::bit_cast<u32>(input.toFloat(&ok));
if (!ok)
{
QMessageBox::warning(this, tr("Invalid register value"), tr("Invalid floating-point register value."));
return false;
}
}
return true;
}
void RegisterView::contextChangeValue()
{
const int categoryIndex = ui.registerTabs->currentIndex();
u64 newVal;
if (contextFetchNewValue(newVal, cpu().getRegister(categoryIndex, m_selectedRow).lo))
{
cpu().setRegister(categoryIndex, m_selectedRow, u128::From64(newVal));
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
}
}
void RegisterView::contextChangeTop()
{
u64 newVal;
u128 oldVal = cpu().getRegister(ui.registerTabs->currentIndex(), m_selectedRow);
if (contextFetchNewValue(newVal, oldVal.hi))
{
oldVal.hi = newVal;
cpu().setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal);
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
}
}
void RegisterView::contextChangeBottom()
{
u64 newVal;
u128 oldVal = cpu().getRegister(ui.registerTabs->currentIndex(), m_selectedRow);
if (contextFetchNewValue(newVal, oldVal.lo))
{
oldVal.lo = newVal;
cpu().setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal);
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
}
}
void RegisterView::contextChangeSegment()
{
u64 newVal;
u128 oldVal = cpu().getRegister(ui.registerTabs->currentIndex(), m_selectedRow);
if (contextFetchNewValue(newVal, oldVal._u32[3 - m_selected128Field], true))
{
oldVal._u32[3 - m_selected128Field] = (u32)newVal;
cpu().setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal);
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
}
}
std::optional<DebuggerEvents::GoToAddress> RegisterView::contextCreateGotoEvent()
{
const int categoryIndex = ui.registerTabs->currentIndex();
u128 regVal = cpu().getRegister(categoryIndex, m_selectedRow);
u32 addr = 0;
if (cpu().getRegisterSize(categoryIndex) == 128)
addr = regVal._u32[3 - m_selected128Field];
else
addr = regVal._u32[0];
if (!cpu().isValidAddress(addr))
{
QMessageBox::warning(
this,
tr("Invalid target address"),
tr("This register holds an invalid address."));
return std::nullopt;
}
DebuggerEvents::GoToAddress event;
event.address = addr;
return event;
}