// license:BSD-3-Clause // copyright-holders:Andrew Gardner #include "emu.h" #include "dasmwindow.h" #include "debug/debugcon.h" #include "debug/debugcpu.h" #include "debug/dvdisasm.h" #include "debug/points.h" #include "util/xmlfile.h" #include #include #include #include #include namespace osd::debugger::qt { DasmWindow::DasmWindow(running_machine &machine, QWidget *parent) : WindowQt(machine, nullptr) { setWindowTitle("Debug: Disassembly View"); if (parent) { QPoint parentPos = parent->pos(); setGeometry(parentPos.x()+100, parentPos.y()+100, 800, 400); } // // The main frame and its input and log widgets // QFrame *mainWindowFrame = new QFrame(this); // The top frame & groupbox that contains the input widgets QFrame *topSubFrame = new QFrame(mainWindowFrame); // The input edit m_inputEdit = new QLineEdit(topSubFrame); connect(m_inputEdit, &QLineEdit::returnPressed, this, &DasmWindow::expressionSubmitted); // The cpu combo box m_cpuComboBox = new QComboBox(topSubFrame); m_cpuComboBox->setObjectName("cpu"); m_cpuComboBox->setMinimumWidth(300); connect(m_cpuComboBox, static_cast(&QComboBox::currentIndexChanged), this, &DasmWindow::cpuChanged); // The main disasm window m_dasmView = new DebuggerView(DVT_DISASSEMBLY, m_machine, this); connect(m_dasmView, &DebuggerView::updated, this, &DasmWindow::dasmViewUpdated); // Force a recompute of the disassembly region m_dasmView->view()->set_expression("curpc"); // Populate the combo box & set the proper CPU populateComboBox(); setToCurrentCpu(); // Layout QHBoxLayout *subLayout = new QHBoxLayout(topSubFrame); subLayout->addWidget(m_inputEdit); subLayout->addWidget(m_cpuComboBox); subLayout->setSpacing(3); subLayout->setContentsMargins(2,2,2,2); QVBoxLayout *vLayout = new QVBoxLayout(mainWindowFrame); vLayout->setSpacing(3); vLayout->setContentsMargins(2,2,2,2); vLayout->addWidget(topSubFrame); vLayout->addWidget(m_dasmView); setCentralWidget(mainWindowFrame); // // Menu bars // // Create three commands m_breakpointToggleAct = new QAction("Toggle Breakpoint at Cursor", this); m_breakpointEnableAct = new QAction("Disable Breakpoint at Cursor", this); m_runToCursorAct = new QAction("Run to Cursor", this); m_breakpointToggleAct->setShortcut(Qt::Key_F9); m_breakpointEnableAct->setShortcut(Qt::SHIFT + Qt::Key_F9); m_runToCursorAct->setShortcut(Qt::Key_F4); connect(m_breakpointToggleAct, &QAction::triggered, this, &DasmWindow::toggleBreakpointAtCursor); connect(m_breakpointEnableAct, &QAction::triggered, this, &DasmWindow::enableBreakpointAtCursor); connect(m_runToCursorAct, &QAction::triggered, this, &DasmWindow::runToCursor); // Right bar options QActionGroup *rightBarGroup = new QActionGroup(this); rightBarGroup->setObjectName("rightbargroup"); QAction *rightActRaw = new QAction("Raw Opcodes", this); QAction *rightActEncrypted = new QAction("Encrypted Opcodes", this); QAction *rightActComments = new QAction("Comments", this); rightActRaw->setData(int(DASM_RIGHTCOL_RAW)); rightActEncrypted->setData(int(DASM_RIGHTCOL_ENCRYPTED)); rightActComments->setData(int(DASM_RIGHTCOL_COMMENTS)); rightActRaw->setCheckable(true); rightActEncrypted->setCheckable(true); rightActComments->setCheckable(true); rightActRaw->setActionGroup(rightBarGroup); rightActEncrypted->setActionGroup(rightBarGroup); rightActComments->setActionGroup(rightBarGroup); rightActRaw->setShortcut(QKeySequence("Ctrl+R")); rightActEncrypted->setShortcut(QKeySequence("Ctrl+E")); rightActComments->setShortcut(QKeySequence("Ctrl+N")); rightActRaw->setChecked(true); connect(rightBarGroup, &QActionGroup::triggered, this, &DasmWindow::rightBarChanged); // Assemble the options menu QMenu *optionsMenu = menuBar()->addMenu("&Options"); optionsMenu->addAction(m_breakpointToggleAct); optionsMenu->addAction(m_breakpointEnableAct); optionsMenu->addAction(m_runToCursorAct); optionsMenu->addSeparator(); optionsMenu->addActions(rightBarGroup->actions()); } DasmWindow::~DasmWindow() { } void DasmWindow::saveConfigurationToNode(util::xml::data_node &node) { WindowQt::saveConfigurationToNode(node); node.set_attribute_int(ATTR_WINDOW_TYPE, WINDOW_TYPE_DISASSEMBLY_VIEWER); debug_view_disasm &dasmview = *m_dasmView->view(); node.set_attribute_int(ATTR_WINDOW_DISASSEMBLY_CPU, m_dasmView->sourceIndex()); node.set_attribute_int(ATTR_WINDOW_DISASSEMBLY_RIGHT_COLUMN, dasmview.right_column()); node.add_child(NODE_WINDOW_EXPRESSION, dasmview.expression()); } void DasmWindow::cpuChanged(int index) { if (index < m_dasmView->view()->source_count()) { m_dasmView->view()->set_source(*m_dasmView->view()->source(index)); m_dasmView->viewport()->update(); } } void DasmWindow::expressionSubmitted() { const QString expression = m_inputEdit->text(); m_dasmView->view()->set_expression(expression.toLocal8Bit().data()); m_dasmView->viewport()->update(); } void DasmWindow::toggleBreakpointAtCursor(bool changedTo) { if (m_dasmView->view()->cursor_visible()) { offs_t const address = m_dasmView->view()->selected_address(); device_t *const device = m_dasmView->view()->source()->device(); device_debug *const cpuinfo = device->debug(); // Find an existing breakpoint at this address const debug_breakpoint *bp = cpuinfo->breakpoint_find(address); // If none exists, add a new one if (!bp) { int32_t bpindex = cpuinfo->breakpoint_set(address); m_machine.debugger().console().printf("Breakpoint %X set\n", bpindex); } else { int32_t bpindex = bp->index(); cpuinfo->breakpoint_clear(bpindex); m_machine.debugger().console().printf("Breakpoint %X cleared\n", bpindex); } m_machine.debug_view().update_all(); m_machine.debugger().refresh_display(); } refreshAll(); } void DasmWindow::enableBreakpointAtCursor(bool changedTo) { if (m_dasmView->view()->cursor_visible()) { offs_t const address = m_dasmView->view()->selected_address(); device_t *const device = m_dasmView->view()->source()->device(); device_debug *const cpuinfo = device->debug(); // Find an existing breakpoint at this address const debug_breakpoint *bp = cpuinfo->breakpoint_find(address); if (bp) { cpuinfo->breakpoint_enable(bp->index(), !bp->enabled()); m_machine.debugger().console().printf("Breakpoint %X %s\n", (uint32_t)bp->index(), bp->enabled() ? "enabled" : "disabled"); m_machine.debug_view().update_all(); m_machine.debugger().refresh_display(); } } refreshAll(); } void DasmWindow::runToCursor(bool changedTo) { if (m_dasmView->view()->cursor_visible()) { offs_t const address = m_dasmView->view()->selected_address(); m_dasmView->view()->source()->device()->debug()->go(address); } } void DasmWindow::rightBarChanged(QAction* changedTo) { debug_view_disasm *const dasmView = m_dasmView->view(); dasmView->set_right_column(disasm_right_column(changedTo->data().toInt())); m_dasmView->viewport()->update(); } void DasmWindow::dasmViewUpdated() { bool const haveCursor = m_dasmView->view()->cursor_visible(); bool haveBreakpoint = false; bool breakpointEnabled = false; if (haveCursor) { offs_t const address = m_dasmView->view()->selected_address(); device_t *const device = m_dasmView->view()->source()->device(); device_debug *const cpuinfo = device->debug(); // Find an existing breakpoint at this address const debug_breakpoint *bp = cpuinfo->breakpoint_find(address); if (bp) { haveBreakpoint = true; breakpointEnabled = bp->enabled(); } } m_breakpointToggleAct->setText(haveBreakpoint ? "Clear Breakpoint at Cursor" : haveCursor ? "Set Breakpoint at Cursor" : "Toggle Breakpoint at Cursor"); m_breakpointEnableAct->setText((!haveBreakpoint || breakpointEnabled) ? "Disable Breakpoint at Cursor" : "Enable Breakpoint at Cursor"); m_breakpointToggleAct->setEnabled(haveCursor); m_breakpointEnableAct->setEnabled(haveBreakpoint); m_runToCursorAct->setEnabled(haveCursor); } void DasmWindow::populateComboBox() { if (!m_dasmView) return; m_cpuComboBox->clear(); for (auto &source : m_dasmView->view()->source_list()) { m_cpuComboBox->addItem(source->name()); } } void DasmWindow::setToCurrentCpu() { device_t *curCpu = m_machine.debugger().console().get_visible_cpu(); if (curCpu) { const debug_view_source *source = m_dasmView->view()->source_for_device(curCpu); if (source) { const int listIndex = m_dasmView->view()->source_index(*source); m_cpuComboBox->setCurrentIndex(listIndex); } } } //========================================================================= // DasmWindowQtConfig //========================================================================= void DasmWindowQtConfig::applyToQWidget(QWidget *widget) { WindowQtConfig::applyToQWidget(widget); DasmWindow *window = dynamic_cast(widget); QComboBox *cpu = window->findChild("cpu"); cpu->setCurrentIndex(m_cpu); QActionGroup *const rightBarGroup = window->findChild("rightbargroup"); for (QAction *action : rightBarGroup->actions()) { if (action->data().toInt() == m_rightBar) { action->trigger(); break; } } } void DasmWindowQtConfig::recoverFromXmlNode(util::xml::data_node const &node) { WindowQtConfig::recoverFromXmlNode(node); m_cpu = node.get_attribute_int(ATTR_WINDOW_DISASSEMBLY_CPU, m_cpu); m_rightBar = node.get_attribute_int(ATTR_WINDOW_DISASSEMBLY_RIGHT_COLUMN, m_rightBar); } } // namespace osd::debugger::qt