diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index a198b4a..5be8bf1 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -21,6 +21,7 @@ jobs: "AKRPG", "DSPICO", "DSTT", + "EZP", "G003", "M3DS", "R4", diff --git a/README.md b/README.md index e375e61..7d8b496 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ Note that there can be some game compatibility differences between different pla | AKRPG | Acekard RPG SD card | ❌ | | DSPICO | DSpico | ✅ | | DSTT | DSTT, SuperCard DSONE SDHC, r4isdhc.com carts 2014+, r4i-sdhc.com carts, various derivatives | ❌ | +| EZP | EZ-Flash Parallel | ❌ | | G003 | M3i Zero (GMP-Z003) | ✅ | | ISNITRO | Supports the IS-NITRO-EMULATOR through agb semihosting. | ❌ | | M3DS | M3 DS Real, M3i Zero, iTouchDS, r4rts.com, r4isdhc.com RTS (black) | ❌ | diff --git a/arm9/source/patches/platform/LoaderPlatformFactory.cpp b/arm9/source/patches/platform/LoaderPlatformFactory.cpp index 0472950..852ac76 100644 --- a/arm9/source/patches/platform/LoaderPlatformFactory.cpp +++ b/arm9/source/patches/platform/LoaderPlatformFactory.cpp @@ -12,6 +12,7 @@ #include "patches/platform/akrpg/AKRPGLoaderPlatform.h" #include "patches/platform/r4idsn/R4iDSNLoaderPlatform.h" #include "patches/platform/supercard/SuperCardLoaderPlatform.h" +#include "patches/platform/ezp/EZPLoaderPlatform.h" #include "LoaderPlatformFactory.h" LoaderPlatform* LoaderPlatformFactory::CreateLoaderPlatform() const @@ -40,6 +41,8 @@ LoaderPlatform* LoaderPlatformFactory::CreateLoaderPlatform() const return new R4iDSNLoaderPlatform(); #elif defined(PICO_LOADER_TARGET_SUPERCARD) return new SuperCardLoaderPlatform(); +#elif defined(PICO_LOADER_TARGET_EZP) + return new EZPLoaderPlatform(); #else #error "No loader platform defined" return nullptr; diff --git a/arm9/source/patches/platform/ezp/EZPLoaderPlatform.h b/arm9/source/patches/platform/ezp/EZPLoaderPlatform.h new file mode 100644 index 0000000..86bb8d6 --- /dev/null +++ b/arm9/source/patches/platform/ezp/EZPLoaderPlatform.h @@ -0,0 +1,33 @@ +#pragma once +#include "common.h" +#include "../LoaderPlatform.h" +#include "ezpReadSectorsAsm.h" +#include "ezpReadSdDataAsm.h" +#include "ezpWriteSectorsAsm.h" + +/// @brief Implementation of LoaderPlatform for the EZ-Flash Parallel flashcard +class EZPLoaderPlatform : public LoaderPlatform +{ +public: + const SdReadPatchCode* CreateSdReadPatchCode( + PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override + { + return patchCodeCollection.GetOrAddSharedPatchCode([&] + { + return new EZPReadSectorsPatchCode(patchHeap, + patchCodeCollection.GetOrAddSharedPatchCode([&] + { + return new EZPReadSDDataPatchCode(patchHeap); + })); + }); + } + + const SdWritePatchCode* CreateSdWritePatchCode( + PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override + { + return patchCodeCollection.GetOrAddSharedPatchCode([&] + { + return new EZPWriteSectorsPatchCode(patchHeap); + }); + } +}; diff --git a/arm9/source/patches/platform/ezp/ezpReadSdDataAsm.h b/arm9/source/patches/platform/ezp/ezpReadSdDataAsm.h new file mode 100644 index 0000000..e1d53dd --- /dev/null +++ b/arm9/source/patches/platform/ezp/ezpReadSdDataAsm.h @@ -0,0 +1,19 @@ +#pragma once +#include "sections.h" +#include "thumbInstructions.h" + +DEFINE_SECTION_SYMBOLS(ezp_readsddata); + +extern "C" void ezp_readSdData(u32 srcSector, void* dst, u32 sectorCount, u32 sectorCountThisRead); + +class EZPReadSDDataPatchCode : public PatchCode +{ +public: + explicit EZPReadSDDataPatchCode(PatchHeap& patchHeap) + : PatchCode(SECTION_START(ezp_readsddata), SECTION_SIZE(ezp_readsddata), patchHeap) { } + + const void* GetReadSDDataFunction() const + { + return GetAddressAtTarget((void*)ezp_readSdData); + } +}; diff --git a/arm9/source/patches/platform/ezp/ezpReadSdDataAsm.s b/arm9/source/patches/platform/ezp/ezpReadSdDataAsm.s new file mode 100644 index 0000000..44f035c --- /dev/null +++ b/arm9/source/patches/platform/ezp/ezpReadSdDataAsm.s @@ -0,0 +1,66 @@ +.cpu arm7tdmi +.section "ezp_readsddata", "ax" +.syntax unified +.thumb + +// r0 = src sector +// r1 = dst +// r2 = sector count +// r3 = sector count for single read +.global ezp_readSdData +.type ezp_readSdData, %function +ezp_readSdData: + push {r4-r7,lr} + ldr r4, =0x040001A0 + + // Setup command for data read + // BA 00 00 00 00 00 00 00 + movs r7, #0xBA + str r7, [r4,#0x8] + movs r7, #0 + str r7, [r4,#0xC] + + // Set up MCCNT for required read size + adr r6, readLengthControlValues + lsls r7, r3, #2 + ldr r5, [r6,r7] + str r5, [r4,#4] + + ldr r6, =0x04100010 + + // Get number of words to read + // 1 sector == 128 words + lsls r3, r3, #7 + +BA_data_loop: + ldrb r7, [r4,#6] + lsrs r7, r7, #8 // check if data is ready + bcc BA_data_loop_check_transfer_end // if not skip reading + + ldr r7, [r6] + // Check if the read word needs to be stored. + cmp r3, #0 + // If we already read all the needed words, skip + beq BA_data_loop_check_transfer_end + subs r3, r3, #1 + stmia r1!, {r7} + +BA_data_loop_check_transfer_end: + ldrb r7, [r4,#7] + lsrs r7, r7, #8 // check if transfer is done + bcs BA_data_loop + + pop {r4-r7,pc} + +.balign 4 + +readLengthControlValues: + .word 0 + .word 0xA15A6000 + .word 0xA25A6000 + .word 0xA35A6000 + .word 0xA35A6000 + +.pool + +.end diff --git a/arm9/source/patches/platform/ezp/ezpReadSectorsAsm.h b/arm9/source/patches/platform/ezp/ezpReadSectorsAsm.h new file mode 100644 index 0000000..9d3b639 --- /dev/null +++ b/arm9/source/patches/platform/ezp/ezpReadSectorsAsm.h @@ -0,0 +1,26 @@ +#pragma once +#include "sections.h" +#include "../SdReadPatchCode.h" +#include "ezpReadSdDataAsm.h" + +DEFINE_SECTION_SYMBOLS(ezp_readsectors); + +extern "C" void ezp_readSectors(u32 srcSector, void* dst, u32 sectorCount); + +extern "C" u32 ezp_readSectors_readSdData_address; + +class EZPReadSectorsPatchCode : public SdReadPatchCode +{ +public: + explicit EZPReadSectorsPatchCode(PatchHeap& patchHeap, + const EZPReadSDDataPatchCode* ezpReadSdDataPatchCode) + : SdReadPatchCode(SECTION_START(ezp_readsectors), SECTION_SIZE(ezp_readsectors), patchHeap) + { + ezp_readSectors_readSdData_address = (u32)ezpReadSdDataPatchCode->GetReadSDDataFunction(); + } + + const SdReadFunc GetSdReadFunction() const override + { + return (const SdReadFunc)GetAddressAtTarget((void*)ezp_readSectors); + } +}; diff --git a/arm9/source/patches/platform/ezp/ezpReadSectorsAsm.s b/arm9/source/patches/platform/ezp/ezpReadSectorsAsm.s new file mode 100644 index 0000000..72b1175 --- /dev/null +++ b/arm9/source/patches/platform/ezp/ezpReadSectorsAsm.s @@ -0,0 +1,83 @@ +.cpu arm7tdmi +.section "ezp_readsectors", "ax" +.syntax unified +.thumb + +// r0 = src sector +// r1 = dst +// r2 = sector count +.global ezp_readSectors +.type ezp_readSectors, %function +ezp_readSectors: + push {r4-r7,lr} + + ldr r4, =0x040001A0 + movs r3, #0x80 + strb r3, [r4,#1] + +sector_loop: + // request sd read for sector 0xaabbccdd + // for xx number of sectors (up to 4 sectors) + // B9 00 00 xx aa bb cc dd + movs r3, #0xB9 + str r3, [r4,#0x8] + + // Maximum 4 sectors at a time. + // Check if 3 or lower first. + movs r7, #3 + ands r7, r7, r2 + bne sector_loop_skip_0_case // if between 1-3, then we can use value as is + movs r7, #4 // if AND returns 0, then it's a 4 sector read +sector_loop_skip_0_case: + strb r7, [r4, #0xB] + + lsrs r3, r0, #24 + strb r3, [r4,#0xC] + lsrs r3, r0, #16 + strb r3, [r4,#0xD] + lsrs r3, r0, #8 + strb r3, [r4,#0xE] + strb r0, [r4,#0xF] + + ldr r6, =0x04100010 + +B9_poll_loop: + ldr r3, =0xA75860C8 + 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, r7 + ldr r6, ezp_readSectors_readSdData_address + bl blx_r6 + + adds r0, r0, r7 + subs r2, r2, r7 + bne sector_loop + + pop {r4-r7,pc} + +blx_r6: + bx r6 + +.balign 4 + +.global ezp_readSectors_readSdData_address +ezp_readSectors_readSdData_address: + .word 0 + +.pool + +.end diff --git a/arm9/source/patches/platform/ezp/ezpWriteSectorsAsm.h b/arm9/source/patches/platform/ezp/ezpWriteSectorsAsm.h new file mode 100644 index 0000000..31358fc --- /dev/null +++ b/arm9/source/patches/platform/ezp/ezpWriteSectorsAsm.h @@ -0,0 +1,19 @@ +#pragma once +#include "sections.h" +#include "../SdWritePatchCode.h" + +DEFINE_SECTION_SYMBOLS(ezp_writesectors); + +extern "C" void ezp_writeSectors(u32 dstSector, const void* src, u32 sectorCount); + +class EZPWriteSectorsPatchCode : public SdWritePatchCode +{ +public: + explicit EZPWriteSectorsPatchCode(PatchHeap& patchHeap) + : SdWritePatchCode(SECTION_START(ezp_writesectors), SECTION_SIZE(ezp_writesectors), patchHeap) { } + + const SdWriteFunc GetSdWriteFunction() const override + { + return (const SdWriteFunc)GetAddressAtTarget((void*)ezp_writeSectors); + } +}; diff --git a/arm9/source/patches/platform/ezp/ezpWriteSectorsAsm.s b/arm9/source/patches/platform/ezp/ezpWriteSectorsAsm.s new file mode 100644 index 0000000..f84484d --- /dev/null +++ b/arm9/source/patches/platform/ezp/ezpWriteSectorsAsm.s @@ -0,0 +1,86 @@ +.cpu arm7tdmi +.section "ezp_writesectors", "ax" +.syntax unified +.thumb + +// r0 = dst sector +// r1 = src +// r2 = sector count +.global ezp_writeSectors +.type ezp_writeSectors, %function +ezp_writeSectors: + push {r4-r7,lr} + + ldr r4, =0x040001A0 + movs r3, #0x80 + strb r3, [r4,#1] + +sector_loop: + // write to sd buffer index 0xaa + // BB 00 00 01 00 00 00 aa + movs r3, #0xBB + str r3, [r4,#0x8] + movs r3, #0x1 + strb r3, [r4,#0xB] + movs r3, #0 + str r3, [r4,#0xC] // storing as little-endian puts the bottom 8 bits as first byte + + ldr r3, =0xE15A7FFF + ldr r6, =0x04100010 + 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 + + // write 0xXX number of sectors at sd sector 0xaabbccdd + // note XX is already filled from previous command + // BB 00 00 XX aa bb cc dd + movs r3, #0xBC + strb r3, [r4,#0x8] + lsrs r3, r0, #24 + strb r3, [r4,#0xC] + lsrs r3, r0, #16 + strb r3, [r4,#0xD] + lsrs r3, r0, #8 + strb r3, [r4,#0xE] + strb r0, [r4,#0xF] + +BC_poll_loop: + ldr r3, =0xA7586190 + 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