citra-qt: Rewrite register widget
This commit is contained in:
parent
a46371329d
commit
868e6f2d82
8 changed files with 352 additions and 67 deletions
|
@ -13,6 +13,8 @@ set(SRCS
|
|||
debugger/graphics_framebuffer.cpp
|
||||
debugger/ramview.cpp
|
||||
debugger/registers.cpp
|
||||
debugger/register_view.cpp
|
||||
debugger/util.cpp
|
||||
util/spinbox.cpp
|
||||
bootmanager.cpp
|
||||
hotkeys.cpp
|
||||
|
@ -33,6 +35,8 @@ set(HEADERS
|
|||
debugger/graphics_framebuffer.h
|
||||
debugger/ramview.h
|
||||
debugger/registers.h
|
||||
debugger/register_view.h
|
||||
debugger/util.h
|
||||
util/spinbox.h
|
||||
bootmanager.h
|
||||
hotkeys.h
|
||||
|
|
168
src/citra_qt/debugger/register_view.cpp
Normal file
168
src/citra_qt/debugger/register_view.cpp
Normal file
|
@ -0,0 +1,168 @@
|
|||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
#include <QShortcut>
|
||||
|
||||
#include "core/debugger/debug_interface.h"
|
||||
|
||||
#include "register_view.h"
|
||||
|
||||
RegisterView::RegisterView(QWidget* parent) : QFrame(parent) {
|
||||
// initialize registers
|
||||
for (int cat = 0; cat < 2; cat++) {
|
||||
for (int index = 0; index < g_debug->GetNumRegsInCategory(cat); index++) {
|
||||
Register reg;
|
||||
reg.category = cat;
|
||||
reg.index = index;
|
||||
reg.oldValue = 0;
|
||||
reg.changed = false;
|
||||
|
||||
registers.append(reg);
|
||||
}
|
||||
}
|
||||
|
||||
initializeSize();
|
||||
|
||||
last_pc = 0;
|
||||
selection = 0;
|
||||
|
||||
QShortcut* up_shortcut = new QShortcut(QKeySequence(Qt::Key_Up), this);
|
||||
up_shortcut->setContext(Qt::WidgetShortcut);
|
||||
connect(up_shortcut, SIGNAL(activated()), this, SLOT(previousRegister()));
|
||||
|
||||
QShortcut* down_shortcut = new QShortcut(QKeySequence(Qt::Key_Down), this);
|
||||
down_shortcut->setContext(Qt::WidgetShortcut);
|
||||
connect(down_shortcut, SIGNAL(activated()), this, SLOT(nextRegister()));
|
||||
|
||||
setFocusPolicy(Qt::FocusPolicy::ClickFocus);
|
||||
}
|
||||
|
||||
void RegisterView::initializeSize() {
|
||||
// calculate max register name length
|
||||
int max_length = 0;
|
||||
for (int i = 0; i < registers.size(); i++) {
|
||||
const char* name = g_debug->GetRegName(registers[i].category,registers[i].index);
|
||||
max_length = qMax<int>(max_length, strlen(name));
|
||||
}
|
||||
|
||||
// initialize positions
|
||||
border_gap = 3;
|
||||
value_offset = border_gap + (max_length + 1) * font.charWidth();
|
||||
|
||||
// characters per line = length of register name + space + length of register value
|
||||
int numChars = max_length + 1 + 8;
|
||||
|
||||
// calculate size of widget. borderGap pixels to all sides,
|
||||
// and as many rows and columns as needed
|
||||
QSize size = QSize(2 * border_gap + numChars*font.charWidth(),
|
||||
2 * border_gap + registers.size()*font.charHeight());
|
||||
|
||||
// make sure the widget has exactly that size
|
||||
setMinimumSize(size);
|
||||
setMaximumSize(size);
|
||||
}
|
||||
|
||||
void RegisterView::refreshChangedRegs() {
|
||||
// the registers need to be refreshed if the PC of the core has
|
||||
// changed since the last call
|
||||
if (g_debug->GetPC() == last_pc)
|
||||
return;
|
||||
|
||||
for (Register& reg: registers) {
|
||||
unsigned int value = g_debug->GetRegValue(reg.category,reg.index);
|
||||
reg.changed = (value != reg.oldValue);
|
||||
reg.oldValue = value;
|
||||
}
|
||||
|
||||
last_pc = g_debug->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.font());
|
||||
|
||||
QPoint pos;
|
||||
QString value;
|
||||
for (int i = 0; i < registers.size(); i++) {
|
||||
Register& reg = registers[i];
|
||||
int y = border_gap + i * font.charHeight();
|
||||
|
||||
// highlight background of selected register
|
||||
if (i == selection)
|
||||
{
|
||||
QColor col(0xE8, 0xEF, 0xFF);
|
||||
p.setBrush(col);
|
||||
p.setPen(col);
|
||||
p.drawRect(0, y - 1, width(), font.charHeight() - 1);
|
||||
}
|
||||
|
||||
// draw register name
|
||||
pos = font.calculateDrawPos(border_gap, y);
|
||||
p.setPen(QColor(0, 0, 0x60));
|
||||
p.drawText(pos, g_debug->GetRegName(reg.category, reg.index));
|
||||
|
||||
// draw register value
|
||||
pos = font.calculateDrawPos(value_offset, y);
|
||||
value.sprintf("%08X", g_debug->GetRegValue(reg.category, reg.index));
|
||||
|
||||
// draw changed registers in red, rest in black
|
||||
if (reg.changed)
|
||||
p.setPen(QColor(Qt::red));
|
||||
else
|
||||
p.setPen(QColor(Qt::black));
|
||||
|
||||
p.drawText(pos, value);
|
||||
}
|
||||
|
||||
// draw frame last, otherwise it gets overwritten
|
||||
QFrame::paintEvent(event);
|
||||
}
|
||||
|
||||
int RegisterView::mapPositionToRegister(QPoint pos) const {
|
||||
int y = pos.y();
|
||||
|
||||
if (y < border_gap || y + border_gap >= height())
|
||||
return -1;
|
||||
|
||||
return (y - border_gap) / font.charHeight();
|
||||
}
|
||||
|
||||
void RegisterView::mousePressEvent(QMouseEvent* event) {
|
||||
int new_selection = mapPositionToRegister(event->pos());
|
||||
if (new_selection != -1 && selection != new_selection) {
|
||||
selection = new_selection;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterView::mouseMoveEvent(QMouseEvent* event) {
|
||||
if (event->buttons() & (Qt::MouseButton::LeftButton | Qt::MouseButton::RightButton)) {
|
||||
int new_selection = mapPositionToRegister(event->pos());
|
||||
if (new_selection != -1 && selection != new_selection)
|
||||
{
|
||||
selection = new_selection;
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterView::moveSelectionUp() {
|
||||
selection = qMax<int>(0, selection - 1);
|
||||
update();
|
||||
}
|
||||
|
||||
void RegisterView::moveSelectionDown() {
|
||||
selection = qMin<int>(registers.size() - 1, selection + 1);
|
||||
update();
|
||||
}
|
52
src/citra_qt/debugger/register_view.h
Normal file
52
src/citra_qt/debugger/register_view.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
#pragma once
|
||||
|
||||
#include <QFrame>
|
||||
#include "util.h"
|
||||
|
||||
class RegisterView: public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RegisterView(QWidget* parent = NULL);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
|
||||
private slots:
|
||||
// Select previous register
|
||||
void moveSelectionUp();
|
||||
|
||||
// Select next register
|
||||
void moveSelectionDown();
|
||||
|
||||
private:
|
||||
// Reloads register values if necessary
|
||||
void refreshChangedRegs();
|
||||
|
||||
// Calculates and sets the widget size and the used positions
|
||||
void initializeSize();
|
||||
|
||||
// Returns the number of the register at the given position
|
||||
int mapPositionToRegister(QPoint pos) const;
|
||||
|
||||
struct Register
|
||||
{
|
||||
unsigned int oldValue;
|
||||
bool changed;
|
||||
int category;
|
||||
int index;
|
||||
};
|
||||
|
||||
DebuggerFont font;
|
||||
int border_gap;
|
||||
int value_offset;
|
||||
|
||||
unsigned int last_pc;
|
||||
QVector<Register> registers;
|
||||
int selection;
|
||||
};
|
|
@ -2,68 +2,53 @@
|
|||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "registers.h"
|
||||
#include <QSpacerItem>
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/debugger/debug_interface.h"
|
||||
|
||||
#include "registers.h"
|
||||
|
||||
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")));
|
||||
|
||||
registers->setExpanded(true);
|
||||
CSPR->setExpanded(true);
|
||||
|
||||
for (int i = 0; i < 16; ++i)
|
||||
for (int i = 0; i < g_debug->GetNumRegsInCategory(REGCAT_CPSR_FLAGS); i++)
|
||||
{
|
||||
QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("R[%1]").arg(i, 2, 10, QLatin1Char('0'))));
|
||||
registers->addChild(child);
|
||||
const char* name = g_debug->GetRegName(REGCAT_CPSR_FLAGS,i);
|
||||
|
||||
QCheckBox* box = new QCheckBox(name,this);
|
||||
cpu_regs_ui.flags->addWidget(box);
|
||||
connect(box,SIGNAL(stateChanged(int)),this,SLOT(OnFlagToggled(int)));
|
||||
|
||||
flagLookup[box] = i;
|
||||
}
|
||||
|
||||
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")));
|
||||
cpu_regs_ui.flags->addItem(new QSpacerItem(0,0,QSizePolicy::Minimum,QSizePolicy::Expanding));
|
||||
}
|
||||
|
||||
void RegistersWidget::OnFlagToggled(int state)
|
||||
{
|
||||
QCheckBox* sender = dynamic_cast<QCheckBox*>(this->sender());
|
||||
int index = flagLookup[sender];
|
||||
|
||||
g_debug->SetRegValue(REGCAT_CPSR_FLAGS,index,state == 0 ? 0 : 1);
|
||||
cpu_regs_ui.registerView->update();
|
||||
}
|
||||
|
||||
void RegistersWidget::OnDebugModeEntered()
|
||||
{
|
||||
ARM_Interface* app_core = Core::g_app_core;
|
||||
for (auto it = flagLookup.begin(); it != flagLookup.end(); it++)
|
||||
{
|
||||
QCheckBox* box = it.key();
|
||||
int index = 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 = g_debug->GetRegValue(REGCAT_CPSR_FLAGS,index) != 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();
|
||||
}
|
||||
|
||||
void RegistersWidget::OnDebugModeLeft()
|
||||
|
|
|
@ -6,9 +6,12 @@
|
|||
|
||||
#include <QDockWidget>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QMap>
|
||||
#include <QCheckBox>
|
||||
|
||||
class QTreeWidget;
|
||||
|
||||
|
||||
class RegistersWidget : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -19,12 +22,9 @@ public:
|
|||
public slots:
|
||||
void OnDebugModeEntered();
|
||||
void OnDebugModeLeft();
|
||||
void OnFlagToggled(int state);
|
||||
|
||||
private:
|
||||
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>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Register</string>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
<property name="lineWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="midLineWidth">
|
||||
<number>3</number>
|
||||
</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>
|
||||
|
|
29
src/citra_qt/debugger/util.cpp
Normal file
29
src/citra_qt/debugger/util.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QFontMetrics>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
DebuggerFont::DebuggerFont() {
|
||||
font_ = QFont("Lucida Console", 10);
|
||||
font_.setStyleHint(QFont::Courier, QFont::PreferMatch);
|
||||
updateMetrics();
|
||||
}
|
||||
|
||||
void DebuggerFont::setBold(bool bold) {
|
||||
font_.setBold(bold);
|
||||
updateMetrics();
|
||||
}
|
||||
|
||||
QPoint DebuggerFont::calculateDrawPos(int left, int top) {
|
||||
return QPoint(left, top + charHeight() - charDescent());
|
||||
}
|
||||
|
||||
void DebuggerFont::updateMetrics() {
|
||||
QFontMetrics metrics = QFontMetrics(font_);
|
||||
width = metrics.width('0');
|
||||
height = metrics.height();
|
||||
descent = metrics.descent();
|
||||
}
|
33
src/citra_qt/debugger/util.h
Normal file
33
src/citra_qt/debugger/util.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
#pragma once
|
||||
|
||||
#include <QFont>
|
||||
#include <QPoint>
|
||||
|
||||
// Abstracts font calculations used by some
|
||||
// debugger widgets
|
||||
class DebuggerFont {
|
||||
public:
|
||||
DebuggerFont();
|
||||
|
||||
// Returns the position needed to draw a string that should
|
||||
// have its upper left corner at the given coordinates
|
||||
QPoint calculateDrawPos(int left, int top);
|
||||
|
||||
QFont& font() { return font_; }
|
||||
int charWidth() const { return width; };
|
||||
int charHeight() const { return height; };
|
||||
int charDescent() const { return descent; };
|
||||
|
||||
void setBold(bool bold);
|
||||
|
||||
private:
|
||||
void updateMetrics();
|
||||
|
||||
QFont font_;
|
||||
int width;
|
||||
int height;
|
||||
int descent;
|
||||
};
|
Loading…
Reference in a new issue