citra-qt: Rewrite register widget

This commit is contained in:
Kingcom 2015-01-06 12:43:12 +01:00
parent 0bf5a0bfc4
commit ff551e4a9e
6 changed files with 348 additions and 63 deletions

View File

@ -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

View 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();
}

View 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;
};

View File

@ -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);
registers->setExpanded(true);
CSPR->setExpanded(true);
cpu_regs_ui.flags->addItem(new QSpacerItem(0,0,QSizePolicy::Minimum,QSizePolicy::Expanding));
}
for (int i = 0; i < 16; ++i)
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)));
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;
for (int i = 0; i < 16; ++i)
registers->child(i)->setText(1, QString("0x%1").arg(app_core->GetReg(i), 8, 16, QLatin1Char('0')));
u32 cpsr = app_core->GetCPSR();
for (auto it = flagLookup.begin(); it != flagLookup.end(); it++)
{
QCheckBox* box = it.key();
int bitPos = it.value();
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
bool check = (cpsr & (1 << bitPos)) != 0;
box->blockSignals(true);
box->setChecked(check);
box->blockSignals(false);
}
cpu_regs_ui.registerView->update();
}

View File

@ -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;
};

View File

@ -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>