diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index dddab13..297b51e 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -27,6 +27,7 @@ jobs: "M3DS", "R4", "R4iDSN", + "STARGATE", "SUPERCARD" ] runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ef89cf6..a7d1c55 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,6 +20,7 @@ jobs: "M3DS", "R4", "R4iDSN", + "STARGATE", "SUPERCARD" ] runs-on: ubuntu-latest diff --git a/README.md b/README.md index b4d8b47..f9adb17 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ Note that there can be some game compatibility differences between different pla | MELONDS | Melon DS support for testing purposes only. | ❌ | | R4 | Original R4DS (non-SDHC), M3 DS Simply | ❌ | | R4iDSN | r4idsn.com | ❌ | +| STARGATE | Stargate 3DS DS-mode | ✅ | | SUPERCARD | SuperCard (Slot-2 flashcart) | ❌ | The DMA column indicates whether DMA card reads are implemented for the platform . Without DMA card reads, some games can have cache related issues.
diff --git a/arm9/source/patches/platform/LoaderPlatformFactory.cpp b/arm9/source/patches/platform/LoaderPlatformFactory.cpp index 53c2f0a..a1297a5 100644 --- a/arm9/source/patches/platform/LoaderPlatformFactory.cpp +++ b/arm9/source/patches/platform/LoaderPlatformFactory.cpp @@ -14,6 +14,7 @@ #include "patches/platform/supercard/SuperCardLoaderPlatform.h" #include "patches/platform/ezp/EZPLoaderPlatform.h" #include "patches/platform/datel/DatelLoaderPlatform.h" +#include "patches/platform/stargate/StargateLoaderPlatform.h" #include "LoaderPlatformFactory.h" LoaderPlatform* LoaderPlatformFactory::CreateLoaderPlatform() const @@ -46,6 +47,8 @@ LoaderPlatform* LoaderPlatformFactory::CreateLoaderPlatform() const return new EZPLoaderPlatform(); #elif defined(PICO_LOADER_TARGET_DATEL) return new DatelLoaderPlatform(); +#elif defined(PICO_LOADER_TARGET_STARGATE) + return new StargateLoaderPlatform(); #else #error "No loader platform defined" return nullptr; diff --git a/arm9/source/patches/platform/stargate/StargateLoaderPlatform.h b/arm9/source/patches/platform/stargate/StargateLoaderPlatform.h new file mode 100644 index 0000000..008528c --- /dev/null +++ b/arm9/source/patches/platform/stargate/StargateLoaderPlatform.h @@ -0,0 +1,40 @@ +#pragma once +#include "common.h" +#include "../LoaderPlatform.h" +#include "stargateReadSdAsm.h" +#include "stargateReadSdDmaAsm.h" +#include "stargateWriteSdAsm.h" + +/// @brief Implementation of LoaderPlatform for the Stargate 3DS flashcard +class StargateLoaderPlatform : public LoaderPlatform +{ +public: + const SdReadPatchCode* CreateSdReadPatchCode( + PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override + { + return patchCodeCollection.GetOrAddSharedPatchCode([&] + { + return new StargateReadSdPatchCode(patchHeap); + }); + } + + const SdReadDmaPatchCode* CreateSdReadDmaPatchCode(PatchCodeCollection& patchCodeCollection, + PatchHeap& patchHeap, const void* miiCardDmaCopy32Ptr) const override + { + return patchCodeCollection.AddUniquePatchCode( + patchHeap, miiCardDmaCopy32Ptr); + } + + const SdWritePatchCode* CreateSdWritePatchCode( + PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override + { + return patchCodeCollection.GetOrAddSharedPatchCode([&] + { + return new StargateWriteSdPatchCode(patchHeap); + }); + } + + LoaderPlatformType GetPlatformType() const override { return LoaderPlatformType::Slot1; } + + bool HasDmaSdReads() const override { return true; } +}; diff --git a/arm9/source/patches/platform/stargate/stargateReadSdAsm.h b/arm9/source/patches/platform/stargate/stargateReadSdAsm.h new file mode 100644 index 0000000..01a597f --- /dev/null +++ b/arm9/source/patches/platform/stargate/stargateReadSdAsm.h @@ -0,0 +1,19 @@ +#pragma once +#include "sections.h" +#include "../SdReadPatchCode.h" + +DEFINE_SECTION_SYMBOLS(stargate_readsd); + +extern "C" void stargate_readSd(u32 srcSector, void* dst, u32 sectorCount); + +class StargateReadSdPatchCode : public SdReadPatchCode +{ +public: + explicit StargateReadSdPatchCode(PatchHeap& patchHeap) + : SdReadPatchCode(SECTION_START(stargate_readsd), SECTION_SIZE(stargate_readsd), patchHeap) { } + + const SdReadFunc GetSdReadFunction() const override + { + return (const SdReadFunc)GetAddressAtTarget((void*)stargate_readSd); + } +}; diff --git a/arm9/source/patches/platform/stargate/stargateReadSdAsm.s b/arm9/source/patches/platform/stargate/stargateReadSdAsm.s new file mode 100644 index 0000000..63df8df --- /dev/null +++ b/arm9/source/patches/platform/stargate/stargateReadSdAsm.s @@ -0,0 +1,83 @@ +.cpu arm7tdmi +.section "stargate_readsd", "ax" +.syntax unified +.thumb + +// r0 = src sector +// r1 = dst +// r2 = sector count +.global stargate_readSd +.type stargate_readSd, %function +stargate_readSd: + push {r4-r7,lr} + + ldr r4, =0x040001A0 + movs r3, #0x80 + strb r3, [r4,#1] + +sector_loop: + // request sd read for sector 0xaabbccdd + // B9 aa bb cc dd 00 00 00 + movs r3, #0xB9 + strb r3, [r4,#0x8] + lsrs r3, r0, #24 + strb r3, [r4,#0x9] + lsrs r3, r0, #16 + strb r3, [r4,#0xA] + lsrs r3, r0, #8 + strb r3, [r4,#0xB] + lsls r7, r0, #24 + lsrs r7, r7, #24 // r7 = dd + str r7, [r4,#0xC] // storing as little-endian puts the bottom 8 bits as first byte + + ldr r6, =0x04100010 + +B9_poll_loop: + ldr r3, =0xA7406000 + str r3, [r4,#4] + +B9_poll_transfer_loop: + ldrb r3, [r4,#6] + lsrs r3, r3, #8 + bcc B9_poll_check_transfer_end + ldr r5, [r6] + +B9_poll_check_transfer_end: + ldrb r3, [r4,#7] + lsrs r3, r3, #8 + bcs B9_poll_transfer_loop + + cmp r5, #0 + bne B9_poll_loop + + movs r3, #0xBA + str r3, [r4,#0x8] + str r5, [r4,#0xC] // r5 is 0 here + + ldr r3, =0xA1406000 + str r3, [r4,#4] + +BA_data_loop: + ldrb r3, [r4,#6] + lsrs r3, r3, #8 // check if data is ready + bcc BA_data_loop_check_transfer_end // if not skip reading + + ldr r3, [r6] + stmia r1!, {r3} + +BA_data_loop_check_transfer_end: + ldrb r3, [r4,#7] + lsrs r3, r3, #8 // check if transfer is done + bcs BA_data_loop + + adds r0, #1 + subs r2, #1 + bne sector_loop + + pop {r4-r7,pc} + +.balign 4 + +.pool + +.end \ No newline at end of file diff --git a/arm9/source/patches/platform/stargate/stargateReadSdDmaAsm.h b/arm9/source/patches/platform/stargate/stargateReadSdDmaAsm.h new file mode 100644 index 0000000..c09dfec --- /dev/null +++ b/arm9/source/patches/platform/stargate/stargateReadSdDmaAsm.h @@ -0,0 +1,30 @@ +#pragma once +#include "sections.h" +#include "../SdReadDmaPatchCode.h" + +DEFINE_SECTION_SYMBOLS(stargate_readsddma); + +extern "C" void stargate_readSdDma(u32 srcSector, u32 previousSrcSector, u32 dmaChannel, void* dst); +extern "C" void stargate_finishReadSdDma(void); + +extern u32 stargate_readSdDma_miiCardDmaCopy32Ptr; + +class StargateReadSdDmaPatchCode : public SdReadDmaPatchCode +{ +public: + StargateReadSdDmaPatchCode(PatchHeap& patchHeap, const void* miiCardDmaCopy32Ptr) + : SdReadDmaPatchCode(SECTION_START(stargate_readsddma), SECTION_SIZE(stargate_readsddma), patchHeap) + { + stargate_readSdDma_miiCardDmaCopy32Ptr = (u32)miiCardDmaCopy32Ptr; + } + + const SdReadDmaFunc GetSdReadDmaFunction() const override + { + return (const SdReadDmaFunc)GetAddressAtTarget((void*)stargate_readSdDma); + } + + const SdReadDmaFinishFunc GetSdReadDmaFinishFunction() const override + { + return (const SdReadDmaFinishFunc)GetAddressAtTarget((void*)stargate_finishReadSdDma); + } +}; diff --git a/arm9/source/patches/platform/stargate/stargateReadSdDmaAsm.s b/arm9/source/patches/platform/stargate/stargateReadSdDmaAsm.s new file mode 100644 index 0000000..c7edc9f --- /dev/null +++ b/arm9/source/patches/platform/stargate/stargateReadSdDmaAsm.s @@ -0,0 +1,94 @@ +.cpu arm946e-s +.section "stargate_readsddma", "ax" +.syntax unified +.thumb + +// r0 = src sector +// r1 = previous src sector +// r2 = dma channel +// r3 = dst +.global stargate_readSdDma +.type stargate_readSdDma, %function +stargate_readSdDma: + push {r4-r7,lr} + + ldr r4, =0x040001A0 + movs r7, #0x80 + strb r7, [r4,#0x1] + +sector_loop: + // request sd read for sector 0xaabbccdd + // B9 aa bb cc dd 00 00 00 + movs r7, #0xB9 + strb r7, [r4,#0x8] + lsrs r7, r0, #24 + strb r7, [r4,#0x9] + lsrs r7, r0, #16 + strb r7, [r4,#0xA] + lsrs r7, r0, #8 + strb r7, [r4,#0xB] + lsls r7, r0, #24 + lsrs r7, r7, #24 // r7 = dd + str r7, [r4,#0xC] // storing as little-endian puts the bottom 8 bits as first byte + + ldr r1, =0x04100010 + +B9_poll_loop: + ldr r7, =0xA7406000 + str r7, [r4,#0x4] + +B9_poll_transfer_loop: + ldrb r7, [r4,#0x6] + lsrs r7, r7, #8 + bcc B9_poll_check_transfer_end + ldr r5, [r1] + +B9_poll_check_transfer_end: + ldrb r7, [r4,#0x7] + lsrs r7, r7, #8 + bcs B9_poll_transfer_loop + + cmp r5, #0 + bne B9_poll_loop + + // Setup data read card command + movs r7, #0xBA + str r7, [r4,#0x8] + str r5, [r4,#0xC] // r5 is 0 here + +readDataWithDma: + movs r0, r2 // DMA channel + movs r2, r3 // Destination + movs r3, #1 + lsls r3, r3, #9 // (1 << 9) = 512 = count + + ldr r6, stargate_readSdDma_miiCardDmaCopy32Ptr + blx r6 + +BA_data_dma: + movs r7, #0xC0 // select rom mode, with irq + strb r7, [r4,#0x1] + ldr r7, =0xA1406000 + str r7, [r4,#0x4] + + pop {r4-r7,pc} + +.balign 4 + +.global stargate_readSdDma_miiCardDmaCopy32Ptr +stargate_readSdDma_miiCardDmaCopy32Ptr: + .word 0 + +.pool + +.global stargate_finishReadSdDma +.type stargate_finishReadSdDma, %function +stargate_finishReadSdDma: + // No cleanup needed on this platform. + bx lr + +.balign 4 + +.pool + +.end diff --git a/arm9/source/patches/platform/stargate/stargateWriteSdAsm.h b/arm9/source/patches/platform/stargate/stargateWriteSdAsm.h new file mode 100644 index 0000000..70560e9 --- /dev/null +++ b/arm9/source/patches/platform/stargate/stargateWriteSdAsm.h @@ -0,0 +1,19 @@ +#pragma once +#include "sections.h" +#include "../SdWritePatchCode.h" + +DEFINE_SECTION_SYMBOLS(stargate_writesd); + +extern "C" void stargate_writeSd(u32 dstSector, const void* src, u32 sectorCount); + +class StargateWriteSdPatchCode : public SdWritePatchCode +{ +public: + explicit StargateWriteSdPatchCode(PatchHeap& patchHeap) + : SdWritePatchCode(SECTION_START(stargate_writesd), SECTION_SIZE(stargate_writesd), patchHeap) { } + + const SdWriteFunc GetSdWriteFunction() const override + { + return (const SdWriteFunc)GetAddressAtTarget((void*)stargate_writeSd); + } +}; diff --git a/arm9/source/patches/platform/stargate/stargateWriteSdAsm.s b/arm9/source/patches/platform/stargate/stargateWriteSdAsm.s new file mode 100644 index 0000000..3b377b2 --- /dev/null +++ b/arm9/source/patches/platform/stargate/stargateWriteSdAsm.s @@ -0,0 +1,82 @@ +.cpu arm7tdmi +.section "stargate_writesd", "ax" +.syntax unified +.thumb + +// r0 = dst sector +// r1 = src +// r2 = sector count +.global stargate_writeSd +.type stargate_writeSd, %function +stargate_writeSd: + push {r4-r7,lr} + + ldr r4, =0x040001A0 + movs r3, #0x80 + strb r3, [r4,#1] + +sector_loop: + // write at sd sector 0xaabbccdd + // BB aa bb cc dd 00 00 00 + movs r3, #0xBB + strb r3, [r4,#0x8] + lsrs r3, r0, #24 + strb r3, [r4,#0x9] + lsrs r3, r0, #16 + strb r3, [r4,#0xA] + lsrs r3, r0, #8 + strb r3, [r4,#0xB] + lsls r7, r0, #24 + lsrs r7, r7, #24 // r7 = dd + str r7, [r4,#0xC] // storing as little-endian puts the bottom 8 bits as first byte + + ldr r6, =0x04100010 + + ldr r3, =0xE1400000 + str r3, [r4,#4] + +BB_data_loop: + ldrb r3, [r4,#6] + lsrs r3, r3, #8 // check if ready to write + bcc BB_data_loop_check_transfer_end // if not skip reading + + ldmia r1!, {r3} + str r3, [r6] + +BB_data_loop_check_transfer_end: + ldrb r3, [r4,#7] + lsrs r3, r3, #8 // check if transfer is done + bcs BB_data_loop + + movs r3, #0xBC + strb r3, [r4,#0x8] + +BC_poll_loop: + ldr r3, =0xA7406000 + str r3, [r4,#4] + +BC_poll_transfer_loop: + ldrb r3, [r4,#6] + lsrs r3, r3, #8 + bcc BC_poll_check_transfer_end + ldr r5, [r6] + +BC_poll_check_transfer_end: + ldrb r3, [r4,#7] + lsrs r3, r3, #8 + bcs BC_poll_transfer_loop + + cmp r5, #0 + bne BC_poll_loop + + adds r0, #1 + subs r2, #1 + bne sector_loop + + pop {r4-r7,pc} + +.balign 4 + +.pool + +.end \ No newline at end of file