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}