From d86ee1d5bfb76d4efd89f4056beece374926500a Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Sun, 8 Dec 2019 13:46:51 -0500 Subject: Add GBA cart model and allow reading from it --- src/GBACart.cpp | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 src/GBACart.cpp (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp new file mode 100644 index 0000000..7c2faad --- /dev/null +++ b/src/GBACart.cpp @@ -0,0 +1,197 @@ +/* + Copyright 2019 Arisotura, Raphaël Zumer + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include +#include +#include "GBACart.h" +#include "CRC32.h" +#include "Platform.h" + + +namespace GBACart_SRAM +{ + +u8* SRAM; +u32 SRAMLength; + +char SRAMPath[1024]; + + +bool Init() +{ + SRAM = NULL; + return true; +} + +void DeInit() +{ + if (SRAM) delete[] SRAM; +} + +void Reset() +{ + if (SRAM) delete[] SRAM; + SRAM = NULL; +} + +void DoSavestate(Savestate* file) +{ + // TODO? +} + +void LoadSave(const char* path) +{ + if (SRAM) delete[] SRAM; + + strncpy(SRAMPath, path, 1023); + SRAMPath[1023] = '\0'; + + FILE* f = Platform::OpenFile(path, "rb"); + if (f) + { + fseek(f, 0, SEEK_END); + SRAMLength = (u32)ftell(f); + SRAM = new u8[SRAMLength]; + + fseek(f, 0, SEEK_SET); + fread(SRAM, SRAMLength, 1, f); + + fclose(f); + } + else + { + int SRAMLength = 65536; // max GBA SRAM size + + SRAM = new u8[SRAMLength]; + memset(SRAM, 0xFF, SRAMLength); + } +} + +void RelocateSave(const char* path, bool write) +{ + if (!write) + { + LoadSave(path); // lazy + return; + } + + strncpy(SRAMPath, path, 1023); + SRAMPath[1023] = '\0'; + + FILE* f = Platform::OpenFile(path, "wb"); + if (!f) + { + printf("GBACart_SRAM::RelocateSave: failed to create new file. fuck\n"); + return; + } + + fwrite(SRAM, SRAMLength, 1, f); + fclose(f); +} + +} + + +namespace GBACart +{ + +bool CartInserted; +u8* CartROM; +u32 CartROMSize; +u32 CartCRC; +u32 CartID; + + +bool Init() +{ + if (!GBACart_SRAM::Init()) return false; + + CartROM = NULL; + + return true; +} + +void DeInit() +{ + if (CartROM) delete[] CartROM; + + GBACart_SRAM::DeInit(); +} + +void Reset() +{ + CartInserted = false; + if (CartROM) delete[] CartROM; + CartROM = NULL; + CartROMSize = 0; + + GBACart_SRAM::Reset(); +} + +void DoSavestate(Savestate* file) +{ + // TODO? +} + +bool LoadROM(const char* path, const char* sram) +{ + FILE* f = Platform::OpenFile(path, "rb"); + if (!f) + { + return false; + } + + fseek(f, 0, SEEK_END); + u32 len = (u32)ftell(f); + + CartROMSize = 0x200; + while (CartROMSize < len) + CartROMSize <<= 1; + + u32 gamecode; + fseek(f, 0xAC, SEEK_SET); + fread(&gamecode, 4, 1, f); + printf("Game code: %c%c%c%c\n", gamecode&0xFF, (gamecode>>8)&0xFF, (gamecode>>16)&0xFF, gamecode>>24); + + CartROM = new u8[CartROMSize]; + memset(CartROM, 0, CartROMSize); + fseek(f, 0, SEEK_SET); + fread(CartROM, 1, len, f); + + fclose(f); + //CartROM = f; + + CartCRC = CRC32(CartROM, CartROMSize); + printf("ROM CRC32: %08X\n", CartCRC); + + CartInserted = true; + + // save + printf("Save file: %s\n", sram); + GBACart_SRAM::LoadSave(sram); + + return true; +} + +void RelocateSave(const char* path, bool write) +{ + // derp herp + GBACart_SRAM::RelocateSave(path, write); +} + +} -- cgit v1.2.3 From 48a8a25548a9aa0ed73e4d0cb2d30471326481af Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Sun, 8 Dec 2019 17:13:56 -0500 Subject: Reset GBA cartridge state when loading a new ROM --- src/GBACart.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 7c2faad..8ce76ab 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -156,6 +156,11 @@ bool LoadROM(const char* path, const char* sram) return false; } + if (CartInserted) + { + Reset(); + } + fseek(f, 0, SEEK_END); u32 len = (u32)ftell(f); -- cgit v1.2.3 From 62b9f51e2329507d5e47b4239259067b5fb66240 Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Sun, 8 Dec 2019 17:56:22 -0500 Subject: Handle GBA cartridge SRAM writes --- src/GBACart.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/GBACart.h | 4 ++++ src/NDS.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 8ce76ab..10f5106 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -104,6 +104,60 @@ void RelocateSave(const char* path, bool write) fclose(f); } +void Write8(u32 addr, u8 val) +{ + u8 prev = *(u8*)&SRAM[addr]; + + if (prev != val) + { + *(u8*)&SRAM[addr] = val;/* + + FILE* f = Platform::OpenFile(SRAMPath, "r+b"); + if (f) + { + fseek(f, addr, SEEK_SET); + fwrite((u8*)&SRAM[addr], 1, 1, f); + fclose(f); + }*/ + } +} + +void Write16(u32 addr, u16 val) +{ + u16 prev = *(u16*)&SRAM[addr]; + + if (prev != val) + { + *(u16*)&SRAM[addr] = val;/* + + FILE* f = Platform::OpenFile(SRAMPath, "r+b"); + if (f) + { + fseek(f, addr, SEEK_SET); + fwrite((u8*)&SRAM[addr], 2, 1, f); + fclose(f); + }*/ + } +} + +void Write32(u32 addr, u32 val) +{ + u32 prev = *(u32*)&SRAM[addr]; + + if (prev != val) + { + *(u32*)&SRAM[addr] = val;/* + + FILE* f = Platform::OpenFile(SRAMPath, "r+b"); + if (f) + { + fseek(f, addr, SEEK_SET); + fwrite((u8*)&SRAM[addr], 3, 1, f); + fclose(f); + }*/ + } +} + } diff --git a/src/GBACart.h b/src/GBACart.h index 94da0b2..e86ea43 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -29,6 +29,10 @@ namespace GBACart_SRAM extern u8* SRAM; extern u32 SRAMLength; +void Write8(u32 addr, u8 val); +void Write16(u32 addr, u16 val); +void Write32(u32 addr, u32 val); + } diff --git a/src/NDS.cpp b/src/NDS.cpp index da36bdc..fe66814 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1802,6 +1802,14 @@ void ARM9Write8(u32 addr, u8 val) case 0x07000000: // checkme return; + + case 0x0A000000: + if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write + if (GBACart::CartInserted) + { + GBACart_SRAM::Write8(addr & (GBACart_SRAM::SRAMLength-1), val); + } + return; } printf("unknown arm9 write8 %08X %02X\n", addr, val); @@ -1845,6 +1853,14 @@ void ARM9Write16(u32 addr, u16 val) if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return; *(u16*)&GPU::OAM[addr & 0x7FF] = val; return; + + case 0x0A000000: + if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write + if (GBACart::CartInserted) + { + GBACart_SRAM::Write16(addr & (GBACart_SRAM::SRAMLength-1), val); + } + return; } //printf("unknown arm9 write16 %08X %04X\n", addr, val); @@ -1888,6 +1904,14 @@ void ARM9Write32(u32 addr, u32 val) if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return; *(u32*)&GPU::OAM[addr & 0x7FF] = val; return; + + case 0x0A000000: + if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write + if (GBACart::CartInserted) + { + GBACart_SRAM::Write32(addr & (GBACart_SRAM::SRAMLength-1), val); + } + return; } printf("unknown arm9 write32 %08X %08X | %08X\n", addr, val, ARM9->R[15]); @@ -2152,6 +2176,14 @@ void ARM7Write8(u32 addr, u8 val) case 0x06800000: GPU::WriteVRAM_ARM7(addr, val); return; + + case 0x0A000000: + if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write + if (GBACart::CartInserted) + { + GBACart_SRAM::Write8(addr & (GBACart_SRAM::SRAMLength-1), val); + } + return; } printf("unknown arm7 write8 %08X %02X @ %08X\n", addr, val, ARM7->R[15]); @@ -2198,6 +2230,14 @@ void ARM7Write16(u32 addr, u16 val) case 0x06800000: GPU::WriteVRAM_ARM7(addr, val); return; + + case 0x0A000000: + if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write + if (GBACart::CartInserted) + { + GBACart_SRAM::Write16(addr & (GBACart_SRAM::SRAMLength-1), val); + } + return; } //printf("unknown arm7 write16 %08X %04X @ %08X\n", addr, val, ARM7->R[15]); @@ -2245,6 +2285,14 @@ void ARM7Write32(u32 addr, u32 val) case 0x06800000: GPU::WriteVRAM_ARM7(addr, val); return; + + case 0x0A000000: + if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write + if (GBACart::CartInserted) + { + GBACart_SRAM::Write32(addr & (GBACart_SRAM::SRAMLength-1), val); + } + return; } //printf("unknown arm7 write32 %08X %08X @ %08X\n", addr, val, ARM7->R[15]); -- cgit v1.2.3 From 5ad85f15c1858108665329fada39d67edc408b39 Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Mon, 9 Dec 2019 04:53:45 -0500 Subject: Add a framework to support non-SRAM GBA saves The support is not yet there, but at least we should not read or write bogus data. --- src/GBACart.cpp | 191 +++++++++++++++++++++++++++++++++++++++++++++++--------- src/GBACart.h | 4 ++ src/NDS.cpp | 12 ++-- 3 files changed, 170 insertions(+), 37 deletions(-) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 10f5106..937958c 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -26,11 +26,40 @@ namespace GBACart_SRAM { + +enum SaveType { + S_NULL, + S_EEPROM4K, + S_EEPROM64K, + S_SRAM256K, + S_FLASH512K, + S_FLASH1M +}; + +struct FlashProperties +{ + u8 state; + u8 cmd; + u8 device; + u8 manufacturer; + u8 bank; +}; + u8* SRAM; u32 SRAMLength; +SaveType SRAMType; +FlashProperties SRAMFlash; char SRAMPath[1024]; +void (*WriteFunc)(u32 addr, u8 val); + + +void Write_Null(u32 addr, u8 val); +void Write_EEPROM(u32 addr, u8 val); +void Write_SRAM(u32 addr, u8 val); +void Write_Flash(u32 addr, u8 val); + bool Init() { @@ -47,6 +76,9 @@ void Reset() { if (SRAM) delete[] SRAM; SRAM = NULL; + SRAMLength = 0; + SRAMType = S_NULL; + SRAMFlash = {}; } void DoSavestate(Savestate* file) @@ -60,6 +92,7 @@ void LoadSave(const char* path) strncpy(SRAMPath, path, 1023); SRAMPath[1023] = '\0'; + SRAMLength = 0; FILE* f = Platform::OpenFile(path, "rb"); if (f) @@ -73,12 +106,48 @@ void LoadSave(const char* path) fclose(f); } - else + + switch (SRAMLength) { - int SRAMLength = 65536; // max GBA SRAM size + case 512: + SRAMType = S_EEPROM4K; + WriteFunc = Write_EEPROM; + break; + case 8192: + SRAMType = S_EEPROM64K; + WriteFunc = Write_EEPROM; + break; + case 32768: + SRAMType = S_SRAM256K; + WriteFunc = Write_SRAM; + break; + case 65536: + SRAMType = S_FLASH512K; + WriteFunc = Write_Flash; + break; + case 128*1024: + SRAMType = S_FLASH1M; + WriteFunc = Write_Flash; + break; + default: + printf("!! BAD SAVE LENGTH %d\n", SRAMLength); + case 0: + SRAMType = S_NULL; + WriteFunc = Write_Null; + break; + } - SRAM = new u8[SRAMLength]; - memset(SRAM, 0xFF, SRAMLength); + if (SRAMType == S_FLASH512K) + { + // Panasonic 64K chip + SRAMFlash.device = 0x1B; + SRAMFlash.manufacturer = 0x32; + } + else if (SRAMType == S_FLASH1M) + { + // Macronix 128K chip + SRAMFlash.device = 0x09; + SRAMFlash.manufacturer = 0xC2; } } @@ -104,21 +173,93 @@ void RelocateSave(const char* path, bool write) fclose(f); } +u8 Read_Flash(u32 addr) +{ + // TODO: pokemen + return 0xFF; +} + +void Write_Null(u32 addr, u8 val) {} + +void Write_EEPROM(u32 addr, u8 val) +{ + // TODO: could be used in homebrew? +} + +void Write_Flash(u32 addr, u8 val) +{ + // TODO: pokemen +} + +void Write_SRAM(u32 addr, u8 val) +{ + *(u8*)&SRAM[addr] = val; + + // bit wasteful to do this for every written byte + FILE* f = Platform::OpenFile(SRAMPath, "r+b"); + if (f) + { + fseek(f, addr, SEEK_SET); + fwrite((u8*)&SRAM[addr], 1, 1, f); + fclose(f); + } +} + +u8 Read8(u32 addr) +{ + if (SRAMType == S_NULL) + { + return 0xFF; + } + + if (SRAMType == S_FLASH512K || SRAMType == S_FLASH1M) + { + return Read_Flash(addr); + } + + return *(u8*)&SRAM[addr]; +} + +u16 Read16(u32 addr) +{ + if (SRAMType == S_NULL) + { + return 0xFFFF; + } + + if (SRAMType == S_FLASH512K || SRAMType == S_FLASH1M) + { + return Read_Flash(addr) & (Read_Flash(addr + 1) << 8); + } + + return *(u16*)&SRAM[addr]; +} + +u32 Read32(u32 addr) +{ + if (SRAMType == S_NULL) + { + return 0xFFFFFFFF; + } + + if (SRAMType == S_FLASH512K || SRAMType == S_FLASH1M) + { + return Read_Flash(addr) & + (Read_Flash(addr + 1) << 8) & + (Read_Flash(addr + 2) << 16) & + (Read_Flash(addr + 3) << 24); + } + + return *(u32*)&SRAM[addr]; +} + void Write8(u32 addr, u8 val) { u8 prev = *(u8*)&SRAM[addr]; if (prev != val) { - *(u8*)&SRAM[addr] = val;/* - - FILE* f = Platform::OpenFile(SRAMPath, "r+b"); - if (f) - { - fseek(f, addr, SEEK_SET); - fwrite((u8*)&SRAM[addr], 1, 1, f); - fclose(f); - }*/ + WriteFunc(addr, val); } } @@ -128,15 +269,8 @@ void Write16(u32 addr, u16 val) if (prev != val) { - *(u16*)&SRAM[addr] = val;/* - - FILE* f = Platform::OpenFile(SRAMPath, "r+b"); - if (f) - { - fseek(f, addr, SEEK_SET); - fwrite((u8*)&SRAM[addr], 2, 1, f); - fclose(f); - }*/ + WriteFunc(addr, val & 0xFF); + WriteFunc(addr + 1, val >> 8 & 0xFF); } } @@ -146,15 +280,10 @@ void Write32(u32 addr, u32 val) if (prev != val) { - *(u32*)&SRAM[addr] = val;/* - - FILE* f = Platform::OpenFile(SRAMPath, "r+b"); - if (f) - { - fseek(f, addr, SEEK_SET); - fwrite((u8*)&SRAM[addr], 3, 1, f); - fclose(f); - }*/ + WriteFunc(addr, val & 0xFF); + WriteFunc(addr + 1, val >> 8 & 0xFF); + WriteFunc(addr + 2, val >> 16 & 0xFF); + WriteFunc(addr + 3, val >> 24 & 0xFF); } } diff --git a/src/GBACart.h b/src/GBACart.h index e86ea43..81fb222 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -29,6 +29,10 @@ namespace GBACart_SRAM extern u8* SRAM; extern u32 SRAMLength; +u8 Read8(u32 addr); +u16 Read16(u32 addr); +u32 Read32(u32 addr); + void Write8(u32 addr, u8 val); void Write16(u32 addr, u16 val); void Write32(u32 addr, u32 val); diff --git a/src/NDS.cpp b/src/NDS.cpp index fe66814..a906fbb 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1639,7 +1639,7 @@ u8 ARM9Read8(u32 addr) if (ExMemCnt[0] & (1<<7)) return 0x00; // deselected CPU is 00h-filled if (GBACart::CartInserted) { - return *(u8*)&GBACart_SRAM::SRAM[addr & (GBACart_SRAM::SRAMLength-1)]; + return GBACart_SRAM::Read8(addr & (GBACart_SRAM::SRAMLength-1)); } return 0xFF; // TODO: proper open bus } @@ -1704,7 +1704,7 @@ u16 ARM9Read16(u32 addr) if (ExMemCnt[0] & (1<<7)) return 0x0000; // deselected CPU is 00h-filled if (GBACart::CartInserted) { - return *(u16*)&GBACart_SRAM::SRAM[addr & (GBACart_SRAM::SRAMLength-1)]; + return GBACart_SRAM::Read16(addr & (GBACart_SRAM::SRAMLength-1)); } return 0xFFFF; // TODO: proper open bus } @@ -1769,7 +1769,7 @@ u32 ARM9Read32(u32 addr) if (ExMemCnt[0] & (1<<7)) return 0x00000000; // deselected CPU is 00h-filled if (GBACart::CartInserted) { - return *(u32*)&GBACart_SRAM::SRAM[addr & (GBACart_SRAM::SRAMLength-1)]; + return GBACart_SRAM::Read32(addr & (GBACart_SRAM::SRAMLength-1)); } return 0xFFFFFFFF; // TODO: proper open bus } @@ -2000,7 +2000,7 @@ u8 ARM7Read8(u32 addr) if (!(ExMemCnt[0] & (1<<7))) return 0x00; // deselected CPU is 00h-filled if (GBACart::CartInserted) { - return *(u8*)&GBACart_SRAM::SRAM[addr & (GBACart_SRAM::SRAMLength-1)]; + return GBACart_SRAM::Read8(addr & (GBACart_SRAM::SRAMLength-1)); } return 0xFF; // TODO: proper open bus } @@ -2067,7 +2067,7 @@ u16 ARM7Read16(u32 addr) if (!(ExMemCnt[0] & (1<<7))) return 0x0000; // deselected CPU is 00h-filled if (GBACart::CartInserted) { - return *(u16*)&GBACart_SRAM::SRAM[addr & (GBACart_SRAM::SRAMLength-1)]; + return GBACart_SRAM::Read16(addr & (GBACart_SRAM::SRAMLength-1)); } return 0xFFFF; // TODO: proper open bus } @@ -2134,7 +2134,7 @@ u32 ARM7Read32(u32 addr) if (!(ExMemCnt[0] & (1<<7))) return 0x00000000; // deselected CPU is 00h-filled if (GBACart::CartInserted) { - return *(u32*)&GBACart_SRAM::SRAM[addr & (GBACart_SRAM::SRAMLength-1)]; + return GBACart_SRAM::Read32(addr & (GBACart_SRAM::SRAMLength-1)); } return 0xFFFFFFFF; // TODO: proper open bus } -- cgit v1.2.3 From 4e8b0c8ce451bc11de8b11b4da14a24242c8c34e Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Mon, 9 Dec 2019 06:09:30 -0500 Subject: Eject GBA cartridges on stop from the UI --- src/GBACart.cpp | 31 +++++++++++++++++++++++++++---- src/libui_sdl/main.cpp | 3 +++ 2 files changed, 30 insertions(+), 4 deletions(-) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 937958c..a0483b1 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -145,9 +145,9 @@ void LoadSave(const char* path) } else if (SRAMType == S_FLASH1M) { - // Macronix 128K chip - SRAMFlash.device = 0x09; - SRAMFlash.manufacturer = 0xC2; + // Sanyo 128K chip + SRAMFlash.device = 0x13; + SRAMFlash.manufacturer = 0x62; } } @@ -175,7 +175,30 @@ void RelocateSave(const char* path, bool write) u8 Read_Flash(u32 addr) { - // TODO: pokemen + if (SRAMFlash.cmd == 0) // no cmd + { + return *(u8*)&SRAM[addr + 0x10000 * SRAMFlash.bank]; + } + + // TODO properly keep track of command sequences, + // and deny unauthorized writes + switch (SRAMFlash.cmd) + { + case 0x90: // chip ID + if (addr == 0x0A000000) return SRAMFlash.manufacturer; + if (addr == 0x0A000001) return SRAMFlash.device; + break; + case 0xF0: // terminate command (TODO: break if non-Macronix chip and not at the end of an ID call?) + SRAMFlash.state = 0; + SRAMFlash.cmd = 0; + break; + case 0xB0: // bank switching (128K only) + break; // we don't track the request for now + default: + printf("GBACart_SRAM::Read_Flash: unknown command 0x%02X @ 0x%08X\n", SRAMFlash.cmd, addr); + break; + } + return 0xFF; } diff --git a/src/libui_sdl/main.cpp b/src/libui_sdl/main.cpp index 1e6069e..31b0488 100644 --- a/src/libui_sdl/main.cpp +++ b/src/libui_sdl/main.cpp @@ -1674,6 +1674,9 @@ void Stop(bool internal) while (EmuStatus != 2); RunningSomething = false; + // eject any inserted GBA cartridge + ROMPath[1][0] = '\0'; + uiWindowSetTitle(MainWindow, "melonDS " MELONDS_VERSION); for (int i = 0; i < 9; i++) uiMenuItemDisable(MenuItem_SaveStateSlot[i]); -- cgit v1.2.3 From 8fc9a33a9f22ecfb8974e57e009deb2670886946 Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Mon, 9 Dec 2019 06:10:26 -0500 Subject: Implement GBA cartridge Flash reading and writing --- src/GBACart.cpp | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 134 insertions(+), 7 deletions(-) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index a0483b1..473d994 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -180,22 +180,20 @@ u8 Read_Flash(u32 addr) return *(u8*)&SRAM[addr + 0x10000 * SRAMFlash.bank]; } - // TODO properly keep track of command sequences, - // and deny unauthorized writes switch (SRAMFlash.cmd) { case 0x90: // chip ID - if (addr == 0x0A000000) return SRAMFlash.manufacturer; - if (addr == 0x0A000001) return SRAMFlash.device; + if (addr == 0x0000) return SRAMFlash.manufacturer; + if (addr == 0x0001) return SRAMFlash.device; break; case 0xF0: // terminate command (TODO: break if non-Macronix chip and not at the end of an ID call?) SRAMFlash.state = 0; SRAMFlash.cmd = 0; break; case 0xB0: // bank switching (128K only) - break; // we don't track the request for now + break; // ignore here, handled during writes default: - printf("GBACart_SRAM::Read_Flash: unknown command 0x%02X @ 0x%08X\n", SRAMFlash.cmd, addr); + printf("GBACart_SRAM::Read_Flash: unknown command 0x%02X @ 0x%04X\n", SRAMFlash.cmd, addr); break; } @@ -211,7 +209,136 @@ void Write_EEPROM(u32 addr, u8 val) void Write_Flash(u32 addr, u8 val) { - // TODO: pokemen + switch (SRAMFlash.state) + { + case 0x00: + if (addr == 0x5555) + { + if (val == 0xF0) + { + // reset + SRAMFlash.state = 0; + SRAMFlash.cmd = 0; + return; + } + else if (val == 0xAA) + { + SRAMFlash.state = 1; + return; + } + } + if (addr == 0x0000) + { + if (SRAMFlash.cmd == 0xB0) + { + // bank switching + SRAMFlash.bank = val; + SRAMFlash.cmd = 0; + return; + } + } + break; + case 0x01: + if (addr == 0x2AAA && val == 0x55) + { + SRAMFlash.state = 2; + return; + } + SRAMFlash.state = 0; + break; + case 0x02: + if (addr == 0x5555) + { + // send command + switch (val) + { + case 0x80: // erase + SRAMFlash.state = 0x80; + break; + case 0x90: // chip ID + SRAMFlash.state = 0x90; + break; + case 0xA0: // write + SRAMFlash.state = 0; + break; + default: + SRAMFlash.state = 0; + break; + } + + SRAMFlash.cmd = val; + return; + } + break; + // erase + case 0x80: + if (addr == 0x5555 && val == 0xAA) + { + SRAMFlash.state = 0x81; + return; + } + SRAMFlash.state = 0; + break; + case 0x81: + if (addr == 0x2AAA && val == 0x55) + { + SRAMFlash.state = 0x82; + return; + } + SRAMFlash.state = 0; + break; + case 0x82: + if (val == 0x30) + { + u32 start_addr = addr + 0x10000 * SRAMFlash.bank; + memset((u8*)&SRAM[start_addr], 0xFF, 0x1000); + + FILE* f = Platform::OpenFile(SRAMPath, "r+b"); + if (f) + { + fseek(f, start_addr, SEEK_SET); + fwrite((u8*)&SRAM[start_addr], 1, 0x1000, f); + fclose(f); + } + } + SRAMFlash.state = 0; + SRAMFlash.cmd = 0; + return; + // chip ID + case 0x90: + if (addr == 0x5555 && val == 0xAA) + { + SRAMFlash.state = 0x91; + return; + } + SRAMFlash.state = 0; + break; + case 0x91: + if (addr == 0x2AAA && val == 0x55) + { + SRAMFlash.state = 0x92; + return; + } + SRAMFlash.state = 0; + break; + case 0x92: + SRAMFlash.state = 0; + SRAMFlash.cmd = 0; + return; + default: + break; + } + + if (SRAMFlash.cmd == 0xA0) // write + { + Write_SRAM(addr + 0x10000 * SRAMFlash.bank, val); + SRAMFlash.state = 0; + SRAMFlash.cmd = 0; + return; + } + + printf("GBACart_SRAM::Write_Flash: unknown write 0x%02X @ 0x%04X (state: 0x%02X)\n", + val, addr, SRAMFlash.state); } void Write_SRAM(u32 addr, u8 val) -- cgit v1.2.3 From 68d3474458b0f3623d69efb54cdbe101e5adadcd Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Mon, 9 Dec 2019 07:06:39 -0500 Subject: Fix GBA Flash read/writes and clean up --- src/GBACart.cpp | 79 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 38 deletions(-) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 473d994..2260e06 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -46,6 +46,7 @@ struct FlashProperties }; u8* SRAM; +FILE* SRAMFile; u32 SRAMLength; SaveType SRAMType; FlashProperties SRAMFlash; @@ -64,18 +65,23 @@ void Write_Flash(u32 addr, u8 val); bool Init() { SRAM = NULL; + SRAMFile = NULL; return true; } void DeInit() { + if (SRAMFile) fclose(SRAMFile); if (SRAM) delete[] SRAM; } void Reset() { + if (SRAMFile) fclose(SRAMFile); if (SRAM) delete[] SRAM; + SRAM = NULL; + SRAMFile = NULL; SRAMLength = 0; SRAMType = S_NULL; SRAMFlash = {}; @@ -94,7 +100,7 @@ void LoadSave(const char* path) SRAMPath[1023] = '\0'; SRAMLength = 0; - FILE* f = Platform::OpenFile(path, "rb"); + FILE* f = Platform::OpenFile(SRAMPath, "r+b"); if (f) { fseek(f, 0, SEEK_END); @@ -104,7 +110,7 @@ void LoadSave(const char* path) fseek(f, 0, SEEK_SET); fread(SRAM, SRAMLength, 1, f); - fclose(f); + SRAMFile = f; } switch (SRAMLength) @@ -162,15 +168,15 @@ void RelocateSave(const char* path, bool write) strncpy(SRAMPath, path, 1023); SRAMPath[1023] = '\0'; - FILE* f = Platform::OpenFile(path, "wb"); + FILE *f = Platform::OpenFile(path, "r+b"); if (!f) { printf("GBACart_SRAM::RelocateSave: failed to create new file. fuck\n"); return; } - fwrite(SRAM, SRAMLength, 1, f); - fclose(f); + SRAMFile = f; + fwrite(SRAM, SRAMLength, 1, SRAMFile); } u8 Read_Flash(u32 addr) @@ -190,6 +196,8 @@ u8 Read_Flash(u32 addr) SRAMFlash.state = 0; SRAMFlash.cmd = 0; break; + case 0xA0: // erase command + break; // ignore here, handled during writes case 0xB0: // bank switching (128K only) break; // ignore here, handled during writes default: @@ -269,6 +277,7 @@ void Write_Flash(u32 addr, u8 val) SRAMFlash.cmd = val; return; } + SRAMFlash.state = 0; break; // erase case 0x80: @@ -293,12 +302,10 @@ void Write_Flash(u32 addr, u8 val) u32 start_addr = addr + 0x10000 * SRAMFlash.bank; memset((u8*)&SRAM[start_addr], 0xFF, 0x1000); - FILE* f = Platform::OpenFile(SRAMPath, "r+b"); - if (f) + if (SRAMFile) { - fseek(f, start_addr, SEEK_SET); - fwrite((u8*)&SRAM[start_addr], 1, 0x1000, f); - fclose(f); + fseek(SRAMFile, start_addr, SEEK_SET); + fwrite((u8*)&SRAM[start_addr], 1, 0x1000, SRAMFile); } } SRAMFlash.state = 0; @@ -343,15 +350,18 @@ void Write_Flash(u32 addr, u8 val) void Write_SRAM(u32 addr, u8 val) { - *(u8*)&SRAM[addr] = val; + u8 prev = *(u8*)&SRAM[addr]; - // bit wasteful to do this for every written byte - FILE* f = Platform::OpenFile(SRAMPath, "r+b"); - if (f) + // TODO: try not to do this for every byte + if (prev != val) { - fseek(f, addr, SEEK_SET); - fwrite((u8*)&SRAM[addr], 1, 1, f); - fclose(f); + *(u8*)&SRAM[addr] = val; + + if (SRAMFile) + { + fseek(SRAMFile, addr, SEEK_SET); + fwrite((u8*)&SRAM[addr], 1, 1, SRAMFile); + } } } @@ -379,7 +389,9 @@ u16 Read16(u32 addr) if (SRAMType == S_FLASH512K || SRAMType == S_FLASH1M) { - return Read_Flash(addr) & (Read_Flash(addr + 1) << 8); + u16 val = Read_Flash(addr + 0) | + (Read_Flash(addr + 1) << 8); + return val; } return *(u16*)&SRAM[addr]; @@ -394,10 +406,11 @@ u32 Read32(u32 addr) if (SRAMType == S_FLASH512K || SRAMType == S_FLASH1M) { - return Read_Flash(addr) & - (Read_Flash(addr + 1) << 8) & - (Read_Flash(addr + 2) << 16) & + u32 val = Read_Flash(addr + 0) | + (Read_Flash(addr + 1) << 8) | + (Read_Flash(addr + 2) << 16) | (Read_Flash(addr + 3) << 24); + return val; } return *(u32*)&SRAM[addr]; @@ -407,34 +420,25 @@ void Write8(u32 addr, u8 val) { u8 prev = *(u8*)&SRAM[addr]; - if (prev != val) - { - WriteFunc(addr, val); - } + WriteFunc(addr, val); } void Write16(u32 addr, u16 val) { u16 prev = *(u16*)&SRAM[addr]; - if (prev != val) - { - WriteFunc(addr, val & 0xFF); - WriteFunc(addr + 1, val >> 8 & 0xFF); - } + WriteFunc(addr + 0, val & 0xFF); + WriteFunc(addr + 1, val >> 8 & 0xFF); } void Write32(u32 addr, u32 val) { u32 prev = *(u32*)&SRAM[addr]; - if (prev != val) - { - WriteFunc(addr, val & 0xFF); - WriteFunc(addr + 1, val >> 8 & 0xFF); - WriteFunc(addr + 2, val >> 16 & 0xFF); - WriteFunc(addr + 3, val >> 24 & 0xFF); - } + WriteFunc(addr + 0, val & 0xFF); + WriteFunc(addr + 1, val >> 8 & 0xFF); + WriteFunc(addr + 2, val >> 16 & 0xFF); + WriteFunc(addr + 3, val >> 24 & 0xFF); } } @@ -512,7 +516,6 @@ bool LoadROM(const char* path, const char* sram) fread(CartROM, 1, len, f); fclose(f); - //CartROM = f; CartCRC = CRC32(CartROM, CartROMSize); printf("ROM CRC32: %08X\n", CartCRC); -- cgit v1.2.3 From 4d1f3d419ee2859a243bc8fb1db37afaffee8a92 Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Tue, 10 Dec 2019 09:57:10 -0500 Subject: Clarify comments and credit DeSmuME for Flash I/O --- src/GBACart.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 2260e06..1011971 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -36,6 +36,7 @@ enum SaveType { S_FLASH1M }; +// from DeSmuME struct FlashProperties { u8 state; @@ -179,6 +180,7 @@ void RelocateSave(const char* path, bool write) fwrite(SRAM, SRAMLength, 1, SRAMFile); } +// mostly ported from DeSmuME u8 Read_Flash(u32 addr) { if (SRAMFlash.cmd == 0) // no cmd @@ -196,10 +198,10 @@ u8 Read_Flash(u32 addr) SRAMFlash.state = 0; SRAMFlash.cmd = 0; break; - case 0xA0: // erase command - break; // ignore here, handled during writes + case 0xA0: // write command + break; // ignore here, handled in Write_Flash() case 0xB0: // bank switching (128K only) - break; // ignore here, handled during writes + break; // ignore here, handled in Write_Flash() default: printf("GBACart_SRAM::Read_Flash: unknown command 0x%02X @ 0x%04X\n", SRAMFlash.cmd, addr); break; @@ -215,6 +217,7 @@ void Write_EEPROM(u32 addr, u8 val) // TODO: could be used in homebrew? } +// mostly ported from DeSmuME void Write_Flash(u32 addr, u8 val) { switch (SRAMFlash.state) @@ -352,7 +355,6 @@ void Write_SRAM(u32 addr, u8 val) { u8 prev = *(u8*)&SRAM[addr]; - // TODO: try not to do this for every byte if (prev != val) { *(u8*)&SRAM[addr] = val; -- cgit v1.2.3 From 00929371481ce0fa29adfe5a58e364f27713099a Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Tue, 10 Dec 2019 16:36:00 -0500 Subject: Rename SRAMFlash to SRAMFlashState --- src/GBACart.cpp | 95 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 47 insertions(+), 48 deletions(-) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 1011971..b544519 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -26,7 +26,6 @@ namespace GBACart_SRAM { - enum SaveType { S_NULL, S_EEPROM4K, @@ -50,7 +49,7 @@ u8* SRAM; FILE* SRAMFile; u32 SRAMLength; SaveType SRAMType; -FlashProperties SRAMFlash; +FlashProperties SRAMFlashState; char SRAMPath[1024]; @@ -85,7 +84,7 @@ void Reset() SRAMFile = NULL; SRAMLength = 0; SRAMType = S_NULL; - SRAMFlash = {}; + SRAMFlashState = {}; } void DoSavestate(Savestate* file) @@ -147,14 +146,14 @@ void LoadSave(const char* path) if (SRAMType == S_FLASH512K) { // Panasonic 64K chip - SRAMFlash.device = 0x1B; - SRAMFlash.manufacturer = 0x32; + SRAMFlashState.device = 0x1B; + SRAMFlashState.manufacturer = 0x32; } else if (SRAMType == S_FLASH1M) { // Sanyo 128K chip - SRAMFlash.device = 0x13; - SRAMFlash.manufacturer = 0x62; + SRAMFlashState.device = 0x13; + SRAMFlashState.manufacturer = 0x62; } } @@ -183,27 +182,27 @@ void RelocateSave(const char* path, bool write) // mostly ported from DeSmuME u8 Read_Flash(u32 addr) { - if (SRAMFlash.cmd == 0) // no cmd + if (SRAMFlashState.cmd == 0) // no cmd { - return *(u8*)&SRAM[addr + 0x10000 * SRAMFlash.bank]; + return *(u8*)&SRAM[addr + 0x10000 * SRAMFlashState.bank]; } - switch (SRAMFlash.cmd) + switch (SRAMFlashState.cmd) { case 0x90: // chip ID - if (addr == 0x0000) return SRAMFlash.manufacturer; - if (addr == 0x0001) return SRAMFlash.device; + if (addr == 0x0000) return SRAMFlashState.manufacturer; + if (addr == 0x0001) return SRAMFlashState.device; break; case 0xF0: // terminate command (TODO: break if non-Macronix chip and not at the end of an ID call?) - SRAMFlash.state = 0; - SRAMFlash.cmd = 0; + SRAMFlashState.state = 0; + SRAMFlashState.cmd = 0; break; case 0xA0: // write command break; // ignore here, handled in Write_Flash() case 0xB0: // bank switching (128K only) break; // ignore here, handled in Write_Flash() default: - printf("GBACart_SRAM::Read_Flash: unknown command 0x%02X @ 0x%04X\n", SRAMFlash.cmd, addr); + printf("GBACart_SRAM::Read_Flash: unknown command 0x%02X @ 0x%04X\n", SRAMFlashState.cmd, addr); break; } @@ -220,7 +219,7 @@ void Write_EEPROM(u32 addr, u8 val) // mostly ported from DeSmuME void Write_Flash(u32 addr, u8 val) { - switch (SRAMFlash.state) + switch (SRAMFlashState.state) { case 0x00: if (addr == 0x5555) @@ -228,23 +227,23 @@ void Write_Flash(u32 addr, u8 val) if (val == 0xF0) { // reset - SRAMFlash.state = 0; - SRAMFlash.cmd = 0; + SRAMFlashState.state = 0; + SRAMFlashState.cmd = 0; return; } else if (val == 0xAA) { - SRAMFlash.state = 1; + SRAMFlashState.state = 1; return; } } if (addr == 0x0000) { - if (SRAMFlash.cmd == 0xB0) + if (SRAMFlashState.cmd == 0xB0) { // bank switching - SRAMFlash.bank = val; - SRAMFlash.cmd = 0; + SRAMFlashState.bank = val; + SRAMFlashState.cmd = 0; return; } } @@ -252,10 +251,10 @@ void Write_Flash(u32 addr, u8 val) case 0x01: if (addr == 0x2AAA && val == 0x55) { - SRAMFlash.state = 2; + SRAMFlashState.state = 2; return; } - SRAMFlash.state = 0; + SRAMFlashState.state = 0; break; case 0x02: if (addr == 0x5555) @@ -264,45 +263,45 @@ void Write_Flash(u32 addr, u8 val) switch (val) { case 0x80: // erase - SRAMFlash.state = 0x80; + SRAMFlashState.state = 0x80; break; case 0x90: // chip ID - SRAMFlash.state = 0x90; + SRAMFlashState.state = 0x90; break; case 0xA0: // write - SRAMFlash.state = 0; + SRAMFlashState.state = 0; break; default: - SRAMFlash.state = 0; + SRAMFlashState.state = 0; break; } - SRAMFlash.cmd = val; + SRAMFlashState.cmd = val; return; } - SRAMFlash.state = 0; + SRAMFlashState.state = 0; break; // erase case 0x80: if (addr == 0x5555 && val == 0xAA) { - SRAMFlash.state = 0x81; + SRAMFlashState.state = 0x81; return; } - SRAMFlash.state = 0; + SRAMFlashState.state = 0; break; case 0x81: if (addr == 0x2AAA && val == 0x55) { - SRAMFlash.state = 0x82; + SRAMFlashState.state = 0x82; return; } - SRAMFlash.state = 0; + SRAMFlashState.state = 0; break; case 0x82: if (val == 0x30) { - u32 start_addr = addr + 0x10000 * SRAMFlash.bank; + u32 start_addr = addr + 0x10000 * SRAMFlashState.bank; memset((u8*)&SRAM[start_addr], 0xFF, 0x1000); if (SRAMFile) @@ -311,44 +310,44 @@ void Write_Flash(u32 addr, u8 val) fwrite((u8*)&SRAM[start_addr], 1, 0x1000, SRAMFile); } } - SRAMFlash.state = 0; - SRAMFlash.cmd = 0; + SRAMFlashState.state = 0; + SRAMFlashState.cmd = 0; return; // chip ID case 0x90: if (addr == 0x5555 && val == 0xAA) { - SRAMFlash.state = 0x91; + SRAMFlashState.state = 0x91; return; } - SRAMFlash.state = 0; + SRAMFlashState.state = 0; break; case 0x91: if (addr == 0x2AAA && val == 0x55) { - SRAMFlash.state = 0x92; + SRAMFlashState.state = 0x92; return; } - SRAMFlash.state = 0; + SRAMFlashState.state = 0; break; case 0x92: - SRAMFlash.state = 0; - SRAMFlash.cmd = 0; + SRAMFlashState.state = 0; + SRAMFlashState.cmd = 0; return; default: break; } - if (SRAMFlash.cmd == 0xA0) // write + if (SRAMFlashState.cmd == 0xA0) // write { - Write_SRAM(addr + 0x10000 * SRAMFlash.bank, val); - SRAMFlash.state = 0; - SRAMFlash.cmd = 0; + Write_SRAM(addr + 0x10000 * SRAMFlashState.bank, val); + SRAMFlashState.state = 0; + SRAMFlashState.cmd = 0; return; } printf("GBACart_SRAM::Write_Flash: unknown write 0x%02X @ 0x%04X (state: 0x%02X)\n", - val, addr, SRAMFlash.state); + val, addr, SRAMFlashState.state); } void Write_SRAM(u32 addr, u8 val) -- cgit v1.2.3 From 9128517b9086b9508dac375fb2764dcdc9ea3947 Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Tue, 10 Dec 2019 16:36:38 -0500 Subject: Add basic GBA GPIO support, solar sensor detection --- src/GBACart.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/GBACart.h | 2 ++ 2 files changed, 76 insertions(+), 3 deletions(-) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index b544519..75bc1dc 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -448,11 +448,34 @@ void Write32(u32 addr, u32 val) namespace GBACart { +const char SOLAR_SENSOR_GAMECODES[10][5] = +{ + "U3IJ", // Bokura no Taiyou - Taiyou Action RPG (Japan) + "U3IE", // Boktai - The Sun Is in Your Hand (USA) + "U3IP", // Boktai - The Sun Is in Your Hand (Europe) + "U32J", // Zoku Bokura no Taiyou - Taiyou Shounen Django (Japan) + "U32E", // Boktai 2 - Solar Boy Django (USA) + "U32P", // Boktai 2 - Solar Boy Django (Europe) + "U33J", // Shin Bokura no Taiyou - Gyakushuu no Sabata (Japan) + "A3IJ" // Boktai - The Sun Is in Your Hand (USA) (Sample) +}; + + +struct GPIO +{ + bool has_solar_sensor; + u16 data; + u16 direction; + u16 control; +}; + + bool CartInserted; u8* CartROM; u32 CartROMSize; u32 CartCRC; u32 CartID; +GPIO CartGPIO; // overridden GPIO parameters bool Init() @@ -477,6 +500,7 @@ void Reset() if (CartROM) delete[] CartROM; CartROM = NULL; CartROMSize = 0; + CartGPIO = {}; GBACart_SRAM::Reset(); } @@ -506,10 +530,20 @@ bool LoadROM(const char* path, const char* sram) while (CartROMSize < len) CartROMSize <<= 1; - u32 gamecode; + char gamecode[5] = { '\0' }; fseek(f, 0xAC, SEEK_SET); - fread(&gamecode, 4, 1, f); - printf("Game code: %c%c%c%c\n", gamecode&0xFF, (gamecode>>8)&0xFF, (gamecode>>16)&0xFF, gamecode>>24); + fread(&gamecode, 1, 4, f); + printf("Game code: %s\n", gamecode); + + for (int i = 0; i < sizeof(SOLAR_SENSOR_GAMECODES)/sizeof(SOLAR_SENSOR_GAMECODES[0]); i++) + { + if (strcmp(gamecode, SOLAR_SENSOR_GAMECODES[i]) == 0) CartGPIO.has_solar_sensor = true; + } + + if (CartGPIO.has_solar_sensor) + { + printf("GBA solar sensor support detected!\n"); + } CartROM = new u8[CartROMSize]; memset(CartROM, 0, CartROMSize); @@ -536,4 +570,41 @@ void RelocateSave(const char* path, bool write) GBACart_SRAM::RelocateSave(path, write); } +// referenced from mGBA +void WriteGPIO(u32 addr, u16 val) +{ + switch (addr) + { + case 0xC4: + CartGPIO.data &= ~CartGPIO.direction; + CartGPIO.data |= val & CartGPIO.direction; + // TODO: process pins + break; + case 0xC6: + CartGPIO.direction = val; + break; + case 0xC8: + CartGPIO.control = val; + break; + default: + printf("Unknown GBA GPIO write 0x%02X @ 0x%04X\n", val, addr); + } + + // write the GPIO values in the ROM (if writable) + if (CartGPIO.control & 1) + { + *(u16*)&CartROM[addr] = CartGPIO.data; + *(u16*)&CartROM[addr + 2] = CartGPIO.direction; + *(u16*)&CartROM[addr + 4] = CartGPIO.control; + } + else + { + // GBATEK: "in write-only mode, reads return 00h (or [possibly] other data (...))" + // ambiguous, but mGBA sets ROM to 00h when switching to write-only, so do the same + *(u16*)&CartROM[addr] = 0; + *(u16*)&CartROM[addr + 2] = 0; + *(u16*)&CartROM[addr + 4] = 0; + } +} + } diff --git a/src/GBACart.h b/src/GBACart.h index 81fb222..22a3a60 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -55,6 +55,8 @@ void DoSavestate(Savestate* file); bool LoadROM(const char* path, const char* sram); void RelocateSave(const char* path, bool write); +void WriteGPIO(u32 addr, u16 val); + } #endif // GBACART_H -- cgit v1.2.3 From f6cd66e5b1eb02f1664a4de083085fb81e6816b6 Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Tue, 10 Dec 2019 17:43:25 -0500 Subject: Implement solar sensor processing --- src/GBACart.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++-------------- src/GBACart.h | 19 ++++++++++++++++ 2 files changed, 70 insertions(+), 16 deletions(-) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 75bc1dc..5b81900 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -461,15 +461,6 @@ const char SOLAR_SENSOR_GAMECODES[10][5] = }; -struct GPIO -{ - bool has_solar_sensor; - u16 data; - u16 direction; - u16 control; -}; - - bool CartInserted; u8* CartROM; u32 CartROMSize; @@ -503,6 +494,7 @@ void Reset() CartGPIO = {}; GBACart_SRAM::Reset(); + GBACart_SolarSensor::Reset(); } void DoSavestate(Savestate* file) @@ -578,7 +570,7 @@ void WriteGPIO(u32 addr, u16 val) case 0xC4: CartGPIO.data &= ~CartGPIO.direction; CartGPIO.data |= val & CartGPIO.direction; - // TODO: process pins + if (CartGPIO.has_solar_sensor) GBACart_SolarSensor::Process(&CartGPIO); break; case 0xC6: CartGPIO.direction = val; @@ -593,17 +585,60 @@ void WriteGPIO(u32 addr, u16 val) // write the GPIO values in the ROM (if writable) if (CartGPIO.control & 1) { - *(u16*)&CartROM[addr] = CartGPIO.data; - *(u16*)&CartROM[addr + 2] = CartGPIO.direction; - *(u16*)&CartROM[addr + 4] = CartGPIO.control; + *(u16*)&CartROM[0xC4] = CartGPIO.data; + *(u16*)&CartROM[0xC6] = CartGPIO.direction; + *(u16*)&CartROM[0xC8] = CartGPIO.control; } else { // GBATEK: "in write-only mode, reads return 00h (or [possibly] other data (...))" // ambiguous, but mGBA sets ROM to 00h when switching to write-only, so do the same - *(u16*)&CartROM[addr] = 0; - *(u16*)&CartROM[addr + 2] = 0; - *(u16*)&CartROM[addr + 4] = 0; + *(u16*)&CartROM[0xC4] = 0; + *(u16*)&CartROM[0xC6] = 0; + *(u16*)&CartROM[0xC8] = 0; + } +} + +} + + +namespace GBACart_SolarSensor +{ + +bool LightEdge; +u8 LightCounter; +u8 LightSample; +u8 LightLevel; // 0-10 range + +// levels from mGBA +const int GBA_LUX_LEVELS[11] = { 0, 5, 11, 18, 27, 42, 62, 84, 109, 139, 183 }; +#define LIGHT_VALUE (0xFF - (0x16 + GBA_LUX_LEVELS[LightLevel])) + + +void Reset() +{ + LightEdge = false; + LightCounter = 0; + LightSample = 0xFF; + LightLevel = 0; +} + +void Process(GBACart::GPIO* gpio) +{ + if (gpio->data & 4) return; // Boktai chip select + if (gpio->data & 2) // Reset + { + LightCounter = 0; + LightSample = LIGHT_VALUE; + } + if (gpio->data & 1 && LightEdge) LightCounter++; + + LightEdge = !(gpio->data & 1); + + bool sendBit = LightCounter >= LightSample; + if (gpio->control & 1) + { + gpio->data = (gpio->data & gpio->direction) | ((sendBit << 3) & ~gpio->direction & 0xF); } } diff --git a/src/GBACart.h b/src/GBACart.h index 22a3a60..32a2171 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -43,6 +43,14 @@ void Write32(u32 addr, u32 val); namespace GBACart { +struct GPIO +{ + bool has_solar_sensor; + u16 data; + u16 direction; + u16 control; +}; + extern bool CartInserted; extern u8* CartROM; extern u32 CartROMSize; @@ -59,4 +67,15 @@ void WriteGPIO(u32 addr, u16 val); } + +namespace GBACart_SolarSensor +{ + +extern u8 LightLevel; + +void Reset(); +void Process(GBACart::GPIO* gpio); + +} + #endif // GBACART_H -- cgit v1.2.3 From ca9f183d24c028cbbbecbe07aefb37bdcd04a581 Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Tue, 10 Dec 2019 17:54:34 -0500 Subject: Hook up solar sensor control to the UI It uses hardcoded keypad left and right arrows. --- src/GBACart.cpp | 8 +++++--- src/GBACart.h | 2 +- src/libui_sdl/main.cpp | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 5b81900..a7ddf9b 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -462,6 +462,7 @@ const char SOLAR_SENSOR_GAMECODES[10][5] = bool CartInserted; +bool HasSolarSensor; u8* CartROM; u32 CartROMSize; u32 CartCRC; @@ -488,6 +489,7 @@ void DeInit() void Reset() { CartInserted = false; + HasSolarSensor = false; if (CartROM) delete[] CartROM; CartROM = NULL; CartROMSize = 0; @@ -529,10 +531,10 @@ bool LoadROM(const char* path, const char* sram) for (int i = 0; i < sizeof(SOLAR_SENSOR_GAMECODES)/sizeof(SOLAR_SENSOR_GAMECODES[0]); i++) { - if (strcmp(gamecode, SOLAR_SENSOR_GAMECODES[i]) == 0) CartGPIO.has_solar_sensor = true; + if (strcmp(gamecode, SOLAR_SENSOR_GAMECODES[i]) == 0) HasSolarSensor = true; } - if (CartGPIO.has_solar_sensor) + if (HasSolarSensor) { printf("GBA solar sensor support detected!\n"); } @@ -570,7 +572,7 @@ void WriteGPIO(u32 addr, u16 val) case 0xC4: CartGPIO.data &= ~CartGPIO.direction; CartGPIO.data |= val & CartGPIO.direction; - if (CartGPIO.has_solar_sensor) GBACart_SolarSensor::Process(&CartGPIO); + if (HasSolarSensor) GBACart_SolarSensor::Process(&CartGPIO); break; case 0xC6: CartGPIO.direction = val; diff --git a/src/GBACart.h b/src/GBACart.h index 32a2171..032c4fc 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -45,13 +45,13 @@ namespace GBACart struct GPIO { - bool has_solar_sensor; u16 data; u16 direction; u16 control; }; extern bool CartInserted; +extern bool HasSolarSensor; extern u8* CartROM; extern u32 CartROMSize; diff --git a/src/libui_sdl/main.cpp b/src/libui_sdl/main.cpp index 31b0488..1b7cfe6 100644 --- a/src/libui_sdl/main.cpp +++ b/src/libui_sdl/main.cpp @@ -38,6 +38,7 @@ #include "DlgWifiSettings.h" #include "../NDS.h" +#include "../GBACart.h" #include "../GPU.h" #include "../SPU.h" #include "../Wifi.h" @@ -1291,6 +1292,20 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt) { if (evt->Modifiers == 0x0) UndoStateLoad(); } + else if (evt->Scancode == 0x4B) // Keypad left + { + if (GBACart::CartInserted && GBACart::HasSolarSensor) + { + if (GBACart_SolarSensor::LightLevel > 0) GBACart_SolarSensor::LightLevel--; + } + } + else if (evt->Scancode == 0x4D) // Keypad right + { + if (GBACart::CartInserted && GBACart::HasSolarSensor) + { + if (GBACart_SolarSensor::LightLevel < 10) GBACart_SolarSensor::LightLevel++; + } + } for (int i = 0; i < 12; i++) if (EventMatchesKey(evt, Config::KeyMapping[i], false)) -- cgit v1.2.3 From f257b007a250e33ecc7a86c7447ccf049964dd8d Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Tue, 10 Dec 2019 18:44:53 -0500 Subject: Properly pass through GBA GPIO writes --- src/GBACart.cpp | 2 ++ src/NDS.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/libui_sdl/main.cpp | 2 ++ 3 files changed, 92 insertions(+) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index a7ddf9b..7e5e3b5 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -630,8 +630,10 @@ void Process(GBACart::GPIO* gpio) if (gpio->data & 4) return; // Boktai chip select if (gpio->data & 2) // Reset { + u8 prev = LightSample; LightCounter = 0; LightSample = LIGHT_VALUE; + printf("Solar sensor reset (sample: 0x%02X -> 0x%02X)\n", prev, LightSample); } if (gpio->data & 1 && LightEdge) LightCounter++; diff --git a/src/NDS.cpp b/src/NDS.cpp index a906fbb..47f96c9 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1803,6 +1803,19 @@ void ARM9Write8(u32 addr, u8 val) // checkme return; + case 0x08000000: + case 0x09000000: + if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write + if (GBACart::CartInserted) + { + if ((addr & 0x00FFFFFF) >= 0xC4 && (addr & 0x00FFFFFF) <= 0xC9) + { + GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val); + return; + } + } + break; + case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write if (GBACart::CartInserted) @@ -1854,6 +1867,21 @@ void ARM9Write16(u32 addr, u16 val) *(u16*)&GPU::OAM[addr & 0x7FF] = val; return; + case 0x08000000: + case 0x09000000: + if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write + if (GBACart::CartInserted) + { + // Note: the lower bound is adjusted such that a write starting + // there will hit the first byte of the GPIO region. + if ((addr & 0x00FFFFFF) >= 0xC3 && (addr & 0x00FFFFFF) <= 0xC9) + { + GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val); + return; + } + } + break; + case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write if (GBACart::CartInserted) @@ -1905,6 +1933,22 @@ void ARM9Write32(u32 addr, u32 val) *(u32*)&GPU::OAM[addr & 0x7FF] = val; return; + case 0x08000000: + case 0x09000000: + if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write + if (GBACart::CartInserted) + { + // Note: the lower bound is adjusted such that a write starting + // there will hit the first byte of the GPIO region. + if ((addr & 0x00FFFFFF) >= 0xC1 && (addr & 0x00FFFFFF) <= 0xC9) + { + GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val & 0xFF); + GBACart::WriteGPIO((addr + 2) & (GBACart::CartROMSize-1), (val >> 16) & 0xFF); + return; + } + } + break; + case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write if (GBACart::CartInserted) @@ -2177,6 +2221,19 @@ void ARM7Write8(u32 addr, u8 val) GPU::WriteVRAM_ARM7(addr, val); return; + case 0x08000000: + case 0x09000000: + if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write + if (GBACart::CartInserted) + { + if ((addr & 0x00FFFFFF) >= 0xC4 && (addr & 0x00FFFFFF) <= 0xC9) + { + GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val); + return; + } + } + break; + case 0x0A000000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write if (GBACart::CartInserted) @@ -2231,6 +2288,21 @@ void ARM7Write16(u32 addr, u16 val) GPU::WriteVRAM_ARM7(addr, val); return; + case 0x08000000: + case 0x09000000: + if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write + if (GBACart::CartInserted) + { + // Note: the lower bound is adjusted such that a write starting + // there will hit the first byte of the GPIO region. + if ((addr & 0x00FFFFFF) >= 0xC3 && (addr & 0x00FFFFFF) <= 0xC9) + { + GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val); + return; + } + } + break; + case 0x0A000000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write if (GBACart::CartInserted) @@ -2286,6 +2358,22 @@ void ARM7Write32(u32 addr, u32 val) GPU::WriteVRAM_ARM7(addr, val); return; + case 0x08000000: + case 0x09000000: + if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write + if (GBACart::CartInserted) + { + // Note: the lower bound is adjusted such that a write starting + // there will hit the first byte of the GPIO region. + if ((addr & 0x00FFFFFF) >= 0xC1 && (addr & 0x00FFFFFF) <= 0xC9) + { + GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val & 0xFF); + GBACart::WriteGPIO((addr + 2) & (GBACart::CartROMSize-1), (val >> 16) & 0xFF); + return; + } + } + break; + case 0x0A000000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write if (GBACart::CartInserted) diff --git a/src/libui_sdl/main.cpp b/src/libui_sdl/main.cpp index 1b7cfe6..d2a38f2 100644 --- a/src/libui_sdl/main.cpp +++ b/src/libui_sdl/main.cpp @@ -1297,6 +1297,7 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt) if (GBACart::CartInserted && GBACart::HasSolarSensor) { if (GBACart_SolarSensor::LightLevel > 0) GBACart_SolarSensor::LightLevel--; + printf("Solar sensor level set to %d\n", GBACart_SolarSensor::LightLevel); } } else if (evt->Scancode == 0x4D) // Keypad right @@ -1304,6 +1305,7 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt) if (GBACart::CartInserted && GBACart::HasSolarSensor) { if (GBACart_SolarSensor::LightLevel < 10) GBACart_SolarSensor::LightLevel++; + printf("Solar sensor level set to %d\n", GBACart_SolarSensor::LightLevel); } } -- cgit v1.2.3 From 8172cbc1706874651d227ce72d213fb636e506d4 Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Tue, 10 Dec 2019 20:28:40 -0500 Subject: Add DoSavestate() function to GBACart_SolarSensor Still empty, but should be implemented along with the rest. --- src/GBACart.cpp | 7 +++++++ src/GBACart.h | 4 ++++ 2 files changed, 11 insertions(+) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 7e5e3b5..1cca812 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -502,6 +502,8 @@ void Reset() void DoSavestate(Savestate* file) { // TODO? + GBACart_SRAM::DoSavestate(file); + GBACart_SolarSensor::DoSavestate(file); } bool LoadROM(const char* path, const char* sram) @@ -625,6 +627,11 @@ void Reset() LightLevel = 0; } +void DoSavestate(Savestate* file) +{ + // TODO? +} + void Process(GBACart::GPIO* gpio) { if (gpio->data & 4) return; // Boktai chip select diff --git a/src/GBACart.h b/src/GBACart.h index 032c4fc..fd26326 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -29,6 +29,9 @@ namespace GBACart_SRAM extern u8* SRAM; extern u32 SRAMLength; +void Reset(); +void DoSavestate(Savestate* file); + u8 Read8(u32 addr); u16 Read16(u32 addr); u32 Read32(u32 addr); @@ -74,6 +77,7 @@ namespace GBACart_SolarSensor extern u8 LightLevel; void Reset(); +void DoSavestate(Savestate* file); void Process(GBACart::GPIO* gpio); } -- cgit v1.2.3 From 2abdcc54dd159a89a2545e8c6c34e1f0687a64f4 Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Thu, 19 Dec 2019 00:12:36 -0500 Subject: Implement DoSavestate() for GBACart classes Admittedly untested. --- src/GBACart.cpp | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 4 deletions(-) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 1cca812..1829ca7 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -89,7 +89,34 @@ void Reset() void DoSavestate(Savestate* file) { - // TODO? + file->Section("GBCS"); // Game Boy [Advance] Cart Save + + // logic mostly copied from NDSCart_SRAM + + u32 oldlen = SRAMLength; + + file->Var32(&SRAMLength); + if (SRAMLength != oldlen) + { + printf("savestate (GBA): VERY BAD!!!! SRAM LENGTH DIFFERENT. %d -> %d\n", oldlen, SRAMLength); + printf("oh well. loading it anyway. iojkjkojo\n"); + + if (oldlen) delete[] SRAM; + if (SRAMLength) SRAM = new u8[SRAMLength]; + } + if (SRAMLength) + { + file->VarArray(SRAM, SRAMLength); + } + + // persist some extra state info + file->Var8(&SRAMFlashState.bank); + file->Var8(&SRAMFlashState.cmd); + file->Var8(&SRAMFlashState.device); + file->Var8(&SRAMFlashState.manufacturer); + file->Var8(&SRAMFlashState.state); + + file->Var8((u8*)&SRAMType); } void LoadSave(const char* path) @@ -501,9 +528,43 @@ void Reset() void DoSavestate(Savestate* file) { - // TODO? + file->Section("GBAC"); // Game Boy Advance Cartridge + + // logic mostly copied from NDSCart + + // first we need to reload the cart itself, + // since unlike with DS, it's not loaded in advance + + u32 oldlen = CartROMSize; + + file->Var32(&CartROMSize); + if (!CartROMSize) return; // no GBA cartridge state? nothing to do here. + + if (CartROMSize != oldlen) // loading a differently-sized cartridge + { + if (oldlen) delete[] CartROM; + CartROM = new u8[CartROMSize]; + } + + // why yes, let's save the whole GBA cart in the state + // TODO: let's maybe not? + + file->VarArray(CartROM, CartROMSize); + + CartInserted = true; // known, because CartROMSize > 0 + file->Var32(&CartCRC); + file->Var32(&CartID); + + file->Var8((u8*)&HasSolarSensor); + + file->Var16(&CartGPIO.control); + file->Var16(&CartGPIO.data); + file->Var16(&CartGPIO.direction); + + // now do the rest + GBACart_SRAM::DoSavestate(file); - GBACart_SolarSensor::DoSavestate(file); + if (HasSolarSensor) GBACart_SolarSensor::DoSavestate(file); } bool LoadROM(const char* path, const char* sram) @@ -629,7 +690,10 @@ void Reset() void DoSavestate(Savestate* file) { - // TODO? + file->Var8((u8*)&LightEdge); + file->Var8(&LightCounter); + file->Var8(&LightSample); + file->Var8(&LightLevel); } void Process(GBACart::GPIO* gpio) -- cgit v1.2.3 From a57ba1368e151484908052d696aa90f28928cfe2 Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Sun, 22 Dec 2019 11:45:11 -0500 Subject: Skip saving/loading GBA flash state if SRAM is null --- src/GBACart.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 1829ca7..f6bed86 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -96,18 +96,24 @@ void DoSavestate(Savestate* file) u32 oldlen = SRAMLength; file->Var32(&SRAMLength); + if (SRAMLength != oldlen) { - printf("savestate (GBA): VERY BAD!!!! SRAM LENGTH DIFFERENT. %d -> %d\n", oldlen, SRAMLength); - printf("oh well. loading it anyway. iojkjkojo\n"); - + // reallocate save memory if (oldlen) delete[] SRAM; if (SRAMLength) SRAM = new u8[SRAMLength]; } if (SRAMLength) { + // fill save memory if data is present file->VarArray(SRAM, SRAMLength); } + else + { + // no save data, nothing left to do + SRAMType = SaveType::S_NULL; + return; + } // persist some extra state info file->Var8(&SRAMFlashState.bank); -- cgit v1.2.3 From f380767fab219df087488a4e4e8a5eab47efb94c Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Sun, 22 Dec 2019 13:58:27 -0500 Subject: Only store the GBA ROM header in save states Also fix some potential crashes due to SRAM state not being cleared correctly on state load. --- src/GBACart.cpp | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index f6bed86..7753482 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -110,8 +110,11 @@ void DoSavestate(Savestate* file) } else { - // no save data, nothing left to do + // no save data, clear the current state SRAMType = SaveType::S_NULL; + if (SRAMFile) fclose(SRAMFile); + SRAM = NULL; + SRAMFile = NULL; return; } @@ -541,21 +544,40 @@ void DoSavestate(Savestate* file) // first we need to reload the cart itself, // since unlike with DS, it's not loaded in advance - u32 oldlen = CartROMSize; - file->Var32(&CartROMSize); if (!CartROMSize) return; // no GBA cartridge state? nothing to do here. - if (CartROMSize != oldlen) // loading a differently-sized cartridge + u32 oldCRC = CartCRC; + file->Var32(&CartCRC); + if (CartCRC != oldCRC) { - if (oldlen) delete[] CartROM; + // delete and reallocate ROM so that it is zero-padded to its full length + if (CartROM) delete[] CartROM; CartROM = new u8[CartROMSize]; - } - // why yes, let's save the whole GBA cart in the state - // TODO: let's maybe not? + // clear the SRAM file handle; writes will not be committed + if (GBACart_SRAM::SRAMFile) + { + fclose(GBACart_SRAM::SRAMFile); + GBACart_SRAM::SRAMFile = NULL; + } + } - file->VarArray(CartROM, CartROMSize); + // only save/load the cartridge header + // + // GBA connectivity on DS mainly involves identifying the title currently + // inserted, reading save data, and issuing commands intercepted here + // (e.g. solar sensor signals). we don't know of any case where GBA ROM is + // read directly from DS software. therefore, it is more practical, both + // from the development and user experience perspectives, to avoid dealing + // with file dependencies, and store a small portion of ROM data that should + // satisfy the needs of all known software that reads from the GBA slot. + // + // note: in case of a state load, only the cartridge header is restored, but + // the rest of the ROM data is only cleared (zero-initialized) if the CRC + // differs. Therefore, loading the GBA cartridge associated with the save state + // in advance will maintain access to the full ROM contents. + file->VarArray(CartROM, 192); CartInserted = true; // known, because CartROMSize > 0 file->Var32(&CartCRC); -- cgit v1.2.3 From 22d11209b0466e3c852da543ddfc512b66735bc2 Mon Sep 17 00:00:00 2001 From: Raphaël Zumer Date: Sun, 22 Dec 2019 15:49:23 -0500 Subject: Split GBA Reset and Eject logic into two sets This allows solving some crashes and provides more flexibility in how GBA cartridges change state between soft and hard resets. Since save states including GBA data do not carry over the original save file path, and the GBA cartridge is being reset along with the other parts of the system, this is needed to avoid losing the GBA state on reset following a state load, while preserving the behavior where cartridges are ejected when calling Stop(). --- src/GBACart.cpp | 35 ++++++++++++++++++++++++++++++----- src/GBACart.h | 2 ++ src/libui_sdl/main.cpp | 22 ++++++++++++++++++++-- 3 files changed, 52 insertions(+), 7 deletions(-) (limited to 'src/GBACart.cpp') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 7753482..4d44d3a 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -76,10 +76,14 @@ void DeInit() } void Reset() +{ + // do nothing, we don't want to clear GBA SRAM on reset +} + +void Eject() { if (SRAMFile) fclose(SRAMFile); if (SRAM) delete[] SRAM; - SRAM = NULL; SRAMFile = NULL; SRAMLength = 0; @@ -524,15 +528,30 @@ void DeInit() void Reset() { + // Do not reset cartridge ROM. + // Prefer keeping the inserted cartridge on reset. + // This allows resetting a DS game without losing GBA state, + // and resetting to firmware without the slot being emptied. + // The Stop function will clear the cartridge state via Eject(). + + GBACart_SRAM::Reset(); + GBACart_SolarSensor::Reset(); +} + +void Eject() +{ + if (CartROM) delete[] CartROM; + CartInserted = false; HasSolarSensor = false; - if (CartROM) delete[] CartROM; CartROM = NULL; CartROMSize = 0; + CartCRC = NULL; + CartID = NULL; CartGPIO = {}; - GBACart_SRAM::Reset(); - GBACart_SolarSensor::Reset(); + GBACart_SRAM::Eject(); + Reset(); } void DoSavestate(Savestate* file) @@ -545,10 +564,16 @@ void DoSavestate(Savestate* file) // since unlike with DS, it's not loaded in advance file->Var32(&CartROMSize); - if (!CartROMSize) return; // no GBA cartridge state? nothing to do here. + if (!CartROMSize) // no GBA cartridge state? nothing to do here + { + // do eject the cartridge if something is inserted + Eject(); + return; + } u32 oldCRC = CartCRC; file->Var32(&CartCRC); + if (CartCRC != oldCRC) { // delete and reallocate ROM so that it is zero-padded to its full length diff --git a/src/GBACart.h b/src/GBACart.h index fd26326..96a05b8 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -57,10 +57,12 @@ extern bool CartInserted; extern bool HasSolarSensor; extern u8* CartROM; extern u32 CartROMSize; +extern u32 CartCRC; bool Init(); void DeInit(); void Reset(); +void Eject(); void DoSavestate(Savestate* file); bool LoadROM(const char* path, const char* sram); diff --git a/src/libui_sdl/main.cpp b/src/libui_sdl/main.cpp index 38804f6..af69f8e 100644 --- a/src/libui_sdl/main.cpp +++ b/src/libui_sdl/main.cpp @@ -1694,6 +1694,7 @@ void Stop(bool internal) RunningSomething = false; // eject any inserted GBA cartridge + GBACart::Eject(); ROMPath[1][0] = '\0'; uiWindowSetTitle(MainWindow, "melonDS " MELONDS_VERSION); @@ -1834,6 +1835,8 @@ void LoadState(int slot) return; } + u32 oldGBACartCRC = GBACart::CartCRC; + // backup Savestate* backup = new Savestate("timewarp.mln", true); NDS::DoSavestate(backup); @@ -1870,9 +1873,24 @@ void LoadState(int slot) NDS::RelocateSave(SRAMPath[0], false); } + bool loadedPartialGBAROM = false; + + // in case we have a GBA cart inserted, and the GBA ROM changes + // due to having loaded a save state, we do not want to reload + // the previous cartridge on reset, or commit writes to any + // loaded save file. therefore, their paths are "nulled". + if (GBACart::CartInserted && GBACart::CartCRC != oldGBACartCRC) + { + ROMPath[1][0] = '\0'; + SRAMPath[1][0] = '\0'; + loadedPartialGBAROM = true; + } + char msg[64]; - if (slot > 0) sprintf(msg, "State loaded from slot %d", slot); - else sprintf(msg, "State loaded from file"); + if (slot > 0) sprintf(msg, "State loaded from slot %d%s", + slot, loadedPartialGBAROM ? " (GBA ROM header only)" : ""); + else sprintf(msg, "State loaded from file%s", + loadedPartialGBAROM ? " (GBA ROM header only)" : ""); OSD::AddMessage(0, msg); SavestateLoaded = true; -- cgit v1.2.3