diff --git a/gui/qt/CEmu.pro b/gui/qt/CEmu.pro index 9f80b424..be999d46 100644 --- a/gui/qt/CEmu.pro +++ b/gui/qt/CEmu.pro @@ -292,6 +292,7 @@ SOURCES += \ ../../core/bus.c \ keyhistorywidget.cpp \ tablewidget.cpp \ + gotodialog.cpp \ basicdebugger.cpp linux|macx: SOURCES += ../../core/os/os-linux.c @@ -390,7 +391,8 @@ HEADERS += \ archive/extractor.h \ ../../core/bus.h \ keyhistorywidget.h \ - tablewidget.h + tablewidget.h \ + gotodialog.h FORMS += \ mainwindow.ui \ diff --git a/gui/qt/CMakeLists.txt b/gui/qt/CMakeLists.txt index f635e1e9..f98ff87a 100644 --- a/gui/qt/CMakeLists.txt +++ b/gui/qt/CMakeLists.txt @@ -155,6 +155,7 @@ set(CEmu_Sources debugger/hexwidget.cpp debugger/hexwidget.h debugger/visualizerdisplaywidget.cpp debugger/visualizerdisplaywidget.h dockwidget.cpp dockwidget.h + gotodialog.cpp gotodialog.h emuthread.cpp emuthread.h ipc.cpp ipc.h keyhistorywidget.cpp keyhistorywidget.h diff --git a/gui/qt/debugger.cpp b/gui/qt/debugger.cpp index 650367f9..76b72038 100644 --- a/gui/qt/debugger.cpp +++ b/gui/qt/debugger.cpp @@ -22,17 +22,24 @@ #include "../../core/realclock.h" #include "../../core/sha256.h" #include "../../core/schedule.h" +#include "gotodialog.h" #include #include #include #include #include +#include +#include +#include +#include +#include #include #include #include #include #include +#include #ifdef _MSC_VER #include @@ -1968,16 +1975,26 @@ void MainWindow::disasmUpdateAddr(int base, bool pane) { // ------------------------------------------------ void MainWindow::gotoPressed() { - bool accept; - if (m_gotoAddr.isEmpty()) { m_gotoAddr = m_disasm->getSelectedAddr(); } - QString address = getAddressString(m_gotoAddr, &accept); + GotoDialog dlg(m_gotoAddr, m_disasmGotoHistory, this); + if (dlg.exec() == QDialog::Accepted) { + QString typed = dlg.text().toUpper().trimmed(); + bool ok = false; + QString resolved = resolveAddressOrEquate(typed, &ok); + if (ok) { + m_gotoAddr = typed; + disasmUpdateAddr(static_cast(hex2int(resolved)), false); - if (accept) { - disasmUpdateAddr(hex2int(m_gotoAddr = address), false); + auto &hist = m_disasmGotoHistory; + hist.erase(std::remove_if(hist.begin(), hist.end(), [&](const QString &s){ return s.compare(typed, Qt::CaseInsensitive) == 0; }), hist.end()); + hist.insert(hist.begin(), typed); + if (hist.size() > 50) { hist.resize(50); } + } else { + QMessageBox::warning(this, MSG_WARNING, tr("Error when reading input string")); + } } } diff --git a/gui/qt/gotodialog.cpp b/gui/qt/gotodialog.cpp new file mode 100644 index 00000000..bfd488fa --- /dev/null +++ b/gui/qt/gotodialog.cpp @@ -0,0 +1,47 @@ +#include "gotodialog.h" + +#include +#include +#include +#include +#include + +GotoDialog::GotoDialog(const QString &seed, + const std::vector &history, + QWidget *parent) + : QDialog(parent) { + setWindowTitle(tr("Goto")); + auto *layout = new QVBoxLayout(this); // NOLINT: Qt parent-ownership handles deletion + + auto *label = new QLabel(tr("Input Address (Or Equate):"), this); // NOLINT: Qt parent-ownership handles deletion + layout->addWidget(label); + + m_combo = new QComboBox(this); // NOLINT: Qt parent-ownership handles deletion + m_combo->setEditable(true); + + for (const QString &h : history) { + m_combo->addItem(h); + } + if (!seed.isEmpty()) { + m_combo->setEditText(seed.toUpper()); + } + layout->addWidget(m_combo); + + auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); // NOLINT: Qt parent-ownership handles deletion + connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + layout->addWidget(buttons); + + layout->setSizeConstraint(QLayout::SetFixedSize); + setMinimumWidth(360); + + setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, true); + + m_combo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + m_combo->setFixedHeight(m_combo->sizeHint().height()); + adjustSize(); +} + +QString GotoDialog::text() const { + return m_combo ? m_combo->currentText() : QString(); +} diff --git a/gui/qt/gotodialog.h b/gui/qt/gotodialog.h new file mode 100644 index 00000000..89dad6d7 --- /dev/null +++ b/gui/qt/gotodialog.h @@ -0,0 +1,25 @@ +#ifndef GOTODIALOG_H +#define GOTODIALOG_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QComboBox; +QT_END_NAMESPACE + +class GotoDialog : public QDialog { + Q_OBJECT +public: + explicit GotoDialog(const QString &seed, + const std::vector &history, + QWidget *parent = nullptr); + + [[nodiscard]] QString text() const; + +private: + QComboBox *m_combo = nullptr; +}; + +#endif // GOTODIALOG_H diff --git a/gui/qt/mainwindow.cpp b/gui/qt/mainwindow.cpp index 7a56e416..229a2963 100644 --- a/gui/qt/mainwindow.cpp +++ b/gui/qt/mainwindow.cpp @@ -638,6 +638,35 @@ MainWindow::MainWindow(CEmuOpts &cliOpts, QWidget *p) : QMainWindow(p), ui(new U setDebugIgnoreBreakpoints(m_config->value(SETTING_DEBUGGER_BREAK_IGNORE, false).toBool()); setDebugSoftCommands(m_config->value(SETTING_DEBUGGER_ENABLE_SOFT, true).toBool()); setAllowAnyRev(m_config->value(SETTING_DEBUGGER_ALLOW_ANY_REV, false).toBool()); + + // disassembly goto history + { + const QStringList list = m_config->value(SETTING_DEBUGGER_DISASM_GOTO_HISTORY).toStringList(); + m_disasmGotoHistory.clear(); + m_disasmGotoHistory.reserve(list.size()); + for (const QString &s : list) { + QString t = s.toUpper().trimmed(); + if (!t.isEmpty()) { + m_disasmGotoHistory.push_back(t); + if (m_disasmGotoHistory.size() >= 50) { break; } + } + } + } + + // memory editors goto history + { + const QStringList list = m_config->value(SETTING_DEBUGGER_MEM_GOTO_HISTORY).toStringList(); + m_memGotoHistory.clear(); + m_memGotoHistory.reserve(list.size()); + for (const QString &s : list) { + QString t = s.toUpper().trimmed(); + if (!t.isEmpty()) { + m_memGotoHistory.push_back(t); + if (m_memGotoHistory.size() >= 50) { break; } + } + } + } + setPythonEdition(qvariant_cast(m_config->value(SETTING_PYTHON_EDITION, Qt::PartiallyChecked))); setNormalOs(m_config->value(SETTING_DEBUGGER_NORM_OS, true).toBool()); setDebugLcdDma(!m_config->value(SETTING_DEBUGGER_IGNORE_DMA, true).toBool()); diff --git a/gui/qt/mainwindow.h b/gui/qt/mainwindow.h index f20948d6..b841f873 100644 --- a/gui/qt/mainwindow.h +++ b/gui/qt/mainwindow.h @@ -33,6 +33,7 @@ #include #include +#include QT_BEGIN_NAMESPACE class QButtonGroup; @@ -727,6 +728,10 @@ private slots: QString m_RamGotoAddr; QString m_memGotoAddr; + std::vector m_disasmGotoHistory; + + std::vector m_memGotoHistory; + QString m_pathConfig; QMenu *m_menuDocks; QMenu *m_menuDebug; @@ -781,6 +786,8 @@ private slots: // Settings definitions static const QString SETTING_DEBUGGER_TEXT_SIZE; + static const QString SETTING_DEBUGGER_DISASM_GOTO_HISTORY; + static const QString SETTING_DEBUGGER_MEM_GOTO_HISTORY; static const QString SETTING_DEBUGGER_DISASM_SPACE; static const QString SETTING_DEBUGGER_DISASM_TAB; static const QString SETTING_DEBUGGER_RESTORE_ON_OPEN; diff --git a/gui/qt/memorywidget.cpp b/gui/qt/memorywidget.cpp index 6804a477..b1e13f88 100644 --- a/gui/qt/memorywidget.cpp +++ b/gui/qt/memorywidget.cpp @@ -1,6 +1,7 @@ #include "mainwindow.h" #include "ui_mainwindow.h" #include "searchwidget.h" +#include "gotodialog.h" #include "dockwidget.h" #include "utils.h" #include "../../core/schedule.h" @@ -12,6 +13,7 @@ #include #include #include +#include #include #ifdef _MSC_VER @@ -105,24 +107,43 @@ void MainWindow::memUpdateEdit(HexWidget *edit, bool force) { } void MainWindow::flashGotoPressed() { - bool accept = false; - QString addrStr = getAddressString(m_flashGotoAddr, &accept); - - if (accept) { - m_flashGotoAddr = addrStr; - ui->flashEdit->setFocus(); - ui->flashEdit->setOffset(hex2int(addrStr)); + if (GotoDialog dlg(m_flashGotoAddr, m_memGotoHistory, this); dlg.exec() == QDialog::Accepted) { + const QString typed = dlg.text().toUpper().trimmed(); + bool ok = false; + const QString resolved = resolveAddressOrEquate(typed, &ok); + if (ok) { + m_flashGotoAddr = typed; + ui->flashEdit->setFocus(); + ui->flashEdit->setOffset(hex2int(resolved)); + + auto &hist = m_memGotoHistory; + std::erase_if(hist, [&](const QString &s){ return s.compare(typed, Qt::CaseInsensitive) == 0; }); + hist.insert(hist.begin(), typed); + if (hist.size() > 50) { hist.resize(50); } + } else { + QMessageBox::warning(this, MSG_WARNING, tr("Error when reading input string")); + } } } void MainWindow::ramGotoPressed() { - bool accept = false; - QString addrStr = getAddressString(m_RamGotoAddr, &accept); - - if (accept) { - m_RamGotoAddr = addrStr; - ui->ramEdit->setFocus(); - ui->ramEdit->setOffset(hex2int(addrStr) - 0xD00000); + GotoDialog dlg(m_RamGotoAddr, m_memGotoHistory, this); + if (dlg.exec() == QDialog::Accepted) { + const QString typed = dlg.text().toUpper().trimmed(); + bool ok = false; + const QString resolved = resolveAddressOrEquate(typed, &ok); + if (ok) { + m_RamGotoAddr = typed; + ui->ramEdit->setFocus(); + ui->ramEdit->setOffset(hex2int(resolved) - 0xD00000); + + auto &hist = m_memGotoHistory; + std::erase_if(hist, [&](const QString &s){ return s.compare(typed, Qt::CaseInsensitive) == 0; }); + hist.insert(hist.begin(), typed); + if (hist.size() > 50) { hist.resize(50); } + } else { + QMessageBox::warning(this, MSG_WARNING, tr("Error when reading input string")); + } } } @@ -194,11 +215,22 @@ void MainWindow::memGotoEdit(HexWidget *edit) { return; } - bool accept = false; - QString address = getAddressString(m_memGotoAddr, &accept); - - if (accept) { - memGoto(edit, static_cast(hex2int(m_memGotoAddr = address))); + GotoDialog dlg(m_memGotoAddr, m_memGotoHistory, this); + if (dlg.exec() == QDialog::Accepted) { + QString typed = dlg.text().toUpper().trimmed(); + bool ok = false; + QString resolved = resolveAddressOrEquate(typed, &ok); + if (ok) { + m_memGotoAddr = typed; + memGoto(edit, static_cast(hex2int(resolved))); + // MRU update + auto &hist = m_memGotoHistory; + hist.erase(std::remove_if(hist.begin(), hist.end(), [&](const QString &s){ return s.compare(typed, Qt::CaseInsensitive) == 0; }), hist.end()); + hist.insert(hist.begin(), typed); + if (hist.size() > 50) { hist.resize(50); } + } else { + QMessageBox::warning(this, MSG_WARNING, tr("Error when reading input string")); + } } } diff --git a/gui/qt/settings.cpp b/gui/qt/settings.cpp index ad535f91..cf00e616 100644 --- a/gui/qt/settings.cpp +++ b/gui/qt/settings.cpp @@ -33,6 +33,8 @@ #endif const QString MainWindow::SETTING_DEBUGGER_TEXT_SIZE = QStringLiteral("Debugger/text_size"); +const QString MainWindow::SETTING_DEBUGGER_DISASM_GOTO_HISTORY = QStringLiteral("Debugger/disasm_goto_history"); +const QString MainWindow::SETTING_DEBUGGER_MEM_GOTO_HISTORY = QStringLiteral("Debugger/mem_goto_history"); const QString MainWindow::SETTING_DEBUGGER_RESTORE_ON_OPEN = QStringLiteral("Debugger/restore_on_open"); const QString MainWindow::SETTING_DEBUGGER_SAVE_ON_CLOSE = QStringLiteral("Debugger/save_on_close"); const QString MainWindow::SETTING_DEBUGGER_RESET_OPENS = QStringLiteral("Debugger/open_on_reset"); @@ -1280,6 +1282,22 @@ void MainWindow::saveSettings() { m_config->setValue(SETTING_WINDOW_KEYHISTORY_DOCKS, m_docksKeyHistory); m_config->setValue(SETTING_WINDOW_KEYHISTORY_CONFIG, QVariant::fromValue(m_docksKeyHistorySize)); + // Disassembly Goto history + { + QStringList list; + list.reserve(static_cast(m_disasmGotoHistory.size())); + for (const QString &s : m_disasmGotoHistory) { list.append(s); } + m_config->setValue(SETTING_DEBUGGER_DISASM_GOTO_HISTORY, list); + } + + // Memory editors Goto history + { + QStringList list; + list.reserve(static_cast(m_memGotoHistory.size())); + for (const QString &s : m_memGotoHistory) { list.append(s); } + m_config->setValue(SETTING_DEBUGGER_MEM_GOTO_HISTORY, list); + } + saveDebug(); stateSaveInfo(); recentSaveInfo(); @@ -1368,4 +1386,3 @@ void MainWindow::keymapExport() { bool MainWindow::isFirstRun() { return !m_config->value(SETTING_FIRST_RUN, false).toBool(); } - diff --git a/gui/qt/utils.cpp b/gui/qt/utils.cpp index 5169588b..b0f1192a 100644 --- a/gui/qt/utils.cpp +++ b/gui/qt/utils.cpp @@ -59,6 +59,37 @@ QString int2hex(uint32_t a, uint8_t l) { return QString::number(a, 16).rightJustified(l, '0', true).toUpper(); } +QString resolveAddressOrEquate(const QString &input, bool *ok) { + if (ok) { + *ok = false; + } + QString in = input.toUpper().trimmed(); + if (in.isEmpty()) { + return {}; + } + + if (QString equ = getAddressOfEquate(in.toStdString()); !equ.isEmpty()) { + if (ok) { *ok = true; } + return equ; + } + + QString s = in; + if (s.startsWith(QStringLiteral("0X"))) { + s = s.mid(2); + } + if (s.isEmpty() || s.length() > 6) { + return {}; + } + std::string ss = s.toStdString(); + if (ss.find_first_not_of("0123456789ABCDEF") != std::string::npos) { + return {}; + } + + const auto value = static_cast(hex2int(in)); + if (ok) { *ok = true; } + return int2hex(value, 6); +} + std::string calc_var_content_string(const calc_var_t &var) { decltype(&tivars::TypeHandlers::TH_TempEqu::makeStringFromData) func; // We need to special case some specific temp-equ variables... diff --git a/gui/qt/utils.h b/gui/qt/utils.h index 04d42529..bab4ee24 100644 --- a/gui/qt/utils.h +++ b/gui/qt/utils.h @@ -42,6 +42,9 @@ QString sendingROM(QDragEnterEvent *e, bool *value); // integer to hex strings and vice versa int hex2int(const QString &str); QString int2hex(uint32_t a, uint8_t l); + +QString resolveAddressOrEquate(const QString &input, bool *ok); + void guiDelay(int ms); bool isProcRunning(pid_t procID);