mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-23 15:50:10 +00:00
citra-qt: Rewrite register widget
This commit is contained in:
parent
0bf5a0bfc4
commit
ff551e4a9e
@ -13,6 +13,7 @@ set(SRCS
|
||||
debugger/graphics_framebuffer.cpp
|
||||
debugger/ramview.cpp
|
||||
debugger/registers.cpp
|
||||
debugger/register_view.cpp
|
||||
util/spinbox.cpp
|
||||
bootmanager.cpp
|
||||
hotkeys.cpp
|
||||
@ -33,6 +34,7 @@ set(HEADERS
|
||||
debugger/graphics_framebuffer.h
|
||||
debugger/ramview.h
|
||||
debugger/registers.h
|
||||
debugger/register_view.h
|
||||
util/spinbox.h
|
||||
bootmanager.h
|
||||
hotkeys.h
|
||||
|
212
src/citra_qt/debugger/register_view.cpp
Normal file
212
src/citra_qt/debugger/register_view.cpp
Normal file
@ -0,0 +1,212 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "register_view.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
#include <QShortcut>
|
||||
#include "core/core.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
|
||||
enum ExtraRegisters { REGISTER_M, REGISTER_IT, REGISTER_GE, REGISTER_DNM, REGISTER_CPSR, REGISTER_EXTRACOUNT };
|
||||
|
||||
RegisterView::RegisterView(QWidget* parent) : QFrame(parent)
|
||||
{
|
||||
font = QFont("Lucida Console", 10);
|
||||
|
||||
QFontMetrics metrics = QFontMetrics(font);
|
||||
charWidth = metrics.width('0');
|
||||
charHeight = metrics.height();
|
||||
charDescent = metrics.descent();
|
||||
|
||||
borderGap = 3;
|
||||
valueX = borderGap + (maxNameLength() + 1)*charWidth;
|
||||
|
||||
lastPc = 0;
|
||||
selectedRegister = 0;
|
||||
changedRegs = new ChangedReg[getNumRegisters()];
|
||||
memset(changedRegs, 0, sizeof(ChangedReg)*getNumRegisters());
|
||||
|
||||
QShortcut* upShortcut = new QShortcut(QKeySequence(Qt::Key_Up), this);
|
||||
upShortcut->setContext(Qt::WidgetShortcut);
|
||||
connect(upShortcut, SIGNAL(activated()), this, SLOT(previousRegister()));
|
||||
|
||||
QShortcut* downShortcut = new QShortcut(QKeySequence(Qt::Key_Down), this);
|
||||
downShortcut->setContext(Qt::WidgetShortcut);
|
||||
connect(downShortcut, SIGNAL(activated()), this, SLOT(nextRegister()));
|
||||
|
||||
setFocusPolicy(Qt::FocusPolicy::ClickFocus);
|
||||
}
|
||||
|
||||
int RegisterView::maxNameLength() const
|
||||
{
|
||||
int maxLength = 0;
|
||||
for (int i = 0; i < getNumRegisters(); i++)
|
||||
{
|
||||
maxLength = qMax<int>(maxLength, getRegisterName(i).length());
|
||||
}
|
||||
|
||||
return maxLength;
|
||||
}
|
||||
|
||||
QSize RegisterView::sizeHint() const
|
||||
{
|
||||
int maxLength = maxNameLength();
|
||||
|
||||
int numChars = maxLength + 1 + 8;
|
||||
return QSize(2 * borderGap + numChars*charWidth, 2 * borderGap + getNumRegisters()*charHeight);
|
||||
}
|
||||
|
||||
int RegisterView::getNumRegisters() const
|
||||
{
|
||||
return 16 + REGISTER_EXTRACOUNT;
|
||||
}
|
||||
|
||||
QString RegisterView::getRegisterName(int index) const
|
||||
{
|
||||
if (index < 16)
|
||||
return QString("r%1").arg(index);
|
||||
|
||||
switch (index - 16)
|
||||
{
|
||||
case REGISTER_CPSR:
|
||||
return "cpsr";
|
||||
case REGISTER_M:
|
||||
return "m";
|
||||
case REGISTER_IT:
|
||||
return "it";
|
||||
case REGISTER_GE:
|
||||
return "ge";
|
||||
case REGISTER_DNM:
|
||||
return "dnm";
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int RegisterView::getRegisterValue(int index) const
|
||||
{
|
||||
ARM_Interface* app_core = Core::g_app_core;
|
||||
if (app_core == nullptr)
|
||||
return 0;
|
||||
|
||||
if (index < 16)
|
||||
return app_core->GetReg(index);
|
||||
|
||||
switch (index - 16)
|
||||
{
|
||||
case REGISTER_CPSR:
|
||||
return app_core->GetCPSR();
|
||||
case REGISTER_M:
|
||||
return app_core->GetCPSR() & 0x1F;
|
||||
case REGISTER_IT:
|
||||
return (app_core->GetCPSR() >> 10) & 0x3F;
|
||||
case REGISTER_GE:
|
||||
return (app_core->GetCPSR() >> 16) & 0xF;
|
||||
case REGISTER_DNM:
|
||||
return (app_core->GetCPSR() >> 20) & 0xF;
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterView::refreshChangedRegs()
|
||||
{
|
||||
ARM_Interface* app_core = Core::g_app_core;
|
||||
if (app_core == nullptr || app_core->GetPC() == lastPc)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < getNumRegisters(); i++)
|
||||
{
|
||||
unsigned int value = getRegisterValue(i);
|
||||
changedRegs[i].changed = (value != changedRegs[i].oldValue);
|
||||
changedRegs[i].oldValue = value;
|
||||
}
|
||||
|
||||
lastPc = app_core->GetPC();
|
||||
}
|
||||
|
||||
void RegisterView::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
QPainter p(this);
|
||||
|
||||
refreshChangedRegs();
|
||||
|
||||
// clear background
|
||||
p.setBrush(QBrush(QColor(Qt::white)));
|
||||
p.setPen(QPen(QColor(Qt::white)));
|
||||
p.drawRect(0, 0, width(), height());
|
||||
|
||||
p.setFont(font);
|
||||
for (int i = 0; i < getNumRegisters(); i++)
|
||||
{
|
||||
int rowY = borderGap + i*charHeight;
|
||||
int textOffset = charHeight - charDescent;
|
||||
|
||||
// highlight selected register
|
||||
if (i == selectedRegister)
|
||||
{
|
||||
QColor col(0xE8, 0xEF, 0xFF);
|
||||
p.setBrush(col);
|
||||
p.setPen(col);
|
||||
p.drawRect(0, rowY - 1, width(), charHeight - 1);
|
||||
}
|
||||
|
||||
p.setPen(QColor(0, 0, 0x60));
|
||||
p.drawText(borderGap, rowY + textOffset, getRegisterName(i));
|
||||
|
||||
if (changedRegs[i].changed)
|
||||
p.setPen(QColor(Qt::red));
|
||||
else
|
||||
p.setPen(QColor(Qt::black));
|
||||
|
||||
QString value;
|
||||
value.sprintf("%08X", getRegisterValue(i));
|
||||
|
||||
p.drawText(valueX, rowY + textOffset, value);
|
||||
}
|
||||
|
||||
// draw frame last, otherwise it gets overwritten
|
||||
QFrame::paintEvent(event);
|
||||
}
|
||||
|
||||
int RegisterView::yToRow(int y) const
|
||||
{
|
||||
if (y < borderGap || y + borderGap >= height())
|
||||
return -1;
|
||||
|
||||
return (y - borderGap) / charHeight;
|
||||
}
|
||||
|
||||
void RegisterView::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
int newRow = yToRow(event->y());
|
||||
if (newRow != -1 && selectedRegister != newRow)
|
||||
{
|
||||
selectedRegister = newRow;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterView::mouseMoveEvent(QMouseEvent* event)
|
||||
{
|
||||
if (event->buttons() & (Qt::MouseButton::LeftButton | Qt::MouseButton::RightButton))
|
||||
{
|
||||
int newRow = yToRow(event->y());
|
||||
if (newRow != -1 && selectedRegister != newRow)
|
||||
{
|
||||
selectedRegister = newRow;
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterView::previousRegister()
|
||||
{
|
||||
selectedRegister = qMax<int>(0, selectedRegister - 1);
|
||||
update();
|
||||
}
|
||||
|
||||
void RegisterView::nextRegister()
|
||||
{
|
||||
selectedRegister = qMin<int>(getNumRegisters() - 1, selectedRegister + 1);
|
||||
update();
|
||||
}
|
47
src/citra_qt/debugger/register_view.h
Normal file
47
src/citra_qt/debugger/register_view.h
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
#pragma once
|
||||
#include <QFrame>
|
||||
|
||||
class RegisterView: public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RegisterView(QWidget* parent = NULL);
|
||||
|
||||
protected:
|
||||
QSize sizeHint() const;
|
||||
QSize minimumSizeHint() const { return sizeHint(); }
|
||||
void paintEvent(QPaintEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
private slots:
|
||||
void previousRegister();
|
||||
void nextRegister();
|
||||
private:
|
||||
int maxNameLength() const;
|
||||
int yToRow(int y) const;
|
||||
int getNumRegisters() const;
|
||||
QString getRegisterName(int index) const;
|
||||
unsigned int getRegisterValue(int index) const;
|
||||
void refreshChangedRegs();
|
||||
|
||||
struct ChangedReg
|
||||
{
|
||||
unsigned int oldValue;
|
||||
bool changed;
|
||||
};
|
||||
|
||||
QFont font;
|
||||
int charWidth;
|
||||
int charHeight;
|
||||
int charDescent;
|
||||
int borderGap;
|
||||
int valueX;
|
||||
|
||||
unsigned int lastPc;
|
||||
ChangedReg* changedRegs;
|
||||
int selectedRegister;
|
||||
};
|
@ -7,61 +7,70 @@
|
||||
#include "core/core.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
|
||||
#include <QSpacerItem>
|
||||
|
||||
RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent)
|
||||
{
|
||||
cpu_regs_ui.setupUi(this);
|
||||
|
||||
tree = cpu_regs_ui.treeWidget;
|
||||
tree->addTopLevelItem(registers = new QTreeWidgetItem(QStringList("Registers")));
|
||||
tree->addTopLevelItem(CSPR = new QTreeWidgetItem(QStringList("CSPR")));
|
||||
addCpsrFlag("n",31);
|
||||
addCpsrFlag("z",30);
|
||||
addCpsrFlag("c",29);
|
||||
addCpsrFlag("v",28);
|
||||
addCpsrFlag("q",27);
|
||||
addCpsrFlag("j",24);
|
||||
addCpsrFlag("e",9);
|
||||
addCpsrFlag("a",8);
|
||||
addCpsrFlag("i",7);
|
||||
addCpsrFlag("f",6);
|
||||
addCpsrFlag("t",5);
|
||||
|
||||
cpu_regs_ui.flags->addItem(new QSpacerItem(0,0,QSizePolicy::Minimum,QSizePolicy::Expanding));
|
||||
}
|
||||
|
||||
registers->setExpanded(true);
|
||||
CSPR->setExpanded(true);
|
||||
void RegistersWidget::addCpsrFlag(QString name, int bitPos)
|
||||
{
|
||||
QCheckBox* box = new QCheckBox(name,this);
|
||||
cpu_regs_ui.flags->addWidget(box);
|
||||
connect(box,SIGNAL(stateChanged(int)),this,SLOT(OnFlagToggled(int)));
|
||||
|
||||
for (int i = 0; i < 16; ++i)
|
||||
flagLookup[box] = bitPos;
|
||||
}
|
||||
|
||||
void RegistersWidget::OnFlagToggled(int state)
|
||||
{
|
||||
QCheckBox* sender = dynamic_cast<QCheckBox*>(this->sender());
|
||||
int bitPos = flagLookup[sender];
|
||||
|
||||
// TODO: should probably not be done while the core is running...
|
||||
ARM_Interface* core = Core::g_app_core;
|
||||
if (core != nullptr)
|
||||
{
|
||||
QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("R[%1]").arg(i, 2, 10, QLatin1Char('0'))));
|
||||
registers->addChild(child);
|
||||
}
|
||||
u32 cpsr = core->GetCPSR() & ~(1 << bitPos);
|
||||
|
||||
CSPR->addChild(new QTreeWidgetItem(QStringList("M")));
|
||||
CSPR->addChild(new QTreeWidgetItem(QStringList("T")));
|
||||
CSPR->addChild(new QTreeWidgetItem(QStringList("F")));
|
||||
CSPR->addChild(new QTreeWidgetItem(QStringList("I")));
|
||||
CSPR->addChild(new QTreeWidgetItem(QStringList("A")));
|
||||
CSPR->addChild(new QTreeWidgetItem(QStringList("E")));
|
||||
CSPR->addChild(new QTreeWidgetItem(QStringList("IT")));
|
||||
CSPR->addChild(new QTreeWidgetItem(QStringList("GE")));
|
||||
CSPR->addChild(new QTreeWidgetItem(QStringList("DNM")));
|
||||
CSPR->addChild(new QTreeWidgetItem(QStringList("J")));
|
||||
CSPR->addChild(new QTreeWidgetItem(QStringList("Q")));
|
||||
CSPR->addChild(new QTreeWidgetItem(QStringList("V")));
|
||||
CSPR->addChild(new QTreeWidgetItem(QStringList("C")));
|
||||
CSPR->addChild(new QTreeWidgetItem(QStringList("Z")));
|
||||
CSPR->addChild(new QTreeWidgetItem(QStringList("N")));
|
||||
if (state != 0)
|
||||
cpsr |= (1 << bitPos);
|
||||
|
||||
core->SetCPSR(cpsr);
|
||||
cpu_regs_ui.registerView->update();
|
||||
}
|
||||
}
|
||||
|
||||
void RegistersWidget::OnCPUStepped()
|
||||
{
|
||||
ARM_Interface* app_core = Core::g_app_core;
|
||||
|
||||
u32 cpsr = app_core->GetCPSR();
|
||||
for (auto it = flagLookup.begin(); it != flagLookup.end(); it++)
|
||||
{
|
||||
QCheckBox* box = it.key();
|
||||
int bitPos = it.value();
|
||||
|
||||
for (int i = 0; i < 16; ++i)
|
||||
registers->child(i)->setText(1, QString("0x%1").arg(app_core->GetReg(i), 8, 16, QLatin1Char('0')));
|
||||
bool check = (cpsr & (1 << bitPos)) != 0;
|
||||
box->blockSignals(true);
|
||||
box->setChecked(check);
|
||||
box->blockSignals(false);
|
||||
}
|
||||
|
||||
CSPR->setText(1, QString("0x%1").arg(app_core->GetCPSR(), 8, 16, QLatin1Char('0')));
|
||||
CSPR->child(0)->setText(1, QString("b%1").arg(app_core->GetCPSR() & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode
|
||||
CSPR->child(1)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 5) & 0x1)); // T - State
|
||||
CSPR->child(2)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 6) & 0x1)); // F - FIQ disable
|
||||
CSPR->child(3)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 7) & 0x1)); // I - IRQ disable
|
||||
CSPR->child(4)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 8) & 0x1)); // A - Imprecise abort
|
||||
CSPR->child(5)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 9) & 0x1)); // E - Data endianess
|
||||
CSPR->child(6)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 10) & 0x3F)); // IT - If-Then state (DNM)
|
||||
CSPR->child(7)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 16) & 0xF)); // GE - Greater-than-or-Equal
|
||||
CSPR->child(8)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 20) & 0xF)); // DNM - Do not modify
|
||||
CSPR->child(9)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 24) & 0x1)); // J - Java state
|
||||
CSPR->child(10)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 27) & 0x1)); // Q - Sticky overflow
|
||||
CSPR->child(11)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 28) & 0x1)); // V - Overflow
|
||||
CSPR->child(12)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 29) & 0x1)); // C - Carry/Borrow/Extend
|
||||
CSPR->child(13)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 30) & 0x1)); // Z - Zero
|
||||
CSPR->child(14)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 31) & 0x1)); // N - Negative/Less than
|
||||
cpu_regs_ui.registerView->update();
|
||||
}
|
||||
|
@ -6,9 +6,12 @@
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QMap>
|
||||
#include <QCheckBox>
|
||||
|
||||
class QTreeWidget;
|
||||
|
||||
|
||||
class RegistersWidget : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -18,12 +21,10 @@ public:
|
||||
|
||||
public slots:
|
||||
void OnCPUStepped();
|
||||
|
||||
void OnFlagToggled(int state);
|
||||
private:
|
||||
void addCpsrFlag(QString name, int bitPos);
|
||||
|
||||
QMap<QCheckBox*,int> flagLookup;
|
||||
Ui::ARMRegisters cpu_regs_ui;
|
||||
|
||||
QTreeWidget* tree;
|
||||
|
||||
QTreeWidgetItem* registers;
|
||||
QTreeWidgetItem* CSPR;
|
||||
};
|
||||
|
@ -14,27 +14,41 @@
|
||||
<string>ARM Registers</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="treeWidget">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="RegisterView" name="registerView">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Panel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="midLineWidth">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Register</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QVBoxLayout" name="flags">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>RegisterView</class>
|
||||
<extends>QFrame</extends>
|
||||
<header>debugger/register_view.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
Loading…
Reference in New Issue
Block a user