From 3bad173cc850ad0cd90353a10747634ad8ce2c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romulo=20Leit=C3=A3o?= Date: Fri, 3 Jan 2025 18:41:48 -0300 Subject: [PATCH] Add profiling support --- .../src/core/sh2/sh2_kronos/src/sh2_opcodes.c | 77 +- yabause/src/port/qt/CMakeLists.txt | 109 +-- yabause/src/port/qt/ui/UIDebugSH2.cpp | 93 +- yabause/src/port/qt/ui/UIDebugSH2.h | 5 + yabause/src/port/qt/ui/UIProfiler.cpp | 324 +++++++ yabause/src/port/qt/ui/UIProfiler.h | 55 ++ yabause/src/port/qt/ui/UIProfiler.ui | 108 +++ yabause/src/port/qt/ui/UISettings.cpp | 824 +++++++++--------- yabause/src/port/qt/ui/UISettings.ui | 53 +- yabause/src/port/qt/ui/UIYabause.cpp | 20 + yabause/src/port/qt/ui/UIYabause.h | 3 + yabause/src/port/qt/ui/UIYabause.ui | 22 + yabause/src/sys/memory/src/cs0.c | 64 +- yabause/src/sys/sh2/include/sh2core.h | 23 + yabause/src/sys/sh2/src/sh2core.c | 54 +- 15 files changed, 1311 insertions(+), 523 deletions(-) create mode 100644 yabause/src/port/qt/ui/UIProfiler.cpp create mode 100644 yabause/src/port/qt/ui/UIProfiler.h create mode 100644 yabause/src/port/qt/ui/UIProfiler.ui diff --git a/yabause/src/core/sh2/sh2_kronos/src/sh2_opcodes.c b/yabause/src/core/sh2/sh2_kronos/src/sh2_opcodes.c index 64a77416f9..4a7a8c0204 100644 --- a/yabause/src/core/sh2/sh2_kronos/src/sh2_opcodes.c +++ b/yabause/src/core/sh2/sh2_kronos/src/sh2_opcodes.c @@ -23,6 +23,7 @@ \brief SH2 interpreter interface */ +#include #include "sh2core.h" #include "cs0.h" #include "debug.h" @@ -33,8 +34,65 @@ #include "sh2int_kronos.h" #include "opcode_functions_define.h" -extern void SH2HandleInterrupts(SH2_struct *context); -extern void SH2ExecCb(SH2_struct *context); +extern void SH2HandleInterrupts(SH2_struct* context); +extern void SH2ExecCb(SH2_struct* context); + +////////////////////////////////////////////////////////////////////////////// + +static u8 FASTCALL SH2ProfilerTrackAddr(u32 addr, SH2_struct* sh) +{ + return addr >= sh->profilerInfo.startMonitorAddress + && addr < sh->profilerInfo.endMonitorAddress; +} + +static void FASTCALL SH2ProfilerTrack(SH2_struct* sh) +{ + if (!sh->profilerInfo.profilerEnabled) { + return; + } + + const u32 pcAddr = sh->regs.PC; + SH2_ProfilerStackInfo* info = NULL; + + if (SH2ProfilerTrackAddr(pcAddr, sh)) + { + const u32 addr = pcAddr - sh->profilerInfo.startMonitorAddress; + assert(pcAddr >= PROFILE_START_ADDRESS); + assert(addr < PROFILE_NUM_INFOS); + + if (sh->profilerInfo.stackPos < PROFILE_STACK_SIZE) + { + ++sh->profilerInfo.stackPos; + assert(sh->profilerInfo.stackPos >= 0); + assert(sh->profilerInfo.stackPos < PROFILE_STACK_SIZE); + + info = &sh->profilerInfo.stack[sh->profilerInfo.stackPos]; + info->address = addr; + info->startTime = YabauseGetTicks(); + } + } +} + +static void FASTCALL SH2ProfilerStopTrack(SH2_struct* sh) +{ + if (!sh->profilerInfo.profilerEnabled) { + return; + } + + const s32 stackPos = sh->profilerInfo.stackPos; + if (stackPos >= 0) + { + SH2_ProfilerStackInfo* stackInfo = &sh->profilerInfo.stack[stackPos]; + SH2_ProfilerInfo* info = &sh->profilerInfo.profile[stackInfo->address]; + + double elapsedTime = (YabauseGetTicks() - stackInfo->startTime) * 1000.0; + elapsedTime /= (double) yabsys.tickfreq; + + info->time += elapsedTime; + ++info->count; + --sh->profilerInfo.stackPos; + } +} ////////////////////////////////////////////////////////////////////////////// @@ -295,6 +353,8 @@ static void SH2bsr(SH2_struct * sh, u32 disp) sh->regs.PC = sh->regs.PC+(disp<<1); sh->regs.PC += 2; + SH2ProfilerTrack(sh); + sh->cycles += 2; SH2delay(sh, temp + 2); } @@ -308,6 +368,9 @@ static void SH2bsrf(SH2_struct * sh, u32 n) sh->regs.PR = sh->regs.PC + 4; sh->regs.PC += sh->regs.R[n]; sh->regs.PC += 2; + + SH2ProfilerTrack(sh); + sh->cycles += 2; SH2delay(sh, temp + 2); } @@ -753,6 +816,9 @@ static void SH2jsr(SH2_struct * sh, u32 m) sh->regs.PR = sh->regs.PC + 4; sh->regs.PC = sh->regs.R[m] - 4; sh->regs.PC += 2; + + SH2ProfilerTrack(sh); + sh->cycles += 2; SH2delay(sh, temp + 2); } @@ -1658,6 +1724,9 @@ static void SH2rte(SH2_struct * sh) { u32 temp; temp=sh->regs.PC; + + SH2ProfilerStopTrack(sh); + sh->regs.PC = SH2MappedMemoryReadLong(sh, sh->regs.R[15]) - 4; sh->regs.R[15] += 4; sh->regs.SR.all = SH2MappedMemoryReadLong(sh, sh->regs.R[15]) & 0x000003F3; @@ -1678,8 +1747,10 @@ static void SH2rte(SH2_struct * sh) static void SH2rts(SH2_struct * sh) { u32 temp; - temp = sh->regs.PC; + + SH2ProfilerStopTrack(sh); + sh->regs.PC = sh->regs.PR - 4; sh->cycles += 2; sh->regs.PC += 2; diff --git a/yabause/src/port/qt/CMakeLists.txt b/yabause/src/port/qt/CMakeLists.txt index 3fa9e5669c..b72a24caf9 100644 --- a/yabause/src/port/qt/CMakeLists.txt +++ b/yabause/src/port/qt/CMakeLists.txt @@ -53,21 +53,22 @@ set( kronos_qt_FORMS ui/UIPadSetting.ui ui/UISTVSetting.ui ui/UI3DControlPadSetting.ui - ui/UIWheelSetting.ui - ui/UIMissionStickSetting.ui - ui/UIDoubleMissionStickSetting.ui + ui/UIWheelSetting.ui + ui/UIMissionStickSetting.ui + ui/UIDoubleMissionStickSetting.ui ui/UIGunSetting.ui ui/UIMouseSetting.ui - ui/UIDebugCPU.ui - ui/UIDebugSCSP.ui - ui/UIDebugSCSPDSP.ui - ui/UIDebugVDP1.ui - ui/UIDebugVDP2.ui - ui/UIDebugVDP2Viewer.ui - ui/UIHexInput.ui - ui/UIMemoryTransfer.ui - ui/UIMemoryEditor.ui - ui/UIMemorySearch.ui ) + ui/UIDebugCPU.ui + ui/UIDebugSCSP.ui + ui/UIDebugSCSPDSP.ui + ui/UIDebugVDP1.ui + ui/UIDebugVDP2.ui + ui/UIDebugVDP2Viewer.ui + ui/UIHexInput.ui + ui/UIProfiler.ui + ui/UIMemoryTransfer.ui + ui/UIMemoryEditor.ui + ui/UIMemorySearch.ui ) # pure C headers set( kronos_qt_HEADERS @@ -89,30 +90,31 @@ set( kronos_qt_MOC_HEADERS ui/UIPortManager.h ui/UIControllerSetting.h ui/UIPadSetting.h - ui/UISTVSetting.h + ui/UISTVSetting.h ui/UI3DControlPadSetting.h - ui/UIWheelSetting.h - ui/UIMissionStickSetting.h - ui/UIDoubleMissionStickSetting.h + ui/UIWheelSetting.h + ui/UIMissionStickSetting.h + ui/UIDoubleMissionStickSetting.h ui/UIGunSetting.h ui/UIMouseSetting.h ui/UIShortcutManager.h - ui/UIDebugCPU.h - ui/UIDebugM68K.h - ui/UIDebugSCSP.h - ui/UIDebugSCSPChan.h - ui/UIDebugSCSPDSP.h - ui/UIDebugSCUDSP.h - ui/UIDebugSH2.h - ui/UIDebugVDP1.h - ui/UIDebugVDP2.h - ui/UIDebugVDP2Viewer.h - ui/UIDisasm.h - ui/UIHexInput.h - ui/UIMemoryTransfer.h - ui/UIHexEditor.h - ui/UIMemoryEditor.h - ui/UIMemorySearch.h + ui/UIDebugCPU.h + ui/UIDebugM68K.h + ui/UIDebugSCSP.h + ui/UIDebugSCSPChan.h + ui/UIDebugSCSPDSP.h + ui/UIDebugSCUDSP.h + ui/UIDebugSH2.h + ui/UIDebugVDP1.h + ui/UIDebugVDP2.h + ui/UIDebugVDP2Viewer.h + ui/UIDisasm.h + ui/UIHexInput.h + ui/UIProfiler.h + ui/UIMemoryTransfer.h + ui/UIHexEditor.h + ui/UIMemoryEditor.h + ui/UIMemorySearch.h YabauseGL.h VolatileSettings.h Settings.h @@ -135,30 +137,31 @@ set( kronos_qt_SOURCES ui/UIPortManager.cpp ui/UIControllerSetting.cpp ui/UIPadSetting.cpp - ui/UISTVSetting.cpp + ui/UISTVSetting.cpp ui/UI3DControlPadSetting.cpp - ui/UIWheelSetting.cpp - ui/UIMissionStickSetting.cpp - ui/UIDoubleMissionStickSetting.cpp + ui/UIWheelSetting.cpp + ui/UIMissionStickSetting.cpp + ui/UIDoubleMissionStickSetting.cpp ui/UIGunSetting.cpp ui/UIMouseSetting.cpp ui/UIShortcutManager.cpp - ui/UIDebugCPU.cpp - ui/UIDebugM68K.cpp - ui/UIDebugSCSP.cpp - ui/UIDebugSCSPChan.cpp - ui/UIDebugSCSPDSP.cpp - ui/UIDebugSCUDSP.cpp - ui/UIDebugSH2.cpp - ui/UIDebugVDP1.cpp - ui/UIDebugVDP2.cpp - ui/UIDebugVDP2Viewer.cpp - ui/UIDisasm.cpp - ui/UIHexInput.cpp - ui/UIMemoryTransfer.cpp - ui/UIHexEditor.cpp - ui/UIMemoryEditor.cpp - ui/UIMemorySearch.cpp + ui/UIDebugCPU.cpp + ui/UIDebugM68K.cpp + ui/UIDebugSCSP.cpp + ui/UIDebugSCSPChan.cpp + ui/UIDebugSCSPDSP.cpp + ui/UIDebugSCUDSP.cpp + ui/UIDebugSH2.cpp + ui/UIDebugVDP1.cpp + ui/UIDebugVDP2.cpp + ui/UIDebugVDP2Viewer.cpp + ui/UIDisasm.cpp + ui/UIHexInput.cpp + ui/UIProfiler.cpp + ui/UIMemoryTransfer.cpp + ui/UIHexEditor.cpp + ui/UIMemoryEditor.cpp + ui/UIMemorySearch.cpp Settings.cpp VolatileSettings.cpp YabauseThread.cpp diff --git a/yabause/src/port/qt/ui/UIDebugSH2.cpp b/yabause/src/port/qt/ui/UIDebugSH2.cpp index ceab249dca..fa2b60a7a8 100644 --- a/yabause/src/port/qt/ui/UIDebugSH2.cpp +++ b/yabause/src/port/qt/ui/UIDebugSH2.cpp @@ -119,6 +119,7 @@ UIDebugSH2::UIDebugSH2(UIDebugCPU::PROCTYPE proc, YabauseThread *mYabauseThread, connect( pbLoadCode, SIGNAL( clicked() ), this, SLOT( loadCodeAddress() ) ); restoreAddr2line(); + restoreCppFilt(); } void UIDebugSH2::restoreAddr2line() @@ -127,6 +128,12 @@ void UIDebugSH2::restoreAddr2line() addr2line = settings->value( "Debug/Addr2Line" ).toString(); } +void UIDebugSH2::restoreCppFilt() +{ + Settings* settings = QtYabause::settings(); + cppfilt = settings->value( "Debug/CppFilt" ).toString(); +} + void UIDebugSH2::updateRegList() { int i; @@ -232,55 +239,59 @@ void UIDebugSH2::loadCodeAddress() updateCodePage(static_cast(std::stoull(newAddress, nullptr, 16))); } +QString UIDebugSH2::findElfPath() +{ + QString elfPath; + VolatileSettings *vs = QtYabause::volatileSettings(); + if (vs->value("General/CdRom") != CDCORE_ISO) + { + return ""; + } + else + { + const QString isoPathString{ vs->value( "Recents/ISOs" ).toString() }; + const QFileInfo fileInfo(isoPathString); + QDir searchPath = fileInfo.dir(); + + const QString filename = fileInfo.completeBaseName() + ".elf"; + YuiMsg("looking for %s\n", filename.toStdString().c_str()); + + if (searchPath.cd("build")) { + if (searchPath.exists(filename)) { + // Found in build folder + return QFileInfo(searchPath, filename).absoluteFilePath(); + } + else { + searchPath.cdUp(); + } + } + + if (elfPath.isEmpty()) { + if (searchPath.exists(filename)) { + // Found in local folder + return QFileInfo(searchPath, filename).absoluteFilePath(); + } + } + } + + return ""; +} + void UIDebugSH2::updateCodePage(u32 evaluateAddress) { YuiMsg("Address to inspect %x\n", evaluateAddress); if (addr2line.isEmpty()) restoreAddr2line(); + + if (cppfilt.isEmpty()) + restoreCppFilt(); + + QString elfPath = findElfPath(); + if (elfPath.isEmpty()) + return; - QString elfPath; const QString program{ addr2line }; - VolatileSettings *vs = QtYabause::volatileSettings(); - if ( vs->value( "General/CdRom" ) != CDCORE_ISO ) - { - YuiMsg("Not using ISO, ignoring code\n"); - return; - } - else - { - const QString isoPathString{ vs->value( "Recents/ISOs" ).toString() }; - const QFileInfo fileInfo(isoPathString); - QDir searchPath = fileInfo.dir(); - const QString filename = fileInfo.completeBaseName() + ".elf"; - - YuiMsg("looking for %s\n", filename.toStdString().c_str()); - - if (searchPath.cd("build")) { - YuiMsg("looking for %s in %s\n", filename.toStdString().c_str(), searchPath.path().toStdString().c_str()); - if (searchPath.exists(filename)) { - //Found in build folder - elfPath = QFileInfo(searchPath, filename).absoluteFilePath(); - printf("Found %s !!\n", elfPath.toStdString().c_str()); - } - else { - searchPath.cdUp(); - } - } - if (elfPath.isEmpty()) { - YuiMsg("looking for %s in %s\n", filename.toStdString().c_str(), searchPath.path().toStdString().c_str()); - if (searchPath.exists(filename)) { - //Found in local folder - YuiMsg("Found %s in %s\n", filename.toStdString().c_str(), searchPath.path().toStdString().c_str()); - elfPath = QFileInfo(searchPath, filename).absoluteFilePath(); - } - else { - // Not found at all - YuiMsg("Could not find elf file, ignoring code\n"); - return; - } - } - } std::stringstream hexAddress; hexAddress << std::setfill('0') << std::setw(8) << std::hex << evaluateAddress; diff --git a/yabause/src/port/qt/ui/UIDebugSH2.h b/yabause/src/port/qt/ui/UIDebugSH2.h index b44ddf5299..45f9dc4171 100644 --- a/yabause/src/port/qt/ui/UIDebugSH2.h +++ b/yabause/src/port/qt/ui/UIDebugSH2.h @@ -28,7 +28,9 @@ class UIDebugSH2 : public UIDebugCPU private: SH2_struct *debugSH2; QString addr2line; + QString cppfilt; void restoreAddr2line(); + void restoreCppFilt(); public: UIDebugSH2( UIDebugCPU::PROCTYPE proc, YabauseThread *mYabauseThread, QWidget* parent = 0 ); @@ -50,6 +52,9 @@ class UIDebugSH2 : public UIDebugCPU void reserved1(); void reserved2(); void reserved3(); + + static QString findElfPath(); + protected: protected slots: diff --git a/yabause/src/port/qt/ui/UIProfiler.cpp b/yabause/src/port/qt/ui/UIProfiler.cpp new file mode 100644 index 0000000000..e7636ec7fe --- /dev/null +++ b/yabause/src/port/qt/ui/UIProfiler.cpp @@ -0,0 +1,324 @@ +/* Copyright 2025 Romulo Leitão + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include "Settings.h" +#include "UIDebugSH2.h" +#include "UIProfiler.h" + +#include +#include +#include +#include +#include + +namespace { + +QString invokeAddr2LineAndFilt(u32 address, const QString& addr2line, const QString& elfPath, const QString& cppFilt) { + if (addr2line.isEmpty() || elfPath.isEmpty()) { + return ""; + } + + // The options are: + // -i --inlines Unwind inlined functions + // -p --pretty-print Make the output easier to read for humans + // -f --functions Show function names + // -r --no-recurse-limit Disable a limit on recursion whilst demangling + // -s --basenames Strip directory names + // -e --exe= Set the input file name (default is a.out) + const QStringList arguments = { + QString("%1").arg(address, 8, 16, QChar('0')), + "-i", "-p", "-f", "-r", "-s", + "-e", elfPath + }; + + QProcess addr2lineProgram; + addr2lineProgram.start(addr2line, arguments); + addr2lineProgram.waitForFinished(); + + const QByteArray pStdout = addr2lineProgram.readAllStandardOutput(); + if (addr2lineProgram.exitCode() == 0) { + if (cppFilt.isEmpty()) + { + QString result(pStdout); + result.replace("\n", ""); + result.replace("\r", ""); + return result; + } + else { + QStringList words = QString(pStdout).split(' ', QString::SkipEmptyParts); + + QString result; + for (const QString& word : words) { + QStringList cppfiltArgs; + if (!word.startsWith("__")) + { + cppfiltArgs.append("-n"); + } + cppfiltArgs.append(word); + + QProcess cppFiltProgram; + cppFiltProgram.start(cppFilt, cppfiltArgs); + cppFiltProgram.waitForFinished(); + + if (cppFiltProgram.exitCode() == 0) + { + result += cppFiltProgram.readAllStandardOutput(); + result += " "; + } + else { + result += word; + result += " "; + } + } + + result.replace("\n", ""); + result.replace("\r", ""); + return result; + } + } + + return ""; +} + +std::pair getTotalExecutionTime() +{ + double totalMasterTime = 0; + double totalSlaveTime = 0; + + for (u32 i = 0; i < PROFILE_NUM_INFOS; ++i) + { + SH2_ProfilerInfo* infoM = &MSH2->profilerInfo.profile[i]; + if (infoM->count > 0) + { + totalMasterTime += infoM->time; + } + + SH2_ProfilerInfo* infoS = &SSH2->profilerInfo.profile[i]; + if (infoS->count > 0) + { + totalSlaveTime += infoS->time; + } + } + + return { totalMasterTime, totalSlaveTime }; +} + +QString addressToHexString(u32 address) +{ + return QString("0x%1").arg(address, 8, 16, QChar('0')) + .trimmed() + .toUpper(); +} + +} // namespace '' + +UIProfiler::UIProfiler( YabauseThread *yabauseThread, QWidget* p ) + : QDialog( p ) +{ + setupUi(this); + mYabauseThread = yabauseThread; + + QStringList headers; + headers << "Count" + << "Time(ms)" + << "Percent" + << "Ptr(H)" + << "Description"; + + mItemModel = new QStandardItemModel(); + mItemModel->setSortRole(Qt::UserRole + 1); + mItemModel->setColumnCount(headers.size()); + mItemModel->setHorizontalHeaderLabels(headers); + + twProfilerResults->setModel(mItemModel); + + twProfilerResults->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + twProfilerResults->horizontalHeader()->setStretchLastSection(true); + twProfilerResults->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + twProfilerResults->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); // Count + twProfilerResults->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents); // Time + twProfilerResults->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents); // Percent + twProfilerResults->horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeToContents); // Ptr + twProfilerResults->horizontalHeader()->setSectionResizeMode(4, QHeaderView::Stretch); // Description + + twProfilerResults->setAlternatingRowColors(true); + + QtYabause::retranslateWidget( this ); + + if (mYabauseThread->init() == 0 && MSH2 && SSH2) + { + populateTable(); + } +} + +void UIProfiler::addRow( u64 count, double timeMs, double percent, u32 ptrH, const QString &description ) +{ + int row = mItemModel->rowCount(); + mItemModel->insertRow(row); + + // Qt::UserRole + 1 is used here for sorting + + QStandardItem* countItem = new QStandardItem(QString::number(count)); + countItem->setData(timeMs, Qt::UserRole + 1); + countItem->setTextAlignment(Qt::AlignCenter); + + QStandardItem* timeItem = new QStandardItem(QString::number(timeMs, 'f', 2)); + timeItem->setData(timeMs, Qt::UserRole + 1); + timeItem->setTextAlignment(Qt::AlignCenter); + + QStandardItem* percentItem = new QStandardItem(QString::number(percent, 'f', 2)); + percentItem->setData(percent, Qt::UserRole + 1); + percentItem->setTextAlignment(Qt::AlignCenter); + + const QString pointerString = addressToHexString(ptrH); + + QStandardItem* pointerItem = new QStandardItem(pointerString); + pointerItem->setData(pointerString, Qt::UserRole + 1); + pointerItem->setTextAlignment(Qt::AlignCenter); + + QStandardItem* descriptionItem = new QStandardItem(description.trimmed()); + pointerItem->setData(description, Qt::UserRole + 1); + pointerItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); + + mItemModel->setItem(row, 0, countItem); + mItemModel->setItem(row, 1, timeItem); + mItemModel->setItem(row, 2, percentItem); + mItemModel->setItem(row, 3, pointerItem); + mItemModel->setItem(row, 4, descriptionItem); + + QString textRow = QString("%1,%2,%3,%4,%5\n").arg(countItem->text(), + timeItem->text(), percentItem->text(), pointerItem->text(), + QString("\"%1\"").arg(descriptionItem->text().replace("\"", "\"\""))); + + mRows.push_back(textRow.toStdString()); +} + +void UIProfiler::populateTable() +{ + Settings* settings = QtYabause::settings(); + QString addr2line = settings->value( "Debug/Addr2Line" ).toString(); + QString cppfilt = settings->value( "Debug/CppFilt" ).toString(); + QString elfFile = UIDebugSH2::findElfPath(); + + const std::pair executionTime = getTotalExecutionTime(); + const double totalMasterTime = executionTime.first; + const double totalSlaveTime = executionTime.second; + + lTitle->setText(QString("Total execution time (M/S): %1 / %2 ms").arg( + QString::number(totalMasterTime, 'f', 2), + QString::number(totalSlaveTime, 'f', 2))); + + mItemModel->setRowCount(0); + mRows.clear(); + twProfilerResults->setSortingEnabled(false); + + for (u32 i = 0; i < PROFILE_NUM_INFOS; ++i) + { + // Master + { + SH2_ProfilerInfo* infoM = &MSH2->profilerInfo.profile[i]; + if (infoM->count > 0) + { + u32 address = MSH2->profilerInfo.startMonitorAddress + i; + QString description = invokeAddr2LineAndFilt(address, addr2line, elfFile, cppfilt); + + addRow(infoM->count, infoM->time, (double)infoM->time / totalMasterTime, + address, description); + } + } + + // Slave + { + SH2_ProfilerInfo* infoS = &SSH2->profilerInfo.profile[i]; + if (infoS->count > 0) + { + u32 address = SSH2->profilerInfo.startMonitorAddress + i; + QString description = invokeAddr2LineAndFilt(address, addr2line, elfFile, cppfilt); + addRow(infoS->count, infoS->time, (double)infoS->time / totalSlaveTime, + address, description); + } + } + } + + twProfilerResults->setSortingEnabled(true); + twProfilerResults->sortByColumn(1 /* Time(ms) */, Qt::DescendingOrder); +} + +void UIProfiler::clearTable() +{ + lTitle->setText("Total execution time (M/S): 0.00 / 0.00 ms"); + for (u32 i = 0; i < PROFILE_NUM_INFOS; ++i) + { + SH2_ProfilerInfo* infoM = &MSH2->profilerInfo.profile[i]; + infoM->time = 0; + infoM->count = 0; + + SH2_ProfilerInfo* infoS = &SSH2->profilerInfo.profile[i]; + infoS->time = 0; + infoS->count = 0; + } + + twProfilerResults->setSortingEnabled(false); + mItemModel->setRowCount(0); + mRows.clear(); +} + +void UIProfiler::on_pbClearResults_clicked() +{ + clearTable(); +} + +void UIProfiler::on_pbExportResults_clicked() +{ + QString fileName = QFileDialog::getSaveFileName(this, + tr("Save File"), QString(), tr("CSV (*.csv)")); + + if (!fileName.isEmpty()) + { + const QByteArray utf8 = fileName.toUtf8(); + std::ofstream outputFile(utf8.data(), std::ios_base::out); + if (!outputFile.is_open()) + { + QMessageBox::critical(this, "Error", "Failed to save CSV file."); + return; + } + + outputFile << "Count,Time(ms),Percent,Ptr(H),Description\n"; + for (const std::string& row : mRows) + { + outputFile << row; + } + + outputFile << "\n"; + outputFile.close(); + } +} + +void UIProfiler::accept() +{ + QDialog::accept(); +} + +void UIProfiler::reject() +{ + QDialog::reject(); +} diff --git a/yabause/src/port/qt/ui/UIProfiler.h b/yabause/src/port/qt/ui/UIProfiler.h new file mode 100644 index 0000000000..779151a697 --- /dev/null +++ b/yabause/src/port/qt/ui/UIProfiler.h @@ -0,0 +1,55 @@ +/* Copyright 2025 Romulo Leitão + + This file is part of Yabause. + + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef UIPROFILER_H +#define UIPROFILER_H + +#include + +#include "ui_UIProfiler.h" +#include "../YabauseThread.h" +#include "../QtYabause.h" + +class QStandardItemModel; + +class UIProfiler : public QDialog, public Ui::UIProfiler +{ + Q_OBJECT + +public: + UIProfiler( YabauseThread *mYabauseThread, QWidget* parent = 0 ); + +protected: + YabauseThread *mYabauseThread; + QStandardItemModel* mItemModel; + std::vector mRows; + + void addRow(u64 count, double timeMs, double percent, u32 ptrH, const QString& description); + + void populateTable(); + + void clearTable(); + +protected slots: + void on_pbClearResults_clicked(); + void on_pbExportResults_clicked(); + void accept() override; + void reject() override; +}; + +#endif // UIProfiler_H diff --git a/yabause/src/port/qt/ui/UIProfiler.ui b/yabause/src/port/qt/ui/UIProfiler.ui new file mode 100644 index 0000000000..41f6dd1920 --- /dev/null +++ b/yabause/src/port/qt/ui/UIProfiler.ui @@ -0,0 +1,108 @@ + + + UIProfiler + + + + 0 + 0 + 600 + 400 + + + + Profiler Results + + + true + + + + + + + + + 75 + true + + + + Total Profiling Time: 0ms + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + Export to CSV... + + + + + + + Clear Results + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + + + + + buttonBox + accepted() + UIProfiler + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + UIProfiler + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/yabause/src/port/qt/ui/UISettings.cpp b/yabause/src/port/qt/ui/UISettings.cpp index b30bafec3e..63e07bfddf 100755 --- a/yabause/src/port/qt/ui/UISettings.cpp +++ b/yabause/src/port/qt/ui/UISettings.cpp @@ -1,21 +1,21 @@ /* Copyright 2005 Guillaume Duhamel - Copyright 2005-2006, 2013 Theo Berkau - Copyright 2008 Filipe Azevedo + Copyright 2005-2006, 2013 Theo Berkau + Copyright 2008 Filipe Azevedo - This file is part of Yabause. + This file is part of Yabause. - Yabause is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + Yabause is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. - Yabause is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + Yabause is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with Yabause; if not, write to the Free Software + You should have received a copy of the GNU General Public License + along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "UISettings.h" @@ -35,13 +35,13 @@ #include "VolatileSettings.h" extern "C" { -extern M68K_struct* M68KCoreList[]; -extern SH2Interface_struct* SH2CoreList[]; -extern PerInterface_struct* PERCoreList[]; -extern CDInterface* CDCoreList[]; -extern SoundInterface_struct* SNDCoreList[]; -extern VideoInterface_struct* VIDCoreList[]; -extern OSD_struct* OSDCoreList[]; +extern M68K_struct *M68KCoreList[]; +extern SH2Interface_struct *SH2CoreList[]; +extern PerInterface_struct *PERCoreList[]; +extern CDInterface *CDCoreList[]; +extern SoundInterface_struct *SNDCoreList[]; +extern VideoInterface_struct *VIDCoreList[]; +extern OSD_struct *OSDCoreList[]; } struct Item @@ -49,12 +49,12 @@ struct Item Item( const QString& i, const QString& n, bool e=true, bool s=true, bool z=false, bool p=false) { id = i; Name = n; enableFlag = e; saveFlag = s; ipFlag = z; pathFlag = p;} - QString id; - QString Name; - bool enableFlag; - bool saveFlag; - bool ipFlag; - bool pathFlag; + QString id; + QString Name; + bool enableFlag; + bool saveFlag; + bool ipFlag; + bool pathFlag; }; typedef QList Items; @@ -75,26 +75,26 @@ const Items mRegions = Items() const Items mCartridgeTypes = Items() << Item( "0", "None", false, false ) - << Item( "1", "Pro Action Replay", true, false) - << Item( "2", "4 Mbit Backup Ram", true, true ) - << Item( "3", "8 Mbit Backup Ram", true, true ) - << Item( "4", "16 Mbit Backup Ram", true, true ) - << Item( "5", "32 Mbit Backup Ram", true, true ) - << Item( "6", "8 Mbit Dram", false, false ) - << Item( "7", "32 Mbit Dram", false, false ) - << Item( "8", "Netlink", false, false, true ) - << Item( "9", "16 Mbit ROM", true, false ) - << Item( "10", "Japanese Modem", false, false, true ) - << Item( "11", "STV Rom game", true, false, false, true ) - << Item( "12", "128 Mbit Dram", false, false ) - << Item( "13", "Development Extension", false, false ); + << Item("1", "Pro Action Replay", true, false) + << Item("2", "4 Mbit Backup Ram", true, true) + << Item("3", "8 Mbit Backup Ram", true, true) + << Item("4", "16 Mbit Backup Ram", true, true) + << Item("5", "32 Mbit Backup Ram", true, true) + << Item("6", "8 Mbit Dram", false, false) + << Item("7", "32 Mbit Dram", false, false) + << Item("8", "Netlink", false, false, true) + << Item("9", "16 Mbit ROM", true, false) + << Item("10", "Japanese Modem", false, false, true) + << Item("11", "STV Rom game", true, false, false, true) + << Item("12", "128 Mbit Dram", false, false) + << Item("13", "Development Extension", false, false); const Items mVideoFilterMode = Items() << Item("0", "None") << Item("1", "Bilinear") << Item("2", "BiCubic") << Item("3", "Deinterlacing Adaptative") - << Item("4", "Deinterlacing Debug Adaptative") + << Item("4", "Deinterlacing Debug Adaptative") << Item("5", "Deinterlacing Bob") << Item("6", "Scanline"); @@ -108,13 +108,13 @@ const Items mResolutionMode = Items() << Item("1", "1x (original resolution of the Saturn)") << Item("8", "2x") << Item("32", "4x") - << Item("64", "Native (current resolution of the window)"); + << Item("64", "Native (current resolution of the window)"); const Items mAspectRatio = Items() << Item("0", "Original aspect ratio") - << Item("1", "Stretch to window") - << Item("2", "Pixel Perfect - Full screen") - << Item("3", "Pixel Perfect"); + << Item("1", "Stretch to window") + << Item("2", "Pixel Perfect - Full screen") + << Item("3", "Pixel Perfect"); const Items mMeshMode = Items() << Item("0", "Original") @@ -128,49 +128,49 @@ const Items mWireframe = Items() << Item("0", "Off") << Item("1", "On"); -UISettings::UISettings(QList *translations, QWidget* p ) +UISettings::UISettings(QList *translations, QWidget *p) : QDialog( p ) { - // setup dialog - setupUi( this ); + // setup dialog + setupUi(this); - QString ipNum("(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])"); + QString ipNum("(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])"); leCartridgeModemIP->setValidator(new QRegExpValidator(QRegExp("^" + ipNum + "\\." + ipNum + "\\." + ipNum + "\\." + ipNum + "$"), leCartridgeModemIP)); leCartridgeModemPort->setValidator(new QIntValidator(1, 65535, leCartridgeModemPort)); - pmPort1->setPort( 1 ); - pmPort1->loadSettings(); - pmPort2->setPort( 2 ); - pmPort2->loadSettings(); + pmPort1->setPort(1); + pmPort1->loadSettings(); + pmPort2->setPort(2); + pmPort2->loadSettings(); if ( p && !p->isFullScreen() ) { - setWindowFlags( Qt::Sheet ); - } + setWindowFlags(Qt::Sheet); + } - setupCdDrives(); + setupCdDrives(); - // load cores informations - loadCores(); + // load cores informations + loadCores(); #ifdef HAVE_LIBMINI18N - trans = *translations; - loadTranslations(); + trans = *translations; + loadTranslations(); #else - lTranslation->hide(); - cbTranslation->hide(); + lTranslation->hide(); + cbTranslation->hide(); #endif - populateShortcutsView(); + populateShortcutsView(); - // load settings - loadSettings(); + // load settings + loadSettings(); - // connections + // connections foreach ( QToolButton* tb, findChildren() ) { - connect( tb, SIGNAL( clicked() ), this, SLOT( tbBrowse_clicked() ) ); - } + connect(tb, SIGNAL(clicked()), this, SLOT(tbBrowse_clicked())); + } } @@ -181,133 +181,133 @@ void UISettings::setCurrentOpenedTab(int idx) int UISettings::exec() { - // load settings - loadSettings(); - // retranslate widgets - QtYabause::retranslateWidget( this ); + // load settings + loadSettings(); + // retranslate widgets + QtYabause::retranslateWidget(this); - return QDialog::exec(); + return QDialog::exec(); } void UISettings::requestFile( const QString& c, QLineEdit* e, const QString& filters, QString proposedPath) { - auto const & path = proposedPath.isEmpty() ? e->text() : proposedPath; - const QString s = CommonDialogs::getOpenFileName(path, c, filters ); - if ( !s.isNull() ) - e->setText( s ); + auto const &path = proposedPath.isEmpty() ? e->text() : proposedPath; + const QString s = CommonDialogs::getOpenFileName(path, c, filters); + if (!s.isNull()) + e->setText(s); } void UISettings::requestNewFile( const QString& c, QLineEdit* e, const QString& filters, QString proposedPath) { - auto const & path = proposedPath.isEmpty() ? e->text(): proposedPath; - const QString s = CommonDialogs::getSaveFileName(path, c, filters ); - if ( !s.isNull() ) - e->setText( s ); + auto const &path = proposedPath.isEmpty() ? e->text() : proposedPath; + const QString s = CommonDialogs::getSaveFileName(path, c, filters); + if (!s.isNull()) + e->setText(s); } void UISettings::requestFolder( const QString& c, QLineEdit* e, QString proposedPath) { - auto const & path = proposedPath.isEmpty() ? e->text() : proposedPath; - const QString s = CommonDialogs::getExistingDirectory(path, c ); - if ( !s.isNull() ) { - e->setText( s ); + auto const &path = proposedPath.isEmpty() ? e->text() : proposedPath; + const QString s = CommonDialogs::getExistingDirectory(path, c); + if (!s.isNull()) { + e->setText(s); } } void UISettings::requestSTVFolder( const QString& c, QLineEdit* e, QString proposedPath ) { - auto const & path = proposedPath.isEmpty() ? e->text() : proposedPath; + auto const &path = proposedPath.isEmpty() ? e->text() : proposedPath; const QString existingDirectoryPath = CommonDialogs::getExistingDirectory(path, c ); - if ( !existingDirectoryPath.isNull() ) { - e->setText( existingDirectoryPath ); - } + if (!existingDirectoryPath.isNull()) { + e->setText(existingDirectoryPath); + } int const nbGames = STVGetRomList(existingDirectoryPath.toStdString().c_str(), 1); - cbSTVGame->clear(); - for(int i = 0; i< nbGames; i++){ - cbSTVGame->addItem(getSTVGameName(i),getSTVRomset(i)); - } - cbSTVGame->model()->sort(0); + cbSTVGame->clear(); + for (int i = 0; i < nbGames; i++) { + cbSTVGame->addItem(getSTVGameName(i), getSTVRomset(i)); + } + cbSTVGame->model()->sort(0); } QStringList getCdDriveList() { - QStringList list; + QStringList list; #if defined Q_OS_WIN - foreach (const QStorageInfo &storage, QStorageInfo::mountedVolumes()) { - QFileInfo drive(storage.rootPath()); - LPCWSTR driveString = (LPCWSTR)drive.filePath().utf16(); - if (GetDriveTypeW(driveString) == DRIVE_CDROM) - list.append(storage.rootPath()); - } + foreach (const QStorageInfo &storage, QStorageInfo::mountedVolumes()) { + QFileInfo drive(storage.rootPath()); + LPCWSTR driveString = (LPCWSTR)drive.filePath().utf16(); + if (GetDriveTypeW(driveString) == DRIVE_CDROM) + list.append(storage.rootPath()); + } #elif defined Q_OS_LINUX - FILE * f = fopen("/proc/sys/dev/cdrom/info", "r"); - char buffer[1024]; - char drive_name[10]; - char drive_path[255]; - - if (f != NULL) { - while (fgets(buffer, 1024, f) != NULL) { - if (sscanf(buffer, "drive name:%s", drive_name) == 1) { - sprintf(drive_path, "/dev/%s", drive_name); - - list.append(drive_path); - } - } - fclose(f); - } + FILE *f = fopen("/proc/sys/dev/cdrom/info", "r"); + char buffer[1024]; + char drive_name[10]; + char drive_path[255]; + + if (f != NULL) { + while (fgets(buffer, 1024, f) != NULL) { + if (sscanf(buffer, "drive name:%s", drive_name) == 1) { + sprintf(drive_path, "/dev/%s", drive_name); + + list.append(drive_path); + } + } + fclose(f); + } #elif defined Q_OS_MAC #endif - return list; + return list; } void UISettings::setupCdDrives() { - QStringList list=getCdDriveList(); - foreach(QString string, list) - cbCdDrive->addItem(string); + QStringList list = getCdDriveList(); + foreach (QString string, list) + cbCdDrive->addItem(string); } void UISettings::on_leBios_textChanged(const QString & text) { - if (QFileInfo(text).exists()) - cbEnableBiosEmulation->setEnabled(true); - else - cbEnableBiosEmulation->setEnabled(false); + if (QFileInfo(text).exists()) + cbEnableBiosEmulation->setEnabled(true); + else + cbEnableBiosEmulation->setEnabled(false); } -void UISettings::on_leBiosSettings_textChanged(const QString & text){ - if (QFileInfo(text).exists()){ - cbSysLanguageID->setVisible(false); - lSysLanguageID->setVisible(false); +void UISettings::on_leBiosSettings_textChanged(const QString &text) { + if (QFileInfo(text).exists()) { + cbSysLanguageID->setVisible(false); + lSysLanguageID->setVisible(false); } else{ - Settings const * const s = QtYabause::settings(); - cbSysLanguageID->setVisible(true); - lSysLanguageID->setVisible(true); + Settings const *const s = QtYabause::settings(); + cbSysLanguageID->setVisible(true); + lSysLanguageID->setVisible(true); cbSysLanguageID->setCurrentIndex( cbSysLanguageID->findData( s->value( "General/SystemLanguageID", mSysLanguageID.at( 0 ).id ).toString() ) ); - } + } } void UISettings::tbBrowse_clicked() { - // get toolbutton sender - QToolButton* tb = qobject_cast( sender() ); + // get toolbutton sender + QToolButton *tb = qobject_cast(sender()); - if ( tb == tbBios ) - requestFile( QtYabause::translate( "Choose a bios file" ), leBios ); - else if ( tb == tbBiosSettings ) + if (tb == tbBios) + requestFile(QtYabause::translate("Choose a bios file"), leBios); + else if (tb == tbBiosSettings) requestNewFile( QtYabause::translate("Choose a file to store bios settings"), leBiosSettings); else if ( tb == tbCdRom ) { if ( cbCdRom->currentText().contains( "dummy", Qt::CaseInsensitive ) ) { CommonDialogs::error( QtYabause::translate( "The dummies cores don't need configuration." ) ); - return; + return; } else if ( cbCdRom->currentText().contains( "iso", Qt::CaseInsensitive ) ) requestFile( QtYabause::translate( "Select your iso/cue/bin/zip file" ), leCdRom, QtYabause::translate( "CD Images (*.iso *.ISO *.cue *.CUE *.bin *.BIN *.mds *.MDS *.ccd *.CCD *.zip *.ZIP *.chd *.CHD)" ) ); - else + else requestFolder( QtYabause::translate( "Choose a cdrom drive/mount point" ), leCdRom ); } else if ( tb == tbSaveStates ) @@ -318,23 +318,23 @@ void UISettings::tbBrowse_clicked() } else if ( tb == tbCartridge ) { - auto pathProposal = leCartridge->text(); + auto pathProposal = leCartridge->text(); if (leCartridge->text().isEmpty()) { - auto path = QtYabause::DefaultPaths::Cartridge(); + auto path = QtYabause::DefaultPaths::Cartridge(); if (mCartridgeTypes[cbCartridge->currentIndex()].saveFlag) { - auto suggestedName = mCartridgeTypes[cbCartridge->currentIndex()].Name; - suggestedName = suggestedName.remove(' '); - path = path.append("/").append(suggestedName).append(".ramfile"); + auto suggestedName = mCartridgeTypes[cbCartridge->currentIndex()].Name; + suggestedName = suggestedName.remove(' '); + path = path.append("/").append(suggestedName).append(".ramfile"); } else if (mCartridgeTypes[cbCartridge->currentIndex()].enableFlag) { - path = path.append("/"); - } - pathProposal = path; - } - if (mCartridgeTypes[cbCartridge->currentIndex()].pathFlag) { + path = path.append("/"); + } + pathProposal = path; + } + if (mCartridgeTypes[cbCartridge->currentIndex()].pathFlag) { requestSTVFolder( QtYabause::translate( "Choose a STV cartridge folder" ), leCartridge, pathProposal); } else if (mCartridgeTypes[cbCartridge->currentIndex()].saveFlag) @@ -347,54 +347,56 @@ void UISettings::tbBrowse_clicked() } } else if ( tb == tbMemory ) - requestNewFile( QtYabause::translate( "Choose a memory file" ), leMemory ); - else if ( tb == tbMpegROM ) - requestFile( QtYabause::translate( "Open a mpeg rom" ), leMpegROM ); - else if ( tb == tbAddr2Line ) - requestFile( QtYabause::translate( "Choose the location of the addr2line executable" ), leAddr2Line ); + requestNewFile(QtYabause::translate("Choose a memory file"), leMemory); + else if (tb == tbMpegROM) + requestFile(QtYabause::translate("Open a mpeg rom"), leMpegROM); + else if (tb == tbAddr2Line) + requestFile(QtYabause::translate("Choose the location of the addr2line executable"), leAddr2Line); + else if (tb == tbCppFilt) + requestFile(QtYabause::translate("Choose the location of the c++filt executable"), leCppFilt); } void UISettings::on_cbInput_currentIndexChanged( int id ) { PerInterface_struct* core = QtYabause::getPERCore( cbInput->itemData( id ).toInt() ); - core->Init(); + core->Init(); - Q_ASSERT( core ); + Q_ASSERT(core); - pmPort1->setCore( core ); - pmPort2->setCore( core ); + pmPort1->setCore(core); + pmPort2->setCore(core); } void UISettings::on_cbCdRom_currentIndexChanged( int id ) { - CDInterface* core = QtYabause::getCDCore( cbCdRom->itemData( id ).toInt() ); + CDInterface *core = QtYabause::getCDCore(cbCdRom->itemData(id).toInt()); - Q_ASSERT( core ); + Q_ASSERT(core); switch (core->id) { - case CDCORE_DUMMY: - cbCdDrive->setVisible(false); - leCdRom->setVisible(false); - tbCdRom->setVisible(false); - break; - case CDCORE_ISO: - cbCdDrive->setVisible(false); - leCdRom->setVisible(true); - tbCdRom->setVisible(true); - break; - case CDCORE_ARCH: - cbCdDrive->setVisible(true); - leCdRom->setVisible(false); - tbCdRom->setVisible(false); - break; + case CDCORE_DUMMY: + cbCdDrive->setVisible(false); + leCdRom->setVisible(false); + tbCdRom->setVisible(false); + break; + case CDCORE_ISO: + cbCdDrive->setVisible(false); + leCdRom->setVisible(true); + tbCdRom->setVisible(true); + break; + case CDCORE_ARCH: + cbCdDrive->setVisible(true); + leCdRom->setVisible(false); + tbCdRom->setVisible(false); + break; default: break; - } + } } void UISettings::on_cbClockSync_stateChanged( int state ) { - dteBaseTime->setVisible( state == Qt::Checked ); + dteBaseTime->setVisible(state == Qt::Checked); } void UISettings::changeAspectRatio(int id) @@ -435,194 +437,194 @@ void UISettings::changeUpscaleMode(int id) void UISettings::on_cbCartridge_currentIndexChanged( int id ) { if (id < 0) return; - Settings const* const s = QtYabause::settings(); - auto const path = s->value(getCartridgePathSettingsKey(id)).toString(); + Settings const *const s = QtYabause::settings(); + auto const path = s->value(getCartridgePathSettingsKey(id)).toString(); if (mCartridgeTypes[id].enableFlag) { - auto const prevSelectedCartridgeType = selectedCartridgeType; + auto const prevSelectedCartridgeType = selectedCartridgeType; if(prevSelectedCartridgeType != id || leCartridge->text().isEmpty()) { if((mCartridgeTypes[id].pathFlag && QDir().exists(path)) || (QFile::exists(path))) { - leCartridge->setText(path); + leCartridge->setText(path); } else { - leCartridge->clear(); - tbCartridge->click(); - } - } + leCartridge->clear(); + tbCartridge->click(); + } + } } else { - leCartridge->clear(); - } - - leCartridge->setVisible(mCartridgeTypes[id].enableFlag); - tbCartridge->setVisible(mCartridgeTypes[id].enableFlag); - if (id == CART_ROMSTV) { - lCartridgeSTVPath->setVisible(mCartridgeTypes[id].enableFlag); - lCartridgePath->setVisible(false); - } else { - lCartridgeSTVPath->setVisible(false); - lCartridgePath->setVisible(mCartridgeTypes[id].enableFlag); - } - lCartridgeModemIP->setVisible(mCartridgeTypes[id].ipFlag); - leCartridgeModemIP->setVisible(mCartridgeTypes[id].ipFlag); - lCartridgeModemPort->setVisible(mCartridgeTypes[id].ipFlag); - leCartridgeModemPort->setVisible(mCartridgeTypes[id].ipFlag); - if (mCartridgeTypes[id].pathFlag) { - QString const & str = leCartridge->text(); - int const nbGames = STVGetRomList(str.toStdString().c_str(), 0); - cbSTVGame->clear(); - for(int i = 0; i < nbGames; i++){ - cbSTVGame->addItem(getSTVGameName(i),getSTVRomset(i)); - } - cbSTVGame->model()->sort(0); - VolatileSettings * const vs = QtYabause::volatileSettings(); + leCartridge->clear(); + } + + leCartridge->setVisible(mCartridgeTypes[id].enableFlag); + tbCartridge->setVisible(mCartridgeTypes[id].enableFlag); + if (id == CART_ROMSTV) { + lCartridgeSTVPath->setVisible(mCartridgeTypes[id].enableFlag); + lCartridgePath->setVisible(false); + } else { + lCartridgeSTVPath->setVisible(false); + lCartridgePath->setVisible(mCartridgeTypes[id].enableFlag); + } + lCartridgeModemIP->setVisible(mCartridgeTypes[id].ipFlag); + leCartridgeModemIP->setVisible(mCartridgeTypes[id].ipFlag); + lCartridgeModemPort->setVisible(mCartridgeTypes[id].ipFlag); + leCartridgeModemPort->setVisible(mCartridgeTypes[id].ipFlag); + if (mCartridgeTypes[id].pathFlag) { + QString const &str = leCartridge->text(); + int const nbGames = STVGetRomList(str.toStdString().c_str(), 0); + cbSTVGame->clear(); + for (int i = 0; i < nbGames; i++) { + cbSTVGame->addItem(getSTVGameName(i), getSTVRomset(i)); + } + cbSTVGame->model()->sort(0); + VolatileSettings *const vs = QtYabause::volatileSettings(); int curGame = cbSTVGame->findData( vs->value( "Cartridge/STVGame" ).toString()); - cbSTVGame->setCurrentIndex( curGame ); - } - cbSTVGame->setVisible(mCartridgeTypes[id].pathFlag); - lRegion->setVisible(mCartridgeTypes[id].pathFlag); - cbRegion->setVisible(mCartridgeTypes[id].pathFlag); - selectedCartridgeType = id; + cbSTVGame->setCurrentIndex(curGame); + } + cbSTVGame->setVisible(mCartridgeTypes[id].pathFlag); + lRegion->setVisible(mCartridgeTypes[id].pathFlag); + cbRegion->setVisible(mCartridgeTypes[id].pathFlag); + selectedCartridgeType = id; } void UISettings::loadCores() { - // CD Drivers - for ( int i = 0; CDCoreList[i] != NULL; i++ ) + // CD Drivers + for (int i = 0; CDCoreList[i] != NULL; i++) cbCdRom->addItem( QtYabause::translate( CDCoreList[i]->Name ), CDCoreList[i]->id ); #if YAB_PORT_OSD - // OSD Drivers - for ( int i = 0; OSDCoreList[i] != NULL; i++ ) + // OSD Drivers + for (int i = 0; OSDCoreList[i] != NULL; i++) cbOSDCore->addItem( QtYabause::translate( OSDCoreList[i]->Name ), OSDCoreList[i]->id ); #else - delete cbOSDCore; - delete lOSDCore; + delete cbOSDCore; + delete lOSDCore; #endif - // Video FilterMode - foreach(const Item& it, mVideoFilterMode) - cbFilterMode->addItem(QtYabause::translate(it.Name), it.id); + // Video FilterMode + foreach (const Item &it, mVideoFilterMode) + cbFilterMode->addItem(QtYabause::translate(it.Name), it.id); connect(cbFilterMode, SIGNAL(currentIndexChanged(int)), this, SLOT(changeFilterMode(int))); - //Upscale Mode - foreach(const Item& it, mUpscaleFilterMode) - cbUpscaleMode->addItem(QtYabause::translate(it.Name), it.id); + // Upscale Mode + foreach (const Item &it, mUpscaleFilterMode) + cbUpscaleMode->addItem(QtYabause::translate(it.Name), it.id); connect(cbUpscaleMode, SIGNAL(currentIndexChanged(int)), this, SLOT(changeUpscaleMode(int))); - // Resolution - foreach(const Item& it, mResolutionMode) + // Resolution + foreach (const Item &it, mResolutionMode) cbResolution->addItem(QtYabause::translate(it.Name), it.id); connect(cbResolution, SIGNAL(currentIndexChanged(int)), this, SLOT(changeResolution(int))); - foreach(const Item& it, mAspectRatio) + foreach (const Item &it, mAspectRatio) cbAspectRatio->addItem(QtYabause::translate(it.Name), it.id); connect(cbAspectRatio, SIGNAL(currentIndexChanged(int)), this, SLOT(changeAspectRatio(int))); - foreach(const Item& it, mWireframe) + foreach (const Item &it, mWireframe) cbWireframeFilter->addItem(QtYabause::translate(it.Name), it.id); connect(cbWireframeFilter, SIGNAL(currentIndexChanged(int)), this, SLOT(changeWireframe(int))); - foreach(const Item& it, mMeshMode) - cbMeshModeFilter->addItem(QtYabause::translate(it.Name), it.id); + foreach (const Item &it, mMeshMode) + cbMeshModeFilter->addItem(QtYabause::translate(it.Name), it.id); - foreach(const Item& it, mBandingMode) - cbBandingModeFilter->addItem(QtYabause::translate(it.Name), it.id); + foreach (const Item &it, mBandingMode) + cbBandingModeFilter->addItem(QtYabause::translate(it.Name), it.id); connect(cbMeshModeFilter, SIGNAL(currentIndexChanged(int)), this, SLOT(changeMeshMode(int))); connect(cbBandingModeFilter, SIGNAL(currentIndexChanged(int)), this, SLOT(changeBandingMode(int))); - // SND Drivers - for ( int i = 0; SNDCoreList[i] != NULL; i++ ) + // SND Drivers + for (int i = 0; SNDCoreList[i] != NULL; i++) cbSoundCore->addItem( QtYabause::translate( SNDCoreList[i]->Name ), SNDCoreList[i]->id ); - // Cartridge Types - foreach ( const Item& it, mCartridgeTypes ) - cbCartridge->addItem( QtYabause::translate( it.Name ), it.id ); + // Cartridge Types + foreach (const Item &it, mCartridgeTypes) + cbCartridge->addItem(QtYabause::translate(it.Name), it.id); - // Input Drivers - for ( int i = 0; PERCoreList[i] != NULL; i++ ) + // Input Drivers + for (int i = 0; PERCoreList[i] != NULL; i++) cbInput->addItem( QtYabause::translate( PERCoreList[i]->Name ), PERCoreList[i]->id ); - // Regions - foreach ( const Item& it, mRegions ) - cbRegion->addItem( QtYabause::translate( it.Name ), it.id ); + // Regions + foreach (const Item &it, mRegions) + cbRegion->addItem(QtYabause::translate(it.Name), it.id); - // images filter that qt can write - QStringList filters; - foreach(QByteArray ba, QImageWriter::supportedImageFormats()) - if (!filters.contains(ba, Qt::CaseInsensitive)) - filters << QString(ba).toLower(); + // images filter that qt can write + QStringList filters; + foreach (QByteArray ba, QImageWriter::supportedImageFormats()) + if (!filters.contains(ba, Qt::CaseInsensitive)) + filters << QString(ba).toLower(); for (auto entry : filters) { - cbScreenshotImageFormat->addItem(entry, entry); - } + cbScreenshotImageFormat->addItem(entry, entry); + } - // System Language - foreach ( const Item& it, mSysLanguageID ) - cbSysLanguageID ->addItem( QtYabause::translate( it.Name ), it.id ); + // System Language + foreach (const Item &it, mSysLanguageID) + cbSysLanguageID->addItem(QtYabause::translate(it.Name), it.id); - // SH2 Interpreters - for ( int i = 0; SH2CoreList[i] != NULL; i++ ) + // SH2 Interpreters + for (int i = 0; SH2CoreList[i] != NULL; i++) cbSH2Interpreter->addItem( QtYabause::translate( SH2CoreList[i]->Name ), SH2CoreList[i]->id ); - //68k cores - for (int i = 0; M68KCoreList[i] != NULL; i++) + // 68k cores + for (int i = 0; M68KCoreList[i] != NULL; i++) cb68kCore->addItem(QtYabause::translate(M68KCoreList[i]->Name), M68KCoreList[i]->id); } void UISettings::loadTranslations() { - cbTranslation->addItem(QString::fromUtf8(_("Use System Locale")), ""); - cbTranslation->addItem("English", "#"); - for (int i = 0; i < this->trans.count(); i++) + cbTranslation->addItem(QString::fromUtf8(_("Use System Locale")), ""); + cbTranslation->addItem("English", "#"); + for (int i = 0; i < this->trans.count(); i++) cbTranslation->addItem(trans[i].name.left(1).toUpper()+trans[i].name.mid(1), trans[i].file); } void UISettings::populateShortcutsView() { - QList actions = parent()->findChildren(); + QList actions = parent()->findChildren(); foreach ( QAction* action, actions ) { if (action->text().isEmpty() || !action->isShortcutVisibleInContextMenu()) - continue; + continue; - actionsList.append(action); - } + actionsList.append(action); + } - int row=0; - twShortcuts->setRowCount(actionsList.count()); + int row = 0; + twShortcuts->setRowCount(actionsList.count()); foreach ( QAction* action, actionsList ) { - QString text = QtYabause::translate(action->text()); - text = text.remove('&'); - QTableWidgetItem *tblItem = new QTableWidgetItem(text); - tblItem->setFlags(tblItem->flags() ^ Qt::ItemIsEditable); - twShortcuts->setItem(row, 0, tblItem); - tblItem = new QTableWidgetItem(action->shortcut().toString()); - twShortcuts->setItem(row, 1, tblItem); - row++; - } - QHeaderView *headerView = twShortcuts->horizontalHeader(); + QString text = QtYabause::translate(action->text()); + text = text.remove('&'); + QTableWidgetItem *tblItem = new QTableWidgetItem(text); + tblItem->setFlags(tblItem->flags() ^ Qt::ItemIsEditable); + twShortcuts->setItem(row, 0, tblItem); + tblItem = new QTableWidgetItem(action->shortcut().toString()); + twShortcuts->setItem(row, 1, tblItem); + row++; + } + QHeaderView *headerView = twShortcuts->horizontalHeader(); #if QT_VERSION >= 0x04FF00 - headerView->setSectionResizeMode(QHeaderView::Stretch); - headerView->setSectionResizeMode(1, QHeaderView::Interactive); + headerView->setSectionResizeMode(QHeaderView::Stretch); + headerView->setSectionResizeMode(1, QHeaderView::Interactive); #else - headerView->setResizeMode(QHeaderView::Stretch); - headerView->setResizeMode(1, QHeaderView::Interactive); + headerView->setResizeMode(QHeaderView::Stretch); + headerView->setResizeMode(1, QHeaderView::Interactive); #endif } @@ -630,75 +632,75 @@ void UISettings::applyShortcuts() { for (int row = 0; row < (int)actionsList.size(); ++row) { - QAction *action = actionsList[row]; - action->setShortcut(QKeySequence(twShortcuts->item(row, 1)->text())); - } + QAction *action = actionsList[row]; + action->setShortcut(QKeySequence(twShortcuts->item(row, 1)->text())); + } } void UISettings::loadSettings() { - // get settings pointer - Settings const * const s = QtYabause::settings(); + // get settings pointer + Settings const *const s = QtYabause::settings(); - // general - leBios->setText( s->value( "General/Bios" ).toString() ); - leBiosSettings->setText( s->value( "General/BiosSettings" ).toString() ); + // general + leBios->setText(s->value("General/Bios").toString()); + leBiosSettings->setText(s->value("General/BiosSettings").toString()); cbEnableBiosEmulation->setChecked( s->value( "General/EnableEmulatedBios" ).toBool() ); - cbUseCache->setChecked( s->value( "General/SH2Cache" ).toBool() ); + cbUseCache->setChecked(s->value("General/SH2Cache").toBool()); cbCdRom->setCurrentIndex( cbCdRom->findData( s->value( "General/CdRom", QtYabause::defaultCDCore().id ).toInt() ) ); - leCdRom->setText( s->value( "General/CdRomISO" ).toString() ); - QtYabause::updateTitle(); + leCdRom->setText(s->value("General/CdRomISO").toString()); + QtYabause::updateTitle(); if (s->value( "General/CdRom", QtYabause::defaultCDCore().id ).toInt() == CDCORE_ARCH) cbCdDrive->setCurrentIndex(leCdRom->text().isEmpty() ? 0 : cbCdDrive->findText(leCdRom->text())); leSaveStates->setText( s->value( "General/SaveStates", getDataDirPath() ).toString() ); - //screenshots - { - auto const defaultScreenshotsPath = QtYabause::DefaultPaths::Screenshots(); + // screenshots + { + auto const defaultScreenshotsPath = QtYabause::DefaultPaths::Screenshots(); leScreenshots->setText(s->value(QtYabause::SettingKeys::ScreenshotsDirectory, defaultScreenshotsPath).toString()); - auto screenshotsDirectory = QDir(leScreenshots->text()); - auto checkAndCreateDirectory = [&screenshotsDirectory]{ + auto screenshotsDirectory = QDir(leScreenshots->text()); + auto checkAndCreateDirectory = [&screenshotsDirectory] { if(!screenshotsDirectory.exists()) { - return screenshotsDirectory.mkdir(screenshotsDirectory.path()); - } - return true; - }; + return screenshotsDirectory.mkdir(screenshotsDirectory.path()); + } + return true; + }; if (!checkAndCreateDirectory()) { //when there is an invalid entry to the setting fall back to the default directory - leScreenshots->setText(defaultScreenshotsPath); - screenshotsDirectory = QDir(leScreenshots->text()); + leScreenshots->setText(defaultScreenshotsPath); + screenshotsDirectory = QDir(leScreenshots->text()); checkAndCreateDirectory(); //we could show an message box if this fails (no write access to our folder) but if so we have plenty of problems and i do not want to spam the user with the message that screenshots folder cant be created. it would be misleading in with the overall problem to not having write access in general } cbScreenshotImageFormat->setCurrentIndex(cbScreenshotImageFormat->findData(s->value(QtYabause::SettingKeys::ScreenshotsFormat, cbScreenshotImageFormat->itemData(0)))); } cbSysLanguageID->setCurrentIndex( cbSysLanguageID->findData( s->value( "General/SystemLanguageID", mSysLanguageID.at( 0 ).id ).toString() ) ); #ifdef HAVE_LIBMINI18N - int i; + int i; if ((i=cbTranslation->findData(s->value( "General/Translation" ).toString())) != -1) - cbTranslation->setCurrentIndex(i); - else - cbTranslation->setCurrentIndex(0); + cbTranslation->setCurrentIndex(i); + else + cbTranslation->setCurrentIndex(0); #endif - cbEnableVSync->setChecked( s->value( "General/EnableVSync", 1 ).toBool() ); - cbShowFPS->setChecked( s->value( "General/ShowFPS" ).toBool() ); - cbAutostart->setChecked( s->value( "autostart", 1 ).toBool() ); + cbEnableVSync->setChecked(s->value("General/EnableVSync", 1).toBool()); + cbShowFPS->setChecked(s->value("General/ShowFPS").toBool()); + cbAutostart->setChecked(s->value("autostart", 1).toBool()); - bool clocksync = s->value( "General/ClockSync" ).toBool(); - cbClockSync->setChecked( clocksync ); - dteBaseTime->setVisible( clocksync ); + bool clocksync = s->value("General/ClockSync").toBool(); + cbClockSync->setChecked(clocksync); + dteBaseTime->setVisible(clocksync); - QString dt = s->value( "General/FixedBaseTime" ).toString(); - if (!dt.isEmpty()) - dteBaseTime->setDateTime( QDateTime::fromString( dt,Qt::ISODate) ); - else - dteBaseTime->setDateTime( QDateTime(QDate(1998, 1, 1), QTime(12, 0, 0)) ); + QString dt = s->value("General/FixedBaseTime").toString(); + if (!dt.isEmpty()) + dteBaseTime->setDateTime(QDateTime::fromString(dt, Qt::ISODate)); + else + dteBaseTime->setDateTime(QDateTime(QDate(1998, 1, 1), QTime(12, 0, 0))); #if YAB_PORT_OSD cbOSDCore->setCurrentIndex( cbOSDCore->findData( s->value( "Video/OSDCore", QtYabause::defaultOSDCore().id ).toInt() ) ); #endif - cbFullscreen->setChecked( s->value( "Video/Fullscreen", false ).toBool() ); + cbFullscreen->setChecked(s->value("Video/Fullscreen", false).toBool()); cbFilterMode->setCurrentIndex(cbFilterMode->findData(s->value("Video/filter_type", mVideoFilterMode.at(0).id).toInt())); cbUpscaleMode->setCurrentIndex(cbUpscaleMode->findData(s->value("Video/upscale_type", mUpscaleFilterMode.at(0).id).toInt())); @@ -708,175 +710,177 @@ void UISettings::loadSettings() cbMeshModeFilter->setCurrentIndex(cbMeshModeFilter->findData(s->value("Video/MeshMode", mMeshMode.at(0).id).toInt())); cbBandingModeFilter->setCurrentIndex(cbBandingModeFilter->findData(s->value("Video/BandingMode", mBandingMode.at(0).id).toInt())); - // sound + // sound cbSoundCore->setCurrentIndex( cbSoundCore->findData( s->value( "Sound/SoundCore", QtYabause::defaultSNDCore().id ).toInt() ) ); - // cartridge/memory - tbCartridge->setEnabled(false); + // cartridge/memory + tbCartridge->setEnabled(false); cbCartridge->setCurrentIndex( cbCartridge->findData( s->value( "Cartridge/Type", mCartridgeTypes.at( 7 ).id ).toInt() ) ); - tbCartridge->setEnabled(true); - leCartridge->setText( s->value(getCartridgePathSettingsKey()).toString() ); + tbCartridge->setEnabled(true); + leCartridge->setText(s->value(getCartridgePathSettingsKey()).toString()); leCartridgeModemIP->setText( s->value( "Cartridge/ModemIP", QString("127.0.0.1") ).toString() ); leCartridgeModemPort->setText( s->value( "Cartridge/ModemPort", QString("1337") ).toString() ); cbSTVGame->setCurrentIndex( cbSTVGame->findData( s->value( "Cartridge/STVGame" ).toString() ) ); leMemory->setText( s->value( "Memory/Path", getDataDirPath().append( "/bkram.bin" ) ).toString() ); - leMpegROM->setText( s->value( "MpegROM/Path" ).toString() ); + leMpegROM->setText(s->value("MpegROM/Path").toString()); checkBox_extended_internal_backup->setChecked(s->value("Memory/ExtendMemory").toBool()); //the path needs to go into the volatile settings since we keep only one cartridge path there to keep things simple. - // input + // input cbInput->setCurrentIndex( cbInput->findData( s->value( "Input/PerCore", QtYabause::defaultPERCore().id ).toInt() ) ); sGunMouseSensitivity->setValue(s->value( "Input/GunMouseSensitivity", 100).toInt() ); - // advanced + // advanced cbRegion->setCurrentIndex( cbRegion->findData( s->value( "STV/Region", mRegions.at( 0 ).id ).toString() ) ); cbSH2Interpreter->setCurrentIndex( cbSH2Interpreter->findData( s->value( "Advanced/SH2Interpreter", QtYabause::defaultSH2Core().id ).toInt() ) ); cb68kCore->setCurrentIndex(cb68kCore->findData(s->value("Advanced/68kCore", QtYabause::default68kCore().id).toInt())); - // debug - leAddr2Line->setText( s->value( "Debug/Addr2Line" ).toString() ); + // debug + leAddr2Line->setText(s->value("Debug/Addr2Line").toString()); + leCppFilt->setText(s->value("Debug/CppFilt").toString()); - //shortcuts - { - auto const actions = parent()->findChildren(); + // shortcuts + { + auto const actions = parent()->findChildren(); for(auto const action : actions) { if (action->text().isEmpty() || !action->isShortcutVisibleInContextMenu()) - continue; + continue; - auto const var = s->value(action->text()); + auto const var = s->value(action->text()); if(!var.isValid() || var.isNull() ) { - continue; - } - action->setShortcut(QKeySequence(var.toString())); - } - } + continue; + } + action->setShortcut(QKeySequence(var.toString())); + } + } } -void UISettings::on_cbAutostart_toggled(bool enable){ - VolatileSettings * const vs = QtYabause::volatileSettings(); - vs->setValue( "autostart", enable); +void UISettings::on_cbAutostart_toggled(bool enable) { + VolatileSettings *const vs = QtYabause::volatileSettings(); + vs->setValue("autostart", enable); } void UISettings::saveSettings() { - // get settings pointer - Settings * const s = QtYabause::settings(); - s->setValue( "General/Version", Settings::programVersion() ); - - // general - s->setValue( "General/Bios", leBios->text() ); - s->setValue( "General/BiosSettings", leBiosSettings->text() ); - s->setValue( "General/EnableEmulatedBios", cbEnableBiosEmulation->isChecked() ); - s->setValue( "General/SH2Cache", cbUseCache->isChecked() ); + // get settings pointer + Settings *const s = QtYabause::settings(); + s->setValue("General/Version", Settings::programVersion()); + + // general + s->setValue("General/Bios", leBios->text()); + s->setValue("General/BiosSettings", leBiosSettings->text()); + s->setValue("General/EnableEmulatedBios", cbEnableBiosEmulation->isChecked()); + s->setValue("General/SH2Cache", cbUseCache->isChecked()); s->setValue( "General/CdRom", cbCdRom->itemData( cbCdRom->currentIndex() ).toInt() ); CDInterface* core = QtYabause::getCDCore( cbCdRom->itemData( cbCdRom->currentIndex() ).toInt() ); - if ( core->id == CDCORE_ARCH ) - s->setValue( "General/CdRomISO", cbCdDrive->currentText() ); - else - s->setValue( "General/CdRomISO", leCdRom->text() ); - s->setValue( "General/SaveStates", leSaveStates->text() ); + if (core->id == CDCORE_ARCH) + s->setValue("General/CdRomISO", cbCdDrive->currentText()); + else + s->setValue("General/CdRomISO", leCdRom->text()); + s->setValue("General/SaveStates", leSaveStates->text()); s->setValue(QtYabause::SettingKeys::ScreenshotsDirectory, leScreenshots->text()); s->setValue(QtYabause::SettingKeys::ScreenshotsFormat, cbScreenshotImageFormat->itemData(cbScreenshotImageFormat->currentIndex()).toString()); s->setValue( "General/SystemLanguageID", cbSysLanguageID->itemData( cbSysLanguageID->currentIndex() ).toString() ); #ifdef HAVE_LIBMINI18N s->setValue( "General/Translation", cbTranslation->itemData(cbTranslation->currentIndex()).toString() ); #endif - s->setValue( "General/EnableVSync", cbEnableVSync->isChecked() ); - s->setValue( "General/ShowFPS", cbShowFPS->isChecked() ); - s->setValue( "autostart", cbAutostart->isChecked() ); + s->setValue("General/EnableVSync", cbEnableVSync->isChecked()); + s->setValue("General/ShowFPS", cbShowFPS->isChecked()); + s->setValue("autostart", cbAutostart->isChecked()); - // video + // video #if YAB_PORT_OSD s->setValue( "Video/OSDCore", cbOSDCore->itemData( cbOSDCore->currentIndex() ).toInt() ); #endif - // Move Outdated window/fullscreen keys - s->remove("Video/Width"); - s->remove("Video/Height"); + // Move Outdated window/fullscreen keys + s->remove("Video/Width"); + s->remove("Video/Height"); - // Save new version of keys + // Save new version of keys s->setValue("Video/AspectRatio", cbAspectRatio->itemData(cbAspectRatio->currentIndex()).toInt()); s->setValue("Video/Wireframe", cbWireframeFilter->itemData(cbWireframeFilter->currentIndex()).toInt()); s->setValue("Video/MeshMode", cbMeshModeFilter->itemData(cbMeshModeFilter->currentIndex()).toInt()); s->setValue("Video/BandingMode", cbBandingModeFilter->itemData(cbBandingModeFilter->currentIndex()).toInt()); - s->setValue( "Video/Fullscreen", cbFullscreen->isChecked() ); + s->setValue("Video/Fullscreen", cbFullscreen->isChecked()); s->setValue( "Video/filter_type", cbFilterMode->itemData(cbFilterMode->currentIndex()).toInt()); s->setValue( "Video/upscale_type", cbUpscaleMode->itemData(cbUpscaleMode->currentIndex()).toInt()); s->setValue("Video/resolution_mode", cbResolution->itemData(cbResolution->currentIndex()).toInt()); - s->setValue( "General/ClockSync", cbClockSync->isChecked() ); + s->setValue("General/ClockSync", cbClockSync->isChecked()); s->setValue( "General/FixedBaseTime", dteBaseTime->dateTime().toString(Qt::ISODate)); - // sound + // sound s->setValue( "Sound/SoundCore", cbSoundCore->itemData( cbSoundCore->currentIndex() ).toInt() ); - // cartridge/memory - int currentCart = s->value( "Cartridge/Type").toInt(); - int newCart = cbCartridge->itemData( cbCartridge->currentIndex() ).toInt(); - if ( currentCart != newCart) { - if (currentCart != CART_ROMSTV) { - s->setValue( "Cartridge/LastCart", currentCart); - } - if ((currentCart == CART_ROMSTV) || (newCart == CART_ROMSTV)) { - yabsys.isReloadingImage = 2; - } - } + // cartridge/memory + int currentCart = s->value("Cartridge/Type").toInt(); + int newCart = cbCartridge->itemData(cbCartridge->currentIndex()).toInt(); + if (currentCart != newCart) { + if (currentCart != CART_ROMSTV) { + s->setValue("Cartridge/LastCart", currentCart); + } + if ((currentCart == CART_ROMSTV) || (newCart == CART_ROMSTV)) { + yabsys.isReloadingImage = 2; + } + } s->setValue( "Cartridge/Type", cbCartridge->itemData( cbCartridge->currentIndex() ).toInt() ); - s->setValue(getCartridgePathSettingsKey(), leCartridge->text() ); - s->setValue( "Cartridge/ModemIP", leCartridgeModemIP->text() ); - s->setValue( "Cartridge/ModemPort", leCartridgeModemPort->text() ); + s->setValue(getCartridgePathSettingsKey(), leCartridge->text()); + s->setValue("Cartridge/ModemIP", leCartridgeModemIP->text()); + s->setValue("Cartridge/ModemPort", leCartridgeModemPort->text()); if (s->value( "Cartridge/STVGame").toString() != cbSTVGame->currentData().toString()) { - yabsys.isReloadingImage = 2; - } - s->setValue( "Cartridge/STVGame", cbSTVGame->currentData().toString() ); - s->setValue( "Cartridge/STVGameName", cbSTVGame->currentText() ); - s->setValue( "Memory/Path", leMemory->text() ); - s->setValue( "MpegROM/Path", leMpegROM->text() ); + yabsys.isReloadingImage = 2; + } + s->setValue("Cartridge/STVGame", cbSTVGame->currentData().toString()); + s->setValue("Cartridge/STVGameName", cbSTVGame->currentText()); + s->setValue("Memory/Path", leMemory->text()); + s->setValue("MpegROM/Path", leMpegROM->text()); s->setValue("Memory/ExtendMemory", checkBox_extended_internal_backup->isChecked()); - s->setValue("Cartridge/Path", leCartridge->text()); + s->setValue("Cartridge/Path", leCartridge->text()); - // input + // input s->setValue( "Input/PerCore", cbInput->itemData( cbInput->currentIndex() ).toInt() ); - s->setValue( "Input/GunMouseSensitivity", sGunMouseSensitivity->value() ); + s->setValue("Input/GunMouseSensitivity", sGunMouseSensitivity->value()); - // advanced + // advanced s->setValue( "STV/Region", cbRegion->itemData( cbRegion->currentIndex() ).toString() ); s->setValue( "Advanced/SH2Interpreter", cbSH2Interpreter->itemData( cbSH2Interpreter->currentIndex() ).toInt() ); s->setValue("Advanced/68kCore", cb68kCore->itemData(cb68kCore->currentIndex()).toInt()); - // shortcuts - applyShortcuts(); - s->beginGroup("Shortcuts"); - auto const actions = parent()->findChildren(); + // shortcuts + applyShortcuts(); + s->beginGroup("Shortcuts"); + auto const actions = parent()->findChildren(); for ( auto const * const action : actions ) { if (action->text().isEmpty() || !action->isShortcutVisibleInContextMenu()) - continue; + continue; - s->setValue(action->text(), action->shortcut().toString()); - } - s->endGroup(); + s->setValue(action->text(), action->shortcut().toString()); + } + s->endGroup(); - // debug - s->setValue( "Debug/Addr2Line", leAddr2Line->text() ); - QtYabause::updateTitle(); + // debug + s->setValue("Debug/Addr2Line", leAddr2Line->text()); + s->setValue("Debug/CppFilt", leCppFilt->text()); + QtYabause::updateTitle(); } void UISettings::accept() { - saveSettings(); - QDialog::accept(); + saveSettings(); + QDialog::accept(); } QString UISettings::getCartridgePathSettingsKey(int cartridgeType) const { - auto name = mCartridgeTypes[selectedCartridgeType].Name; + auto name = mCartridgeTypes[selectedCartridgeType].Name; if (cartridgeType != -1) { - name = mCartridgeTypes[cartridgeType].Name; - } - name = name.remove(' '); - return "Cartridge/Path/" + name; + name = mCartridgeTypes[cartridgeType].Name; + } + name = name.remove(' '); + return "Cartridge/Path/" + name; } diff --git a/yabause/src/port/qt/ui/UISettings.ui b/yabause/src/port/qt/ui/UISettings.ui index 26d8185a82..350c602e54 100644 --- a/yabause/src/port/qt/ui/UISettings.ui +++ b/yabause/src/port/qt/ui/UISettings.ui @@ -924,6 +924,20 @@ Debug + + + + + + + + + ... + + + + + @@ -940,6 +954,32 @@ + + + + + 75 + true + + + + c++filt Executable + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -954,19 +994,6 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - diff --git a/yabause/src/port/qt/ui/UIYabause.cpp b/yabause/src/port/qt/ui/UIYabause.cpp index bce3301b2e..fdc981c785 100644 --- a/yabause/src/port/qt/ui/UIYabause.cpp +++ b/yabause/src/port/qt/ui/UIYabause.cpp @@ -35,6 +35,7 @@ #include "UIDebugSCSPDSP.h" #include "UIMemoryEditor.h" #include "UIMemoryTransfer.h" +#include "UIProfiler.h" #include "UIAbout.h" #include "../YabauseGL.h" #include "../QtYabause.h" @@ -1169,6 +1170,25 @@ void UIYabause::on_aViewDebugMemoryEditor_triggered() UIMemoryEditor( UIDebugCPU::PROC_MSH2, mYabauseThread, this ).exec(); } +void UIYabause::on_aProfilerToggle_triggered() { + YabauseLocker locker( mYabauseThread ); + if (mYabauseThread->init() == 0 && MSH2 && SSH2) { + MSH2->profilerInfo.profilerEnabled ^= 1; + SSH2->profilerInfo.profilerEnabled ^= 1; + YuiMsg( "Master profiler %s\n", + MSH2->profilerInfo.profilerEnabled ? "enabled" : "disabled" ); + YuiMsg( "Slave profiler %s\n", + SSH2->profilerInfo.profilerEnabled ? "enabled" : "disabled" ); + } else { + QMessageBox::critical(this, "Error", "Please start emulation first"); + } +} + +void UIYabause::on_aProfilerShowResults_triggered() { + YabauseLocker locker( mYabauseThread ); + UIProfiler( mYabauseThread, this ).exec(); +} + void UIYabause::on_aHelpCompatibilityList_triggered() { QDesktopServices::openUrl( QUrl( aHelpCompatibilityList->statusTip() ) ); } diff --git a/yabause/src/port/qt/ui/UIYabause.h b/yabause/src/port/qt/ui/UIYabause.h index b73b01b82d..e258738301 100644 --- a/yabause/src/port/qt/ui/UIYabause.h +++ b/yabause/src/port/qt/ui/UIYabause.h @@ -217,6 +217,9 @@ protected slots: void on_aViewDebugSCSPChan_triggered(); void on_aViewDebugSCUDSP_triggered(); void on_aViewDebugMemoryEditor_triggered(); + // profiler menu + void on_aProfilerToggle_triggered(); + void on_aProfilerShowResults_triggered(); // help menu void on_aHelpReport_triggered(); void on_aHelpCompatibilityList_triggered(); diff --git a/yabause/src/port/qt/ui/UIYabause.ui b/yabause/src/port/qt/ui/UIYabause.ui index 0feb7d39b1..012f653e32 100644 --- a/yabause/src/port/qt/ui/UIYabause.ui +++ b/yabause/src/port/qt/ui/UIYabause.ui @@ -242,10 +242,22 @@ &Tools + + + Profiler + + + + :/actions/icons/actions/frame_skipping.png:/actions/icons/actions/frame_skipping.png + + + + + @@ -974,6 +986,16 @@ SCSP Channels + + + Toggle Profiler + + + + + Results + + diff --git a/yabause/src/sys/memory/src/cs0.c b/yabause/src/sys/memory/src/cs0.c index 91a29161f8..e62777acd3 100644 --- a/yabause/src/sys/memory/src/cs0.c +++ b/yabause/src/sys/memory/src/cs0.c @@ -44,6 +44,19 @@ static u8 decryptOn = 0; static uint8_t log_buffer[DEV_LOG_SIZE]; static uint8_t *log_pos = log_buffer; +#define DEV_CMD_PUSH 0x1010 +#define DEV_CMD_POP 0x1014 +#define DEV_CMD_ADDRESS 0x1020 + +enum DevCartridgeCommands { + DEV_CMD_ENABLE_PROFILER = 0x00000010, + DEV_CMD_DISABLE_PROFILER = 0x00000011, +}; + +#define COMMAND_STACK_SIZE 8 +static uint32_t command_stack[COMMAND_STACK_SIZE]; +static uint32_t command_stack_pointer = 0; + ////////////////////////////////////////////////////////////////////////////// // Dummy/No Cart Functions ////////////////////////////////////////////////////////////////////////////// @@ -884,6 +897,50 @@ static void FASTCALL DevCs1WriteWord(SH2_struct *context, UNUSED u8* memory, u32 static void FASTCALL DevCs1WriteLong(SH2_struct *context, UNUSED u8* memory, u32 addr, u32 val) { + addr &= 0x1FFFFFF; + + switch (addr) { + case DEV_CMD_PUSH: + if (command_stack_pointer < (COMMAND_STACK_SIZE - 1)) { + command_stack[command_stack_pointer++] = val; + } else { + YuiMsg("Failed to push command stack, command stack limit reached\n"); + } + return; + + case DEV_CMD_POP: + if (command_stack_pointer > 0) { + --command_stack_pointer; + } else { + YuiMsg("Failed to pop command stack, command stack limit reached\n"); + } + return; + + case DEV_CMD_ADDRESS: + // Game -> Kronos communication (commands) + switch (val) { + case DEV_CMD_ENABLE_PROFILER: + context->profilerInfo.profilerEnabled = 1; + if (command_stack_pointer >= 2) { + context->profilerInfo.startMonitorAddress = command_stack[0]; + context->profilerInfo.endMonitorAddress = command_stack[1]; + } + YuiMsg("Enabled profiler mode (from 0x%X to 0x%X)\n", + context->profilerInfo.startMonitorAddress, context->profilerInfo.endMonitorAddress); + break; + case DEV_CMD_DISABLE_PROFILER: + context->profilerInfo.profilerEnabled = 0; + YuiMsg("Disabled profiler mode\n"); + break; + + default: + break; + } + return; + + default: + break; + } } ////////////////////////////////////////////////////////////////////////////// @@ -1507,6 +1564,10 @@ int CartInit(const char * filename, int type) CartridgeArea->cartid = 0x5C; + // TODO: Make this more custom + log_pos = log_buffer; + command_stack_pointer = 0; + // Setup Functions CartridgeArea->Cs0ReadByte = &DRAM32MBITCs0ReadByte; CartridgeArea->Cs0ReadWord = &DRAM32MBITCs0ReadWord; @@ -1615,6 +1676,7 @@ int CartInit(const char * filename, int type) CartridgeArea->Cs1ReadLong = &DevCs1ReadLong; CartridgeArea->Cs1WriteByte = &DevCs1WriteByte; CartridgeArea->Cs1WriteWord = &DevCs1WriteWord; + CartridgeArea->Cs1WriteLong = &DevCs1WriteLong; if ((CartridgeArea->dram = T1MemoryInit(0x400000)) == NULL) return -1; @@ -1629,8 +1691,6 @@ int CartInit(const char * filename, int type) CartridgeArea->Cs0WriteWord = &DRAM32MBITCs0WriteWord; CartridgeArea->Cs0WriteLong = &DRAM32MBITCs0WriteLong; break; - CartridgeArea->Cs1WriteLong = &DevCs1WriteLong; - break; } default: // No Cart diff --git a/yabause/src/sys/sh2/include/sh2core.h b/yabause/src/sys/sh2/include/sh2core.h index 527c1dd638..999fd030f1 100644 --- a/yabause/src/sys/sh2/include/sh2core.h +++ b/yabause/src/sys/sh2/include/sh2core.h @@ -426,7 +426,21 @@ typedef struct } backtrace_struct; //END debug +typedef struct +{ + u32 address; + u64 startTime; +} SH2_ProfilerStackInfo; + +typedef struct +{ + double time; + u32 count; +} SH2_ProfilerInfo; +#define PROFILE_STACK_SIZE 16384 +#define PROFILE_NUM_INFOS (1024 * 1024) + void SH2IntcSetIrl(SH2_struct *sh, u8 irl, u8 d); void SH2IntcSetNmi(SH2_struct *sh); void SH2EvaluateInterrupt(SH2_struct *sh); @@ -521,6 +535,15 @@ typedef struct SH2_struct_s u32 isDelayed; u32 divcycles; //ENd debug + + struct { + SH2_ProfilerInfo profile[PROFILE_NUM_INFOS]; + SH2_ProfilerStackInfo stack[PROFILE_STACK_SIZE]; + s32 stackPos; + u32 startMonitorAddress; + u32 endMonitorAddress; + u8 profilerEnabled; + } profilerInfo; } SH2_struct; typedef struct diff --git a/yabause/src/sys/sh2/src/sh2core.c b/yabause/src/sys/sh2/src/sh2core.c index f48950357a..5db00c683f 100644 --- a/yabause/src/sys/sh2/src/sh2core.c +++ b/yabause/src/sys/sh2/src/sh2core.c @@ -41,7 +41,6 @@ static void WDTExec(SH2_struct *context); u8 SCIReceiveByte(void); void SCITransmitByte(u8); - void enableCache(SH2_struct *ctx); void disableCache(SH2_struct *ctx); void InvalidateCache(SH2_struct *ctx); @@ -54,6 +53,10 @@ static void (*SH2StandardExec)(SH2_struct *context, u32 cycles); void DMATransferCycles(SH2_struct *context, Dmac * dmac, int cycles); int DMAProc(SH2_struct *context, int cycles ); +// Profiler forward declaration +int SH2ProfilerInit(SH2_struct* context); +void SH2ProfilerDeInit(SH2_struct *context); + ////////////////////////////////////////////////////////////////////////////// void SH2IntcSetIrl(SH2_struct *sh, u8 irl, u8 d) @@ -238,6 +241,9 @@ int SH2Init(int coreid) if (SH2TrackInfLoopInit(MSH2) != 0) return -1; + if (SH2ProfilerInit(MSH2) != 0) + return -1; + MSH2->onchip.BCR1 = 0x0000; MSH2->isslave = 0; MSH2->isAccessingCPUBUS = 0; @@ -267,6 +273,9 @@ int SH2Init(int coreid) if (SH2TrackInfLoopInit(SSH2) != 0) return -1; + if (SH2ProfilerInit(SSH2) != 0) + return -1; + SSH2->interruptReturnAddress = 0; SSH2->onchip.BCR1 = 0x8000; SSH2->isslave = 1; @@ -330,11 +339,13 @@ void SH2DeInit() { if (SH2Core) SH2Core->DeInit(); + SH2Core = NULL; if (MSH2) { SH2TrackInfLoopDeInit(MSH2); + SH2ProfilerDeInit(MSH2); free(MSH2); } MSH2 = NULL; @@ -342,8 +353,10 @@ void SH2DeInit() if (SSH2) { SH2TrackInfLoopDeInit(SSH2); + SH2ProfilerDeInit(SSH2); free(SSH2); } + SSH2 = NULL; } @@ -628,6 +641,45 @@ void SH2NMI(SH2_struct *context) ////////////////////////////////////////////////////////////////////////////// +int SH2ProfilerInit(SH2_struct* context) +{ + if (context) + { + context->profilerInfo.stackPos = -1; + context->profilerInfo.startMonitorAddress = 0x06002000; + context->profilerInfo.endMonitorAddress = 0x06104000; + context->profilerInfo.profilerEnabled = 0; + memset(context->profilerInfo.profile, 0, + PROFILE_NUM_INFOS * sizeof(SH2_ProfilerInfo)); + + memset(context->profilerInfo.stack, 0, + PROFILE_STACK_SIZE * sizeof(SH2_ProfilerStackInfo)); + + return 0; + } + else + { + return -1; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void SH2ProfilerDeInit(SH2_struct *context) +{ + if (!context) + return; + + for (u32 i = 0; i < PROFILE_NUM_INFOS; ++i) + { + SH2_ProfilerInfo* info = &context->profilerInfo.profile[i]; + info->time = 0; + info->count = 0; + } +} + +////////////////////////////////////////////////////////////////////////////// + void SH2GetRegisters(SH2_struct *context, sh2regs_struct * r) { if (r != NULL) {