From e5f09b8be65c06927164428b5d400024e2388dbc Mon Sep 17 00:00:00 2001 From: Mathieu Vaillancourt Date: Fri, 18 Apr 2014 18:30:53 -0400 Subject: UI/debugger changes --- src/citra_qt/debugger/callstack.cpp | 66 ++++++++++++++ src/citra_qt/debugger/callstack.hxx | 19 ++++ src/citra_qt/debugger/callstack.ui | 36 ++++++++ src/citra_qt/debugger/disassembler.cpp | 161 +++++++++++++++++++++++++++++++++ src/citra_qt/debugger/disassembler.hxx | 42 +++++++++ src/citra_qt/debugger/disassembler.ui | 88 ++++++++++++++++++ src/citra_qt/debugger/ramview.cpp | 13 +++ src/citra_qt/debugger/ramview.hxx | 12 +++ src/citra_qt/debugger/registers.cpp | 63 +++++++++++++ src/citra_qt/debugger/registers.hxx | 25 +++++ src/citra_qt/debugger/registers.ui | 40 ++++++++ 11 files changed, 565 insertions(+) create mode 100644 src/citra_qt/debugger/callstack.cpp create mode 100644 src/citra_qt/debugger/callstack.hxx create mode 100644 src/citra_qt/debugger/callstack.ui create mode 100644 src/citra_qt/debugger/disassembler.cpp create mode 100644 src/citra_qt/debugger/disassembler.hxx create mode 100644 src/citra_qt/debugger/disassembler.ui create mode 100644 src/citra_qt/debugger/ramview.cpp create mode 100644 src/citra_qt/debugger/ramview.hxx create mode 100644 src/citra_qt/debugger/registers.cpp create mode 100644 src/citra_qt/debugger/registers.hxx create mode 100644 src/citra_qt/debugger/registers.ui (limited to 'src/citra_qt/debugger') diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp new file mode 100644 index 00000000..f59f2d8c --- /dev/null +++ b/src/citra_qt/debugger/callstack.cpp @@ -0,0 +1,66 @@ +#include + +#include "callstack.hxx" + +#include "core/core.h" +#include "core/arm/arm_interface.h" +#include "core/mem_map.h" +#include "common/symbols.h" +#include "core/arm/disassembler/arm_disasm.h" + +CallstackWidget::CallstackWidget(QWidget* parent): QDockWidget(parent) +{ + ui.setupUi(this); + + callstack_model = new QStandardItemModel(this); + callstack_model->setColumnCount(4); + callstack_model->setHeaderData(0, Qt::Horizontal, "Stack pointer"); + callstack_model->setHeaderData(2, Qt::Horizontal, "Return address"); + callstack_model->setHeaderData(1, Qt::Horizontal, "Call address"); + callstack_model->setHeaderData(3, Qt::Horizontal, "Function"); + ui.treeView->setModel(callstack_model); +} + +void CallstackWidget::OnCPUStepped() +{ + ARM_Disasm* disasm = new ARM_Disasm(); + ARM_Interface* app_core = Core::g_app_core; + + u32 sp = app_core->GetReg(13); //stack pointer + u32 addr, ret_addr, call_addr, func_addr; + + int counter = 0; + for (int addr = 0x10000000; addr >= sp; addr -= 4) + { + ret_addr = Memory::Read32(addr); + call_addr = ret_addr - 4; //get call address??? + + /* TODO (mattvail) clean me, move to debugger interface */ + u32 insn = Memory::Read32(call_addr); + if (disasm->decode(insn) == OP_BL) + { + std::string name; + // ripped from disasm + uint8_t cond = (insn >> 28) & 0xf; + uint32_t i_offset = insn & 0xffffff; + // Sign-extend the 24-bit offset + if ((i_offset >> 23) & 1) + i_offset |= 0xff000000; + + // Pre-compute the left-shift and the prefetch offset + i_offset <<= 2; + i_offset += 8; + func_addr = call_addr + i_offset; + + callstack_model->setItem(counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0')))); + callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(ret_addr, 8, 16, QLatin1Char('0')))); + callstack_model->setItem(counter, 2, new QStandardItem(QString("0x%1").arg(call_addr, 8, 16, QLatin1Char('0')))); + + name = Symbols::HasSymbol(func_addr) ? Symbols::GetSymbol(func_addr).name : "unknown"; + callstack_model->setItem(counter, 3, new QStandardItem(QString("%1_%2").arg(QString::fromStdString(name)) + .arg(QString("0x%1").arg(func_addr, 8, 16, QLatin1Char('0'))))); + + counter++; + } + } +} \ No newline at end of file diff --git a/src/citra_qt/debugger/callstack.hxx b/src/citra_qt/debugger/callstack.hxx new file mode 100644 index 00000000..3ad2af28 --- /dev/null +++ b/src/citra_qt/debugger/callstack.hxx @@ -0,0 +1,19 @@ +#include +#include "../ui_callstack.h" + +class QStandardItemModel; + +class CallstackWidget : public QDockWidget +{ + Q_OBJECT + +public: + CallstackWidget(QWidget* parent = 0); + +public slots: + void OnCPUStepped(); + +private: + Ui::CallStack ui; + QStandardItemModel* callstack_model; +}; diff --git a/src/citra_qt/debugger/callstack.ui b/src/citra_qt/debugger/callstack.ui new file mode 100644 index 00000000..b3c4db63 --- /dev/null +++ b/src/citra_qt/debugger/callstack.ui @@ -0,0 +1,36 @@ + + + CallStack + + + + 0 + 0 + 400 + 300 + + + + Call stack + + + + + + + true + + + false + + + false + + + + + + + + + diff --git a/src/citra_qt/debugger/disassembler.cpp b/src/citra_qt/debugger/disassembler.cpp new file mode 100644 index 00000000..cc4cb13f --- /dev/null +++ b/src/citra_qt/debugger/disassembler.cpp @@ -0,0 +1,161 @@ +#include + +#include "disassembler.hxx" + +#include "../bootmanager.hxx" +#include "../hotkeys.hxx" + +#include "common/common.h" +#include "core/mem_map.h" + +#include "core/core.h" +#include "common/break_points.h" +#include "common/symbols.h" +#include "core/arm/interpreter/armdefs.h" +#include "core/arm/disassembler/arm_disasm.h" + +DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread& emu_thread) : QDockWidget(parent), base_addr(0), emu_thread(emu_thread) +{ + disasm_ui.setupUi(this); + + breakpoints = new BreakPoints(); + + model = new QStandardItemModel(this); + model->setColumnCount(3); + disasm_ui.treeView->setModel(model); + disasm_ui.tableView->setModel(model); + RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut); + RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut); + RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut); + RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9), Qt::ApplicationShortcut); + + connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), this, SLOT(OnSetBreakpoint())); + connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep())); + connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause())); + connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue())); + + connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this, SLOT(OnToggleStartStop())); + connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep())); + connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this, SLOT(OnStepInto())); + connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), this, SLOT(OnSetBreakpoint())); +} + +void DisassemblerWidget::Init() +{ + ARM_Disasm* disasm = new ARM_Disasm(); + + base_addr = Core::g_app_core->GetPC(); + unsigned int curInstAddr = base_addr; + char result[255]; + + for (int i = 0; i < 10000; i++) // fixed for now + { + disasm->disasm(curInstAddr, Memory::Read32(curInstAddr), result); + model->setItem(i, 0, new QStandardItem(QString("0x%1").arg((uint)(curInstAddr), 8, 16, QLatin1Char('0')))); + model->setItem(i, 1, new QStandardItem(QString(result))); + if (Symbols::HasSymbol(curInstAddr)) + { + TSymbol symbol = Symbols::GetSymbol(curInstAddr); + model->setItem(i, 2, new QStandardItem(QString("%1 - Size:%2").arg(QString::fromStdString(symbol.name)) + .arg(symbol.size / 4))); // divide by 4 to get instruction count + + } + curInstAddr += 4; + } + disasm_ui.treeView->resizeColumnToContents(0); + disasm_ui.treeView->resizeColumnToContents(1); + disasm_ui.treeView->resizeColumnToContents(2); + disasm_ui.tableView->resizeColumnToContents(0); + disasm_ui.tableView->resizeColumnToContents(1); + disasm_ui.tableView->resizeColumnToContents(2); + + QModelIndex model_index = model->index(0, 0); + disasm_ui.treeView->scrollTo(model_index); + disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + + disasm_ui.tableView->scrollTo(model_index); + disasm_ui.tableView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); +} + +void DisassemblerWidget::OnSetBreakpoint() +{ + int selected_row = SelectedRow(); + + if (selected_row == -1) + return; + + u32 address = base_addr + (selected_row * 4); + if (breakpoints->IsAddressBreakPoint(address)) + { + breakpoints->Remove(address); + model->item(selected_row, 0)->setBackground(QBrush()); + model->item(selected_row, 1)->setBackground(QBrush()); + } + else + { + breakpoints->Add(address); + model->item(selected_row, 0)->setBackground(QBrush(QColor(0xFF, 0x99, 0x99))); + model->item(selected_row, 1)->setBackground(QBrush(QColor(0xFF, 0x99, 0x99))); + } +} + +void DisassemblerWidget::OnContinue() +{ + emu_thread.SetCpuRunning(true); +} + +void DisassemblerWidget::OnStep() +{ + OnStepInto(); // change later +} + +void DisassemblerWidget::OnStepInto() +{ + emu_thread.SetCpuRunning(false); + emu_thread.ExecStep(); +} + +void DisassemblerWidget::OnPause() +{ + emu_thread.SetCpuRunning(false); +} + +void DisassemblerWidget::OnToggleStartStop() +{ + emu_thread.SetCpuRunning(!emu_thread.IsCpuRunning()); +} + +void DisassemblerWidget::OnCPUStepped() +{ + ARMword next_instr = Core::g_app_core->GetPC(); + + if (breakpoints->IsAddressBreakPoint(next_instr)) + { + emu_thread.SetCpuRunning(false); + } + + unsigned int index = (next_instr - base_addr) / 4; + QModelIndex model_index = model->index(index, 0); + disasm_ui.treeView->scrollTo(model_index); + disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + + disasm_ui.tableView->scrollTo(model_index); + disasm_ui.tableView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + disasm_ui.tableView->selectionModel()->select(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); +} + +int DisassemblerWidget::SelectedRow() +{ + QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex(); + if (!index.isValid()) + return -1; + + return model->itemFromIndex(disasm_ui.treeView->selectionModel()->currentIndex())->row(); +} +/* +void DisassemblerWidget::paintEvent() +{ + QPainter painter(this); + painter.drawRect(10, 10, 50, 50); +} +*/ \ No newline at end of file diff --git a/src/citra_qt/debugger/disassembler.hxx b/src/citra_qt/debugger/disassembler.hxx new file mode 100644 index 00000000..e5b152d2 --- /dev/null +++ b/src/citra_qt/debugger/disassembler.hxx @@ -0,0 +1,42 @@ +#include +#include "../ui_disassembler.h" + +#include "common/common.h" +#include "common/break_points.h" + +class QAction; +class QStandardItemModel; +class EmuThread; + +class DisassemblerWidget : public QDockWidget +{ + Q_OBJECT + +public: + DisassemblerWidget(QWidget* parent, EmuThread& emu_thread); + + void Init(); + +public slots: + void OnSetBreakpoint(); + void OnContinue(); + void OnStep(); + void OnStepInto(); + void OnPause(); + void OnToggleStartStop(); + + void OnCPUStepped(); + +private: + // returns -1 if no row is selected + int SelectedRow(); + + Ui::DockWidget disasm_ui; + QStandardItemModel* model; + + u32 base_addr; + + BreakPoints* breakpoints; + + EmuThread& emu_thread; +}; diff --git a/src/citra_qt/debugger/disassembler.ui b/src/citra_qt/debugger/disassembler.ui new file mode 100644 index 00000000..e65b0aa9 --- /dev/null +++ b/src/citra_qt/debugger/disassembler.ui @@ -0,0 +1,88 @@ + + + DockWidget + + + + 0 + 0 + 430 + 401 + + + + Disassembly + + + + + + + + + Step + + + + + + + Pause + + + + + + + Continue + + + + + + + Step Into + + + + + + + Set Breakpoint + + + + + + + + + true + + + 20 + + + false + + + false + + + + + + + true + + + false + + + + + + + + + diff --git a/src/citra_qt/debugger/ramview.cpp b/src/citra_qt/debugger/ramview.cpp new file mode 100644 index 00000000..3f899b95 --- /dev/null +++ b/src/citra_qt/debugger/ramview.cpp @@ -0,0 +1,13 @@ +#include "ramview.hxx" + +#include "common/common.h" +#include "core/mem_map.h" +GRamView::GRamView(QWidget* parent) : QHexEdit(parent) +{ +} + +void GRamView::OnCPUStepped() +{ + // TODO: QHexEdit doesn't show vertical scroll bars for > 10MB data streams... + //setData(QByteArray((const char*)Mem_RAM,sizeof(Mem_RAM)/8)); +} \ No newline at end of file diff --git a/src/citra_qt/debugger/ramview.hxx b/src/citra_qt/debugger/ramview.hxx new file mode 100644 index 00000000..1db1546a --- /dev/null +++ b/src/citra_qt/debugger/ramview.hxx @@ -0,0 +1,12 @@ +#include "qhexedit.h" + +class GRamView : public QHexEdit +{ + Q_OBJECT + +public: + GRamView(QWidget* parent = NULL); + +public slots: + void OnCPUStepped(); +}; diff --git a/src/citra_qt/debugger/registers.cpp b/src/citra_qt/debugger/registers.cpp new file mode 100644 index 00000000..96ceed48 --- /dev/null +++ b/src/citra_qt/debugger/registers.cpp @@ -0,0 +1,63 @@ +#include "registers.hxx" + +#include "core/core.h" +#include "core/arm/arm_interface.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) + { + QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("R[%1]").arg(i, 2, 10, QLatin1Char('0')))); + registers->addChild(child); + } + + 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"))); +} + +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'))); + + 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 +} diff --git a/src/citra_qt/debugger/registers.hxx b/src/citra_qt/debugger/registers.hxx new file mode 100644 index 00000000..318d9582 --- /dev/null +++ b/src/citra_qt/debugger/registers.hxx @@ -0,0 +1,25 @@ +#include "../ui_registers.h" + +#include +#include + +class QTreeWidget; + +class RegistersWidget : public QDockWidget +{ + Q_OBJECT + +public: + RegistersWidget(QWidget* parent = NULL); + +public slots: + void OnCPUStepped(); + +private: + Ui::ARMRegisters cpu_regs_ui; + + QTreeWidget* tree; + + QTreeWidgetItem* registers; + QTreeWidgetItem* CSPR; +}; diff --git a/src/citra_qt/debugger/registers.ui b/src/citra_qt/debugger/registers.ui new file mode 100644 index 00000000..6537c9cd --- /dev/null +++ b/src/citra_qt/debugger/registers.ui @@ -0,0 +1,40 @@ + + + ARMRegisters + + + + 0 + 0 + 400 + 300 + + + + ARM registers + + + + + + + true + + + + Register + + + + + Value + + + + + + + + + + -- cgit v1.2.3