all repos — mgba @ 2f23829b66712f5d0a41e9e6089c006fb3fb5752

mGBA Game Boy Advance Emulator

src/platform/qt/MemoryView.cpp (view raw)

  1/* Copyright (c) 2013-2015 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6
  7#include "MemoryView.h"
  8
  9#include "GameController.h"
 10
 11#include <mgba/core/core.h>
 12#ifdef M_CORE_GBA
 13#include <mgba/internal/gba/memory.h>
 14#endif
 15#ifdef M_CORE_GB
 16#include <mgba/internal/gb/memory.h>
 17#endif
 18
 19using namespace QGBA;
 20
 21struct IndexInfo {
 22	const char* name;
 23	const char* longName;
 24	uint32_t base;
 25	uint32_t size;
 26	int maxSegment;
 27};
 28#ifdef M_CORE_GBA
 29const static struct IndexInfo indexInfoGBA[] = {
 30	{ "All", "All", 0, 0x10000000 },
 31	{ "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS },
 32	{ "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, SIZE_WORKING_RAM },
 33	{ "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, SIZE_WORKING_IRAM },
 34	{ "MMIO", "Memory-Mapped I/O", BASE_IO, SIZE_IO },
 35	{ "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, SIZE_PALETTE_RAM },
 36	{ "VRAM", "Video RAM (96kiB)", BASE_VRAM, SIZE_VRAM },
 37	{ "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, SIZE_OAM },
 38	{ "ROM", "Game Pak (32MiB)", BASE_CART0, SIZE_CART0 },
 39	{ "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, SIZE_CART1 },
 40	{ "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, SIZE_CART2 },
 41	{ "SRAM", "Static RAM (64kiB)", BASE_CART_SRAM, SIZE_CART_SRAM },
 42	{ nullptr, nullptr, 0, 0, 0 }
 43};
 44#endif
 45#ifdef M_CORE_GB
 46const static struct IndexInfo indexInfoGB[] = {
 47	{ "All", "All", 0, 0x10000 },
 48	{ "ROM", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_SIZE_CART_BANK0 * 2, 511 },
 49	{ "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_SIZE_VRAM, 1 },
 50	{ "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM, 3 },
 51	{ "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_SIZE_WORKING_RAM_BANK0 * 2, 7 },
 52	{ "OAM", "OBJ Attribute Memory", GB_BASE_OAM, GB_SIZE_OAM },
 53	{ "IO", "Memory-Mapped I/O", GB_BASE_IO, GB_SIZE_IO },
 54	{ "HRAM", "High RAM", GB_BASE_HRAM, GB_SIZE_HRAM },
 55	{ nullptr, nullptr, 0, 0, 0 }
 56};
 57#endif
 58
 59MemoryView::MemoryView(GameController* controller, QWidget* parent)
 60	: QWidget(parent)
 61	, m_controller(controller)
 62{
 63	m_ui.setupUi(this);
 64
 65	m_ui.hexfield->setController(controller);
 66
 67	mCore* core = m_controller->thread()->core;
 68	const IndexInfo* info = nullptr;
 69	switch (core->platform(core)) {
 70#ifdef M_CORE_GBA
 71	case PLATFORM_GBA:
 72		info = indexInfoGBA;
 73		break;
 74#endif
 75#ifdef M_CORE_GB
 76	case PLATFORM_GB:
 77		info = indexInfoGB;
 78		break;
 79#endif
 80	default:
 81		break;
 82	}
 83
 84	connect(m_ui.regions, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
 85	        this, &MemoryView::setIndex);
 86	connect(m_ui.segments, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
 87	        this, &MemoryView::setSegment);
 88
 89	if (info) {
 90		for (size_t i = 0; info[i].name; ++i) {
 91			m_ui.regions->addItem(tr(info[i].longName));
 92		}
 93	}
 94
 95	connect(m_ui.width8, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(1); });
 96	connect(m_ui.width16, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(2); });
 97	connect(m_ui.width32, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(4); });
 98	connect(m_ui.setAddress, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
 99	        m_ui.hexfield, static_cast<void (MemoryModel::*)(uint32_t)>(&MemoryModel::jumpToAddress));
100	connect(m_ui.hexfield, &MemoryModel::selectionChanged, this, &MemoryView::updateSelection);
101
102	connect(controller, &GameController::gameStopped, this, &QWidget::close);
103
104	connect(controller, &GameController::frameAvailable, this, &MemoryView::update);
105	connect(controller, &GameController::gamePaused, this, &MemoryView::update);
106	connect(controller, &GameController::stateLoaded, this, &MemoryView::update);
107	connect(controller, &GameController::rewound, this, &MemoryView::update);
108
109	connect(m_ui.copy, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::copy);
110	connect(m_ui.save, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::save);
111	connect(m_ui.paste, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::paste);
112	connect(m_ui.load, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::load);
113
114	connect(m_ui.loadTBL, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::loadTBL);
115}
116
117void MemoryView::setIndex(int index) {
118	mCore* core = m_controller->thread()->core;
119	IndexInfo info;
120	switch (core->platform(core)) {
121#ifdef M_CORE_GBA
122	case PLATFORM_GBA:
123		info = indexInfoGBA[index];
124		break;
125#endif
126#ifdef M_CORE_GB
127	case PLATFORM_GB:
128		info = indexInfoGB[index];
129		break;
130#endif
131	default:
132		return;
133	}
134	m_ui.segments->setValue(-1);
135	m_ui.segments->setVisible(info.maxSegment > 0);
136	m_ui.segments->setMaximum(info.maxSegment);
137	m_ui.hexfield->setRegion(info.base, info.size, info.name);
138}
139
140void MemoryView::setSegment(int segment) {
141	mCore* core = m_controller->thread()->core;
142	IndexInfo info;
143	switch (core->platform(core)) {
144#ifdef M_CORE_GBA
145	case PLATFORM_GBA:
146		info = indexInfoGBA[m_ui.regions->currentIndex()];
147		break;
148#endif
149#ifdef M_CORE_GB
150	case PLATFORM_GB:
151		info = indexInfoGB[m_ui.regions->currentIndex()];
152		break;
153#endif
154	default:
155		return;
156	}
157	m_ui.hexfield->setSegment(info.maxSegment < segment ? info.maxSegment : segment);
158}
159
160void MemoryView::update() {
161	m_ui.hexfield->viewport()->update();
162	updateStatus();
163}
164
165void MemoryView::updateSelection(uint32_t start, uint32_t end) {
166	m_selection.first = start;
167	m_selection.second = end;
168	updateStatus();
169}
170
171void MemoryView::updateStatus() {
172	int align = m_ui.hexfield->alignment();
173	if (!m_controller->isLoaded()) {
174		return;
175	}
176	mCore* core = m_controller->thread()->core;
177	QByteArray selection(m_ui.hexfield->serialize());
178	QString text(m_ui.hexfield->decodeText(selection));
179	m_ui.stringVal->setText(text);
180
181	if (m_selection.first & (align - 1) || m_selection.second - m_selection.first != align) {
182		m_ui.sintVal->clear();
183		m_ui.uintVal->clear();
184		return;
185	}
186	union {
187		uint32_t u32;
188		int32_t i32;
189		uint16_t u16;
190		int16_t i16;
191		uint8_t u8;
192		int8_t i8;
193	} value;
194	switch (align) {
195	case 1:
196		value.u8 = core->rawRead8(core, m_selection.first, m_ui.segments->value());
197		m_ui.sintVal->setText(QString::number(value.i8));
198		m_ui.uintVal->setText(QString::number(value.u8));
199		break;
200	case 2:
201		value.u16 = core->rawRead16(core, m_selection.first, m_ui.segments->value());
202		m_ui.sintVal->setText(QString::number(value.i16));
203		m_ui.uintVal->setText(QString::number(value.u16));
204		break;
205	case 4:
206		value.u32 = core->rawRead32(core, m_selection.first, m_ui.segments->value());
207		m_ui.sintVal->setText(QString::number(value.i32));
208		m_ui.uintVal->setText(QString::number(value.u32));
209		break;
210	}
211}