From 67475a6da8236eb6b8d307732db8ee8f50d69715 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 24 Feb 2021 01:20:48 -0800 Subject: [PATCH] GBA Memory: Improved AGBPrint emulation of edge cases (fixes #1867) --- CHANGES | 1 + include/mgba/internal/gba/memory.h | 9 +++- src/gba/memory.c | 85 ++++++++++++++++++++++++------ 3 files changed, 76 insertions(+), 19 deletions(-) diff --git a/CHANGES b/CHANGES index bb5513fd3..a302bb2c0 100644 --- a/CHANGES +++ b/CHANGES @@ -47,6 +47,7 @@ Emulation fixes: - GBA Memory: Improve robustness of Matrix memory support - GBA Memory: Mark Famicom Mini games 22 through 28 as non-mirroring - GBA Memory: Return correct byte for odd ROM open bus addresses + - GBA Memory: Improved AGBPrint emulation of edge cases (fixes mgba.io/i/1867) - GBA Serialize: Fix alignment check when loading states - GBA SIO: Fix copying Normal mode transfer values - GBA SIO: Fix Normal mode being totally broken (fixes mgba.io/i/1800) diff --git a/include/mgba/internal/gba/memory.h b/include/mgba/internal/gba/memory.h index f98b7e94a..11d4a5811 100644 --- a/include/mgba/internal/gba/memory.h +++ b/include/mgba/internal/gba/memory.h @@ -84,7 +84,7 @@ enum { AGB_PRINT_BASE = 0x00FD0000, AGB_PRINT_TOP = 0x00FE0000, AGB_PRINT_PROTECT = 0x00FE2FFE, - AGB_PRINT_STRUCT = 0x01FE20F8, + AGB_PRINT_STRUCT = 0x00FE20F8, AGB_PRINT_FLUSH_ADDR = 0x01FE209C, }; @@ -127,9 +127,14 @@ struct GBAMemory { int activeDMA; uint32_t dmaTransferRegister; - uint16_t agbPrint; + uint32_t agbPrintBase; + uint16_t agbPrintProtect; struct GBAPrintContext agbPrintCtx; uint16_t* agbPrintBuffer; + uint16_t agbPrintProtectBackup; + struct GBAPrintContext agbPrintCtxBackup; + uint32_t agbPrintFuncBackup; + uint16_t* agbPrintBufferBackup; bool mirroring; }; diff --git a/src/gba/memory.c b/src/gba/memory.c index d2735cf3a..5ff5cf9ac 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -25,7 +25,7 @@ static void _pristineCow(struct GBA* gba); static void _agbPrintStore(struct GBA* gba, uint32_t address, int16_t value); static int16_t _agbPrintLoad(struct GBA* gba, uint32_t address); static uint8_t _deadbeef[4] = { 0x10, 0xB7, 0x10, 0xE7 }; // Illegal instruction on both ARM and Thumb -static uint8_t _agbPrintFunc[4] = { 0xFA, 0xDF /* swi 0xFF */, 0x70, 0x47 /* bx lr */ }; +static const uint32_t _agbPrintFunc = 0x4770DFFA; // swi 0xFA; bx lr static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t region); static int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait); @@ -84,9 +84,10 @@ void GBAMemoryInit(struct GBA* gba) { gba->memory.biosPrefetch = 0; gba->memory.mirroring = false; - gba->memory.agbPrint = 0; + gba->memory.agbPrintProtect = 0; memset(&gba->memory.agbPrintCtx, 0, sizeof(gba->memory.agbPrintCtx)); gba->memory.agbPrintBuffer = NULL; + gba->memory.agbPrintBufferBackup = NULL; gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM + SIZE_WORKING_IRAM); gba->memory.iwram = &gba->memory.wram[SIZE_WORKING_RAM >> 2]; @@ -103,6 +104,9 @@ void GBAMemoryDeinit(struct GBA* gba) { if (gba->memory.agbPrintBuffer) { mappedMemoryFree(gba->memory.agbPrintBuffer, SIZE_AGB_PRINT); } + if (gba->memory.agbPrintBufferBackup) { + mappedMemoryFree(gba->memory.agbPrintBufferBackup, SIZE_AGB_PRINT); + } } void GBAMemoryReset(struct GBA* gba) { @@ -117,11 +121,17 @@ void GBAMemoryReset(struct GBA* gba) { memset(gba->memory.io, 0, sizeof(gba->memory.io)); GBAAdjustWaitstates(gba, 0); - gba->memory.agbPrint = 0; + gba->memory.agbPrintProtect = 0; + gba->memory.agbPrintBase = 0; memset(&gba->memory.agbPrintCtx, 0, sizeof(gba->memory.agbPrintCtx)); if (gba->memory.agbPrintBuffer) { + mappedMemoryFree(gba->memory.agbPrintBuffer, SIZE_AGB_PRINT); gba->memory.agbPrintBuffer = NULL; } + if (gba->memory.agbPrintBufferBackup) { + mappedMemoryFree(gba->memory.agbPrintBufferBackup, SIZE_AGB_PRINT); + gba->memory.agbPrintBufferBackup = NULL; + } gba->memory.prefetch = false; gba->memory.lastPrefetchedPc = 0; @@ -303,8 +313,8 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { if ((address & (SIZE_CART0 - 1)) < memory->romSize) { break; } - if ((address & (SIZE_CART0 - 1)) == AGB_PRINT_FLUSH_ADDR && memory->agbPrint == 0x20) { - cpu->memory.activeRegion = (uint32_t*) _agbPrintFunc; + if ((address & (SIZE_CART0 - 1)) == AGB_PRINT_FLUSH_ADDR && memory->agbPrintProtect == 0x20) { + cpu->memory.activeRegion = &_agbPrintFunc; cpu->memory.activeMask = sizeof(_agbPrintFunc) - 1; break; } @@ -560,8 +570,8 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { } else if ((address & (SIZE_CART0 - 1)) >= AGB_PRINT_BASE) { uint32_t agbPrintAddr = address & 0x00FFFFFF; if (agbPrintAddr == AGB_PRINT_PROTECT) { - value = memory->agbPrint; - } else if (agbPrintAddr < AGB_PRINT_TOP || (agbPrintAddr & 0x00FFFFF8) == (AGB_PRINT_STRUCT & 0x00FFFFF8)) { + value = memory->agbPrintProtect; + } else if (agbPrintAddr < AGB_PRINT_TOP || (agbPrintAddr & 0x00FFFFF8) == AGB_PRINT_STRUCT) { value = _agbPrintLoad(gba, address); } else { mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load16: 0x%08X", address); @@ -915,11 +925,34 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle if ((address & 0x00FFFFFF) >= AGB_PRINT_BASE) { uint32_t agbPrintAddr = address & 0x00FFFFFF; if (agbPrintAddr == AGB_PRINT_PROTECT) { - memory->agbPrint = value; - _agbPrintStore(gba, address, value); + bool wasProtected = memory->agbPrintProtect != 0x20; + memory->agbPrintProtect = value; + + if (!memory->agbPrintBuffer) { + memory->agbPrintBuffer = anonymousMemoryMap(SIZE_AGB_PRINT); + if (memory->romSize >= SIZE_CART0 / 2) { + int base = 0; + if (memory->romSize == SIZE_CART0) { + base = address & 0x01000000; + } + memory->agbPrintBase = base; + memory->agbPrintBufferBackup = anonymousMemoryMap(SIZE_AGB_PRINT); + memcpy(memory->agbPrintBufferBackup, &memory->rom[(AGB_PRINT_TOP | base) >> 2], SIZE_AGB_PRINT); + LOAD_16(memory->agbPrintProtectBackup, AGB_PRINT_PROTECT | base, memory->rom); + LOAD_16(memory->agbPrintCtxBackup.request, AGB_PRINT_STRUCT | base, memory->rom); + LOAD_16(memory->agbPrintCtxBackup.bank, (AGB_PRINT_STRUCT | base) + 2, memory->rom); + LOAD_16(memory->agbPrintCtxBackup.get, (AGB_PRINT_STRUCT | base) + 4, memory->rom); + LOAD_16(memory->agbPrintCtxBackup.put, (AGB_PRINT_STRUCT | base) + 6, memory->rom); + LOAD_32(memory->agbPrintFuncBackup, AGB_PRINT_FLUSH_ADDR | base, memory->rom); + } + } + + if (value == 0x20) { + _agbPrintStore(gba, address, value); + } break; } - if (memory->agbPrint == 0x20 && (agbPrintAddr < AGB_PRINT_TOP || (agbPrintAddr & 0x00FFFFF8) == (AGB_PRINT_STRUCT & 0x00FFFFF8))) { + if (memory->agbPrintProtect == 0x20 && (agbPrintAddr < AGB_PRINT_TOP || (agbPrintAddr & 0x00FFFFF8) == AGB_PRINT_STRUCT)) { _agbPrintStore(gba, address, value); break; } @@ -1629,6 +1662,28 @@ void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) { cpu->memory.activeNonseqCycles32 = memory->waitstatesNonseq32[memory->activeRegion]; cpu->memory.activeNonseqCycles16 = memory->waitstatesNonseq16[memory->activeRegion]; + + if (memory->agbPrintBufferBackup) { + int phi = (parameters >> 11) & 3; + uint32_t base = memory->agbPrintBase; + if (phi == 3) { + memcpy(&memory->rom[(AGB_PRINT_TOP | base) >> 2], memory->agbPrintBuffer, SIZE_AGB_PRINT); + STORE_16(memory->agbPrintProtect, AGB_PRINT_PROTECT | base, memory->rom); + STORE_16(memory->agbPrintCtx.request, AGB_PRINT_STRUCT | base, memory->rom); + STORE_16(memory->agbPrintCtx.bank, (AGB_PRINT_STRUCT | base) + 2, memory->rom); + STORE_16(memory->agbPrintCtx.get, (AGB_PRINT_STRUCT | base) + 4, memory->rom); + STORE_16(memory->agbPrintCtx.put, (AGB_PRINT_STRUCT | base) + 6, memory->rom); + STORE_32(_agbPrintFunc, AGB_PRINT_FLUSH_ADDR | base, memory->rom); + } else { + memcpy(&memory->rom[(AGB_PRINT_TOP | base) >> 2], memory->agbPrintBufferBackup, SIZE_AGB_PRINT); + STORE_16(memory->agbPrintProtectBackup, AGB_PRINT_PROTECT | base, memory->rom); + STORE_16(memory->agbPrintCtxBackup.request, AGB_PRINT_STRUCT | base, memory->rom); + STORE_16(memory->agbPrintCtxBackup.bank, (AGB_PRINT_STRUCT | base) + 2, memory->rom); + STORE_16(memory->agbPrintCtxBackup.get, (AGB_PRINT_STRUCT | base) + 4, memory->rom); + STORE_16(memory->agbPrintCtxBackup.put, (AGB_PRINT_STRUCT | base) + 6, memory->rom); + STORE_32(memory->agbPrintFuncBackup, AGB_PRINT_FLUSH_ADDR | base, memory->rom); + } + } } int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) { @@ -1750,7 +1805,7 @@ void GBAPrintFlush(struct GBA* gba) { oolBuf[i + 1] = 0; ++gba->memory.agbPrintCtx.get; } - _agbPrintStore(gba, AGB_PRINT_STRUCT + 4, gba->memory.agbPrintCtx.get); + _agbPrintStore(gba, (AGB_PRINT_STRUCT + 4) | gba->memory.agbPrintBase, gba->memory.agbPrintCtx.get); mLOG(GBA_DEBUG, INFO, "%s", oolBuf); } @@ -1758,16 +1813,12 @@ void GBAPrintFlush(struct GBA* gba) { static void _agbPrintStore(struct GBA* gba, uint32_t address, int16_t value) { struct GBAMemory* memory = &gba->memory; if ((address & 0x00FFFFFF) < AGB_PRINT_TOP) { - if (!memory->agbPrintBuffer) { - memory->agbPrintBuffer = anonymousMemoryMap(SIZE_AGB_PRINT); - } STORE_16(value, address & (SIZE_AGB_PRINT - 2), memory->agbPrintBuffer); - } else if ((address & 0x00FFFFF8) == (AGB_PRINT_STRUCT & 0x00FFFFF8)) { + } else if ((address & 0x00FFFFF8) == AGB_PRINT_STRUCT) { (&memory->agbPrintCtx.request)[(address & 7) >> 1] = value; } if (memory->romSize == SIZE_CART0) { _pristineCow(gba); - memcpy(&memory->rom[AGB_PRINT_FLUSH_ADDR >> 2], _agbPrintFunc, sizeof(_agbPrintFunc)); STORE_16(value, address & (SIZE_CART0 - 2), memory->rom); } else if (memory->agbPrintCtx.bank == 0xFD && memory->romSize >= SIZE_CART0 / 2) { _pristineCow(gba); @@ -1780,7 +1831,7 @@ static int16_t _agbPrintLoad(struct GBA* gba, uint32_t address) { int16_t value = address >> 1; if (address < AGB_PRINT_TOP && memory->agbPrintBuffer) { LOAD_16(value, address & (SIZE_AGB_PRINT - 1), memory->agbPrintBuffer); - } else if ((address & 0x00FFFFF8) == (AGB_PRINT_STRUCT & 0x00FFFFF8)) { + } else if ((address & 0x00FFFFF8) == AGB_PRINT_STRUCT) { value = (&memory->agbPrintCtx.request)[(address & 7) >> 1]; } return value;