citra-qt: Remove disassembler widget
It has performance problems, a very misleading UI, and is broken in general. It has essentially been superceded by the GDB stub, but if we wanted a built-in disassembler in the future it'd essentially need to be rewritten from scratch anyway. Closes #427, #1480
This commit is contained in:
		| @@ -12,7 +12,6 @@ set(SRCS | ||||
|             configuration/configure_input.cpp | ||||
|             configuration/configure_system.cpp | ||||
|             debugger/callstack.cpp | ||||
|             debugger/disassembler.cpp | ||||
|             debugger/graphics/graphics.cpp | ||||
|             debugger/graphics/graphics_breakpoint_observer.cpp | ||||
|             debugger/graphics/graphics_breakpoints.cpp | ||||
| @@ -44,7 +43,6 @@ set(HEADERS | ||||
|             configuration/configure_input.h | ||||
|             configuration/configure_system.h | ||||
|             debugger/callstack.h | ||||
|             debugger/disassembler.h | ||||
|             debugger/graphics/graphics.h | ||||
|             debugger/graphics/graphics_breakpoint_observer.h | ||||
|             debugger/graphics/graphics_breakpoints.h | ||||
| @@ -75,7 +73,6 @@ set(UIS | ||||
|             configuration/configure_input.ui | ||||
|             configuration/configure_system.ui | ||||
|             debugger/callstack.ui | ||||
|             debugger/disassembler.ui | ||||
|             debugger/registers.ui | ||||
|             hotkeys.ui | ||||
|             main.ui | ||||
|   | ||||
| @@ -1,272 +0,0 @@ | ||||
| // Copyright 2014 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <QShortcut> | ||||
| #include "citra_qt/bootmanager.h" | ||||
| #include "citra_qt/debugger/disassembler.h" | ||||
| #include "citra_qt/hotkeys.h" | ||||
| #include "citra_qt/util/util.h" | ||||
| #include "common/break_points.h" | ||||
| #include "common/symbols.h" | ||||
| #include "core/arm/arm_interface.h" | ||||
| #include "core/arm/disassembler/arm_disasm.h" | ||||
| #include "core/core.h" | ||||
| #include "core/memory.h" | ||||
|  | ||||
| DisassemblerModel::DisassemblerModel(QObject* parent) | ||||
|     : QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0), | ||||
|       selection(QModelIndex()) {} | ||||
|  | ||||
| int DisassemblerModel::columnCount(const QModelIndex& parent) const { | ||||
|     return 3; | ||||
| } | ||||
|  | ||||
| int DisassemblerModel::rowCount(const QModelIndex& parent) const { | ||||
|     return code_size; | ||||
| } | ||||
|  | ||||
| QVariant DisassemblerModel::data(const QModelIndex& index, int role) const { | ||||
|     switch (role) { | ||||
|     case Qt::DisplayRole: { | ||||
|         u32 address = base_address + index.row() * 4; | ||||
|         u32 instr = Memory::Read32(address); | ||||
|         std::string disassembly = ARM_Disasm::Disassemble(address, instr); | ||||
|  | ||||
|         if (index.column() == 0) { | ||||
|             return QString("0x%1").arg((uint)(address), 8, 16, QLatin1Char('0')); | ||||
|         } else if (index.column() == 1) { | ||||
|             return QString::fromStdString(disassembly); | ||||
|         } else if (index.column() == 2) { | ||||
|             if (Symbols::HasSymbol(address)) { | ||||
|                 TSymbol symbol = Symbols::GetSymbol(address); | ||||
|                 return QString("%1 - Size:%2") | ||||
|                     .arg(QString::fromStdString(symbol.name)) | ||||
|                     .arg(symbol.size / 4); // divide by 4 to get instruction count | ||||
|             } else if (ARM_Disasm::Decode(instr) == OP_BL) { | ||||
|                 u32 offset = instr & 0xFFFFFF; | ||||
|  | ||||
|                 // Sign-extend the 24-bit offset | ||||
|                 if ((offset >> 23) & 1) | ||||
|                     offset |= 0xFF000000; | ||||
|  | ||||
|                 // Pre-compute the left-shift and the prefetch offset | ||||
|                 offset <<= 2; | ||||
|                 offset += 8; | ||||
|  | ||||
|                 TSymbol symbol = Symbols::GetSymbol(address + offset); | ||||
|                 return QString("    --> %1").arg(QString::fromStdString(symbol.name)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     case Qt::BackgroundRole: { | ||||
|         unsigned int address = base_address + 4 * index.row(); | ||||
|  | ||||
|         if (breakpoints.IsAddressBreakPoint(address)) | ||||
|             return QBrush(QColor(0xFF, 0xC0, 0xC0)); | ||||
|         else if (address == program_counter) | ||||
|             return QBrush(QColor(0xC0, 0xC0, 0xFF)); | ||||
|  | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     case Qt::FontRole: { | ||||
|         if (index.column() == 0 || index.column() == 1) { // 2 is the symbols column | ||||
|             return GetMonospaceFont(); | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     return QVariant(); | ||||
| } | ||||
|  | ||||
| QModelIndex DisassemblerModel::IndexFromAbsoluteAddress(unsigned int address) const { | ||||
|     return index((address - base_address) / 4, 0); | ||||
| } | ||||
|  | ||||
| const BreakPoints& DisassemblerModel::GetBreakPoints() const { | ||||
|     return breakpoints; | ||||
| } | ||||
|  | ||||
| void DisassemblerModel::ParseFromAddress(unsigned int address) { | ||||
|  | ||||
|     // NOTE: A too large value causes lagging when scrolling the disassembly | ||||
|     const unsigned int chunk_size = 1000 * 500; | ||||
|  | ||||
|     // If we haven't loaded anything yet, initialize base address to the parameter address | ||||
|     if (code_size == 0) | ||||
|         base_address = address; | ||||
|  | ||||
|     // If the new area is already loaded, just continue | ||||
|     if (base_address + code_size > address + chunk_size && base_address <= address) | ||||
|         return; | ||||
|  | ||||
|     // Insert rows before currently loaded data | ||||
|     if (base_address > address) { | ||||
|         unsigned int num_rows = (address - base_address) / 4; | ||||
|  | ||||
|         beginInsertRows(QModelIndex(), 0, num_rows); | ||||
|         code_size += num_rows; | ||||
|         base_address = address; | ||||
|  | ||||
|         endInsertRows(); | ||||
|     } | ||||
|  | ||||
|     // Insert rows after currently loaded data | ||||
|     if (base_address + code_size < address + chunk_size) { | ||||
|         unsigned int num_rows = (base_address + chunk_size - code_size - address) / 4; | ||||
|  | ||||
|         beginInsertRows(QModelIndex(), 0, num_rows); | ||||
|         code_size += num_rows; | ||||
|         endInsertRows(); | ||||
|     } | ||||
|  | ||||
|     SetNextInstruction(address); | ||||
| } | ||||
|  | ||||
| void DisassemblerModel::OnSelectionChanged(const QModelIndex& new_selection) { | ||||
|     selection = new_selection; | ||||
| } | ||||
|  | ||||
| void DisassemblerModel::OnSetOrUnsetBreakpoint() { | ||||
|     if (!selection.isValid()) | ||||
|         return; | ||||
|  | ||||
|     unsigned int address = base_address + selection.row() * 4; | ||||
|  | ||||
|     if (breakpoints.IsAddressBreakPoint(address)) { | ||||
|         breakpoints.Remove(address); | ||||
|     } else { | ||||
|         breakpoints.Add(address); | ||||
|     } | ||||
|  | ||||
|     emit dataChanged(selection, selection); | ||||
| } | ||||
|  | ||||
| void DisassemblerModel::SetNextInstruction(unsigned int address) { | ||||
|     QModelIndex cur_index = IndexFromAbsoluteAddress(program_counter); | ||||
|     QModelIndex prev_index = IndexFromAbsoluteAddress(address); | ||||
|  | ||||
|     program_counter = address; | ||||
|  | ||||
|     emit dataChanged(cur_index, cur_index); | ||||
|     emit dataChanged(prev_index, prev_index); | ||||
| } | ||||
|  | ||||
| DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread) | ||||
|     : QDockWidget(parent), base_addr(0), emu_thread(emu_thread) { | ||||
|  | ||||
|     disasm_ui.setupUi(this); | ||||
|  | ||||
|     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_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())); | ||||
|  | ||||
|     setEnabled(false); | ||||
| } | ||||
|  | ||||
| void DisassemblerWidget::Init() { | ||||
|     model->ParseFromAddress(Core::CPU().GetPC()); | ||||
|  | ||||
|     disasm_ui.treeView->resizeColumnToContents(0); | ||||
|     disasm_ui.treeView->resizeColumnToContents(1); | ||||
|     disasm_ui.treeView->resizeColumnToContents(2); | ||||
|  | ||||
|     QModelIndex model_index = model->IndexFromAbsoluteAddress(Core::CPU().GetPC()); | ||||
|     disasm_ui.treeView->scrollTo(model_index); | ||||
|     disasm_ui.treeView->selectionModel()->setCurrentIndex( | ||||
|         model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); | ||||
| } | ||||
|  | ||||
| void DisassemblerWidget::OnContinue() { | ||||
|     emu_thread->SetRunning(true); | ||||
| } | ||||
|  | ||||
| void DisassemblerWidget::OnStep() { | ||||
|     OnStepInto(); // change later | ||||
| } | ||||
|  | ||||
| void DisassemblerWidget::OnStepInto() { | ||||
|     emu_thread->SetRunning(false); | ||||
|     emu_thread->ExecStep(); | ||||
| } | ||||
|  | ||||
| void DisassemblerWidget::OnPause() { | ||||
|     emu_thread->SetRunning(false); | ||||
|  | ||||
|     // TODO: By now, the CPU might not have actually stopped... | ||||
|     if (Core::System::GetInstance().IsPoweredOn()) { | ||||
|         model->SetNextInstruction(Core::CPU().GetPC()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void DisassemblerWidget::OnToggleStartStop() { | ||||
|     emu_thread->SetRunning(!emu_thread->IsRunning()); | ||||
| } | ||||
|  | ||||
| void DisassemblerWidget::OnDebugModeEntered() { | ||||
|     u32 next_instr = Core::CPU().GetPC(); | ||||
|  | ||||
|     if (model->GetBreakPoints().IsAddressBreakPoint(next_instr)) | ||||
|         emu_thread->SetRunning(false); | ||||
|  | ||||
|     model->SetNextInstruction(next_instr); | ||||
|  | ||||
|     QModelIndex model_index = model->IndexFromAbsoluteAddress(next_instr); | ||||
|     disasm_ui.treeView->scrollTo(model_index); | ||||
|     disasm_ui.treeView->selectionModel()->setCurrentIndex( | ||||
|         model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); | ||||
| } | ||||
|  | ||||
| void DisassemblerWidget::OnDebugModeLeft() {} | ||||
|  | ||||
| int DisassemblerWidget::SelectedRow() { | ||||
|     QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex(); | ||||
|     if (!index.isValid()) | ||||
|         return -1; | ||||
|  | ||||
|     return disasm_ui.treeView->selectionModel()->currentIndex().row(); | ||||
| } | ||||
|  | ||||
| void DisassemblerWidget::OnEmulationStarting(EmuThread* emu_thread) { | ||||
|     this->emu_thread = emu_thread; | ||||
|  | ||||
|     model = new DisassemblerModel(this); | ||||
|     disasm_ui.treeView->setModel(model); | ||||
|  | ||||
|     connect(disasm_ui.treeView->selectionModel(), | ||||
|             SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), model, | ||||
|             SLOT(OnSelectionChanged(const QModelIndex&))); | ||||
|     connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint())); | ||||
|     connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model, | ||||
|             SLOT(OnSetOrUnsetBreakpoint())); | ||||
|  | ||||
|     Init(); | ||||
|     setEnabled(true); | ||||
| } | ||||
|  | ||||
| void DisassemblerWidget::OnEmulationStopping() { | ||||
|     disasm_ui.treeView->setModel(nullptr); | ||||
|     delete model; | ||||
|     emu_thread = nullptr; | ||||
|     setEnabled(false); | ||||
| } | ||||
| @@ -1,76 +0,0 @@ | ||||
| // Copyright 2014 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <QAbstractListModel> | ||||
| #include <QDockWidget> | ||||
| #include "common/break_points.h" | ||||
| #include "common/common_types.h" | ||||
| #include "ui_disassembler.h" | ||||
|  | ||||
| class QAction; | ||||
| class EmuThread; | ||||
|  | ||||
| class DisassemblerModel : public QAbstractListModel { | ||||
|     Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     explicit DisassemblerModel(QObject* parent); | ||||
|  | ||||
|     int columnCount(const QModelIndex& parent = QModelIndex()) const override; | ||||
|     int rowCount(const QModelIndex& parent = QModelIndex()) const override; | ||||
|     QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; | ||||
|  | ||||
|     QModelIndex IndexFromAbsoluteAddress(unsigned int address) const; | ||||
|     const BreakPoints& GetBreakPoints() const; | ||||
|  | ||||
| public slots: | ||||
|     void ParseFromAddress(unsigned int address); | ||||
|     void OnSelectionChanged(const QModelIndex&); | ||||
|     void OnSetOrUnsetBreakpoint(); | ||||
|     void SetNextInstruction(unsigned int address); | ||||
|  | ||||
| private: | ||||
|     unsigned int base_address; | ||||
|     unsigned int code_size; | ||||
|     unsigned int program_counter; | ||||
|  | ||||
|     QModelIndex selection; | ||||
|     BreakPoints breakpoints; | ||||
| }; | ||||
|  | ||||
| class DisassemblerWidget : public QDockWidget { | ||||
|     Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     DisassemblerWidget(QWidget* parent, EmuThread* emu_thread); | ||||
|  | ||||
|     void Init(); | ||||
|  | ||||
| public slots: | ||||
|     void OnContinue(); | ||||
|     void OnStep(); | ||||
|     void OnStepInto(); | ||||
|     void OnPause(); | ||||
|     void OnToggleStartStop(); | ||||
|  | ||||
|     void OnDebugModeEntered(); | ||||
|     void OnDebugModeLeft(); | ||||
|  | ||||
|     void OnEmulationStarting(EmuThread* emu_thread); | ||||
|     void OnEmulationStopping(); | ||||
|  | ||||
| private: | ||||
|     // returns -1 if no row is selected | ||||
|     int SelectedRow(); | ||||
|  | ||||
|     Ui::DockWidget disasm_ui; | ||||
|  | ||||
|     DisassemblerModel* model; | ||||
|  | ||||
|     u32 base_addr; | ||||
|  | ||||
|     EmuThread* emu_thread; | ||||
| }; | ||||
| @@ -1,81 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>DockWidget</class> | ||||
|  <widget class="QDockWidget" name="DockWidget"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>430</width> | ||||
|     <height>401</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Disassembly</string> | ||||
|   </property> | ||||
|   <widget class="QWidget" name="dockWidgetContents"> | ||||
|    <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|     <item> | ||||
|      <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|       <item> | ||||
|        <widget class="QPushButton" name="button_step"> | ||||
|         <property name="text"> | ||||
|          <string>Step</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QPushButton" name="button_pause"> | ||||
|         <property name="text"> | ||||
|          <string>Pause</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QPushButton" name="button_continue"> | ||||
|         <property name="text"> | ||||
|          <string>Continue</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QPushButton" name="pushButton"> | ||||
|         <property name="text"> | ||||
|          <string>Step Into</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QPushButton" name="button_breakpoint"> | ||||
|         <property name="text"> | ||||
|          <string>Set Breakpoint</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </item> | ||||
|     <item> | ||||
|      <widget class="QTreeView" name="treeView"> | ||||
|       <property name="alternatingRowColors"> | ||||
|        <bool>true</bool> | ||||
|       </property> | ||||
|       <property name="indentation"> | ||||
|        <number>20</number> | ||||
|       </property> | ||||
|       <property name="rootIsDecorated"> | ||||
|        <bool>false</bool> | ||||
|       </property> | ||||
|       <property name="uniformRowHeights"> | ||||
|        <bool>true</bool> | ||||
|       </property> | ||||
|       <attribute name="headerVisible"> | ||||
|        <bool>false</bool> | ||||
|       </attribute> | ||||
|      </widget> | ||||
|     </item> | ||||
|    </layout> | ||||
|   </widget> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections/> | ||||
| </ui> | ||||
| @@ -17,7 +17,6 @@ | ||||
| #include "citra_qt/configuration/config.h" | ||||
| #include "citra_qt/configuration/configure_dialog.h" | ||||
| #include "citra_qt/debugger/callstack.h" | ||||
| #include "citra_qt/debugger/disassembler.h" | ||||
| #include "citra_qt/debugger/graphics/graphics.h" | ||||
| #include "citra_qt/debugger/graphics/graphics_breakpoints.h" | ||||
| #include "citra_qt/debugger/graphics/graphics_cmdlists.h" | ||||
| @@ -130,15 +129,6 @@ void GMainWindow::InitializeDebugWidgets() { | ||||
|     debug_menu->addAction(microProfileDialog->toggleViewAction()); | ||||
| #endif | ||||
|  | ||||
|     disasmWidget = new DisassemblerWidget(this, emu_thread.get()); | ||||
|     addDockWidget(Qt::BottomDockWidgetArea, disasmWidget); | ||||
|     disasmWidget->hide(); | ||||
|     debug_menu->addAction(disasmWidget->toggleViewAction()); | ||||
|     connect(this, &GMainWindow::EmulationStarting, disasmWidget, | ||||
|             &DisassemblerWidget::OnEmulationStarting); | ||||
|     connect(this, &GMainWindow::EmulationStopping, disasmWidget, | ||||
|             &DisassemblerWidget::OnEmulationStopping); | ||||
|  | ||||
|     registersWidget = new RegistersWidget(this); | ||||
|     addDockWidget(Qt::RightDockWidgetArea, registersWidget); | ||||
|     registersWidget->hide(); | ||||
| @@ -391,16 +381,12 @@ void GMainWindow::BootGame(const QString& filename) { | ||||
|     connect(render_window, SIGNAL(Closed()), this, SLOT(OnStopGame())); | ||||
|     // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views | ||||
|     // before the CPU continues | ||||
|     connect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), | ||||
|             Qt::BlockingQueuedConnection); | ||||
|     connect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget, | ||||
|             SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); | ||||
|     connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget, | ||||
|             SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); | ||||
|     connect(emu_thread.get(), SIGNAL(DebugModeEntered()), waitTreeWidget, | ||||
|             SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); | ||||
|     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), | ||||
|             Qt::BlockingQueuedConnection); | ||||
|     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), | ||||
|             Qt::BlockingQueuedConnection); | ||||
|     connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), | ||||
|   | ||||
| @@ -12,7 +12,6 @@ | ||||
|  | ||||
| class CallstackWidget; | ||||
| class Config; | ||||
| class DisassemblerWidget; | ||||
| class EmuThread; | ||||
| class GameList; | ||||
| class GImageInfo; | ||||
| @@ -152,7 +151,6 @@ private: | ||||
|     // Debugger panes | ||||
|     ProfilerWidget* profilerWidget; | ||||
|     MicroProfileDialog* microProfileDialog; | ||||
|     DisassemblerWidget* disasmWidget; | ||||
|     RegistersWidget* registersWidget; | ||||
|     CallstackWidget* callstackWidget; | ||||
|     GPUCommandStreamWidget* graphicsWidget; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Yuri Kunde Schlesner
					Yuri Kunde Schlesner