all repos — mgba @ 81fd7e3c1a811e0dea0c8995143c4c7f899fd672

mGBA Game Boy Advance Emulator

Qt: Memory range dumping (closes #1298)
Vicki Pfau vi@endrift.com
Sun, 22 Sep 2019 20:21:04 -0700
commit

81fd7e3c1a811e0dea0c8995143c4c7f899fd672

parent

c0768784954ab3ac8fd9152d8c205303efc1c73d

M CHANGESCHANGES

@@ -21,6 +21,7 @@ - Support for unlicensed Wisdom Tree Game Boy mapper

- Qt: Add export button for tile view (closes mgba.io/i/1507) - Qt: Add recent game list clearing (closes mgba.io/i/1380) - GB: Yanking gamepak now supported + - Qt: Memory range dumping (closes mgba.io/i/1298) Emulation fixes: - GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208) - GBA: Reset now reloads multiboot ROMs
M src/gb/core.csrc/gb/core.c

@@ -40,7 +40,7 @@ };

static const struct mCoreMemoryBlock _GBMemoryBlocks[] = { { -1, "mem", "All", "All", 0, 0x10000, 0x10000, mCORE_MEMORY_VIRTUAL }, - { GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED, 511 }, + { GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 }, { GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { GB_REGION_EXTERNAL_RAM, "sram", "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_BASE_EXTERNAL_RAM + GB_SIZE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM * 4, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 3 }, { GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2 , GB_SIZE_WORKING_RAM_BANK0 * 2, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },

@@ -51,10 +51,10 @@ };

static const struct mCoreMemoryBlock _GBCMemoryBlocks[] = { { -1, "mem", "All", "All", 0, 0x10000, 0x10000, mCORE_MEMORY_VIRTUAL }, - { GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED, 511 }, + { GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 }, { GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM * 2, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 }, { GB_REGION_EXTERNAL_RAM, "sram", "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_BASE_EXTERNAL_RAM + GB_SIZE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM * 4, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 3 }, - { GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2, GB_SIZE_WORKING_RAM_BANK0 * 8, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 7 }, + { GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2, GB_SIZE_WORKING_RAM_BANK0 * 8, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 7, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 }, { GB_BASE_OAM, "oam", "OAM", "OBJ Attribute Memory", GB_BASE_OAM, GB_BASE_OAM + GB_SIZE_OAM, GB_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { GB_BASE_IO, "io", "MMIO", "Memory-Mapped I/O", GB_BASE_IO, GB_BASE_IO + GB_SIZE_IO, GB_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { GB_BASE_HRAM, "hram", "HRAM", "High RAM", GB_BASE_HRAM, GB_BASE_HRAM + GB_SIZE_HRAM, GB_SIZE_HRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
M src/platform/qt/CMakeLists.txtsrc/platform/qt/CMakeLists.txt

@@ -85,6 +85,7 @@ LogController.cpp

LogConfigModel.cpp LogView.cpp MapView.cpp + MemoryDump.cpp MemoryModel.cpp MemorySearch.cpp MemoryView.cpp

@@ -126,6 +127,7 @@ IOViewer.ui

LoadSaveState.ui LogView.ui MapView.ui + MemoryDump.ui MemorySearch.ui MemoryView.ui ObjView.ui
A src/platform/qt/MemoryDump.cpp

@@ -0,0 +1,120 @@

+/* Copyright (c) 2013-2019 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "MemoryDump.h" + +#include "CoreController.h" +#include "GBAApp.h" +#include "LogController.h" + +using namespace QGBA; + +MemoryDump::MemoryDump(std::shared_ptr<CoreController> controller, QWidget* parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) + , m_controller(controller) +{ + m_ui.setupUi(this); + + connect(this, &QDialog::accepted, this, &MemoryDump::save); +} + +void MemoryDump::save() { + QString filename = GBAApp::app()->getSaveFileName(this, tr("Save memory region")); + if (filename.isNull()) { + return; + } + QFile outfile(filename); + if (!outfile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + LOG(QT, WARN) << tr("Failed to open output file: %1").arg(filename); + return; + } + QByteArray out(serialize()); + outfile.write(out); +} + +void MemoryDump::setAddress(uint32_t start) { + m_ui.address->setValue(start); +} + +void MemoryDump::setSegment(int seg) { + m_ui.segment->setValue(seg); +} + +void MemoryDump::setByteCount(uint32_t count) { + m_ui.bytes->setValue(count); +} + +QByteArray MemoryDump::serialize() { + CoreController::Interrupter interrupter(m_controller); + mCore* core = m_controller->thread()->core; + const mCoreMemoryBlock* blocks; + size_t nBlocks = core->listMemoryBlocks(core, &blocks); + + int size = m_ui.bytes->value(); + uint32_t start = m_ui.address->value(); + int segment = m_ui.segment->value(); + bool spanSegments = m_ui.spanSegments->isChecked(); + + QByteArray mem; + while (size) { + const mCoreMemoryBlock* bestMatch = NULL; + const char* block = NULL; + size_t blockSize = 0; + for (size_t i = 0; i < nBlocks; ++i) { + if (blocks[i].end <= start) { + continue; + } + if (blocks[i].start > start) { + continue; + } + block = static_cast<const char*>(core->getMemoryBlock(core, blocks[i].id, &blockSize)); + if (block) { + bestMatch = &blocks[i]; + break; + } else if (!bestMatch) { + bestMatch = &blocks[i]; + blockSize = 0; + } + } + if (!spanSegments) { + blockSize = bestMatch->end - bestMatch->start; + } else if (!blockSize) { + blockSize = bestMatch->size; + } + size_t segmentSize = bestMatch->end - bestMatch->start; + if (bestMatch->segmentStart) { + segmentSize = bestMatch->segmentStart - bestMatch->start; + } + if (segment > 0) { + start += segment * segmentSize; + } + int maxFromRegion = blockSize - (start - bestMatch->start); + if (maxFromRegion <= 0) { + continue; + } + int fromRegion = std::min(size, maxFromRegion); + if (block && (segment >= 0 || segmentSize == blockSize)) { + block = &block[start - bestMatch->start]; + mem.append(QByteArray::fromRawData(block, fromRegion)); + size -= fromRegion; + start += fromRegion; + if (spanSegments) { + break; + } + } else { + for (int i = 0; i < 16; ++i) { + char datum = core->rawRead8(core, start, segment); + mem.append(datum); + ++start; + --size; + if (!size) { + break; + } + } + } + } + + return mem; +}
A src/platform/qt/MemoryDump.h

@@ -0,0 +1,38 @@

+/* Copyright (c) 2013-2019 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#pragma once + +#include "ui_MemoryDump.h" + +#include <memory> + +namespace QGBA { + +class CoreController; + +class MemoryDump : public QDialog { +Q_OBJECT + +public: + MemoryDump(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr); + +public slots: + void setSegment(int); + void setAddress(uint32_t address); + void setByteCount(uint32_t); + +private slots: + void save(); + +private: + QByteArray serialize(); + + Ui::MemoryDump m_ui; + + std::shared_ptr<CoreController> m_controller; +}; + +}
A src/platform/qt/MemoryDump.ui

@@ -0,0 +1,159 @@

+<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MemoryDump</class> + <widget class="QDialog" name="MemoryDump"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>448</width> + <height>208</height> + </rect> + </property> + <property name="windowTitle"> + <string>Save Memory Range</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Start Address:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QSpinBox" name="segment"> + <property name="minimum"> + <number>-1</number> + </property> + <property name="maximum"> + <number>511</number> + </property> + <property name="displayIntegerBase"> + <number>16</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>:</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="address"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="accelerated"> + <bool>true</bool> + </property> + <property name="prefix"> + <string>0x</string> + </property> + <property name="maximum"> + <number>268435455</number> + </property> + <property name="singleStep"> + <number>16</number> + </property> + <property name="displayIntegerBase"> + <number>16</number> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Byte Count:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QSpinBox" name="bytes"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="prefix"> + <string>0x</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>268435456</number> + </property> + <property name="value"> + <number>256</number> + </property> + <property name="displayIntegerBase"> + <number>16</number> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QCheckBox" name="spanSegments"> + <property name="text"> + <string>Dump across banks</string> + </property> + </widget> + </item> + <item row="3" column="0" colspan="2"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>MemoryDump</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>MemoryDump</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui>
M src/platform/qt/MemoryView.cppsrc/platform/qt/MemoryView.cpp

@@ -7,6 +7,7 @@

#include "MemoryView.h" #include "CoreController.h" +#include "MemoryDump.h" #include <mgba/core/core.h>

@@ -44,6 +45,7 @@ connect(m_ui.width32, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(4); });

connect(m_ui.setAddress, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, static_cast<void (MemoryView::*)(uint32_t)>(&MemoryView::jumpToAddress)); connect(m_ui.hexfield, &MemoryModel::selectionChanged, this, &MemoryView::updateSelection); + connect(m_ui.saveRange, &QAbstractButton::clicked, this, &MemoryView::saveRange); connect(controller.get(), &CoreController::stopping, this, &QWidget::close);

@@ -69,6 +71,7 @@

m_region = qMakePair(info.start, info.end); m_ui.segments->setValue(-1); m_ui.segments->setVisible(info.maxSegment > 0); + m_ui.segmentColon->setVisible(info.maxSegment > 0); m_ui.segments->setMaximum(info.maxSegment); m_ui.hexfield->setRegion(info.start, info.end - info.start, info.shortName); }

@@ -142,3 +145,12 @@ m_ui.uintVal->setText(QString::number(value.u32));

break; } } + +void MemoryView::saveRange() { + MemoryDump* memdump = new MemoryDump(m_controller); + memdump->setAttribute(Qt::WA_DeleteOnClose); + memdump->setAddress(m_selection.first); + memdump->setSegment(m_ui.segments->value()); + memdump->setByteCount(m_selection.second - m_selection.first); + memdump->show(); +}
M src/platform/qt/MemoryView.hsrc/platform/qt/MemoryView.h

@@ -28,6 +28,7 @@ void setIndex(int);

void setSegment(int); void updateSelection(uint32_t start, uint32_t end); void updateStatus(); + void saveRange(); private: Ui::MemoryView m_ui;
M src/platform/qt/MemoryView.uisrc/platform/qt/MemoryView.ui

@@ -6,8 +6,8 @@ <property name="geometry">

<rect> <x>0</x> <y>0</y> - <width>822</width> - <height>886</height> + <width>874</width> + <height>900</height> </rect> </property> <property name="windowTitle">

@@ -49,6 +49,13 @@ <number>0</number>

</property> <property name="displayIntegerBase"> <number>16</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="segmentColon"> + <property name="text"> + <string>:</string> </property> </widget> </item>

@@ -268,6 +275,13 @@ <item>

<widget class="QPushButton" name="save"> <property name="text"> <string>Save Selection</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="saveRange"> + <property name="text"> + <string>Save Range</string> </property> </widget> </item>