#include "debugqtmainwindow.h"
#include "debug/debugcon.h"
#include "debug/debugcpu.h"
#include "debug/dvdisasm.h"
MainWindow::MainWindow(running_machine* machine, QWidget* parent) :
WindowQt(machine, parent),
m_historyIndex(0),
m_inputHistory()
{
setGeometry(300, 300, 1000, 600);
//
// The main frame and its input and log widgets
//
QFrame* mainWindowFrame = new QFrame(this);
// The input line
m_inputEdit = new QLineEdit(mainWindowFrame);
connect(m_inputEdit, SIGNAL(returnPressed()), this, SLOT(executeCommand()));
m_inputEdit->installEventFilter(this);
// The log view
m_consoleView = new DebuggerView(DVT_CONSOLE,
m_machine,
mainWindowFrame);
m_consoleView->setFocusPolicy(Qt::NoFocus);
m_consoleView->setPreferBottom(true);
QVBoxLayout* vLayout = new QVBoxLayout(mainWindowFrame);
vLayout->addWidget(m_consoleView);
vLayout->addWidget(m_inputEdit);
vLayout->setSpacing(3);
vLayout->setContentsMargins(4,0,4,2);
setCentralWidget(mainWindowFrame);
//
// Menu bars
//
// Create two commands
QAction* breakpointSetAct = new QAction("Toggle Breakpoint At Cursor", this);
QAction* runToCursorAct = new QAction("Run To Cursor", this);
breakpointSetAct->setShortcut(Qt::Key_F9);
runToCursorAct->setShortcut(Qt::Key_F4);
connect(breakpointSetAct, SIGNAL(triggered(bool)), this, SLOT(toggleBreakpointAtCursor(bool)));
connect(runToCursorAct, SIGNAL(triggered(bool)), this, SLOT(runToCursor(bool)));
// Right bar options
QActionGroup* rightBarGroup = new QActionGroup(this);
QAction* rightActRaw = new QAction("Raw Opcodes", this);
QAction* rightActEncrypted = new QAction("Encrypted Opcodes", this);
QAction* rightActComments = new QAction("Comments", this);
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+C"));
rightActRaw->setChecked(true);
connect(rightBarGroup, SIGNAL(triggered(QAction*)), this, SLOT(rightBarChanged(QAction*)));
// Assemble the options menu
QMenu* optionsMenu = menuBar()->addMenu("&Options");
optionsMenu->addAction(breakpointSetAct);
optionsMenu->addAction(runToCursorAct);
optionsMenu->addSeparator();
optionsMenu->addActions(rightBarGroup->actions());
//
// Dock windows
//
QMenu* dockMenu = menuBar()->addMenu("Doc&ks");
setCorner(Qt::TopRightCorner, Qt::TopDockWidgetArea);
setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
// The processor dock
QDockWidget* cpuDock = new QDockWidget("processor", this);
cpuDock->setAllowedAreas(Qt::LeftDockWidgetArea);
m_procFrame = new ProcessorDockWidget(m_machine, cpuDock);
cpuDock->setWidget(dynamic_cast<QWidget*>(m_procFrame));
addDockWidget(Qt::LeftDockWidgetArea, cpuDock);
dockMenu->addAction(cpuDock->toggleViewAction());
// The disassembly dock
QDockWidget* dasmDock = new QDockWidget("dasm", this);
dasmDock->setAllowedAreas(Qt::TopDockWidgetArea);
m_dasmFrame = new DasmDockWidget(m_machine, dasmDock);
dasmDock->setWidget(m_dasmFrame);
addDockWidget(Qt::TopDockWidgetArea, dasmDock);
dockMenu->addAction(dasmDock->toggleViewAction());
}
void MainWindow::setProcessor(device_t* processor)
{
// Cpu swap
m_procFrame->view()->view()->set_source(*m_procFrame->view()->view()->source_list().match_device(processor));
m_dasmFrame->view()->view()->set_source(*m_dasmFrame->view()->view()->source_list().match_device(processor));
// Scrollbar refresh - seems I should be able to do in the DebuggerView
m_dasmFrame->view()->verticalScrollBar()->setValue(m_dasmFrame->view()->view()->visible_position().y);
m_dasmFrame->view()->verticalScrollBar()->setValue(m_dasmFrame->view()->view()->visible_position().y);
// Window title
astring title;
title.printf("Debug: %s - %s '%s'", m_machine->system().name, processor->name(), processor->tag());
setWindowTitle(title.cstr());
}
// Used to intercept the user clicking 'X' in the upper corner
void MainWindow::closeEvent(QCloseEvent* event)
{
debugActQuit();
}
// Used to intercept the user hitting the up arrow in the input widget
bool MainWindow::eventFilter(QObject* obj, QEvent* event)
{
// Only filter keypresses
QKeyEvent* keyEvent = NULL;
if (event->type() == QEvent::KeyPress)
{
keyEvent = static_cast<QKeyEvent*>(event);
}
else
{
return QObject::eventFilter(obj, event);
}
// Catch up & down keys
if (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)
{
if (keyEvent->key() == Qt::Key_Up)
{
if (m_historyIndex > 0)
m_historyIndex--;
}
else if (keyEvent->key() == Qt::Key_Down)
{
if (m_historyIndex < m_inputHistory.size())
m_historyIndex++;
}
// Populate the input edit or clear it if you're at the end
if (m_historyIndex == m_inputHistory.size())
{
m_inputEdit->setText("");
}
else
{
m_inputEdit->setText(m_inputHistory[m_historyIndex]);
}
}
else if (keyEvent->key() == Qt::Key_Enter)
{
executeCommand(false);
}
else
{
return QObject::eventFilter(obj, event);
}
return true;
}
void MainWindow::toggleBreakpointAtCursor(bool changedTo)
{
debug_view_disasm* dasmView = downcast<debug_view_disasm*>(m_dasmFrame->view()->view());
if (dasmView->cursor_visible())
{
if (debug_cpu_get_visible_cpu(*m_machine) == dasmView->source()->device())
{
offs_t address = downcast<debug_view_disasm *>(dasmView)->selected_address();
device_debug *cpuinfo = dasmView->source()->device()->debug();
// Find an existing breakpoint at this address
INT32 bpindex = -1;
for (device_debug::breakpoint* bp = cpuinfo->breakpoint_first();
bp != NULL;
bp = bp->next())
{
if (address == bp->address())
{
bpindex = bp->index();
break;
}
}
// If none exists, add a new one
astring command;
if (bpindex == -1)
{
command.printf("bpset 0x%X", address);
}
else
{
command.printf("bpclear 0x%X", bpindex);
}
debug_console_execute_command(*m_machine, command, 1);
}
}
refreshAll();
}
void MainWindow::runToCursor(bool changedTo)
{
debug_view_disasm* dasmView = downcast<debug_view_disasm*>(m_dasmFrame->view()->view());
if (dasmView->cursor_visible())
{
if (debug_cpu_get_visible_cpu(*m_machine) == dasmView->source()->device())
{
offs_t address = downcast<debug_view_disasm*>(dasmView)->selected_address();
astring command;
command.printf("go 0x%X", address);
debug_console_execute_command(*m_machine, command, 1);
}
}
}
void MainWindow::rightBarChanged(QAction* changedTo)
{
debug_view_disasm* dasmView = downcast<debug_view_disasm*>(m_dasmFrame->view()->view());
if (changedTo->text() == "Raw Opcodes")
{
dasmView->set_right_column(DASM_RIGHTCOL_RAW);
}
else if (changedTo->text() == "Encrypted Opcodes")
{
dasmView->set_right_column(DASM_RIGHTCOL_ENCRYPTED);
}
else if (changedTo->text() == "Comments")
{
dasmView->set_right_column(DASM_RIGHTCOL_COMMENTS);
}
m_dasmFrame->view()->viewport()->update();
}
void MainWindow::executeCommand(bool withClear)
{
QString command = m_inputEdit->text();
// A blank command is a "silent step"
if (command == "")
{
debug_cpu_get_visible_cpu(*m_machine)->debug()->single_step();
return;
}
// If the user asked for help on a specific command, enhance the call
if (command.trimmed().startsWith("help", Qt::CaseInsensitive))
{
if (command.split(" ", QString::SkipEmptyParts).length() == 2)
{
const int width = m_consoleView->view()->visible_size().x;
command.append(QString(", %1").arg(width, 1, 16));
}
}
// Send along the command
debug_console_execute_command(*m_machine,
command.toLocal8Bit().data(),
true);
// Add history & set the index to be the top of the stack
addToHistory(command);
// Clear out the text and reset the history pointer only if asked
if (withClear)
{
m_inputEdit->clear();
m_historyIndex = m_inputHistory.size();
}
// Refresh
m_consoleView->viewport()->update();
m_procFrame->view()->update();
m_dasmFrame->view()->update();
}
void MainWindow::debugActClose()
{
m_machine->schedule_exit();
}
void MainWindow::addToHistory(const QString& command)
{
if (command == "")
return;
// Always push back when there is no previous history
if (m_inputHistory.size() == 0)
{
m_inputHistory.push_back(m_inputEdit->text());
return;
}
// If there is previous history, make sure it's not what you just executed
if (m_inputHistory.back() != m_inputEdit->text())
{
m_inputHistory.push_back(m_inputEdit->text());
}
}