From f9e03e718238a83ffcb732c8ab7fb012d4489bf0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 7 Jun 2022 00:05:27 -0700 Subject: [PATCH] GBA Memory: Implement adjustable EWRAM waitstates (closes #1276) --- CHANGES | 1 + include/mgba/internal/gba/io.h | 7 ++++++ include/mgba/internal/gba/memory.h | 1 + include/mgba/internal/gba/serialize.h | 2 +- src/gba/io.c | 35 ++++++++++++++++++++++----- src/gba/memory.c | 26 ++++++++++++++++++++ src/gba/serialize.c | 2 +- 7 files changed, 66 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index e644ba15e..17b2df376 100644 --- a/CHANGES +++ b/CHANGES @@ -67,6 +67,7 @@ Misc: - GBA: Automatically skip BIOS if ROM has invalid logo - GBA: Refine multiboot detection (fixes mgba.io/i/2192) - GBA Cheats: Implement "never" type codes (closes mgba.io/i/915) + - GBA Memory: Implement adjustable EWRAM waitstates (closes mgba.io/i/1276) - GBA DMA: Enhanced logging (closes mgba.io/i/2454) - GBA Video: Implement layer placement for OpenGL renderer (fixes mgba.io/i/1962) - GBA Video: Fix highlighting for sprites with mid-frame palette changes diff --git a/include/mgba/internal/gba/io.h b/include/mgba/internal/gba/io.h index 9875061f3..fdd480037 100644 --- a/include/mgba/internal/gba/io.h +++ b/include/mgba/internal/gba/io.h @@ -154,6 +154,13 @@ enum GBAIORegisters { REG_POSTFLG = 0x300, REG_HALTCNT = 0x301, + REG_EXWAITCNT_LO = 0x800, + REG_EXWAITCNT_HI = 0x802, + + REG_INTERNAL_EXWAITCNT_LO = 0x210, + REG_INTERNAL_EXWAITCNT_HI = 0x212, + REG_INTERNAL_MAX = 0x214, + REG_DEBUG_STRING = 0xFFF600, REG_DEBUG_FLAGS = 0xFFF700, REG_DEBUG_ENABLE = 0xFFF780, diff --git a/include/mgba/internal/gba/memory.h b/include/mgba/internal/gba/memory.h index 59b22b457..93d565a16 100644 --- a/include/mgba/internal/gba/memory.h +++ b/include/mgba/internal/gba/memory.h @@ -172,6 +172,7 @@ uint32_t GBAStoreMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum int* cycleCounter); void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters); +void GBAAdjustEWRAMWaitstates(struct GBA* gba, uint16_t parameters); struct GBASerializedState; void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state); diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index 01b4476b3..f3ed332dc 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -20,7 +20,7 @@ extern MGBA_EXPORT const uint32_t GBASavestateVersion; mLOG_DECLARE_CATEGORY(GBA_STATE); /* Savestate format: - * 0x00000 - 0x00003: Version Magic (0x01000005) + * 0x00000 - 0x00003: Version Magic (0x01000006) * 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS) * 0x00008 - 0x0000B: ROM CRC32 * 0x0000C - 0x0000F: Master cycles diff --git a/src/gba/io.c b/src/gba/io.c index 34e7471bb..ca0cbe5b5 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -199,7 +199,7 @@ const char* const GBAIORegisterNames[] = { "IME" }; -static const int _isValidRegister[REG_MAX >> 1] = { +static const int _isValidRegister[REG_INTERNAL_MAX >> 1] = { // Video 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -238,10 +238,12 @@ static const int _isValidRegister[REG_MAX >> 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Interrupts - 1, 1, 1, 0, 1 + 1, 1, 1, 0, 1, 0, 0, 0, + // Internal registers + 1, 1 }; -static const int _isRSpecialRegister[REG_MAX >> 1] = { +static const int _isRSpecialRegister[REG_INTERNAL_MAX >> 1] = { // Video 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, @@ -280,9 +282,12 @@ static const int _isRSpecialRegister[REG_MAX >> 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Interrupts + 0, 0, 0, 0, 0, 0, 0, 0, + // Internal registers + 1, 1 }; -static const int _isWSpecialRegister[REG_MAX >> 1] = { +static const int _isWSpecialRegister[REG_INTERNAL_MAX >> 1] = { // Video 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -321,7 +326,9 @@ static const int _isWSpecialRegister[REG_MAX >> 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Interrupts - 1, 1, 0, 0, 1 + 1, 1, 0, 0, 1, 0, 0, 0, + // Internal registers + 1, 1 }; void GBAIOInit(struct GBA* gba) { @@ -333,6 +340,8 @@ void GBAIOInit(struct GBA* gba) { gba->memory.io[REG_BG2PD >> 1] = 0x100; gba->memory.io[REG_BG3PA >> 1] = 0x100; gba->memory.io[REG_BG3PD >> 1] = 0x100; + gba->memory.io[REG_INTERNAL_EXWAITCNT_LO >> 1] = 0x20; + gba->memory.io[REG_INTERNAL_EXWAITCNT_HI >> 1] = 0xD00; if (!gba->biosVf) { gba->memory.io[REG_VCOUNT >> 1] = 0x7E; @@ -574,6 +583,12 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { case REG_MAX: // Some bad interrupt libraries will write to this break; + case REG_EXWAITCNT_HI: + // This register sits outside of the normal I/O block, so we need to stash it somewhere unused + address = REG_INTERNAL_EXWAITCNT_HI; + value &= 0xFF00; + GBAAdjustEWRAMWaitstates(gba, value); + break; case REG_DEBUG_ENABLE: gba->debug = value == 0xC0DE; return; @@ -936,6 +951,11 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { case 0x206: mLOG(GBA_IO, GAME_ERROR, "Read from unused I/O register: %03X", address); return 0; + // These registers sit outside of the normal I/O block, so we need to stash them somewhere unused + case REG_EXWAITCNT_LO: + case REG_EXWAITCNT_HI: + address += REG_INTERNAL_EXWAITCNT_LO - REG_EXWAITCNT_LO; + break; case REG_DEBUG_ENABLE: if (gba->debug) { return 0x1DEA; @@ -950,7 +970,7 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) { int i; - for (i = 0; i < REG_MAX; i += 2) { + for (i = 0; i < REG_INTERNAL_MAX; i += 2) { if (_isRSpecialRegister[i >> 1]) { STORE_16(gba->memory.io[i >> 1], i, state->io); } else if (_isValidRegister[i >> 1]) { @@ -991,6 +1011,9 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) { GBAIOWrite(gba, i, reg); } } + if (state->versionMagic >= 0x01000006) { + GBAIOWrite(gba, REG_EXWAITCNT_HI, gba->memory.io[REG_INTERNAL_EXWAITCNT_HI >> 1]); + } uint32_t when; for (i = 0; i < 4; ++i) { diff --git a/src/gba/memory.c b/src/gba/memory.c index 8bacf4a79..1878e7bb2 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -127,6 +127,7 @@ void GBAMemoryReset(struct GBA* gba) { memset(gba->memory.io, 0, sizeof(gba->memory.io)); GBAAdjustWaitstates(gba, 0); + GBAAdjustEWRAMWaitstates(gba, 0x0D00); gba->memory.activeRegion = -1; gba->memory.agbPrintProtect = 0; @@ -1714,6 +1715,31 @@ void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) { } } +void GBAAdjustEWRAMWaitstates(struct GBA* gba, uint16_t parameters) { + struct GBAMemory* memory = &gba->memory; + struct ARMCore* cpu = gba->cpu; + + int wait = 15 - ((parameters >> 8) & 0xF); + if (wait) { + memory->waitstatesNonseq16[REGION_WORKING_RAM] = wait; + memory->waitstatesSeq16[REGION_WORKING_RAM] = wait; + memory->waitstatesNonseq32[REGION_WORKING_RAM] = 2 * wait + 1; + memory->waitstatesSeq32[REGION_WORKING_RAM] = 2 * wait + 1; + + cpu->memory.activeSeqCycles32 = memory->waitstatesSeq32[memory->activeRegion]; + cpu->memory.activeSeqCycles16 = memory->waitstatesSeq16[memory->activeRegion]; + + cpu->memory.activeNonseqCycles32 = memory->waitstatesNonseq32[memory->activeRegion]; + cpu->memory.activeNonseqCycles16 = memory->waitstatesNonseq16[memory->activeRegion]; + } else { + if (!gba->hardCrash) { + mLOG(GBA_MEM, GAME_ERROR, "Cannot set EWRAM to 0 waitstates"); + } else { + mLOG(GBA_MEM, FATAL, "Cannot set EWRAM to 0 waitstates"); + } + } +} + int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) { struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; diff --git a/src/gba/serialize.c b/src/gba/serialize.c index d2bd5c5e8..9c5416e72 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -15,7 +15,7 @@ #include MGBA_EXPORT const uint32_t GBASavestateMagic = 0x01000000; -MGBA_EXPORT const uint32_t GBASavestateVersion = 0x00000005; +MGBA_EXPORT const uint32_t GBASavestateVersion = 0x00000006; mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate", "gba.serialize");