diff --git a/CHANGES b/CHANGES index 29cc31c72..3b89f7921 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,7 @@ Features: - Menu items for specific solar sensor brightness levels - Remappable controls for tilt and gyroscope sensors - Status messages for actions taken while a game is running (e.g. save/load state) + - Memory inspector Bugfixes: - GBA: Fix timers not updating timing when writing to only the reload register - All: Fix sanitize-deb script not cleaning up after itself diff --git a/src/gba/memory.c b/src/gba/memory.c index 24e6cfb4e..64c1de475 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -941,6 +941,63 @@ void GBAPatch16(struct ARMCore* cpu, uint32_t address, int16_t value, int16_t* o } } +void GBAPatch8(struct ARMCore* cpu, uint32_t address, int8_t value, int8_t* old) { + struct GBA* gba = (struct GBA*) cpu->master; + struct GBAMemory* memory = &gba->memory; + int8_t oldValue = -1; + + switch (address >> BASE_OFFSET) { + case REGION_WORKING_RAM: + oldValue = ((int8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)]; + ((int8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)] = value; + break; + case REGION_WORKING_IRAM: + oldValue = ((int8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)]; + ((int8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)] = value; + break; + case REGION_IO: + GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Patch8: 0x%08X", address); + break; + case REGION_PALETTE_RAM: + GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Patch8: 0x%08X", address); + break; + case REGION_VRAM: + GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Patch8: 0x%08X", address); + break; + case REGION_OAM: + GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Patch8: 0x%08X", address); + break; + case REGION_CART0: + case REGION_CART0_EX: + case REGION_CART1: + case REGION_CART1_EX: + case REGION_CART2: + case REGION_CART2_EX: + _pristineCow(gba); + if ((address & (SIZE_CART0 - 1)) < gba->memory.romSize) { + gba->memory.romSize = (address & (SIZE_CART0 - 2)) + 2; + } + oldValue = ((int8_t*) memory->rom)[address & (SIZE_CART0 - 1)]; + ((int8_t*) memory->rom)[address & (SIZE_CART0 - 1)] = value; + break; + case REGION_CART_SRAM: + case REGION_CART_SRAM_MIRROR: + if (memory->savedata.type == SAVEDATA_SRAM) { + oldValue = ((int8_t*) memory->savedata.data)[address & (SIZE_CART_SRAM - 1)]; + ((int8_t*) memory->savedata.data)[address & (SIZE_CART_SRAM - 1)] = value; + } else { + GBALog(gba, GBA_LOG_GAME_ERROR, "Writing to non-existent SRAM: 0x%08X", address); + } + break; + default: + GBALog(gba, GBA_LOG_WARN, "Bad memory Patch8: 0x%08X", address); + break; + } + if (old) { + *old = oldValue; + } +} + #define LDM_LOOP(LDM) \ for (i = 0; i < 16; i += 4) { \ if (UNLIKELY(mask & (1 << i))) { \ diff --git a/src/gba/memory.h b/src/gba/memory.h index ef39f42db..053e71f20 100644 --- a/src/gba/memory.h +++ b/src/gba/memory.h @@ -154,6 +154,7 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo void GBAPatch32(struct ARMCore* cpu, uint32_t address, int32_t value, int32_t* old); void GBAPatch16(struct ARMCore* cpu, uint32_t address, int16_t value, int16_t* old); +void GBAPatch8(struct ARMCore* cpu, uint32_t address, int8_t value, int8_t* old); uint32_t GBALoadMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction, int* cycleCounter); uint32_t GBAStoreMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction, int* cycleCounter); diff --git a/src/platform/qt/MemoryModel.cpp b/src/platform/qt/MemoryModel.cpp index 307ab8144..3210f7be6 100644 --- a/src/platform/qt/MemoryModel.cpp +++ b/src/platform/qt/MemoryModel.cpp @@ -34,6 +34,8 @@ MemoryModel::MemoryModel(QWidget* parent) m_cellHeight = metrics.height(); m_letterWidth = metrics.averageCharWidth(); + setFocusPolicy(Qt::StrongFocus); + for (int i = 0; i < 256; ++i) { QStaticText str(QString("%0").arg(i, 2, 16, QChar('0')).toUpper()); str.prepare(QTransform(), m_font); @@ -83,6 +85,8 @@ void MemoryModel::setAlignment(int width) { return; } m_align = width; + m_buffer = 0; + m_bufferedNybbles = 0; viewport()->update(); } @@ -104,6 +108,8 @@ void MemoryModel::jumpToAddress(uint32_t address) { m_top = (address - m_base) / 16; boundsCheck(); verticalScrollBar()->setValue(m_top); + m_buffer = 0; + m_bufferedNybbles = 0; } void MemoryModel::resizeEvent(QResizeEvent*) { @@ -204,6 +210,8 @@ void MemoryModel::mousePressEvent(QMouseEvent* event) { uint32_t address = int(position.x() / m_cellSize.width()) + (int(position.y() / m_cellSize.height()) + m_top) * 16 + m_base; m_selectionAnchor = address & ~(m_align - 1); m_selection = qMakePair(m_selectionAnchor, m_selectionAnchor + m_align); + m_buffer = 0; + m_bufferedNybbles = 0; emit selectionChanged(m_selection.first, m_selection.second); viewport()->update(); } @@ -220,10 +228,68 @@ void MemoryModel::mouseMoveEvent(QMouseEvent* event) { } else { m_selection = qMakePair(m_selectionAnchor, (address & ~(m_align - 1)) + m_align); } + m_buffer = 0; + m_bufferedNybbles = 0; emit selectionChanged(m_selection.first, m_selection.second); viewport()->update(); } +void MemoryModel::keyPressEvent(QKeyEvent* event) { + if (m_selection.first >= m_selection.second) { + return; + } + int key = event->key(); + uint8_t nybble = 0; + switch (key) { + case Qt::Key_0: + case Qt::Key_1: + case Qt::Key_2: + case Qt::Key_3: + case Qt::Key_4: + case Qt::Key_5: + case Qt::Key_6: + case Qt::Key_7: + case Qt::Key_8: + case Qt::Key_9: + nybble = key - Qt::Key_0; + break; + case Qt::Key_A: + case Qt::Key_B: + case Qt::Key_C: + case Qt::Key_D: + case Qt::Key_E: + case Qt::Key_F: + nybble = key - Qt::Key_A + 10; + break; + default: + return; + } + m_buffer <<= 4; + m_buffer |= nybble; + ++m_bufferedNybbles; + if (m_bufferedNybbles == m_align * 2) { + switch (m_align) { + case 1: + GBAPatch8(m_cpu, m_selection.first, m_buffer, nullptr); + break; + case 2: + GBAPatch16(m_cpu, m_selection.first, m_buffer, nullptr); + break; + case 4: + GBAPatch32(m_cpu, m_selection.first, m_buffer, nullptr); + break; + } + m_bufferedNybbles = 0; + m_buffer = 0; + m_selection.first += m_align; + if (m_selection.second <= m_selection.first) { + m_selection.second = m_selection.first + m_align; + } + emit selectionChanged(m_selection.first, m_selection.second); + viewport()->update(); + } +} + void MemoryModel::boundsCheck() { if (m_top < 0) { m_top = 0; diff --git a/src/platform/qt/MemoryModel.h b/src/platform/qt/MemoryModel.h index 267812d68..9162a0478 100644 --- a/src/platform/qt/MemoryModel.h +++ b/src/platform/qt/MemoryModel.h @@ -44,6 +44,7 @@ protected: void wheelEvent(QWheelEvent*) override; void mousePressEvent(QMouseEvent*) override; void mouseMoveEvent(QMouseEvent*) override; + void keyPressEvent(QKeyEvent*) override; private: void boundsCheck(); @@ -65,6 +66,8 @@ private: QSizeF m_cellSize; QPair m_selection; uint32_t m_selectionAnchor; + uint32_t m_buffer; + int m_bufferedNybbles; }; }