From b17e38ba8135408742b4f72b4939744df9e3a527 Mon Sep 17 00:00:00 2001 From: shinyquagsire23 Date: Fri, 18 Jul 2025 15:36:17 -0600 Subject: [PATCH] Add support for NDS devkit (8MB) and 3DS (32MB) MainRAM configurations --- src/ARMJIT_Memory.cpp | 11 ++ src/DSi.cpp | 123 +++++++++++++++++++++- src/MemConstants.h | 3 +- src/NDS.cpp | 9 +- src/NDS.h | 8 +- src/frontend/qt_sdl/Config.cpp | 1 + src/frontend/qt_sdl/EmuInstance.cpp | 5 +- src/frontend/qt_sdl/EmuInstance.h | 2 + src/frontend/qt_sdl/EmuSettingsDialog.cpp | 7 +- src/frontend/qt_sdl/RAMInfoDialog.cpp | 5 +- 10 files changed, 156 insertions(+), 18 deletions(-) diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index 748b6710..e0627a0d 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -1038,6 +1038,12 @@ bool ARMJIT_Memory::GetMirrorLocation(int region, u32 num, u32 addr, u32& memory } return false; case memregion_MainRAM: + // 0x02xxxxxx region is limited to 16MiB + if (addr < 0x0C000000) { + mirrorStart = addr & ~(NDS.MainRAMMask & 0xFFFFFF); + mirrorSize = (NDS.MainRAMMask & 0xFFFFFF) + 1; + return true; + } mirrorStart = addr & ~NDS.MainRAMMask; mirrorSize = NDS.MainRAMMask + 1; return true; @@ -1283,6 +1289,8 @@ int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept return memregion_VRAM; case 0x0C000000: return (NDS.ConsoleType==1) ? memregion_MainRAM : memregion_Other; + case 0x0D000000: + return (NDS.ConsoleType==1 && NDS.MainRAMMask>=0x1FFFFFF) ? memregion_MainRAM : memregion_Other; default: return memregion_Other; } @@ -1341,6 +1349,9 @@ int ARMJIT_Memory::ClassifyAddress7(u32 addr) const noexcept case 0x0C000000: case 0x0C800000: return (NDS.ConsoleType==1) ? memregion_MainRAM : memregion_Other; + case 0x0D000000: + case 0x0D800000: + return (NDS.ConsoleType==1 && NDS.MainRAMMask>=0x1FFFFFF) ? memregion_MainRAM : memregion_Other; default: return memregion_Other; } diff --git a/src/DSi.cpp b/src/DSi.cpp index c9f73c21..fc72650a 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -89,7 +89,7 @@ const u32 NDMAModes[] = }*/ DSi::DSi(DSiArgs&& args, void* userdata) noexcept : - NDS(std::move(args), 1, userdata), + NDS(std::move(args), 1, 0, userdata), NDMAs { DSi_NDMA(0, 0, *this), DSi_NDMA(0, 1, *this), @@ -1278,15 +1278,18 @@ void DSi::ApplyNewRAMSize(u32 size) switch (size) { case 0: - case 1: + case 1: // TODO(shinyquagsire23): Is 1 actually 8MiB (NDS devkit)? MainRAMMask = 0x3FFFFF; Log(LogLevel::Debug, "RAM: 4MB\n"); break; - case 2: - case 3: // TODO: debug console w/ 32MB? + case 2: // Retail 16MiB MainRAMMask = 0xFFFFFF; Log(LogLevel::Debug, "RAM: 16MB\n"); break; + case 3: // Debug/3DS 32MiB + MainRAMMask = 0x1FFFFFF; + Log(LogLevel::Debug, "RAM: 32MB\n"); + break; } } @@ -1367,6 +1370,12 @@ u8 DSi::ARM9Read8(u32 addr) case 0x0C000000: return *(u8*)&MainRAM[addr & MainRAMMask]; + case 0x0D000000: + // open-bus behavior + if (MainRAMPresent<=0x1000000 && MainRAMMask>=0x1FFFFFF) { + break; + } + return *(u8*)&MainRAM[addr & MainRAMMask]; } return NDS::ARM9Read8(addr); @@ -1418,6 +1427,12 @@ u16 DSi::ARM9Read16(u32 addr) case 0x0C000000: return *(u16*)&MainRAM[addr & MainRAMMask]; + case 0x0D000000: + // open-bus behavior + if (MainRAMPresent<=0x1000000 && MainRAMMask>=0x1FFFFFF) { + break; + } + return *(u16*)&MainRAM[addr & MainRAMMask]; } return NDS::ARM9Read16(addr); @@ -1474,6 +1489,12 @@ u32 DSi::ARM9Read32(u32 addr) case 0x0C000000: return *(u32*)&MainRAM[addr & MainRAMMask]; + case 0x0D000000: + // open-bus behavior + if (MainRAMPresent<=0x1000000 && MainRAMMask>=0x1FFFFFF) { + break; + } + return *(u32*)&MainRAM[addr & MainRAMMask]; } return NDS::ARM9Read32(addr); @@ -1569,6 +1590,14 @@ void DSi::ARM9Write8(u32 addr, u8 val) JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); *(u8*)&MainRAM[addr & MainRAMMask] = val; return; + case 0x0D000000: + // open-bus behavior + if (MainRAMPresent<=0x1000000 && MainRAMMask>=0x1FFFFFF) { + break; + } + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); + *(u8*)&MainRAM[addr & MainRAMMask] = val; + return; } return NDS::ARM9Write8(addr, val); @@ -1654,6 +1683,14 @@ void DSi::ARM9Write16(u32 addr, u16 val) JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); *(u16*)&MainRAM[addr & MainRAMMask] = val; return; + case 0x0D000000: + // open-bus behavior + if (MainRAMPresent<=0x1000000 && MainRAMMask>=0x1FFFFFF) { + break; + } + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); + *(u16*)&MainRAM[addr & MainRAMMask] = val; + return; } return NDS::ARM9Write16(addr, val); @@ -1739,6 +1776,15 @@ void DSi::ARM9Write32(u32 addr, u32 val) JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); *(u32*)&MainRAM[addr & MainRAMMask] = val; return; + + case 0x0D000000: + // open-bus behavior + if (MainRAMPresent<=0x1000000 && MainRAMMask>=0x1FFFFFF) { + break; + } + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); + *(u32*)&MainRAM[addr & MainRAMMask] = val; + return; } return NDS::ARM9Write32(addr, val); @@ -1754,6 +1800,16 @@ bool DSi::ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) region->Mem = MainRAM; region->Mask = MainRAMMask; return true; + + case 0x0D000000: + // open-bus behavior + if (MainRAMPresent<=0x1000000 && MainRAMMask>=0x1FFFFFF) { + region->Mem = NULL; + return false; + } + region->Mem = MainRAM; + region->Mask = MainRAMMask; + return true; } if ((addr & 0xFFFF0000) == 0xFFFF0000 && !write) @@ -1835,6 +1891,13 @@ u8 DSi::ARM7Read8(u32 addr) case 0x0C000000: case 0x0C800000: return *(u8*)&MainRAM[addr & MainRAMMask]; + case 0x0D000000: + case 0x0D800000: + // open-bus behavior + if (MainRAMPresent<=0x1000000 && MainRAMMask>=0x1FFFFFF) { + break; + } + return *(u8*)&MainRAM[addr & MainRAMMask]; } return NDS::ARM7Read8(addr); @@ -1894,7 +1957,14 @@ u16 DSi::ARM7Read16(u32 addr) case 0x0C000000: case 0x0C800000: - return *(u16*)&MainRAM[addr & MainRAMMask]; + return *(u16*)&MainRAM[addr & NDS::MainRAMMask]; + case 0x0D000000: + case 0x0D800000: + // open-bus behavior + if (NDS::MainRAMPresent<=0x1000000 && NDS::MainRAMMask>=0x1FFFFFF) { + break; + } + return *(u16*)&MainRAM[addr & NDS::MainRAMMask]; } return NDS::ARM7Read16(addr); @@ -1954,6 +2024,13 @@ u32 DSi::ARM7Read32(u32 addr) case 0x0C000000: case 0x0C800000: return *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask]; + case 0x0D000000: + case 0x0D800000: + // open-bus behavior + if (NDS::MainRAMPresent<=0x1000000 && NDS::MainRAMMask>=0x1FFFFFF) { + break; + } + return *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask]; } return NDS::ARM7Read32(addr); @@ -2042,6 +2119,15 @@ void DSi::ARM7Write8(u32 addr, u8 val) JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; return; + case 0x0D000000: + case 0x0D800000: + // open-bus behavior + if (NDS::MainRAMPresent<=0x1000000 && NDS::MainRAMMask>=0x1FFFFFF) { + break; + } + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); + *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; + return; } return NDS::ARM7Write8(addr, val); @@ -2132,6 +2218,15 @@ void DSi::ARM7Write16(u32 addr, u16 val) JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; return; + case 0x0D000000: + case 0x0D800000: + // open-bus behavior + if (NDS::MainRAMPresent<=0x1000000 && NDS::MainRAMMask>=0x1FFFFFF) { + break; + } + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); + *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; + return; } return NDS::ARM7Write16(addr, val); @@ -2222,6 +2317,15 @@ void DSi::ARM7Write32(u32 addr, u32 val) JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; return; + case 0x0D000000: + case 0x0D800000: + // open-bus behavior + if (NDS::MainRAMPresent<=0x1000000 && NDS::MainRAMMask>=0x1FFFFFF) { + break; + } + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); + *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; + return; } return NDS::ARM7Write32(addr, val); @@ -2239,6 +2343,15 @@ bool DSi::ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) region->Mem = NDS::MainRAM; region->Mask = NDS::MainRAMMask; return true; + case 0x0D000000: + case 0x0D800000: + // open-bus behavior + if (NDS::MainRAMPresent<=0x1000000 && NDS::MainRAMMask>=0x1FFFFFF) { + break; + } + region->Mem = NDS::MainRAM; + region->Mask = NDS::MainRAMMask; + return true; } // BIOS. ARM7 PC has to be within range. diff --git a/src/MemConstants.h b/src/MemConstants.h index 5750e075..1f56affd 100644 --- a/src/MemConstants.h +++ b/src/MemConstants.h @@ -23,7 +23,8 @@ namespace melonDS { -constexpr u32 MainRAMMaxSize = 0x1000000; +constexpr u32 MainRAMPrimaryMappingMaxSize = 0x1000000; +constexpr u32 MainRAMMaxSize = 0x2000000; constexpr u32 SharedWRAMSize = 0x8000; constexpr u32 ARM7WRAMSize = 0x10000; constexpr u32 NWRAMSize = 0x40000; diff --git a/src/NDS.cpp b/src/NDS.cpp index 340cd020..378535be 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -87,8 +87,9 @@ NDS::NDS() noexcept : { } -NDS::NDS(NDSArgs&& args, int type, void* userdata) noexcept : +NDS::NDS(NDSArgs&& args, int type, int debugbc, void* userdata) noexcept : ConsoleType(type), + DebugBoardConfig(debugbc), UserData(userdata), ARM7BIOS(*args.ARM7BIOS), ARM9BIOS(*args.ARM9BIOS), @@ -446,12 +447,14 @@ void NDS::Reset() // BIOS files are now loaded by the frontend ARM9ClockShift = 2; - MainRAMMask = 0xFFFFFF; + MainRAMMask = DebugBoardConfig ? 0x1FFFFFF : 0x1FFFFFF; // Intentional default + MainRAMPresent = DebugBoardConfig ? 0x2000000 : 0x1000000; // 32MiB and 16MiB } else { ARM9ClockShift = 1; - MainRAMMask = 0x3FFFFF; + MainRAMMask = DebugBoardConfig ? 0x7FFFFF : 0x3FFFFF; + MainRAMPresent = DebugBoardConfig ? 0x800000 : 0x400000; // 8MiB and 4MiB } // has to be called before InitTimings // otherwise some PU settings are completely diff --git a/src/NDS.h b/src/NDS.h index e7340c76..f92b549e 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -249,6 +249,7 @@ public: // TODO: Encapsulate the rest of these members void* UserData; int ConsoleType; + int DebugBoardConfig; int CurCPU; SchedEvent SchedList[Event_MAX] {}; @@ -293,8 +294,7 @@ public: // TODO: Encapsulate the rest of these members u8* MainRAM; u32 MainRAMMask; - - const u32 MainRAMMaxSize = 0x1000000; + u32 MainRAMPresent; const u32 SharedWRAMSize = 0x8000; u8* SharedWRAM; @@ -547,7 +547,7 @@ private: u32 RunFrame(); public: - NDS(NDSArgs&& args, void* userdata = nullptr) noexcept : NDS(std::move(args), 0, userdata) {} + NDS(NDSArgs&& args, void* userdata = nullptr) noexcept : NDS(std::move(args), 0, 0, userdata) {} NDS() noexcept; virtual ~NDS() noexcept; NDS(const NDS&) = delete; @@ -557,7 +557,7 @@ public: static thread_local NDS* Current; protected: - explicit NDS(NDSArgs&& args, int type, void* userdata) noexcept; + explicit NDS(NDSArgs&& args, int type, int debugbc, void* userdata) noexcept; virtual void DoSavestateExtra(Savestate* file) {} }; diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 86516a91..4f90c5e6 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -77,6 +77,7 @@ DefaultList DefaultInts = RangeList IntRanges = { {"Emu.ConsoleType", {0, 1}}, + {"Emu.DebugBoardConfig", {0, 1}}, {"3D.Renderer", {0, renderer3D_Max-1}}, {"Screen.VSyncInterval", {1, 20}}, {"3D.GL.ScaleFactor", {1, 16}}, diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 13e026d6..29b24d4c 100755 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -70,6 +70,7 @@ EmuInstance::EmuInstance(int inst) : deleting(false), localCfg(Config::GetLocalTable(inst)) { consoleType = globalCfg.GetInt("Emu.ConsoleType"); + debugBoardConfig = globalCfg.GetInt("Emu.DebugBoardConfig"); ndsSave = nullptr; cartType = -1; @@ -1240,6 +1241,7 @@ bool EmuInstance::updateConsole() noexcept { // update the console type consoleType = globalCfg.GetInt("Emu.ConsoleType"); + debugBoardConfig = globalCfg.GetInt("Emu.DebugBoardConfig"); // Let's get the cart we want to use; // if we want to keep the cart, we'll eject it from the existing console first. @@ -1353,7 +1355,7 @@ bool EmuInstance::updateConsole() noexcept } renderLock.lock(); - if ((!nds) || (consoleType != nds->ConsoleType)) + if ((!nds) || (consoleType != nds->ConsoleType) || (debugBoardConfig != nds->DebugBoardConfig)) { if (nds) { @@ -1366,6 +1368,7 @@ bool EmuInstance::updateConsole() noexcept else nds = new NDS(std::move(ndsargs), this); + nds->DebugBoardConfig = debugBoardConfig; nds->Reset(); loadRTCData(); //emuThread->updateVideoRenderer(); // not actually needed? diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 295e9bf6..0410b6a2 100755 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -88,6 +88,7 @@ public: int getInstanceID() { return instanceID; } int getConsoleType() { return consoleType; } + int getDebugBoardConfig() { return debugBoardConfig; } EmuThread* getEmuThread() { return emuThread; } melonDS::NDS* getNDS() { return nds; } @@ -264,6 +265,7 @@ private: Config::Table localCfg; int consoleType; + int debugBoardConfig; melonDS::NDS* nds; int cartType; diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp index e8c5cc90..aac1f6c6 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp +++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp @@ -72,8 +72,10 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new ui->txtDSiNANDPath->setText(cfg.GetQString("DSi.NANDPath")); ui->cbxConsoleType->addItem("DS"); + ui->cbxConsoleType->addItem("DS devkit (experimental)"); ui->cbxConsoleType->addItem("DSi (experimental)"); - ui->cbxConsoleType->setCurrentIndex(cfg.GetInt("Emu.ConsoleType")); + ui->cbxConsoleType->addItem("3DS (experimental)"); + ui->cbxConsoleType->setCurrentIndex((cfg.GetInt("Emu.ConsoleType")<<1)+cfg.GetInt("Emu.DebugBoardConfig")); ui->chkDirectBoot->setChecked(cfg.GetBool("Emu.DirectBoot")); @@ -297,7 +299,8 @@ void EmuSettingsDialog::done(int r) instcfg.SetBool("Gdb.ARM9.BreakOnStartup", ui->cbGdbBOSA9->isChecked()); #endif - cfg.SetInt("Emu.ConsoleType", ui->cbxConsoleType->currentIndex()); + cfg.SetInt("Emu.ConsoleType", ui->cbxConsoleType->currentIndex()>>1); + cfg.SetInt("Emu.DebugBoardConfig", ui->cbxConsoleType->currentIndex()&1); cfg.SetBool("Emu.DirectBoot", ui->chkDirectBoot->isChecked()); Config::Save(); diff --git a/src/frontend/qt_sdl/RAMInfoDialog.cpp b/src/frontend/qt_sdl/RAMInfoDialog.cpp index 8c3a5fbf..d6b9be32 100644 --- a/src/frontend/qt_sdl/RAMInfoDialog.cpp +++ b/src/frontend/qt_sdl/RAMInfoDialog.cpp @@ -242,14 +242,15 @@ void RAMSearchThread::run() if (SearchMode == ramInfoSTh_SearchAll || RowDataVector->size() == 0) { // First search mode - for (u32 addr = 0x02000000; SearchRunning && addr < 0x02000000+MainRAMMaxSize; addr += SearchByteType) + // TODO: 3DS/devit 32MiB + for (u32 addr = 0x02000000; SearchRunning && addr < 0x02000000+MainRAMPrimaryMappingMaxSize; addr += SearchByteType) { const s32& value = GetMainRAMValue(*Dialog->emuInstance->getNDS(), addr, SearchByteType); RowDataVector->push_back({ addr, value, value }); // A solution to prevent to call too many slot. - u32 newProgress = (int)((addr-0x02000000) / (MainRAMMaxSize-1.0f) * 100); + u32 newProgress = (int)((addr-0x02000000) / (MainRAMPrimaryMappingMaxSize-1.0f) * 100); if (progress < newProgress) { progress = newProgress;