mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-06-02 09:16:49 +02:00
Initial commit
This commit is contained in:
57
arm9/source/patches/platform/LoaderPlatform.h
Normal file
57
arm9/source/patches/platform/LoaderPlatform.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "SdReadPatchCode.h"
|
||||
#include "SdReadDmaPatchCode.h"
|
||||
#include "SdWritePatchCode.h"
|
||||
#include "../PatchHeap.h"
|
||||
#include "../PatchCodeCollection.h"
|
||||
|
||||
/// @brief Abstract class for platform (flashcard or other sd access method) specific parts of the loader.
|
||||
class LoaderPlatform
|
||||
{
|
||||
public:
|
||||
/// @brief Creates the SD read patch code for the platform.
|
||||
/// @param patchCodeCollection The patch code collection.
|
||||
/// @param patchHeap The patch heap.
|
||||
/// @return A unique pointer to the created SD read patch code.
|
||||
virtual const SdReadPatchCode* CreateSdReadPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const = 0;
|
||||
|
||||
/// @brief Creates the SD read dma patch code for the platform.
|
||||
/// @param patchCodeCollection The patch code collection.
|
||||
/// @param patchHeap The patch heap.
|
||||
/// @return A unique pointer to the created SD read dma patch code.
|
||||
virtual const SdReadDmaPatchCode* CreateSdReadDmaPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap, const void* miiCardDmaCopy32Ptr) const { return nullptr; }
|
||||
|
||||
/// @brief Creates the SD write patch code for the platform.
|
||||
/// @param patchCodeCollection The patch code collection.
|
||||
/// @param patchHeap The patch heap.
|
||||
/// @return A unique pointer to the created SD write patch code.
|
||||
virtual const SdWritePatchCode* CreateSdWritePatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const = 0;
|
||||
|
||||
/// @brief Creates the rom read patch code for the platform.
|
||||
/// @param patchCodeCollection The patch code collection.
|
||||
/// @param patchHeap The patch heap.
|
||||
/// @return A unique pointer to the created rom read patch code.
|
||||
virtual const SdReadPatchCode* CreateRomReadPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const { return nullptr; }
|
||||
|
||||
/// @brief Checks if the platform supports rom reads directly.
|
||||
/// @return True if the platform supports rom reads, or false otherwise.
|
||||
virtual bool HasRomReads() const { return false; }
|
||||
|
||||
/// @brief Checks if the platform supports sd reads via dma.
|
||||
/// @return True if the platform supports sd reads via dma, or false otherwise.
|
||||
virtual bool HasDmaSdReads() const { return false; }
|
||||
|
||||
/// @brief Prepares the platform for running a rom.
|
||||
/// @param romDirSector The directory sector of the rom file, or 0 if not applicable.
|
||||
/// @param romDirSectorOffset The byte offset of the rom fat entry in the directory sector.
|
||||
virtual void PrepareRomBoot(u32 romDirSector, u32 romDirSectorOffset) const { }
|
||||
|
||||
/// @brief For platforms that need it this function may be used to (re)initialize the sd card.
|
||||
/// @return True if the sd card initialization was successful, or false otherwise.
|
||||
virtual bool InitializeSdCard() { return true; }
|
||||
};
|
||||
47
arm9/source/patches/platform/LoaderPlatformFactory.cpp
Normal file
47
arm9/source/patches/platform/LoaderPlatformFactory.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "common.h"
|
||||
#include "patches/platform/LoaderPlatform.h"
|
||||
#include "patches/platform/dspico/DSPicoLoaderPlatform.h"
|
||||
#include "patches/platform/isnitro/IsNitroLoaderPlatform.h"
|
||||
#include "patches/platform/m3ds/M3DSLoaderPlatform.h"
|
||||
#include "patches/platform/r4/R4LoaderPlatform.h"
|
||||
#include "patches/platform/ace3ds/Ace3DSLoaderPlatform.h"
|
||||
#include "patches/platform/g003/G003LoaderPlatform.h"
|
||||
#include "patches/platform/melonds/MelonDSLoaderPlatform.h"
|
||||
#include "patches/platform/dstt/DSTTLoaderPlatform.h"
|
||||
#include "patches/platform/ak2/AK2LoaderPlatform.h"
|
||||
#include "patches/platform/akrpg/AKRPGLoaderPlatform.h"
|
||||
#include "patches/platform/r4idsn/R4iDSNLoaderPlatform.h"
|
||||
#include "patches/platform/supercard/SuperCardLoaderPlatform.h"
|
||||
#include "LoaderPlatformFactory.h"
|
||||
|
||||
LoaderPlatform* LoaderPlatformFactory::CreateLoaderPlatform() const
|
||||
{
|
||||
#if defined(PICO_LOADER_TARGET_ISNITRO)
|
||||
return new IsNitroLoaderPlatform();
|
||||
#elif defined(PICO_LOADER_TARGET_DSPICO)
|
||||
return new DSPicoLoaderPlatform();
|
||||
#elif defined(PICO_LOADER_TARGET_M3DS)
|
||||
return new M3DSLoaderPlatform();
|
||||
#elif defined(PICO_LOADER_TARGET_R4)
|
||||
return new R4LoaderPlatform();
|
||||
#elif defined(PICO_LOADER_TARGET_ACE3DS)
|
||||
return new Ace3DSLoaderPlatform();
|
||||
#elif defined(PICO_LOADER_TARGET_G003)
|
||||
return new G003LoaderPlatform();
|
||||
#elif defined(PICO_LOADER_TARGET_MELONDS)
|
||||
return new MelonDSLoaderPlatform();
|
||||
#elif defined(PICO_LOADER_TARGET_DSTT)
|
||||
return new DSTTLoaderPlatform();
|
||||
#elif defined(PICO_LOADER_TARGET_AK2)
|
||||
return new AK2LoaderPlatform();
|
||||
#elif defined(PICO_LOADER_TARGET_AKRPG)
|
||||
return new AKRPGLoaderPlatform();
|
||||
#elif defined(PICO_LOADER_TARGET_R4iDSN)
|
||||
return new R4iDSNLoaderPlatform();
|
||||
#elif defined(PICO_LOADER_TARGET_SUPERCARD)
|
||||
return new SuperCardLoaderPlatform();
|
||||
#else
|
||||
#error "No loader platform defined"
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
9
arm9/source/patches/platform/LoaderPlatformFactory.h
Normal file
9
arm9/source/patches/platform/LoaderPlatformFactory.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
class LoaderPlatform;
|
||||
|
||||
class LoaderPlatformFactory
|
||||
{
|
||||
public:
|
||||
LoaderPlatform* CreateLoaderPlatform() const;
|
||||
};
|
||||
28
arm9/source/patches/platform/SdReadDmaPatchCode.h
Normal file
28
arm9/source/patches/platform/SdReadDmaPatchCode.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include "../PatchCode.h"
|
||||
|
||||
/// @brief Abstract base class for patch code implementing dma SD reads
|
||||
class SdReadDmaPatchCode : public PatchCode
|
||||
{
|
||||
protected:
|
||||
SdReadDmaPatchCode(const void* code, u32 size, PatchHeap& patchHeap)
|
||||
: PatchCode(code, size, patchHeap) { }
|
||||
|
||||
public:
|
||||
/// @brief Pointer to a function for reading a single SD sector using the given dmaChannel.
|
||||
/// In first sector of batch read, previousSrcSector will be 0.
|
||||
/// On further reads previousSrcSector will contains the previous srcSector
|
||||
/// which can be used to continue a sequential read.
|
||||
typedef void (*SdReadDmaFunc)(u32 srcSector, u32 previousSrcSector, u32 dmaChannel, void* dst);
|
||||
|
||||
/// @brief Pointer to a function for finishing a batch of dma reads.
|
||||
typedef void (*SdReadDmaFinishFunc)(void);
|
||||
|
||||
/// @brief Gets a pointer to the dma SD read function in the patch code.
|
||||
/// @return The pointer to the dma SD read function.
|
||||
virtual const SdReadDmaFunc GetSdReadDmaFunction() const = 0;
|
||||
|
||||
/// @brief Gets a pointer to the dma SD read finish function in the patch code.
|
||||
/// @return The pointer to the dma SD read finish function.
|
||||
virtual const SdReadDmaFinishFunc GetSdReadDmaFinishFunction() const = 0;
|
||||
};
|
||||
19
arm9/source/patches/platform/SdReadPatchCode.h
Normal file
19
arm9/source/patches/platform/SdReadPatchCode.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "../PatchCode.h"
|
||||
|
||||
/// @brief Abstract base class for patch code implementing SD reads
|
||||
class SdReadPatchCode : public PatchCode
|
||||
{
|
||||
protected:
|
||||
SdReadPatchCode(const void* code, u32 size, PatchHeap& patchHeap)
|
||||
: PatchCode(code, size, patchHeap) { }
|
||||
|
||||
public:
|
||||
/// @brief Pointer to a function for reading sectorCount SD sectors
|
||||
/// from srcSector to the given dst buffer.
|
||||
typedef void (*SdReadFunc)(u32 srcSector, void* dst, u32 sectorCount);
|
||||
|
||||
/// @brief Gets a pointer to the SD read function in the patch code.
|
||||
/// @return The pointer to the SD read function.
|
||||
virtual const SdReadFunc GetSdReadFunction() const = 0;
|
||||
};
|
||||
19
arm9/source/patches/platform/SdWritePatchCode.h
Normal file
19
arm9/source/patches/platform/SdWritePatchCode.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "../PatchCode.h"
|
||||
|
||||
/// @brief Abstract base class for patch code implementing SD writes
|
||||
class SdWritePatchCode : public PatchCode
|
||||
{
|
||||
protected:
|
||||
SdWritePatchCode(const void* code, u32 size, PatchHeap& patchHeap)
|
||||
: PatchCode(code, size, patchHeap) { }
|
||||
|
||||
public:
|
||||
/// @brief Pointer to a function for writing sectorCount SD sectors
|
||||
/// from the given src buffer to dstSector.
|
||||
typedef void (*SdWriteFunc)(u32 dstSector, const void* src, u32 sectorCount);
|
||||
|
||||
/// @brief Gets a pointer to the SD write function in the patch code.
|
||||
/// @return The pointer to the SD write function.
|
||||
virtual const SdWriteFunc GetSdWriteFunction() const = 0;
|
||||
};
|
||||
25
arm9/source/patches/platform/SdioDefinitions.h
Normal file
25
arm9/source/patches/platform/SdioDefinitions.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
/// SDIO commands.
|
||||
#define SD_CMD0_GO_IDLE_STATE 0
|
||||
#define SD_CMD2_ALL_SEND_CID 2
|
||||
#define SD_CMD3_SEND_RELATIVE_ADDR 3
|
||||
#define SD_CMD7_SELECT_CARD 7
|
||||
#define SD_CMD8_SEND_IF_COND 8
|
||||
#define SD_CMD9_SEND_CSD 9
|
||||
#define SD_CMD12_STOP_TRANSMISSION 12
|
||||
#define SD_CMD13_SEND_STATUS 13
|
||||
#define SD_CMD16_SET_BLOCKLEN 16
|
||||
#define SD_CMD55_APP_CMD 55
|
||||
|
||||
#define SD_ACMD6_SET_BUS_WIDTH 6
|
||||
#define SD_ACMD41_SD_SEND_OP_COND 41
|
||||
|
||||
/// CMD8 check pattern.
|
||||
#define SD_IF_COND_PATTERN 0x1AA
|
||||
|
||||
/// Response lengths
|
||||
#define SD_R1_RESPONSE_LENGTH_BITS 48
|
||||
#define SD_R1_RESPONSE_LENGTH_BYTES (SD_R1_RESPONSE_LENGTH_BITS / 8)
|
||||
#define SD_R2_RESPONSE_LENGTH_BITS 136
|
||||
#define SD_R2_RESPONSE_LENGTH_BYTES (SD_R2_RESPONSE_LENGTH_BITS / 8)
|
||||
38
arm9/source/patches/platform/ace3ds/Ace3DSLoaderPlatform.h
Normal file
38
arm9/source/patches/platform/ace3ds/Ace3DSLoaderPlatform.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "../LoaderPlatform.h"
|
||||
#include "ace3dsReadSdAsm.h"
|
||||
#include "ace3dsReadSdDmaAsm.h"
|
||||
#include "ace3dsWriteSdAsm.h"
|
||||
|
||||
/// @brief Implementation of LoaderPlatform for the Ace3DS+ flashcard
|
||||
class Ace3DSLoaderPlatform : public LoaderPlatform
|
||||
{
|
||||
public:
|
||||
const SdReadPatchCode* CreateSdReadPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new Ace3DSReadSdPatchCode(patchHeap);
|
||||
});
|
||||
}
|
||||
|
||||
const SdReadDmaPatchCode* CreateSdReadDmaPatchCode(PatchCodeCollection& patchCodeCollection,
|
||||
PatchHeap& patchHeap, const void* miiCardDmaCopy32Ptr) const override
|
||||
{
|
||||
return patchCodeCollection.AddUniquePatchCode<Ace3DSReadSdDmaPatchCode>(
|
||||
patchHeap, miiCardDmaCopy32Ptr);
|
||||
}
|
||||
|
||||
const SdWritePatchCode* CreateSdWritePatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new Ace3DSWriteSdPatchCode(patchHeap);
|
||||
});
|
||||
}
|
||||
|
||||
bool HasDmaSdReads() const override { return true; }
|
||||
};
|
||||
19
arm9/source/patches/platform/ace3ds/ace3dsReadSdAsm.h
Normal file
19
arm9/source/patches/platform/ace3ds/ace3dsReadSdAsm.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdReadPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(ace3ds_readsd);
|
||||
|
||||
extern "C" void ace3ds_readSd(u32 srcSector, void* dst, u32 sectorCount);
|
||||
|
||||
class Ace3DSReadSdPatchCode : public SdReadPatchCode
|
||||
{
|
||||
public:
|
||||
explicit Ace3DSReadSdPatchCode(PatchHeap& patchHeap)
|
||||
: SdReadPatchCode(SECTION_START(ace3ds_readsd), SECTION_SIZE(ace3ds_readsd), patchHeap) { }
|
||||
|
||||
const SdReadFunc GetSdReadFunction() const override
|
||||
{
|
||||
return (const SdReadFunc)GetAddressAtTarget((void*)ace3ds_readSd);
|
||||
}
|
||||
};
|
||||
83
arm9/source/patches/platform/ace3ds/ace3dsReadSdAsm.s
Normal file
83
arm9/source/patches/platform/ace3ds/ace3dsReadSdAsm.s
Normal file
@@ -0,0 +1,83 @@
|
||||
.cpu arm7tdmi
|
||||
.section "ace3ds_readsd", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = dst
|
||||
// r2 = sector count
|
||||
.global ace3ds_readSd
|
||||
.type ace3ds_readSd, %function
|
||||
ace3ds_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, =0xA7586000
|
||||
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, =0xA1586000
|
||||
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
|
||||
30
arm9/source/patches/platform/ace3ds/ace3dsReadSdDmaAsm.h
Normal file
30
arm9/source/patches/platform/ace3ds/ace3dsReadSdDmaAsm.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdReadDmaPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(ace3ds_readsddma);
|
||||
|
||||
extern "C" void ace3ds_readSdDma(u32 srcSector, u32 previousSrcSector, u32 dmaChannel, void* dst);
|
||||
extern "C" void ace3ds_finishReadSdDma(void);
|
||||
|
||||
extern u32 ace3ds_readSdDma_miiCardDmaCopy32Ptr;
|
||||
|
||||
class Ace3DSReadSdDmaPatchCode : public SdReadDmaPatchCode
|
||||
{
|
||||
public:
|
||||
explicit Ace3DSReadSdDmaPatchCode(PatchHeap& patchHeap, const void* miiCardDmaCopy32Ptr)
|
||||
: SdReadDmaPatchCode(SECTION_START(ace3ds_readsddma), SECTION_SIZE(ace3ds_readsddma), patchHeap)
|
||||
{
|
||||
ace3ds_readSdDma_miiCardDmaCopy32Ptr = (u32)miiCardDmaCopy32Ptr;
|
||||
}
|
||||
|
||||
const SdReadDmaFunc GetSdReadDmaFunction() const override
|
||||
{
|
||||
return (const SdReadDmaFunc)GetAddressAtTarget((void*)ace3ds_readSdDma);
|
||||
}
|
||||
|
||||
const SdReadDmaFinishFunc GetSdReadDmaFinishFunction() const override
|
||||
{
|
||||
return (const SdReadDmaFinishFunc)GetAddressAtTarget((void*)ace3ds_finishReadSdDma);
|
||||
}
|
||||
};
|
||||
94
arm9/source/patches/platform/ace3ds/ace3dsReadSdDmaAsm.s
Normal file
94
arm9/source/patches/platform/ace3ds/ace3dsReadSdDmaAsm.s
Normal file
@@ -0,0 +1,94 @@
|
||||
.cpu arm946e-s
|
||||
.section "ace3ds_readsddma", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = previous src sector
|
||||
// r2 = dma channel
|
||||
// r3 = dst
|
||||
.global ace3ds_readSdDma
|
||||
.type ace3ds_readSdDma, %function
|
||||
ace3ds_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, =0xA7586000
|
||||
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, ace3ds_readSdDma_miiCardDmaCopy32Ptr
|
||||
blx r6
|
||||
|
||||
BA_data_dma:
|
||||
movs r7, #0xC0 // select rom mode, with irq
|
||||
strb r7, [r4,#0x1]
|
||||
ldr r7, =0xA1586000
|
||||
str r7, [r4,#0x4]
|
||||
|
||||
pop {r4-r7,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.global ace3ds_readSdDma_miiCardDmaCopy32Ptr
|
||||
ace3ds_readSdDma_miiCardDmaCopy32Ptr:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.global ace3ds_finishReadSdDma
|
||||
.type ace3ds_finishReadSdDma, %function
|
||||
ace3ds_finishReadSdDma:
|
||||
// No cleanup needed on this platform.
|
||||
bx lr
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
19
arm9/source/patches/platform/ace3ds/ace3dsWriteSdAsm.h
Normal file
19
arm9/source/patches/platform/ace3ds/ace3dsWriteSdAsm.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdWritePatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(ace3ds_writesd);
|
||||
|
||||
extern "C" void ace3ds_writeSd(u32 dstSector, const void* src, u32 sectorCount);
|
||||
|
||||
class Ace3DSWriteSdPatchCode : public SdWritePatchCode
|
||||
{
|
||||
public:
|
||||
explicit Ace3DSWriteSdPatchCode(PatchHeap& patchHeap)
|
||||
: SdWritePatchCode(SECTION_START(ace3ds_writesd), SECTION_SIZE(ace3ds_writesd), patchHeap) { }
|
||||
|
||||
const SdWriteFunc GetSdWriteFunction() const override
|
||||
{
|
||||
return (const SdWriteFunc)GetAddressAtTarget((void*)ace3ds_writeSd);
|
||||
}
|
||||
};
|
||||
88
arm9/source/patches/platform/ace3ds/ace3dsWriteSdAsm.s
Normal file
88
arm9/source/patches/platform/ace3ds/ace3dsWriteSdAsm.s
Normal file
@@ -0,0 +1,88 @@
|
||||
.cpu arm7tdmi
|
||||
.section "ace3ds_writesd", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = dst sector
|
||||
// r1 = src
|
||||
// r2 = sector count
|
||||
.global ace3ds_writeSd
|
||||
.type ace3ds_writeSd, %function
|
||||
ace3ds_writeSd:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
sector_loop:
|
||||
movs r5, #0xBC // for calculating command checksum
|
||||
// write at sd sector 0xaabbccdd
|
||||
// BB aa bb cc dd 00 00 00
|
||||
movs r3, #0xBB
|
||||
strb r3, [r4,#0x8]
|
||||
lsrs r3, r0, #24
|
||||
eors r5, r3
|
||||
strb r3, [r4,#0x9]
|
||||
lsrs r3, r0, #16
|
||||
eors r5, r3
|
||||
strb r3, [r4,#0xA]
|
||||
lsrs r3, r0, #8
|
||||
eors r5, r3
|
||||
strb r3, [r4,#0xB]
|
||||
lsls r7, r0, #24
|
||||
lsrs r7, r7, #24 // r7 = dd
|
||||
eors r5, r7
|
||||
str r7, [r4,#0xC] // storing as little-endian puts the bottom 8 bits as first byte
|
||||
|
||||
ldr r6, =0x04100010
|
||||
|
||||
ldr r3, =0xE1586000
|
||||
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]
|
||||
strb r5, [r4,#0xF] // checksum
|
||||
|
||||
BC_poll_loop:
|
||||
ldr r3, =0xA7586000
|
||||
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
|
||||
@@ -0,0 +1,155 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/card/card.h>
|
||||
#include "ioRPGDefinitions.h"
|
||||
#include "../SdioDefinitions.h"
|
||||
#include "waitByLoop.h"
|
||||
#include "IoRPGLoaderPlatform.h"
|
||||
|
||||
bool IoRPGLoaderPlatform::InitializeSdCard()
|
||||
{
|
||||
bool isSdhc = false;
|
||||
bool isSdVersion2 = false;
|
||||
u32 responseR1 = 0;
|
||||
|
||||
// CMD0
|
||||
SdSendSdioCommand(IoRpgCmdSdio(SD_CMD0_GO_IDLE_STATE, IORPG_SDIO_NORESPONSE, 0), nullptr, 0);
|
||||
|
||||
// CMD8
|
||||
responseR1 = SdSendR1Command(SD_CMD8_SEND_IF_COND, SD_IF_COND_PATTERN);
|
||||
isSdVersion2 = responseR1 == SD_IF_COND_PATTERN;
|
||||
|
||||
for (u32 i = 0; i < 10000; i++)
|
||||
{
|
||||
// CMD55
|
||||
SdSendR1Command(SD_CMD55_APP_CMD, 0);
|
||||
|
||||
// ACMD41
|
||||
u32 argument = 0x00FF8000;
|
||||
if (isSdVersion2)
|
||||
{
|
||||
argument |= BIT(30);
|
||||
}
|
||||
responseR1 = SdSendR1Command(SD_ACMD41_SD_SEND_OP_COND, argument);
|
||||
if (responseR1 & BIT(31))
|
||||
{
|
||||
isSdhc = responseR1 & BIT(30);
|
||||
break;
|
||||
}
|
||||
waitByLoop(1666);
|
||||
}
|
||||
|
||||
// CMD2
|
||||
SdSendR2Command(SD_CMD2_ALL_SEND_CID, 0);
|
||||
|
||||
// CMD3
|
||||
responseR1 = SdSendR1Command(SD_CMD3_SEND_RELATIVE_ADDR, 0);
|
||||
u32 sdioRca = responseR1 >> 16;
|
||||
|
||||
// CMD9
|
||||
SdSendR2Command(SD_CMD9_SEND_CSD, (sdioRca << 16));
|
||||
|
||||
// CMD7
|
||||
SdSendR1Command(SD_CMD7_SELECT_CARD, (sdioRca << 16));
|
||||
|
||||
// ACMD6
|
||||
SdSendR1Command(SD_CMD55_APP_CMD, (sdioRca << 16));
|
||||
SdSendR1Command(SD_ACMD6_SET_BUS_WIDTH, 2);
|
||||
|
||||
// CMD13
|
||||
responseR1 = SdSendR1Command(SD_CMD13_SEND_STATUS, (sdioRca << 16));
|
||||
bool isSuccess = (responseR1 >> 9) == 4;
|
||||
|
||||
if (isSuccess && isSdhc)
|
||||
{
|
||||
card_romSetCmd(IORPG_CMD_SET_SD_MODE_SDHC);
|
||||
card_romStartXfer(IORPG_CTRL_POLL, false);
|
||||
card_romWaitBusy();
|
||||
PatchSdscShift();
|
||||
}
|
||||
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
void IoRPGLoaderPlatform::SdSendSdioCommand(u64 command, u8* buffer, u32 length) const
|
||||
{
|
||||
u32 flags;
|
||||
if (length == 0)
|
||||
{
|
||||
flags = IORPG_CTRL_POLL | MCCNT1_LATENCY1(80);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Actually the original driver used 4KB reads.
|
||||
// But with SDIO, it only really reads up to 136 bytes,
|
||||
// so 4KB is unnecessary.
|
||||
flags = IORPG_CTRL_READ_512B | MCCNT1_LATENCY1(40);
|
||||
}
|
||||
|
||||
// param SDIO, get response
|
||||
card_romSetCmd(command);
|
||||
card_romStartXfer(flags, false);
|
||||
|
||||
// don't poll if we aren't reading
|
||||
if (length == 0)
|
||||
{
|
||||
card_romWaitBusy();
|
||||
return;
|
||||
}
|
||||
|
||||
u32 i = 0;
|
||||
u32 data[2];
|
||||
u8* target = buffer + (length >> 3);
|
||||
do
|
||||
{
|
||||
// Read data if available
|
||||
if (card_romIsDataReady())
|
||||
{
|
||||
data[i & 1] = card_romGetData();
|
||||
if (i & 1)
|
||||
{
|
||||
target--;
|
||||
// Since every byte is a bit, read two words,
|
||||
// then pack it into a single byte to represent the
|
||||
// true value
|
||||
// BIT(7) is the needed bit from the return values
|
||||
for (int j = 0; j < 8; ++j)
|
||||
{
|
||||
u8 bit = (((u8*)data)[7 - j] & BIT(7)) >> 7;
|
||||
*target |= bit << j;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// Exit loop when we reach the number of words needed
|
||||
if (i >= (length >> 2))
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (card_romIsBusy());
|
||||
|
||||
// Here we cut the transfer short. We don't actually read all 512 bytes as seen
|
||||
// in MCCNT1.
|
||||
// Cutting the transfer length in the middle of transfer causes undefined
|
||||
// behaviour, so wait until then
|
||||
card_romWaitDataReady();
|
||||
// add a delay just in case
|
||||
waitByLoop(33);
|
||||
// Cut transmission
|
||||
REG_MCCNT1 = 0;
|
||||
card_romStartXfer(IORPG_CTRL_POLL, false);
|
||||
card_romWaitBusy();
|
||||
}
|
||||
|
||||
u32 IoRPGLoaderPlatform::SdSendR1Command(u8 cmd, u32 argument) const
|
||||
{
|
||||
u64 buffer = 0;
|
||||
SdSendSdioCommand(IoRpgCmdSdio(cmd, IORPG_SDIO_READ_RESPONSE, argument), (u8*)&buffer, SD_R1_RESPONSE_LENGTH_BITS);
|
||||
|
||||
return (u32)(buffer >> 9);
|
||||
}
|
||||
|
||||
void IoRPGLoaderPlatform::SdSendR2Command(u8 cmd, u32 argument) const
|
||||
{
|
||||
ALIGN(4) u8 ret[136 >> 3] = {};
|
||||
SdSendSdioCommand(IoRpgCmdSdio(cmd, IORPG_SDIO_READ_RESPONSE, argument), ret, SD_R2_RESPONSE_LENGTH_BITS);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "../LoaderPlatform.h"
|
||||
#include "iorpgSendSdioCommandAsm.h"
|
||||
#include "iorpgSdWaitForStateAsm.h"
|
||||
|
||||
/// @brief Implementation of LoaderPlatform for flashcarts based on the Acekard RPG family
|
||||
class IoRPGLoaderPlatform : public LoaderPlatform
|
||||
{
|
||||
public:
|
||||
explicit IoRPGLoaderPlatform(u8 ioRpgCmdSdioByte)
|
||||
: _ioRpgCmdSdioByte(ioRpgCmdSdioByte) { }
|
||||
|
||||
bool InitializeSdCard() override;
|
||||
|
||||
protected:
|
||||
virtual void PatchSdscShift() const {};
|
||||
|
||||
private:
|
||||
u32 _ioRpgCmdSdioByte;
|
||||
|
||||
/// @brief Sends an SDIO command to the cartridge.
|
||||
/// SDIO commands send every bit in a byte in order from MSB to LSB.
|
||||
/// @note This function doesn't handle unaligned reads.
|
||||
/// @param Card command to be written to REG_MCCMD.
|
||||
/// @param Pointer to buffer for the SDIO response.
|
||||
/// @param Number of bits in the SDIO response.
|
||||
void SdSendSdioCommand(u64 command, u8* buffer, u32 length) const;
|
||||
|
||||
/// @brief Sends an R1 type SDIO command.
|
||||
/// This function gets the full R1 response, and truncates it to bits 8 - 39 in a single u32.
|
||||
/// QUIRK: RPG doesn't send the MSB. We must assume the MSB is 0, and shift the response >> 1.
|
||||
/// @return full R1 response truncated to bits 8 - 39 in a single u32.
|
||||
u32 SdSendR1Command(u8 cmd, u32 argument) const;
|
||||
|
||||
/// @brief Sends an R2 type SDIO command.
|
||||
/// QUIRK: RPG doesn't send the MSB. We must assume the MSB is 0, and shift the response >> 1.
|
||||
/// TODO: actually verify this buffer. We don't actually need it in DLDI, just need to send command and clear the FIFO.
|
||||
void SdSendR2Command(u8 cmd, u32 argument) const;
|
||||
|
||||
/// @brief Builds a card command containing the SDIO command, SDIO parameter and the parameter type
|
||||
/// @param SDIO command
|
||||
/// @param Parameter type seen in ioRPGSdioParamTypes
|
||||
/// @param Parameter to SDIO command.
|
||||
/// @return A u64 to be written to REG_MCCMD0
|
||||
u64 IoRpgCmdSdio(u8 sdio, u8 paramType, u32 parameter) const
|
||||
{
|
||||
// All AKRPG-based *SD* drivers use SDIO.
|
||||
// Format:
|
||||
// 0xAABB00CC, 0xDDDDDDDD
|
||||
// AA = IORPG_CMD_SDIO_BYTE // this is different depending on the flashcart
|
||||
// BB = SDIO response type
|
||||
// CC = SDIO command
|
||||
// DDDDDDDD = SDIO parameter
|
||||
u32 command0 = (((u32)paramType << 16) | ((u32)sdio));
|
||||
return (((u64)_ioRpgCmdSdioByte << 56) | (((u64)command0) << 32) | (u64)parameter);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include <libtwl/card/card.h>
|
||||
|
||||
/// All RPG-based SDIO drivers use this as base, and add latency/size as needed
|
||||
#define IORPG_CTRL_BASE (MCCNT1_CMD_SCRAMBLE | MCCNT1_CLOCK_SCRAMBLER | MCCNT1_READ_DATA_DESCRAMBLE | MCCNT1_LATENCY2(0) | MCCNT1_LATENCY1(0))
|
||||
#define IORPG_CTRL_POLL (IORPG_CTRL_BASE | MCCNT1_ENABLE | MCCNT1_RESET_OFF | MCCNT1_LEN_0)
|
||||
#define IORPG_CTRL_READ_512B (IORPG_CTRL_POLL | MCCNT1_LEN_512)
|
||||
|
||||
/// This command instructs the SD host to switch to SDHC mode.
|
||||
#define IORPG_CMD_SET_SD_MODE_SDHC (0xC101000000000000ull)
|
||||
|
||||
/// @brief SDIO parameter types. Used with IORPG_CMD_SDIO command.
|
||||
enum ioRPGSdioParamTypes
|
||||
{
|
||||
IORPG_SDIO_NORESPONSE = 0ull,
|
||||
IORPG_SDIO_READ_RESPONSE = 1ull,
|
||||
IORPG_SDIO_READ_SINGLE_BLOCK = 3ull,
|
||||
IORPG_SDIO_READ_MULTI_BLOCK = 4ull,
|
||||
IORPG_SDIO_WRITE_SINGLE_BLOCK = 5ull,
|
||||
IORPG_SDIO_WRITE_MULTI_BLOCK = 6ull
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "thumbInstructions.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(iorpg_sdwaitforstate);
|
||||
|
||||
extern "C" void iorpg_sdWaitForState(u32 status_shift, u8 state);
|
||||
|
||||
class ioRPGSDWaitForStatePatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit ioRPGSDWaitForStatePatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(iorpg_sdwaitforstate), SECTION_SIZE(iorpg_sdwaitforstate), patchHeap) { }
|
||||
|
||||
const void* GetSDWaitForStateFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)iorpg_sdWaitForState);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
.cpu arm7tdmi
|
||||
.syntax unified
|
||||
.section "iorpg_sdwaitforstate", "ax"
|
||||
.thumb
|
||||
|
||||
.global iorpg_sdWaitForState
|
||||
.type iorpg_sdWaitForState, %function
|
||||
// r0 = state shift, some carts put it in different bytes
|
||||
// r1 = state to wait for
|
||||
iorpg_sdWaitForState:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
ldr r5, =0x04100010
|
||||
|
||||
// Reset cmd buffer
|
||||
movs r6, #0xC0
|
||||
str r6, [r4,#0x8] // storing as little-endian puts the bottom 8 bits as first byte
|
||||
movs r6, #0
|
||||
str r6, [r4,#0xC] // clear buffer
|
||||
|
||||
iorpg_sdWaitForState_read_loop:
|
||||
ldr r6, =0xA7406004
|
||||
str r6, [r4, #0x4]
|
||||
|
||||
iorpg_sdWaitForState_read_wait_data_ready:
|
||||
// Check MCCNT1_DATA_READY
|
||||
ldrb r7, [r4,#6]
|
||||
lsrs r7, r7, #8 // check if data is ready
|
||||
bcc iorpg_sdWaitForState_read_wait_data_ready // if not, loop
|
||||
|
||||
ldr r7, [r5]
|
||||
ldr r6, =0xFC2 // card ID
|
||||
cmp r7, r6
|
||||
beq iorpg_sdWaitForState_read_loop
|
||||
lsrs r7, r7, r0
|
||||
movs r6, #0xF
|
||||
ands r7, r7, r6
|
||||
cmp r1, r7
|
||||
bne iorpg_sdWaitForState_read_loop
|
||||
|
||||
pop {r4-r7,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "thumbInstructions.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(iorpg_sendsdiocommand);
|
||||
|
||||
extern "C" void iorpg_sendSdioCommand(u8 sdio, u8 param_type, u32 param, u32 read_len);
|
||||
|
||||
class ioRPGSendSDIOCommandPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit ioRPGSendSDIOCommandPatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(iorpg_sendsdiocommand), SECTION_SIZE(iorpg_sendsdiocommand), patchHeap) { }
|
||||
|
||||
const void* GetSendSdioCommandFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)iorpg_sendSdioCommand);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
.cpu arm7tdmi
|
||||
.syntax unified
|
||||
.section "iorpg_sendsdiocommand", "ax"
|
||||
.thumb
|
||||
|
||||
// r0 = first 4 bytes of card command, bswap32'd
|
||||
// r1 = param
|
||||
.global iorpg_sendSdioCommand
|
||||
.type iorpg_sendSdioCommand, %function
|
||||
iorpg_sendSdioCommand:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
|
||||
// start copy to MCCMD1
|
||||
str r0, [r4,#0x8]
|
||||
// SDIO param
|
||||
// need to bswap32...
|
||||
lsrs r6, r1, #24
|
||||
strb r6, [r4,#0xC]
|
||||
lsrs r6, r1, #16
|
||||
strb r6, [r4,#0xD]
|
||||
lsrs r6, r1, #8
|
||||
strb r6, [r4,#0xE]
|
||||
strb r1, [r4,#0xF]
|
||||
|
||||
// flag when read size 0
|
||||
// SDIO cmds should add some latency as well
|
||||
ldr r6, =0xA0406050
|
||||
str r6, [r4, #0x4]
|
||||
|
||||
// loop until card is ready
|
||||
iorpg_sendSdioCommand_read_loop:
|
||||
// Check MCCNT1_ENABLE
|
||||
ldrb r5, [r4,#7]
|
||||
lsrs r5, r5, #8
|
||||
bcs iorpg_sendSdioCommand_read_loop
|
||||
|
||||
pop {r4-r7,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
64
arm9/source/patches/platform/ak2/AK2LoaderPlatform.h
Normal file
64
arm9/source/patches/platform/ak2/AK2LoaderPlatform.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "../acekard-common/IoRPGLoaderPlatform.h"
|
||||
#include "ak2ReadSdAsm.h"
|
||||
#include "ak2SdReadSectorAsm.h"
|
||||
#include "ak2WriteSdAsm.h"
|
||||
|
||||
/// @brief Implementation of LoaderPlatform for the Acekard 2 flashcard
|
||||
class AK2LoaderPlatform : public IoRPGLoaderPlatform
|
||||
{
|
||||
private:
|
||||
enum
|
||||
{
|
||||
IORPG_CMD_SDIO_BYTE = 0xD5
|
||||
};
|
||||
|
||||
public:
|
||||
AK2LoaderPlatform() : IoRPGLoaderPlatform(IORPG_CMD_SDIO_BYTE) { }
|
||||
|
||||
const SdReadPatchCode* CreateSdReadPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
auto* waitForStatePatchCode = patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new ioRPGSDWaitForStatePatchCode(patchHeap);
|
||||
});
|
||||
return new AK2ReadSdPatchCode(patchHeap,
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new ioRPGSendSDIOCommandPatchCode(patchHeap);
|
||||
}),
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new AK2SDReadSectorPatchCode(patchHeap, waitForStatePatchCode);
|
||||
}),
|
||||
waitForStatePatchCode);
|
||||
});
|
||||
}
|
||||
|
||||
const SdWritePatchCode* CreateSdWritePatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new AK2WriteSdPatchCode(patchHeap,
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new ioRPGSendSDIOCommandPatchCode(patchHeap);
|
||||
}),
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new ioRPGSDWaitForStatePatchCode(patchHeap);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
void PatchSdscShift(void) const override
|
||||
{
|
||||
ak2_readSd_sdsc_shift = THUMB_MOVS_REG(THUMB_R4, THUMB_R0);
|
||||
ak2_writeSd_sdsc_shift = THUMB_MOVS_REG(THUMB_R7, THUMB_R0);
|
||||
}
|
||||
};
|
||||
36
arm9/source/patches/platform/ak2/ak2ReadSdAsm.h
Normal file
36
arm9/source/patches/platform/ak2/ak2ReadSdAsm.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "ak2SdReadSectorAsm.h"
|
||||
#include "../SdReadPatchCode.h"
|
||||
#include "../acekard-common/iorpgSendSdioCommandAsm.h"
|
||||
#include "../acekard-common/iorpgSdWaitForStateAsm.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(ak2_readsd);
|
||||
|
||||
extern "C" void ak2_readSd(u32 srcSector, void* dst, u32 sectorCount);
|
||||
|
||||
extern u32 ak2_readSd_sendSdioCommand_address;
|
||||
extern u32 ak2_readSd_sdReadSector_address;
|
||||
extern u32 ak2_readSd_sdWaitForState_address;
|
||||
extern u16 ak2_readSd_sdsc_shift;
|
||||
|
||||
class AK2ReadSdPatchCode : public SdReadPatchCode
|
||||
{
|
||||
public:
|
||||
explicit AK2ReadSdPatchCode(PatchHeap& patchHeap,
|
||||
const ioRPGSendSDIOCommandPatchCode* iorpgSendSdioCommandPatchCode,
|
||||
const AK2SDReadSectorPatchCode* ak2SdReadSectorPatchCode,
|
||||
const ioRPGSDWaitForStatePatchCode* iorpgSdWaitForStatePatchCode)
|
||||
: SdReadPatchCode(SECTION_START(ak2_readsd), SECTION_SIZE(ak2_readsd), patchHeap)
|
||||
{
|
||||
ak2_readSd_sendSdioCommand_address = (u32)iorpgSendSdioCommandPatchCode->GetSendSdioCommandFunction();
|
||||
ak2_readSd_sdReadSector_address = (u32)ak2SdReadSectorPatchCode->GetSDReadSectorFunction();
|
||||
ak2_readSd_sdWaitForState_address = (u32)iorpgSdWaitForStatePatchCode->GetSDWaitForStateFunction();
|
||||
}
|
||||
|
||||
const SdReadFunc GetSdReadFunction() const override
|
||||
{
|
||||
return (const SdReadFunc)GetAddressAtTarget((void*)ak2_readSd);
|
||||
}
|
||||
};
|
||||
69
arm9/source/patches/platform/ak2/ak2ReadSdAsm.s
Normal file
69
arm9/source/patches/platform/ak2/ak2ReadSdAsm.s
Normal file
@@ -0,0 +1,69 @@
|
||||
.cpu arm7tdmi
|
||||
.syntax unified
|
||||
.section "ak2_readsd", "ax"
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = dst
|
||||
// r2 = sector count
|
||||
.global ak2_readSd
|
||||
.type ak2_readSd, %function
|
||||
ak2_readSd:
|
||||
push {r4,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
.global ak2_readSd_sdsc_shift
|
||||
ak2_readSd_sdsc_shift:
|
||||
lsls r4, r0, #9
|
||||
|
||||
push {r0-r1}
|
||||
|
||||
ldr r0, =0x120004D5
|
||||
movs r1, r4
|
||||
ldr r3, ak2_readSd_sendSdioCommand_address
|
||||
bl blx_r3
|
||||
|
||||
// Wait for SD state
|
||||
movs r0, #4
|
||||
movs r1, #7
|
||||
ldr r3, ak2_readSd_sdWaitForState_address
|
||||
bl blx_r3
|
||||
|
||||
pop {r0-r1}
|
||||
|
||||
// Read sectors. Parameters identical to readSd
|
||||
ldr r3, ak2_readSd_sdReadSector_address
|
||||
bl blx_r3
|
||||
|
||||
// Send CMD12 == STOP_TRANSMISSION
|
||||
push {r0-r1}
|
||||
ldr r0, =0x0C0001D5
|
||||
movs r1, #0
|
||||
ldr r3, ak2_readSd_sendSdioCommand_address
|
||||
bl blx_r3
|
||||
|
||||
pop {r0-r1,r4,pc}
|
||||
|
||||
blx_r3:
|
||||
bx r3
|
||||
|
||||
.balign 4
|
||||
|
||||
.global ak2_readSd_sendSdioCommand_address
|
||||
ak2_readSd_sendSdioCommand_address:
|
||||
.word 0
|
||||
|
||||
.global ak2_readSd_sdReadSector_address
|
||||
ak2_readSd_sdReadSector_address:
|
||||
.word 0
|
||||
|
||||
.global ak2_readSd_sdWaitForState_address
|
||||
ak2_readSd_sdWaitForState_address:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
26
arm9/source/patches/platform/ak2/ak2SdReadSectorAsm.h
Normal file
26
arm9/source/patches/platform/ak2/ak2SdReadSectorAsm.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "../acekard-common/iorpgSdWaitForStateAsm.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(ak2_sdreadsector);
|
||||
|
||||
extern "C" void ak2_sdReadSector(u32 srcSector, void* dst, u32 sectorCount);
|
||||
|
||||
extern u32 ak2_sdReadSector_sdWaitForState_address;
|
||||
|
||||
class AK2SDReadSectorPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit AK2SDReadSectorPatchCode(PatchHeap& patchHeap,
|
||||
const ioRPGSDWaitForStatePatchCode* iorpgSdWaitForStatePatchCode)
|
||||
: PatchCode(SECTION_START(ak2_sdreadsector), SECTION_SIZE(ak2_sdreadsector), patchHeap)
|
||||
{
|
||||
ak2_sdReadSector_sdWaitForState_address = (u32)iorpgSdWaitForStatePatchCode->GetSDWaitForStateFunction();
|
||||
}
|
||||
|
||||
const void* GetSDReadSectorFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)ak2_sdReadSector);
|
||||
}
|
||||
};
|
||||
67
arm9/source/patches/platform/ak2/ak2SdReadSectorAsm.s
Normal file
67
arm9/source/patches/platform/ak2/ak2SdReadSectorAsm.s
Normal file
@@ -0,0 +1,67 @@
|
||||
.cpu arm7tdmi
|
||||
.syntax unified
|
||||
.section "ak2_sdreadsector", "ax"
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = dst
|
||||
// r2 = sector count
|
||||
.global ak2_sdReadSector
|
||||
.type ak2_sdReadSector, %function
|
||||
ak2_sdReadSector:
|
||||
push {r4-r5,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
ldr r5, =0x04100010
|
||||
|
||||
sector_loop:
|
||||
// NOW we can start reading things
|
||||
movs r3, #0xB7
|
||||
str r3, [r4,#0x8]
|
||||
// The full CMD is B7 00 00 00 00 13 00 00
|
||||
// If we use 0x1300 and str, then it goes into the expected place
|
||||
movs r3, #0x13
|
||||
lsls r3, r3, #8
|
||||
str r3, [r4,#0xC]
|
||||
|
||||
ldr r3, =0xA1406004
|
||||
str r3, [r4,#4]
|
||||
|
||||
ak2_sdReadSector_read_loop:
|
||||
ldrb r3, [r4,#6]
|
||||
lsrs r3, r3, #8 // check if data is ready
|
||||
bcc ak2_sdReadSector_read_loop_check_transfer_end // if not skip reading
|
||||
|
||||
ldr r3, [r5]
|
||||
stmia r1!, {r3}
|
||||
|
||||
ak2_sdReadSector_read_loop_check_transfer_end:
|
||||
ldrb r3, [r4,#7]
|
||||
lsrs r3, r3, #8 // check if transfer is done
|
||||
bcs ak2_sdReadSector_read_loop
|
||||
|
||||
// Wait for SD state
|
||||
push {r0-r1}
|
||||
movs r0, #4
|
||||
movs r1, #7
|
||||
ldr r3, ak2_sdReadSector_sdWaitForState_address
|
||||
bl blx_r3
|
||||
pop {r0-r1}
|
||||
|
||||
subs r2, #1
|
||||
bne sector_loop
|
||||
|
||||
pop {r4-r5,pc}
|
||||
|
||||
blx_r3:
|
||||
bx r3
|
||||
|
||||
.balign 4
|
||||
|
||||
.global ak2_sdReadSector_sdWaitForState_address
|
||||
ak2_sdReadSector_sdWaitForState_address:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
32
arm9/source/patches/platform/ak2/ak2WriteSdAsm.h
Normal file
32
arm9/source/patches/platform/ak2/ak2WriteSdAsm.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "../SdWritePatchCode.h"
|
||||
#include "../acekard-common/iorpgSendSdioCommandAsm.h"
|
||||
#include "../acekard-common/iorpgSdWaitForStateAsm.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(ak2_writesd);
|
||||
|
||||
extern "C" void ak2_writeSd(u32 srcSector, void* dst, u32 sectorCount);
|
||||
|
||||
extern u32 ak2_writeSd_sendSdioCommand_address;
|
||||
extern u32 ak2_writeSd_sdWaitForState_address;
|
||||
extern u16 ak2_writeSd_sdsc_shift;
|
||||
|
||||
class AK2WriteSdPatchCode : public SdWritePatchCode
|
||||
{
|
||||
public:
|
||||
explicit AK2WriteSdPatchCode(PatchHeap& patchHeap,
|
||||
const ioRPGSendSDIOCommandPatchCode* iorpgSendSdioCommandPatchCode,
|
||||
const ioRPGSDWaitForStatePatchCode* iorpgSdWaitForStatePatchCode)
|
||||
: SdWritePatchCode(SECTION_START(ak2_writesd), SECTION_SIZE(ak2_writesd), patchHeap)
|
||||
{
|
||||
ak2_writeSd_sendSdioCommand_address = (u32)iorpgSendSdioCommandPatchCode->GetSendSdioCommandFunction();
|
||||
ak2_writeSd_sdWaitForState_address = (u32)iorpgSdWaitForStatePatchCode->GetSDWaitForStateFunction();
|
||||
}
|
||||
|
||||
const SdWriteFunc GetSdWriteFunction() const override
|
||||
{
|
||||
return (const SdWriteFunc)GetAddressAtTarget((void*)ak2_writeSd);
|
||||
}
|
||||
};
|
||||
81
arm9/source/patches/platform/ak2/ak2WriteSdAsm.s
Normal file
81
arm9/source/patches/platform/ak2/ak2WriteSdAsm.s
Normal file
@@ -0,0 +1,81 @@
|
||||
.cpu arm7tdmi
|
||||
.syntax unified
|
||||
.section "ak2_writesd", "ax"
|
||||
.thumb
|
||||
|
||||
// r0 = dst sector
|
||||
// r1 = src
|
||||
// r2 = sector count
|
||||
.global ak2_writeSd
|
||||
.type ak2_writeSd, %function
|
||||
ak2_writeSd:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
sector_loop:
|
||||
.global ak2_writeSd_sdsc_shift
|
||||
ak2_writeSd_sdsc_shift:
|
||||
lsls r7, r0, #9
|
||||
|
||||
push {r0-r1}
|
||||
// SDIO cmd
|
||||
ldr r0, =0x180005D5
|
||||
// param
|
||||
movs r1, r7
|
||||
ldr r6, ak2_writeSd_sendSdioCommand_address
|
||||
bl blx_r6
|
||||
pop {r0-r1}
|
||||
|
||||
// NOW we can start writing things
|
||||
movs r7, #1
|
||||
lsls r7, r7, #9
|
||||
ak2_writeSd_block_write_loop:
|
||||
// start copy to MCCMD1
|
||||
ldmia r1!, {r5, r6}
|
||||
str r5, [r4, #0x8]
|
||||
str r6, [r4, #0xc]
|
||||
|
||||
ldr r3, =0xA0406000
|
||||
str r3, [r4,#4]
|
||||
|
||||
ak2_writeSd_block_write_wait_busy:
|
||||
ldr r3, [r4,#4]
|
||||
cmp r3, #0
|
||||
blt ak2_writeSd_block_write_wait_busy
|
||||
|
||||
subs r7, r7, #8
|
||||
bne ak2_writeSd_block_write_loop
|
||||
|
||||
push {r0-r1}
|
||||
// Wait for SD state
|
||||
movs r0, #4
|
||||
movs r1, #0
|
||||
ldr r6, ak2_writeSd_sdWaitForState_address
|
||||
bl blx_r6
|
||||
pop {r0-r1}
|
||||
|
||||
adds r0, #1
|
||||
subs r2, #1
|
||||
bne sector_loop
|
||||
|
||||
pop {r4-r7,pc}
|
||||
|
||||
blx_r6:
|
||||
bx r6
|
||||
|
||||
.balign 4
|
||||
|
||||
.global ak2_writeSd_sendSdioCommand_address
|
||||
ak2_writeSd_sendSdioCommand_address:
|
||||
.word 0
|
||||
|
||||
.global ak2_writeSd_sdWaitForState_address
|
||||
ak2_writeSd_sdWaitForState_address:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
64
arm9/source/patches/platform/akrpg/AKRPGLoaderPlatform.h
Normal file
64
arm9/source/patches/platform/akrpg/AKRPGLoaderPlatform.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "../acekard-common/IoRPGLoaderPlatform.h"
|
||||
#include "akrpgReadSdAsm.h"
|
||||
#include "akrpgSdReadSectorAsm.h"
|
||||
#include "akrpgWriteSdAsm.h"
|
||||
|
||||
/// @brief Implementation of LoaderPlatform for the Acekard RPG SD card.
|
||||
class AKRPGLoaderPlatform : public IoRPGLoaderPlatform
|
||||
{
|
||||
private:
|
||||
enum
|
||||
{
|
||||
IORPG_CMD_SDIO_BYTE = 0xD5
|
||||
};
|
||||
|
||||
public:
|
||||
AKRPGLoaderPlatform() : IoRPGLoaderPlatform(IORPG_CMD_SDIO_BYTE) { }
|
||||
|
||||
const SdReadPatchCode* CreateSdReadPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
auto* waitForStatePatchCode = patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new ioRPGSDWaitForStatePatchCode(patchHeap);
|
||||
});
|
||||
return new AKRPGReadSdPatchCode(patchHeap,
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new ioRPGSendSDIOCommandPatchCode(patchHeap);
|
||||
}),
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new AKRPGSDReadSectorPatchCode(patchHeap, waitForStatePatchCode);
|
||||
}),
|
||||
waitForStatePatchCode);
|
||||
});
|
||||
}
|
||||
|
||||
const SdWritePatchCode* CreateSdWritePatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new AKRPGWriteSdPatchCode(patchHeap,
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new ioRPGSendSDIOCommandPatchCode(patchHeap);
|
||||
}),
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new ioRPGSDWaitForStatePatchCode(patchHeap);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
void PatchSdscShift(void) const override
|
||||
{
|
||||
akrpg_readSd_sdsc_shift = THUMB_MOVS_REG(THUMB_R4, THUMB_R0);
|
||||
akrpg_writeSd_sdsc_shift = THUMB_MOVS_REG(THUMB_R7, THUMB_R0);
|
||||
}
|
||||
};
|
||||
36
arm9/source/patches/platform/akrpg/akrpgReadSdAsm.h
Normal file
36
arm9/source/patches/platform/akrpg/akrpgReadSdAsm.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "akrpgSdReadSectorAsm.h"
|
||||
#include "../SdReadPatchCode.h"
|
||||
#include "../acekard-common/iorpgSendSdioCommandAsm.h"
|
||||
#include "../acekard-common/iorpgSdWaitForStateAsm.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(akrpg_readsd);
|
||||
|
||||
extern "C" void akrpg_readSd(u32 srcSector, void* dst, u32 sectorCount);
|
||||
|
||||
extern u32 akrpg_readSd_sendSdioCommand_address;
|
||||
extern u32 akrpg_readSd_sdReadSector_address;
|
||||
extern u32 akrpg_readSd_sdWaitForState_address;
|
||||
extern u16 akrpg_readSd_sdsc_shift;
|
||||
|
||||
class AKRPGReadSdPatchCode : public SdReadPatchCode
|
||||
{
|
||||
public:
|
||||
explicit AKRPGReadSdPatchCode(PatchHeap& patchHeap,
|
||||
const ioRPGSendSDIOCommandPatchCode* iorpgSendSdioCommandPatchCode,
|
||||
const AKRPGSDReadSectorPatchCode* akrpgSdReadSectorPatchCode,
|
||||
const ioRPGSDWaitForStatePatchCode* iorpgSdWaitForStatePatchCode)
|
||||
: SdReadPatchCode(SECTION_START(akrpg_readsd), SECTION_SIZE(akrpg_readsd), patchHeap)
|
||||
{
|
||||
akrpg_readSd_sendSdioCommand_address = (u32)iorpgSendSdioCommandPatchCode->GetSendSdioCommandFunction();
|
||||
akrpg_readSd_sdReadSector_address = (u32)akrpgSdReadSectorPatchCode->GetSDReadSectorFunction();
|
||||
akrpg_readSd_sdWaitForState_address = (u32)iorpgSdWaitForStatePatchCode->GetSDWaitForStateFunction();
|
||||
}
|
||||
|
||||
const SdReadFunc GetSdReadFunction() const override
|
||||
{
|
||||
return (const SdReadFunc)GetAddressAtTarget((void*)akrpg_readSd);
|
||||
}
|
||||
};
|
||||
69
arm9/source/patches/platform/akrpg/akrpgReadSdAsm.s
Normal file
69
arm9/source/patches/platform/akrpg/akrpgReadSdAsm.s
Normal file
@@ -0,0 +1,69 @@
|
||||
.cpu arm7tdmi
|
||||
.syntax unified
|
||||
.section "akrpg_readsd", "ax"
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = dst
|
||||
// r2 = sector count
|
||||
.global akrpg_readSd
|
||||
.type akrpg_readSd, %function
|
||||
akrpg_readSd:
|
||||
push {r4,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
.global akrpg_readSd_sdsc_shift
|
||||
akrpg_readSd_sdsc_shift:
|
||||
lsls r4, r0, #9
|
||||
|
||||
push {r0-r1}
|
||||
|
||||
ldr r0, =0x120004D5
|
||||
movs r1, r4
|
||||
ldr r3, akrpg_readSd_sendSdioCommand_address
|
||||
bl blx_r3
|
||||
|
||||
// Wait for SD state
|
||||
movs r0, #0
|
||||
movs r1, #7
|
||||
ldr r3, akrpg_readSd_sdWaitForState_address
|
||||
bl blx_r3
|
||||
|
||||
pop {r0-r1}
|
||||
|
||||
// Read sectors. Parameters identical to readSd
|
||||
ldr r3, akrpg_readSd_sdReadSector_address
|
||||
bl blx_r3
|
||||
|
||||
// Send CMD12 == STOP_TRANSMISSION
|
||||
push {r0-r1}
|
||||
ldr r0, =0x0C0001D5
|
||||
movs r1, #0
|
||||
ldr r3, akrpg_readSd_sendSdioCommand_address
|
||||
bl blx_r3
|
||||
|
||||
pop {r0-r1,r4,pc}
|
||||
|
||||
blx_r3:
|
||||
bx r3
|
||||
|
||||
.balign 4
|
||||
|
||||
.global akrpg_readSd_sendSdioCommand_address
|
||||
akrpg_readSd_sendSdioCommand_address:
|
||||
.word 0
|
||||
|
||||
.global akrpg_readSd_sdReadSector_address
|
||||
akrpg_readSd_sdReadSector_address:
|
||||
.word 0
|
||||
|
||||
.global akrpg_readSd_sdWaitForState_address
|
||||
akrpg_readSd_sdWaitForState_address:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
26
arm9/source/patches/platform/akrpg/akrpgSdReadSectorAsm.h
Normal file
26
arm9/source/patches/platform/akrpg/akrpgSdReadSectorAsm.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "../acekard-common/iorpgSdWaitForStateAsm.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(akrpg_sdreadsector);
|
||||
|
||||
extern "C" void akrpg_sdReadSector(u32 srcSector, void* dst, u32 sectorCount);
|
||||
|
||||
extern u32 akrpg_sdReadSector_sdWaitForState_address;
|
||||
|
||||
class AKRPGSDReadSectorPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit AKRPGSDReadSectorPatchCode(PatchHeap& patchHeap,
|
||||
const ioRPGSDWaitForStatePatchCode* iorpgSdWaitForStatePatchCode)
|
||||
: PatchCode(SECTION_START(akrpg_sdreadsector), SECTION_SIZE(akrpg_sdreadsector), patchHeap)
|
||||
{
|
||||
akrpg_sdReadSector_sdWaitForState_address = (u32)iorpgSdWaitForStatePatchCode->GetSDWaitForStateFunction();
|
||||
}
|
||||
|
||||
const void* GetSDReadSectorFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)akrpg_sdReadSector);
|
||||
}
|
||||
};
|
||||
67
arm9/source/patches/platform/akrpg/akrpgSdReadSectorAsm.s
Normal file
67
arm9/source/patches/platform/akrpg/akrpgSdReadSectorAsm.s
Normal file
@@ -0,0 +1,67 @@
|
||||
.cpu arm7tdmi
|
||||
.syntax unified
|
||||
.section "akrpg_sdreadsector", "ax"
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = dst
|
||||
// r2 = sector count
|
||||
.global akrpg_sdReadSector
|
||||
.type akrpg_sdReadSector, %function
|
||||
akrpg_sdReadSector:
|
||||
push {r4-r5,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
ldr r5, =0x04100010
|
||||
|
||||
sector_loop:
|
||||
// NOW we can start reading things
|
||||
movs r3, #0xB7
|
||||
str r3, [r4,#0x8]
|
||||
// The full CMD is B7 00 00 00 00 13 00 00
|
||||
// If we use 0x1300 and str, then it goes into the expected place
|
||||
movs r3, #0x13
|
||||
lsls r3, r3, #8
|
||||
str r3, [r4,#0xC]
|
||||
|
||||
ldr r3, =0xA1406004
|
||||
str r3, [r4,#4]
|
||||
|
||||
akrpg_sdReadSector_read_loop:
|
||||
ldrb r3, [r4,#6]
|
||||
lsrs r3, r3, #8 // check if data is ready
|
||||
bcc akrpg_sdReadSector_read_loop_check_transfer_end // if not skip reading
|
||||
|
||||
ldr r3, [r5]
|
||||
stmia r1!, {r3}
|
||||
|
||||
akrpg_sdReadSector_read_loop_check_transfer_end:
|
||||
ldrb r3, [r4,#7]
|
||||
lsrs r3, r3, #8 // check if transfer is done
|
||||
bcs akrpg_sdReadSector_read_loop
|
||||
|
||||
// Wait for SD state
|
||||
push {r0-r1}
|
||||
movs r0, #0
|
||||
movs r1, #7
|
||||
ldr r3, akrpg_sdReadSector_sdWaitForState_address
|
||||
bl blx_r3
|
||||
pop {r0-r1}
|
||||
|
||||
subs r2, #1
|
||||
bne sector_loop
|
||||
|
||||
pop {r4-r5,pc}
|
||||
|
||||
blx_r3:
|
||||
bx r3
|
||||
|
||||
.balign 4
|
||||
|
||||
.global akrpg_sdReadSector_sdWaitForState_address
|
||||
akrpg_sdReadSector_sdWaitForState_address:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
32
arm9/source/patches/platform/akrpg/akrpgWriteSdAsm.h
Normal file
32
arm9/source/patches/platform/akrpg/akrpgWriteSdAsm.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "../SdWritePatchCode.h"
|
||||
#include "../acekard-common/iorpgSendSdioCommandAsm.h"
|
||||
#include "../acekard-common/iorpgSdWaitForStateAsm.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(akrpg_writesd);
|
||||
|
||||
extern "C" void akrpg_writeSd(u32 srcSector, void* dst, u32 sectorCount);
|
||||
|
||||
extern u32 akrpg_writeSd_sendSdioCommand_address;
|
||||
extern u32 akrpg_writeSd_sdWaitForState_address;
|
||||
extern u16 akrpg_writeSd_sdsc_shift;
|
||||
|
||||
class AKRPGWriteSdPatchCode : public SdWritePatchCode
|
||||
{
|
||||
public:
|
||||
explicit AKRPGWriteSdPatchCode(PatchHeap& patchHeap,
|
||||
const ioRPGSendSDIOCommandPatchCode* iorpgSendSdioCommandPatchCode,
|
||||
const ioRPGSDWaitForStatePatchCode* iorpgSdWaitForStatePatchCode)
|
||||
: SdWritePatchCode(SECTION_START(akrpg_writesd), SECTION_SIZE(akrpg_writesd), patchHeap)
|
||||
{
|
||||
akrpg_writeSd_sendSdioCommand_address = (u32)iorpgSendSdioCommandPatchCode->GetSendSdioCommandFunction();
|
||||
akrpg_writeSd_sdWaitForState_address = (u32)iorpgSdWaitForStatePatchCode->GetSDWaitForStateFunction();
|
||||
}
|
||||
|
||||
const SdWriteFunc GetSdWriteFunction() const override
|
||||
{
|
||||
return (const SdWriteFunc)GetAddressAtTarget((void*)akrpg_writeSd);
|
||||
}
|
||||
};
|
||||
81
arm9/source/patches/platform/akrpg/akrpgWriteSdAsm.s
Normal file
81
arm9/source/patches/platform/akrpg/akrpgWriteSdAsm.s
Normal file
@@ -0,0 +1,81 @@
|
||||
.cpu arm7tdmi
|
||||
.syntax unified
|
||||
.section "akrpg_writesd", "ax"
|
||||
.thumb
|
||||
|
||||
// r0 = dst sector
|
||||
// r1 = src
|
||||
// r2 = sector count
|
||||
.global akrpg_writeSd
|
||||
.type akrpg_writeSd, %function
|
||||
akrpg_writeSd:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
sector_loop:
|
||||
.global akrpg_writeSd_sdsc_shift
|
||||
akrpg_writeSd_sdsc_shift:
|
||||
lsls r7, r0, #9
|
||||
|
||||
push {r0-r1}
|
||||
// SDIO cmd
|
||||
ldr r0, =0x180005D5
|
||||
// param
|
||||
movs r1, r7
|
||||
ldr r6, akrpg_writeSd_sendSdioCommand_address
|
||||
bl blx_r6
|
||||
pop {r0-r1}
|
||||
|
||||
// NOW we can start writing things
|
||||
movs r7, #1
|
||||
lsls r7, r7, #9
|
||||
akrpg_writeSd_block_write_loop:
|
||||
// start copy to MCCMD1
|
||||
ldmia r1!, {r5, r6}
|
||||
str r5, [r4, #0x8]
|
||||
str r6, [r4, #0xc]
|
||||
|
||||
ldr r3, =0xA0406000
|
||||
str r3, [r4,#4]
|
||||
|
||||
akrpg_writeSd_block_write_wait_busy:
|
||||
ldr r3, [r4,#4]
|
||||
cmp r3, #0
|
||||
blt akrpg_writeSd_block_write_wait_busy
|
||||
|
||||
subs r7, r7, #8
|
||||
bne akrpg_writeSd_block_write_loop
|
||||
|
||||
push {r0-r1}
|
||||
// Wait for SD state
|
||||
movs r0, #0
|
||||
movs r1, #0
|
||||
ldr r6, akrpg_writeSd_sdWaitForState_address
|
||||
bl blx_r6
|
||||
pop {r0-r1}
|
||||
|
||||
adds r0, #1
|
||||
subs r2, #1
|
||||
bne sector_loop
|
||||
|
||||
pop {r4-r7,pc}
|
||||
|
||||
blx_r6:
|
||||
bx r6
|
||||
|
||||
.balign 4
|
||||
|
||||
.global akrpg_writeSd_sendSdioCommand_address
|
||||
akrpg_writeSd_sendSdioCommand_address:
|
||||
.word 0
|
||||
|
||||
.global akrpg_writeSd_sdWaitForState_address
|
||||
akrpg_writeSd_sdWaitForState_address:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
47
arm9/source/patches/platform/dspico/DSPicoLoaderPlatform.h
Normal file
47
arm9/source/patches/platform/dspico/DSPicoLoaderPlatform.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "../LoaderPlatform.h"
|
||||
#include "dspicoReadSectorsAsm.h"
|
||||
#include "dspicoReadSdSectorDmaAsm.h"
|
||||
#include "dspicoWriteSectorsAsm.h"
|
||||
|
||||
/// @brief Implementation of LoaderPlatform for the DS pico flashcard
|
||||
class DSPicoLoaderPlatform : public LoaderPlatform
|
||||
{
|
||||
public:
|
||||
const SdReadPatchCode* CreateSdReadPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new DSPicoReadSdSectorsPatchCode(patchHeap,
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new DSPicoReadSdSectorsDirectPatchCode(patchHeap);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
const SdReadDmaPatchCode* CreateSdReadDmaPatchCode(PatchCodeCollection& patchCodeCollection,
|
||||
PatchHeap& patchHeap, const void* miiCardDmaCopy32Ptr) const override
|
||||
{
|
||||
auto pollSdDataReadyPatchCode = patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new DSPicoReadSdSectorDmaPollSdDataReadyPatchCode(patchHeap);
|
||||
});
|
||||
|
||||
return patchCodeCollection.AddUniquePatchCode<DSPicoReadSdSectorDmaPatchCode>(
|
||||
patchHeap, pollSdDataReadyPatchCode, miiCardDmaCopy32Ptr);
|
||||
}
|
||||
|
||||
const SdWritePatchCode* CreateSdWritePatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new DSPicoWriteSdSectorsPatchCode(patchHeap);
|
||||
});
|
||||
}
|
||||
|
||||
bool HasDmaSdReads() const override { return true; }
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdReadDmaPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(dspico_readsdsectordma);
|
||||
DEFINE_SECTION_SYMBOLS(dspico_readsdsectordma_pollSdDataReady);
|
||||
|
||||
extern "C" void dspico_readSdSectorDma(u32 srcSector, u32 previousSrcSector, u32 dmaChannel, void* dst);
|
||||
extern "C" void dspico_readSdSectorDma_pollSdDataReady();
|
||||
extern "C" void dspico_finishReadSdSectorDma(void);
|
||||
|
||||
extern u32 dspico_readSdSectorDma_miiCardDmaCopy32Ptr;
|
||||
extern u32 dspico_readSdSectorDma_pollSdDataReadyPtr;
|
||||
|
||||
class DSPicoReadSdSectorDmaPollSdDataReadyPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit DSPicoReadSdSectorDmaPollSdDataReadyPatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(dspico_readsdsectordma_pollSdDataReady), SECTION_SIZE(dspico_readsdsectordma_pollSdDataReady), patchHeap) { }
|
||||
|
||||
const void* GetPollSdDataReadyFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)dspico_readSdSectorDma_pollSdDataReady);
|
||||
}
|
||||
|
||||
const SdReadDmaPatchCode::SdReadDmaFinishFunc GetSdReadDmaFinishFunction() const
|
||||
{
|
||||
return (const SdReadDmaPatchCode::SdReadDmaFinishFunc)GetAddressAtTarget((void*)dspico_finishReadSdSectorDma);
|
||||
}
|
||||
};
|
||||
|
||||
class DSPicoReadSdSectorDmaPatchCode : public SdReadDmaPatchCode
|
||||
{
|
||||
public:
|
||||
DSPicoReadSdSectorDmaPatchCode(PatchHeap& patchHeap,
|
||||
const DSPicoReadSdSectorDmaPollSdDataReadyPatchCode* pollSdDataReadyPatchCode, const void* miiCardDmaCopy32Ptr)
|
||||
: SdReadDmaPatchCode(SECTION_START(dspico_readsdsectordma), SECTION_SIZE(dspico_readsdsectordma), patchHeap)
|
||||
, _sdReadDmaFinishFunc(pollSdDataReadyPatchCode->GetSdReadDmaFinishFunction())
|
||||
{
|
||||
dspico_readSdSectorDma_miiCardDmaCopy32Ptr = (u32)miiCardDmaCopy32Ptr;
|
||||
dspico_readSdSectorDma_pollSdDataReadyPtr = (u32)pollSdDataReadyPatchCode->GetPollSdDataReadyFunction();
|
||||
}
|
||||
|
||||
const SdReadDmaFunc GetSdReadDmaFunction() const override
|
||||
{
|
||||
return (const SdReadDmaFunc)GetAddressAtTarget((void*)dspico_readSdSectorDma);
|
||||
}
|
||||
|
||||
const SdReadDmaFinishFunc GetSdReadDmaFinishFunction() const override
|
||||
{
|
||||
return _sdReadDmaFinishFunc;
|
||||
}
|
||||
|
||||
private:
|
||||
const SdReadDmaFinishFunc _sdReadDmaFinishFunc;
|
||||
};
|
||||
135
arm9/source/patches/platform/dspico/dspicoReadSdSectorDmaAsm.s
Normal file
135
arm9/source/patches/platform/dspico/dspicoReadSdSectorDmaAsm.s
Normal file
@@ -0,0 +1,135 @@
|
||||
.cpu arm946e-s
|
||||
.section "dspico_readsdsectordma", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = previous src sector
|
||||
// r2 = dma channel
|
||||
// r3 = dst
|
||||
.global dspico_readSdSectorDma
|
||||
.type dspico_readSdSectorDma, %function
|
||||
dspico_readSdSectorDma:
|
||||
push {r2,r3,r4,r5,r6,r7,lr}
|
||||
adr r5, readRequestSettings
|
||||
// readRequestSettings, regBase, cardDataRegAddress, miiCardDmaCopy32Ptr, pollSdDataReadyPtr
|
||||
ldmia r5, {r2,r4,r5,r6,r7}
|
||||
movs r3, #0x80
|
||||
strb r3, [r4, #0x9] // select rom mode, without irq
|
||||
|
||||
cmp r1, #0 // if first sector
|
||||
beq 1f // first sector, skip poll
|
||||
blx r7 // pollSdDataReady
|
||||
1:
|
||||
subs r1, r0, r1
|
||||
cmp r1, #1 // if sequential
|
||||
beq readDataWithDma
|
||||
|
||||
// card_romSetCmd(0xE300000000000000ull | sector);
|
||||
movs r3, #0xE3
|
||||
str r3, [r4, #0x10] // E3 00 00 00
|
||||
movs r3, r0
|
||||
rors r3, r4 // ror 24
|
||||
str r3, [r4, #0x14] // AA XX CC XX
|
||||
strb r0, [r4, #0x17] // AA XX CC DD
|
||||
lsrs r3, r0, #16
|
||||
strb r3, [r4, #0x15]
|
||||
|
||||
// card_romStartXfer
|
||||
str r2, [r4, #0xC] // REG_MCCNT1 = MCCNT1_ENABLE | settings;
|
||||
|
||||
blx r7 // pollSdDataReady
|
||||
|
||||
readDataWithDma:
|
||||
pop {r0,r2} // dma channel, dst
|
||||
movs r1, r5 // src = 0x04100010
|
||||
movs r3, #1
|
||||
lsls r3, r3, #9 // (1 << 9) = 512 = count
|
||||
blx r6
|
||||
|
||||
3:
|
||||
ldrb r0, [r4, #0xF]
|
||||
lsrs r0, r0, #8
|
||||
bcs 3b
|
||||
// r0 is zero here
|
||||
|
||||
str r0, [r4, #0x14] // 00 00 00 00
|
||||
movs r0, #0xE5
|
||||
str r0, [r4, #0x10] // E5 00 00 00
|
||||
|
||||
ldr r0,= 0xA1444000
|
||||
|
||||
movs r1, #0xC0
|
||||
strb r1, [r4, #0x9] // select rom mode, with irq
|
||||
|
||||
str r0, [r4, #0xC] // REG_MCCNT1 = MCCNT1_ENABLE | settings;
|
||||
|
||||
pop {r4,r5,r6,r7,pc} // popping pc is safe for armv4t when not switching mode
|
||||
|
||||
.balign 4
|
||||
|
||||
readRequestSettings:
|
||||
.word 0xA0406000
|
||||
|
||||
regBase:
|
||||
.word 0x04000198
|
||||
|
||||
cardDataRegAddress:
|
||||
.word 0x04100010
|
||||
|
||||
.global dspico_readSdSectorDma_miiCardDmaCopy32Ptr
|
||||
dspico_readSdSectorDma_miiCardDmaCopy32Ptr:
|
||||
.word 0
|
||||
|
||||
.global dspico_readSdSectorDma_pollSdDataReadyPtr
|
||||
dspico_readSdSectorDma_pollSdDataReadyPtr:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.section "dspico_readsdsectordma_pollSdDataReady", "ax"
|
||||
.thumb
|
||||
.global dspico_readSdSectorDma_pollSdDataReady
|
||||
.type dspico_readSdSectorDma_pollSdDataReady, %function
|
||||
dspico_readSdSectorDma_pollSdDataReady:
|
||||
ldrb r3, [r4, #0xF]
|
||||
lsrs r3, r3, #8
|
||||
bcs dspico_readSdSectorDma_pollSdDataReady
|
||||
// r3 is zero here
|
||||
|
||||
// card_romSetCmd(0xE400000000000000ull);
|
||||
str r3, [r4, #0x14] // 00 00 00 00
|
||||
movs r3, #0xE4
|
||||
str r3, [r4, #0x10] // E4 00 00 00
|
||||
|
||||
// card_romStartXfer
|
||||
ldr r3,= 0xA7446000
|
||||
str r3, [r4, #0xC] // REG_MCCNT1 = MCCNT1_ENABLE | settings;
|
||||
|
||||
1:
|
||||
ldrb r3, [r4, #0xE]
|
||||
lsrs r3, r3, #8
|
||||
bcc 1b
|
||||
|
||||
ldr r3, [r5] // [0x04100010]
|
||||
cmp r3, #0
|
||||
beq dspico_readSdSectorDma_pollSdDataReady
|
||||
bx lr
|
||||
|
||||
.global dspico_finishReadSdSectorDma
|
||||
.type dspico_finishReadSdSectorDma, %function
|
||||
dspico_finishReadSdSectorDma:
|
||||
push {r4,r5,lr}
|
||||
ldr r4,= 0x04000198
|
||||
ldr r5,= 0x04100010
|
||||
|
||||
movs r3, #0x80
|
||||
strb r3, [r4, #0x9] // select rom mode, without irq
|
||||
|
||||
bl dspico_readSdSectorDma_pollSdDataReady
|
||||
pop {r4,r5,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
.end
|
||||
38
arm9/source/patches/platform/dspico/dspicoReadSectorsAsm.h
Normal file
38
arm9/source/patches/platform/dspico/dspicoReadSectorsAsm.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdReadPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(dspico_readsdsectors);
|
||||
DEFINE_SECTION_SYMBOLS(dspico_readsdsectors_readdirect);
|
||||
|
||||
extern "C" void dspico_readSdSectors(u32 srcSector, void* dst, u32 sectorCount);
|
||||
extern "C" void dspico_readSdSectorsDirect();
|
||||
|
||||
extern u32 dspico_readsdsectors_readDirectAddress;
|
||||
|
||||
class DSPicoReadSdSectorsDirectPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit DSPicoReadSdSectorsDirectPatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(dspico_readsdsectors_readdirect), SECTION_SIZE(dspico_readsdsectors_readdirect), patchHeap) { }
|
||||
|
||||
const void* GetReadSdSectorsDirectFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)dspico_readSdSectorsDirect);
|
||||
}
|
||||
};
|
||||
|
||||
class DSPicoReadSdSectorsPatchCode : public SdReadPatchCode
|
||||
{
|
||||
public:
|
||||
DSPicoReadSdSectorsPatchCode(PatchHeap& patchHeap, const DSPicoReadSdSectorsDirectPatchCode* readSdSectorsDirectPatchCode)
|
||||
: SdReadPatchCode(SECTION_START(dspico_readsdsectors), SECTION_SIZE(dspico_readsdsectors), patchHeap)
|
||||
{
|
||||
dspico_readsdsectors_readDirectAddress = (u32)readSdSectorsDirectPatchCode->GetReadSdSectorsDirectFunction();
|
||||
}
|
||||
|
||||
const SdReadFunc GetSdReadFunction() const override
|
||||
{
|
||||
return (const SdReadFunc)GetAddressAtTarget((void*)dspico_readSdSectors);
|
||||
}
|
||||
};
|
||||
129
arm9/source/patches/platform/dspico/dspicoReadSectorsAsm.s
Normal file
129
arm9/source/patches/platform/dspico/dspicoReadSectorsAsm.s
Normal file
@@ -0,0 +1,129 @@
|
||||
.cpu arm7tdmi
|
||||
.section "dspico_readsdsectors", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = dst
|
||||
// r2 = sector count
|
||||
.global dspico_readSdSectors
|
||||
.type dspico_readSdSectors, %function
|
||||
dspico_readSdSectors:
|
||||
push {r0,r1,r2,r4-r7,lr}
|
||||
pop {r4,r5,r6}
|
||||
|
||||
1:
|
||||
// card_romSetCmd(0xE300000000000000ull | sector);
|
||||
adr r2, read_request_settings
|
||||
ldmia r2, {r1,r2}
|
||||
movs r0, #0xE3
|
||||
str r0, [r2, #0x10] // E3 00 00 00
|
||||
movs r0, r4
|
||||
rors r0, r2 // ror 24
|
||||
str r0, [r2, #0x14] // AA XX CC XX
|
||||
strb r4, [r2, #0x17] // AA XX CC DD
|
||||
lsrs r0, r4, #16
|
||||
strb r0, [r2, #0x15]
|
||||
|
||||
// card_romStartXfer
|
||||
movs r0, #0x80
|
||||
strb r0, [r2, #0x9]
|
||||
str r1, [r2, #0xC] // REG_MCCNT1 = MCCNT1_ENABLE | settings;
|
||||
|
||||
ldr r3, dspico_readsdsectors_readDirectAddress
|
||||
bl blx_r3
|
||||
|
||||
pop {r4-r7,pc} // popping pc is safe for armv4t when not switching mode
|
||||
|
||||
blx_r3:
|
||||
bx r3
|
||||
|
||||
.balign 4
|
||||
|
||||
read_request_settings:
|
||||
.word 0xA0406000
|
||||
|
||||
reg_base:
|
||||
.word 0x04000198
|
||||
|
||||
.global dspico_readsdsectors_readDirectAddress
|
||||
dspico_readsdsectors_readDirectAddress:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.section "dspico_readsdsectors_readdirect", "ax"
|
||||
.thumb
|
||||
.global dspico_readSdSectorsDirect
|
||||
.type dspico_readSdSectorsDirect, %function
|
||||
dspico_readSdSectorsDirect:
|
||||
push {r4,r6,r7,lr}
|
||||
b poll_read_done
|
||||
1:
|
||||
movs r0, #0xE5
|
||||
str r0, [r2, #0x10] // E5 00 00 00
|
||||
movs r0, #0
|
||||
str r0, [r2, #0x14] // 00 00 00 00
|
||||
|
||||
ldr r0,= 0xA1444000
|
||||
|
||||
// card_romStartXfer
|
||||
3:
|
||||
ldrb r4, [r2, #0xF]
|
||||
lsrs r4, r4, #8
|
||||
bcs 3b
|
||||
|
||||
str r0, [r2, #0xC] // REG_MCCNT1 = MCCNT1_ENABLE | settings;
|
||||
|
||||
movs r0, #128
|
||||
ldr r4,= 0x04100000
|
||||
2:
|
||||
ldrb r3, [r2, #0xE]
|
||||
lsrs r3, r3, #8
|
||||
bcc 2b
|
||||
|
||||
ldr r3, [r4, #0x10]
|
||||
stmia r5!, {r3}
|
||||
subs r0, #1
|
||||
bne 2b
|
||||
|
||||
subs r6, #1 // count -= len
|
||||
|
||||
// card_romWaitBusy
|
||||
poll_read_done:
|
||||
3:
|
||||
ldrb r1, [r2, #0xF]
|
||||
lsrs r1, r1, #8
|
||||
bcs 3b
|
||||
|
||||
// card_romSetCmd(0xE400000000000000ull);
|
||||
movs r1, #0xE4
|
||||
str r1, [r2, #0x10] // E4 00 00 00
|
||||
movs r1, #0
|
||||
str r1, [r2, #0x14] // 00 00 00 00
|
||||
|
||||
// card_romStartXfer
|
||||
ldr r1,= 0xA7446000
|
||||
str r1, [r2, #0xC] // REG_MCCNT1 = MCCNT1_ENABLE | settings;
|
||||
|
||||
4:
|
||||
ldrb r1, [r2, #0xE]
|
||||
lsrs r1, r1, #8
|
||||
bcc 4b
|
||||
|
||||
movs r1, #0x41
|
||||
lsls r1, r1, #20 // 0x04100000
|
||||
ldr r1, [r1, #0x10]
|
||||
cmp r1, #0
|
||||
beq poll_read_done
|
||||
|
||||
cmp r6, #0
|
||||
bne 1b
|
||||
|
||||
pop {r4,r6,r7,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
19
arm9/source/patches/platform/dspico/dspicoWriteSectorsAsm.h
Normal file
19
arm9/source/patches/platform/dspico/dspicoWriteSectorsAsm.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdWritePatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(dspico_writesdsectors);
|
||||
|
||||
extern "C" void dspico_writeSdSectors(u32 dstSector, const void* src, u32 sectorCount);
|
||||
|
||||
class DSPicoWriteSdSectorsPatchCode : public SdWritePatchCode
|
||||
{
|
||||
public:
|
||||
explicit DSPicoWriteSdSectorsPatchCode(PatchHeap& patchHeap)
|
||||
: SdWritePatchCode(SECTION_START(dspico_writesdsectors), SECTION_SIZE(dspico_writesdsectors), patchHeap) { }
|
||||
|
||||
const SdWriteFunc GetSdWriteFunction() const override
|
||||
{
|
||||
return (const SdWriteFunc)GetAddressAtTarget((void*)dspico_writeSdSectors);
|
||||
}
|
||||
};
|
||||
91
arm9/source/patches/platform/dspico/dspicoWriteSectorsAsm.s
Normal file
91
arm9/source/patches/platform/dspico/dspicoWriteSectorsAsm.s
Normal file
@@ -0,0 +1,91 @@
|
||||
.cpu arm7tdmi
|
||||
.section "dspico_writesdsectors", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = dst sector
|
||||
// r1 = src
|
||||
// r2 = sector count
|
||||
.global dspico_writeSdSectors
|
||||
.type dspico_writeSdSectors, %function
|
||||
dspico_writeSdSectors:
|
||||
push {r0,r1,r2,r4-r7,lr}
|
||||
pop {r4,r5,r6}
|
||||
|
||||
1:
|
||||
// card_romSetCmd(0xF6E10D9B00000000ull | sector);
|
||||
adr r2, write_cmd_rev
|
||||
ldmia r2, {r0,r1,r2,r3}
|
||||
str r0, [r2, #0x10] // F6 E1 0D 9B
|
||||
movs r0, r4
|
||||
rors r0, r2 // ror 24
|
||||
str r0, [r2, #0x14] // AA XX CC XX
|
||||
strb r4, [r2, #0x17] // AA XX CC DD
|
||||
lsrs r0, r4, #16
|
||||
strb r0, [r2, #0x15]
|
||||
|
||||
// card_romStartXfer
|
||||
movs r0, #0x80
|
||||
strb r0, [r2, #0x9]
|
||||
str r1, [r2, #0xC] // REG_MCCNT1 = MCCNT1_ENABLE | settings;
|
||||
|
||||
movs r1, #0x41
|
||||
lsls r1, r1, #20 // 0x04100000
|
||||
2:
|
||||
ldrb r7, [r2, #0xE]
|
||||
lsrs r7, r7, #8
|
||||
bcc 2b
|
||||
|
||||
ldmia r5!, {r7}
|
||||
str r7, [r1, #0x10]
|
||||
subs r0, #1
|
||||
bne 2b
|
||||
|
||||
// card_romWaitBusy
|
||||
poll_write_done:
|
||||
3:
|
||||
ldrb r7, [r2, #0xF]
|
||||
lsrs r7, r7, #8
|
||||
bcs 3b
|
||||
|
||||
// card_romSetCmd(0xE400000000000000ull);
|
||||
movs r7, #0xE4
|
||||
str r7, [r2, #0x10] // E4 00 00 00
|
||||
movs r7, #0
|
||||
str r7, [r2, #0x14] // 00 00 00 00
|
||||
|
||||
// card_romStartXfer
|
||||
str r3, [r2, #0xC] // REG_MCCNT1 = MCCNT1_ENABLE | settings;
|
||||
|
||||
4:
|
||||
ldrb r7, [r2, #0xE]
|
||||
lsrs r7, r7, #8
|
||||
bcc 4b
|
||||
|
||||
ldr r7, [r1, #0x10]
|
||||
cmp r7, #0
|
||||
beq poll_write_done
|
||||
|
||||
adds r4, #1
|
||||
subs r6, #1
|
||||
bne 1b
|
||||
|
||||
pop {r4-r7,pc} // popping pc is safe for armv4t when not switching mode
|
||||
|
||||
.balign 4
|
||||
|
||||
write_cmd_rev:
|
||||
.word 0x9B0DE1F6
|
||||
|
||||
write_cmd_settings:
|
||||
.word 0xE1486000
|
||||
|
||||
reg_base:
|
||||
.word 0x04000198
|
||||
|
||||
write_poll_settings:
|
||||
.word 0xA7446000
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
197
arm9/source/patches/platform/dstt/DSTTLoaderPlatform.cpp
Normal file
197
arm9/source/patches/platform/dstt/DSTTLoaderPlatform.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/card/card.h>
|
||||
#include "dsttDefinitions.h"
|
||||
#include "../SdioDefinitions.h"
|
||||
#include "waitByLoop.h"
|
||||
#include "DSTTLoaderPlatform.h"
|
||||
|
||||
/// @brief Sets the mode of the SD host. Mostly used for SDIO commands.
|
||||
/// @param sdio An SDIO command to send, if any. Set 0 otherwise.
|
||||
/// @param parameter Parameter to send alongside the SDIO command, if any. Set 0 otherwise.
|
||||
/// @param response_type A response type defined in DSTTSdHostModes.
|
||||
/// @param latency Additional MCCNT1 latency to apply when applying the command.
|
||||
static u32 sdHostSetMode(u8 sdio, u32 parameter, u8 response_type, u32 latency)
|
||||
{
|
||||
u64 command = ((u64)DSTT_CMD_SD_HOST_PARAM << 56) | ((u64)parameter << 24) | ((u64)sdio << 16) | ((u64)response_type << 8);
|
||||
card_romSetCmd(command);
|
||||
card_romStartXfer(DSTT_CTRL_READ_4B | MCCNT1_LATENCY1(latency), false);
|
||||
card_romWaitDataReady();
|
||||
return card_romGetData();
|
||||
}
|
||||
|
||||
/// @brief Checks if the SD host is busy.
|
||||
/// @return 0 if not busy, 1 otherwise.
|
||||
static u32 isSdHostBusy(void)
|
||||
{
|
||||
REG_MCCMD0_U8[0] = DSTT_CMD_SD_HOST_BUSY;
|
||||
card_romStartXfer(DSTT_CTRL_READ_4B, false);
|
||||
card_romWaitDataReady();
|
||||
return card_romGetData();
|
||||
}
|
||||
|
||||
/// @brief Retrieves the response from the SD host.
|
||||
/// @return A u32 response. If the SD host was in SDIO mode, only bits 8-40 are returned.
|
||||
static u32 sdHostGetResponse(void)
|
||||
{
|
||||
REG_MCCMD0_U8[0] = DSTT_CMD_SD_HOST_RESPONSE;
|
||||
card_romStartXfer(DSTT_CTRL_READ_4B, false);
|
||||
card_romWaitDataReady();
|
||||
return __builtin_bswap32(card_romGetData());
|
||||
}
|
||||
|
||||
/// @brief Sends an SDIO R0-type command to the cartridge.
|
||||
static void sdSendR0Command(u8 sdio, u32 parameter, u32 latency)
|
||||
{
|
||||
sdHostSetMode(sdio, parameter, DSTT_SD_HOST_NORESPONSE, latency);
|
||||
while (isSdHostBusy())
|
||||
;
|
||||
}
|
||||
|
||||
/// @brief Sends an SDIO R1-type command to the cartridge.
|
||||
/// @return Bits 8-40 of the R1 response.
|
||||
static u32 sdSendR1Command(u8 sdio, u32 parameter, u32 latency)
|
||||
{
|
||||
sdHostSetMode(sdio, parameter, DSTT_SD_HOST_READ_4B, latency);
|
||||
while (isSdHostBusy())
|
||||
;
|
||||
return sdHostGetResponse();
|
||||
}
|
||||
|
||||
/// @brief Sends an SDIO R2-type command to the cartridge.
|
||||
/// TODO: save the response to a buffer (also figure out in which order they're sent)
|
||||
static void sdSendR2Command(u8 sdio, u32 parameter, u32 latency)
|
||||
{
|
||||
sdHostSetMode(sdio, parameter, DSTT_SD_HOST_READ_4B_MULTI, latency);
|
||||
while (isSdHostBusy())
|
||||
;
|
||||
|
||||
// TODO: parse this response
|
||||
sdHostGetResponse();
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
sdHostSetMode(sdio, parameter, DSTT_SD_HOST_NEXT_4B, latency);
|
||||
while (isSdHostBusy())
|
||||
;
|
||||
// TODO: parse this response
|
||||
sdHostGetResponse();
|
||||
}
|
||||
sdHostSetMode(sdio, parameter, DSTT_SD_HOST_SEND_STOP_CLK, 0);
|
||||
while (isSdHostBusy())
|
||||
;
|
||||
}
|
||||
|
||||
/// @brief Manipulates the cartridge's SD host register.
|
||||
static void sdHostSetRegister(u8 bits)
|
||||
{
|
||||
u64 command = ((u64)DSTT_CMD_SD_HOST_SET_REGISTER << 56) | ((u64)(0x30 | bits) << 48);
|
||||
card_romSetCmd(command);
|
||||
card_romStartXfer(DSTT_CTRL_READ_4B, false);
|
||||
card_romWaitDataReady();
|
||||
card_romGetData();
|
||||
waitByLoop(0x600);
|
||||
}
|
||||
|
||||
bool DSTTLoaderPlatform::InitializeSdCard(void)
|
||||
{
|
||||
bool isSdVersion2 = false;
|
||||
bool isSdhc = false;
|
||||
u32 response = 0;
|
||||
|
||||
// TODO: What is this command doing?
|
||||
card_romSetCmd(0x6600000000000000ull);
|
||||
card_romStartXfer(DSTT_CTRL_SET_CARD_MODE, false);
|
||||
card_romWaitDataReady();
|
||||
card_romGetData();
|
||||
|
||||
// Reset SD host
|
||||
sdHostSetRegister(DSTT_SD_HOST_REG_CLEAR_ALL);
|
||||
|
||||
// Init
|
||||
sdHostSetRegister(DSTT_SD_HOST_REG_RESET | DSTT_SD_HOST_REG_400KHZ_CLK | DSTT_SD_HOST_REG_CLEAN_ROM_MODE);
|
||||
sdHostSetMode(0, 0, DSTT_SD_HOST_SEND_CLK, DSTT_CTRL_SD_LOW_CLK_LATENCY);
|
||||
waitByLoop(0x2000);
|
||||
|
||||
// CMD0
|
||||
sdSendR0Command(0, 0, DSTT_CTRL_SD_LOW_CLK_LATENCY);
|
||||
sdHostSetMode(SD_CMD0_GO_IDLE_STATE, 0, DSTT_SD_HOST_SEND_STOP_CLK, DSTT_CTRL_SD_LOW_CLK_LATENCY);
|
||||
|
||||
// CMD8
|
||||
sdHostSetMode(SD_CMD8_SEND_IF_COND, SD_IF_COND_PATTERN, DSTT_SD_HOST_READ_4B, DSTT_CTRL_SD_LOW_CLK_LATENCY);
|
||||
|
||||
u32 retryCount = 9999;
|
||||
while (true)
|
||||
{
|
||||
if (!isSdHostBusy())
|
||||
{
|
||||
response = sdHostGetResponse();
|
||||
break;
|
||||
}
|
||||
if (--retryCount == 0)
|
||||
{
|
||||
sdHostSetRegister(DSTT_SD_HOST_REG_CLEAR_ALL);
|
||||
sdHostSetRegister(DSTT_SD_HOST_REG_RESET | DSTT_SD_HOST_REG_400KHZ_CLK | DSTT_SD_HOST_REG_CLEAN_ROM_MODE);
|
||||
response = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (response == SD_IF_COND_PATTERN)
|
||||
{
|
||||
isSdVersion2 = true;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
// CMD55
|
||||
sdHostSetMode(SD_CMD55_APP_CMD, 0, DSTT_SD_HOST_READ_4B, DSTT_CTRL_SD_LOW_CLK_LATENCY);
|
||||
retryCount = 9999;
|
||||
while (isSdHostBusy())
|
||||
{
|
||||
if (--retryCount == 0)
|
||||
{
|
||||
sdHostSetRegister(DSTT_SD_HOST_REG_CLEAR_ALL);
|
||||
sdHostSetRegister(DSTT_SD_HOST_REG_RESET | DSTT_SD_HOST_REG_400KHZ_CLK | DSTT_SD_HOST_REG_CLEAN_ROM_MODE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
sdHostGetResponse();
|
||||
|
||||
// ACMD41
|
||||
u32 parameter = 0x00FC0000;
|
||||
if (isSdVersion2)
|
||||
{
|
||||
parameter |= BIT(30);
|
||||
}
|
||||
response = sdSendR1Command(SD_ACMD41_SD_SEND_OP_COND, parameter, DSTT_CTRL_SD_LOW_CLK_LATENCY);
|
||||
} while (!(response & BIT(31)));
|
||||
|
||||
isSdhc = (response & BIT(30)) != 0;
|
||||
|
||||
// CMD2
|
||||
sdSendR2Command(SD_CMD2_ALL_SEND_CID, 0, DSTT_CTRL_SD_LOW_CLK_LATENCY);
|
||||
|
||||
// CMD3
|
||||
response = sdSendR1Command(SD_CMD3_SEND_RELATIVE_ADDR, 0, DSTT_CTRL_SD_LOW_CLK_LATENCY);
|
||||
u32 sdioRca = response & 0xFFFF0000;
|
||||
|
||||
// CMD7
|
||||
sdSendR1Command(SD_CMD7_SELECT_CARD, sdioRca, DSTT_CTRL_SD_LOW_CLK_LATENCY);
|
||||
|
||||
// ACMD6
|
||||
sdSendR1Command(SD_CMD55_APP_CMD, sdioRca, DSTT_CTRL_SD_LOW_CLK_LATENCY);
|
||||
sdSendR1Command(SD_ACMD6_SET_BUS_WIDTH, 2, DSTT_CTRL_SD_LOW_CLK_LATENCY);
|
||||
|
||||
// CMD16
|
||||
sdSendR1Command(SD_CMD16_SET_BLOCKLEN, 512, DSTT_CTRL_SD_LOW_CLK_LATENCY);
|
||||
|
||||
sdHostSetRegister(DSTT_SD_HOST_REG_CLEAR_ALL);
|
||||
sdHostSetRegister(DSTT_SD_HOST_REG_RESET | DSTT_SD_HOST_REG_CLEAN_ROM_MODE);
|
||||
if (isSdhc)
|
||||
{
|
||||
sdHostSetRegister(DSTT_SD_HOST_REG_RESET | DSTT_SD_HOST_REG_CLEAN_ROM_MODE | DSTT_SD_HOST_REG_SDHC);
|
||||
dstt_readSd_sdsc_shift = THUMB_MOVS_REG(THUMB_R7, THUMB_R0);
|
||||
dstt_writeSd_sdsc_shift = THUMB_NOP;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
38
arm9/source/patches/platform/dstt/DSTTLoaderPlatform.h
Normal file
38
arm9/source/patches/platform/dstt/DSTTLoaderPlatform.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "../LoaderPlatform.h"
|
||||
#include "dsttReadSdAsm.h"
|
||||
#include "dsttWriteSdAsm.h"
|
||||
|
||||
/// @brief Implementation of LoaderPlatform for the DSTT flashcard
|
||||
class DSTTLoaderPlatform : public LoaderPlatform
|
||||
{
|
||||
public:
|
||||
const SdReadPatchCode* CreateSdReadPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new DSTTReadSdPatchCode(patchHeap,
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new DSTTReadSdStopTransmissionPatchCode(patchHeap);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
const SdWritePatchCode* CreateSdWritePatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new DSTTWriteSdPatchCode(patchHeap,
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new DSTTWriteSdContinuePatchCode(patchHeap);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
bool InitializeSdCard() override;
|
||||
};
|
||||
75
arm9/source/patches/platform/dstt/dsttDefinitions.h
Normal file
75
arm9/source/patches/platform/dstt/dsttDefinitions.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include <libtwl/card/card.h>
|
||||
|
||||
/// libtwl workaround. Certain DSTT clones need bytewise access to MCCMD
|
||||
#define REG_MCCMD0_U8 ((vu8*)®_MCCMD0)
|
||||
|
||||
/// Common DSTT MCCNT1 flags.
|
||||
#define DSTT_CTRL_BASE (MCCNT1_ENABLE | MCCNT1_RESET_OFF | MCCNT1_LATENCY2(24) | MCCNT1_LATENCY1(0))
|
||||
#define DSTT_CTRL_READ_4B (DSTT_CTRL_BASE | MCCNT1_LEN_4)
|
||||
#define DSTT_CTRL_SET_CARD_MODE (DSTT_CTRL_READ_4B | MCCNT1_CMD_SCRAMBLE | MCCNT1_CLOCK_SCRAMBLER | MCCNT1_READ_DATA_DESCRAMBLE)
|
||||
|
||||
/// Common MCCNT1 LATENCY1.
|
||||
/// During SDIO init SD host commands have a higher latency; in all other cases this is 0.
|
||||
#define DSTT_CTRL_SD_LOW_CLK_LATENCY 0x1000
|
||||
|
||||
/// SD host related commands.
|
||||
///
|
||||
/// Note:
|
||||
/// While this is where the SDIO happens, it isn't always SDIO.
|
||||
/// Thus, it can sometimes be 0.
|
||||
///
|
||||
/// Command structure:
|
||||
/// 51 AA AA AA AA BB CC 00
|
||||
/// AAAAAAAA = SDIO parameter
|
||||
/// BB = SDIO command
|
||||
/// CC = SD host mode, see DSTTSdHostModes enum.
|
||||
#define DSTT_CMD_SD_HOST_PARAM 0x51
|
||||
|
||||
/// SD host miscellaneous commands.
|
||||
/// return 0 == idle
|
||||
/// return non-0 == not idle
|
||||
#define DSTT_CMD_SD_HOST_BUSY 0x50
|
||||
|
||||
/// Retrieves the response from previous SD host param.
|
||||
/// Returns a value if the sent mode is 1 or 2.
|
||||
#define DSTT_CMD_SD_HOST_RESPONSE 0x52
|
||||
|
||||
/// Command to write to host register. The register bits can be found below.
|
||||
#define DSTT_CMD_SD_HOST_SET_REGISTER 0x5F
|
||||
|
||||
/// SD host modes.
|
||||
/// Used with DSTT_CMD_SD_HOST_PARAM command.
|
||||
enum DSTTSdHostModes
|
||||
{
|
||||
DSTT_SD_HOST_NORESPONSE = 0,
|
||||
DSTT_SD_HOST_READ_4B = 1,
|
||||
DSTT_SD_HOST_READ_4B_MULTI = 2, // use mode 3 to continue this read
|
||||
DSTT_SD_HOST_NEXT_4B = 3,
|
||||
DSTT_SD_HOST_SEND_CLK = 4,
|
||||
DSTT_SD_HOST_SEND_STOP_CLK = 5,
|
||||
DSTT_SD_HOST_READ_DATABLOCK = 6,
|
||||
DSTT_SD_HOST_NEXT_DATABLOCK = 7,
|
||||
DSTT_SD_HOST_CMD17_READ_DATA = 8, // Send SDIO CMD17 & read data
|
||||
DSTT_SD_HOST_CMD18_READ_DATA = 9, // Send SDIO CMD18 & read data until stop
|
||||
DSTT_SD_HOST_COMMIT_FIFO_DATA = 0xA, // commit data in FIFO to SD card
|
||||
DSTT_SD_HOST_CMD24_WRITE_DATA = 0xB, // Send SDIO CMD24 & send data in SRAM buffer
|
||||
DSTT_SD_HOST_WAIT_BUSY = 0xC // wait until data transfer ends
|
||||
};
|
||||
|
||||
/// SD host control registers.
|
||||
/// The 0x5F sets raw registers related to the SD host, which is a single u8.
|
||||
/// Bits:
|
||||
/// 0: Reset
|
||||
/// 1: Set 400k low clk
|
||||
/// 2: Use 0xB7 as alternative of 0x5B for ROM reads
|
||||
/// 3: Set SDHC mode
|
||||
/// 4-5: 1
|
||||
/// 6-7: 0
|
||||
#define DSTT_SD_HOST_REG_CLEAR_ALL 0
|
||||
#define DSTT_SD_HOST_REG_RESET BIT(0)
|
||||
#define DSTT_SD_HOST_REG_400KHZ_CLK BIT(1)
|
||||
#define DSTT_SD_HOST_REG_CLEAN_ROM_MODE BIT(2)
|
||||
#define DSTT_SD_HOST_REG_SDHC BIT(3)
|
||||
41
arm9/source/patches/platform/dstt/dsttReadSdAsm.h
Normal file
41
arm9/source/patches/platform/dstt/dsttReadSdAsm.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "../SdReadPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(dstt_readsd);
|
||||
DEFINE_SECTION_SYMBOLS(dstt_readsd_stopTransmission);
|
||||
|
||||
extern "C" void dstt_readSd(u32 srcSector, void* dst, u32 sectorCount);
|
||||
extern "C" void dstt_stopTransmission();
|
||||
|
||||
extern u32 dstt_stopTransmission_address;
|
||||
extern u16 dstt_readSd_sdsc_shift;
|
||||
|
||||
class DSTTReadSdStopTransmissionPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit DSTTReadSdStopTransmissionPatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(dstt_readsd_stopTransmission), SECTION_SIZE(dstt_readsd_stopTransmission), patchHeap) { }
|
||||
|
||||
const void* GetStopTransmissionFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)dstt_stopTransmission);
|
||||
}
|
||||
};
|
||||
|
||||
class DSTTReadSdPatchCode : public SdReadPatchCode
|
||||
{
|
||||
public:
|
||||
explicit DSTTReadSdPatchCode(PatchHeap& patchHeap,
|
||||
const DSTTReadSdStopTransmissionPatchCode* dsttReadSdStopTransmissionPatchCode)
|
||||
: SdReadPatchCode(SECTION_START(dstt_readsd), SECTION_SIZE(dstt_readsd), patchHeap)
|
||||
{
|
||||
dstt_stopTransmission_address = (u32)dsttReadSdStopTransmissionPatchCode->GetStopTransmissionFunction();
|
||||
}
|
||||
|
||||
const SdReadFunc GetSdReadFunction() const override
|
||||
{
|
||||
return (const SdReadFunc)GetAddressAtTarget((void*)dstt_readSd);
|
||||
}
|
||||
};
|
||||
175
arm9/source/patches/platform/dstt/dsttReadSdAsm.s
Normal file
175
arm9/source/patches/platform/dstt/dsttReadSdAsm.s
Normal file
@@ -0,0 +1,175 @@
|
||||
.cpu arm7tdmi
|
||||
.section "dstt_readsd", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = dst
|
||||
// r2 = sector count
|
||||
.global dstt_readSd
|
||||
.type dstt_readSd, %function
|
||||
dstt_readSd:
|
||||
push {r4-r7,lr}
|
||||
|
||||
.global dstt_readSd_sdsc_shift
|
||||
dstt_readSd_sdsc_shift:
|
||||
lsls r7, r0, #9
|
||||
|
||||
adr r0, dstt_stopTransmission_address
|
||||
ldmia r0, {r0, r3, r4}
|
||||
|
||||
// request sd read for sector 0xaabbccdd
|
||||
// 54 aa bb cc dd 00 00 00
|
||||
movs r5, #0x54
|
||||
strb r5, [r4,#0x8]
|
||||
lsrs r5, r7, #24
|
||||
strb r5, [r4,#0x9]
|
||||
lsrs r5, r7, #16
|
||||
strb r5, [r4,#0xA]
|
||||
lsrs r5, r7, #8
|
||||
strb r5, [r4,#0xB]
|
||||
// make sure the last 3 bytes are zero
|
||||
movs r5, #0xFF
|
||||
ands r7, r5
|
||||
str r7, [r4,#0xC] // storing as little-endian puts the bottom 8 bits as first byte
|
||||
|
||||
movs r7, #0x80
|
||||
strb r7, [r4,#1]
|
||||
|
||||
movs r6, #0x41
|
||||
lsls r6, r6, #20
|
||||
|
||||
sector_loop:
|
||||
str r3, [r4,#4]
|
||||
|
||||
cmd54_wait_loop:
|
||||
ldrb r5, [r4,#6]
|
||||
lsrs r5, r5, #8
|
||||
bcc cmd54_wait_loop
|
||||
|
||||
ldr r5, [r6, #0x10]
|
||||
// sd fifo full poll
|
||||
// 80 xx xx xx xx xx xx xx
|
||||
// xx bytes are leftover from previous command
|
||||
strb r7, [r4,#0x8]
|
||||
|
||||
cmd80_poll_loop:
|
||||
str r3, [r4,#4]
|
||||
|
||||
cmd80_wait_loop:
|
||||
ldrb r5, [r4,#6]
|
||||
lsrs r5, r5, #8
|
||||
bcc cmd80_wait_loop
|
||||
ldr r5, [r6, #0x10]
|
||||
cmp r5, #0
|
||||
bne cmd80_poll_loop
|
||||
|
||||
// read sd fifo
|
||||
// 81 xx xx xx xx xx xx xx
|
||||
// xx bytes are leftover from previous command
|
||||
movs r5, #0x81
|
||||
strb r5, [r4,#0x8]
|
||||
movs r5, #0xA1
|
||||
strb r5, [r4,#7]
|
||||
|
||||
cmd81_data_loop:
|
||||
ldrb r5, [r4,#6]
|
||||
lsrs r5, r5, #8 // check if data is ready
|
||||
bcc cmd81_data_loop_check_transfer_end // if not skip reading
|
||||
|
||||
ldr r5, [r6, #0x10]
|
||||
stmia r1!, {r5}
|
||||
|
||||
cmd81_data_loop_check_transfer_end:
|
||||
ldrb r5, [r4,#7]
|
||||
lsrs r5, r5, #8 // check if transfer is done
|
||||
bcs cmd81_data_loop
|
||||
|
||||
subs r2, #1
|
||||
beq stop_transmission
|
||||
|
||||
// set sd host mode DSTT_MODE_RECEIVE_DATA_BLOCK_STOP_CLOCK
|
||||
// 51 00 00 00 00 00 07 00
|
||||
movs r5, #0x51
|
||||
str r5, [r4,#0x8]
|
||||
movs r5, #7
|
||||
lsls r5, r5, #16
|
||||
str r5, [r4,#0xC]
|
||||
|
||||
b sector_loop
|
||||
|
||||
stop_transmission:
|
||||
bx r0
|
||||
|
||||
.balign 4
|
||||
|
||||
.global dstt_stopTransmission_address
|
||||
dstt_stopTransmission_address:
|
||||
.word 0
|
||||
|
||||
settings:
|
||||
.word 0xA7180000
|
||||
|
||||
regbase:
|
||||
.word 0x040001A0
|
||||
|
||||
.pool
|
||||
|
||||
.section "dstt_readsd_stopTransmission", "ax"
|
||||
.thumb
|
||||
.global dstt_stopTransmission
|
||||
.type dstt_stopTransmission, %function
|
||||
dstt_stopTransmission:
|
||||
// send SD_CMD12_STOP_TRANSMISSION
|
||||
|
||||
// set sd host mode DSTT_MODE_SDCMD_RESPONSE_32BIT, SD_CMD12_STOP_TRANSMISSION
|
||||
// 51 00 00 00 00 0C 01 00
|
||||
movs r5, #0x51
|
||||
str r5, [r4,#0x8]
|
||||
ldr r5,= 0x00010C00
|
||||
str r5, [r4,#0xC]
|
||||
str r3, [r4,#4]
|
||||
|
||||
cmd51_1_wait_loop:
|
||||
ldrb r5, [r4,#6]
|
||||
lsrs r5, r5, #8
|
||||
bcc cmd51_1_wait_loop
|
||||
ldr r5, [r6, #0x10]
|
||||
|
||||
// sd busy poll
|
||||
// 50 xx xx xx xx xx xx xx
|
||||
// xx bytes are leftover from previous command
|
||||
movs r5, #0x50
|
||||
strb r5, [r4,#0x8]
|
||||
|
||||
cmd50_poll_loop:
|
||||
str r3, [r4,#4]
|
||||
|
||||
cmd50_wait_loop:
|
||||
ldrb r5, [r4,#6]
|
||||
lsrs r5, r5, #8
|
||||
bcc cmd50_wait_loop
|
||||
ldr r5, [r6, #0x10]
|
||||
cmp r5, #0
|
||||
bne cmd50_poll_loop
|
||||
|
||||
// read sd response
|
||||
// 52 xx xx xx xx xx xx xx
|
||||
// xx bytes are leftover from previous command
|
||||
movs r5, #0x52
|
||||
strb r5, [r4,#0x8]
|
||||
str r3, [r4,#4]
|
||||
|
||||
cmd52_wait_loop:
|
||||
ldrb r5, [r4,#6]
|
||||
lsrs r5, r5, #8
|
||||
bcc cmd52_wait_loop
|
||||
ldr r5, [r6, #0x10]
|
||||
|
||||
pop {r4-r7,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
40
arm9/source/patches/platform/dstt/dsttWriteSdAsm.h
Normal file
40
arm9/source/patches/platform/dstt/dsttWriteSdAsm.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdWritePatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(dstt_writesd);
|
||||
DEFINE_SECTION_SYMBOLS(dstt_writesd_continue);
|
||||
|
||||
extern "C" void dstt_writeSd(u32 dstSector, const void* src, u32 sectorCount);
|
||||
extern "C" void dstt_writeSdContinue();
|
||||
|
||||
extern u32 dstt_writeSdContinue_address;
|
||||
extern u16 dstt_writeSd_sdsc_shift;
|
||||
|
||||
class DSTTWriteSdContinuePatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit DSTTWriteSdContinuePatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(dstt_writesd_continue), SECTION_SIZE(dstt_writesd_continue), patchHeap) { }
|
||||
|
||||
const void* GetWriteSdContinueFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)dstt_writeSdContinue);
|
||||
}
|
||||
};
|
||||
|
||||
class DSTTWriteSdPatchCode : public SdWritePatchCode
|
||||
{
|
||||
public:
|
||||
explicit DSTTWriteSdPatchCode(PatchHeap& patchHeap,
|
||||
const DSTTWriteSdContinuePatchCode* dsttWriteSdContinuePatchCode)
|
||||
: SdWritePatchCode(SECTION_START(dstt_writesd), SECTION_SIZE(dstt_writesd), patchHeap)
|
||||
{
|
||||
dstt_writeSdContinue_address = (u32)dsttWriteSdContinuePatchCode->GetWriteSdContinueFunction();
|
||||
}
|
||||
|
||||
const SdWriteFunc GetSdWriteFunction() const override
|
||||
{
|
||||
return (const SdWriteFunc)GetAddressAtTarget((void*)dstt_writeSd);
|
||||
}
|
||||
};
|
||||
157
arm9/source/patches/platform/dstt/dsttWriteSdAsm.s
Normal file
157
arm9/source/patches/platform/dstt/dsttWriteSdAsm.s
Normal file
@@ -0,0 +1,157 @@
|
||||
.cpu arm7tdmi
|
||||
.section "dstt_writesd", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = dst sector
|
||||
// r1 = src
|
||||
// r2 = sector count
|
||||
.global dstt_writeSd
|
||||
.type dstt_writeSd, %function
|
||||
dstt_writeSd:
|
||||
push {r4-r7,lr}
|
||||
|
||||
.global dstt_writeSd_sdsc_shift
|
||||
dstt_writeSd_sdsc_shift:
|
||||
lsls r0, r0, #9
|
||||
|
||||
adr r7, settings
|
||||
ldmia r7, {r3, r4, r5, r6, r7}
|
||||
|
||||
mov lr, r7
|
||||
movs r7, #0x80
|
||||
strb r7, [r4,#1]
|
||||
mov r7, lr
|
||||
|
||||
// send SD_CMD25_WRITE_MULTIPLE_BLOCK for sector 0xaabbccdd
|
||||
|
||||
// set sd host mode DSTT_MODE_SDCMD_RESPONSE_32BIT, SD_CMD25_WRITE_MULTIPLE_BLOCK
|
||||
// 51 aa bb cc dd 19 01 00
|
||||
str r5, [r4,#0xC]
|
||||
movs r5, #0x51
|
||||
strb r5, [r4,#0x8]
|
||||
lsrs r5, r0, #24
|
||||
strb r5, [r4,#0x9]
|
||||
lsrs r5, r0, #16
|
||||
strb r5, [r4,#0xA]
|
||||
lsrs r5, r0, #8
|
||||
strb r5, [r4,#0xB]
|
||||
strb r0, [r4,#0xC]
|
||||
|
||||
ldr r0, dstt_writeSdContinue_address
|
||||
bx r0
|
||||
|
||||
.balign 4
|
||||
|
||||
.global dstt_writeSdContinue_address
|
||||
dstt_writeSdContinue_address:
|
||||
.word 0
|
||||
|
||||
settings:
|
||||
.word 0xA7180000
|
||||
|
||||
regbase:
|
||||
.word 0x040001A0
|
||||
|
||||
writeStartCommandLo:
|
||||
.word 0x00011900
|
||||
|
||||
regbase2:
|
||||
.word 0x04100000
|
||||
|
||||
terminateCommandLo:
|
||||
.word 0x00010C00
|
||||
|
||||
.pool
|
||||
|
||||
.section "dstt_writesd_continue", "ax"
|
||||
.thumb
|
||||
.global dstt_writeSdContinue
|
||||
.type dstt_writeSdContinue, %function
|
||||
dstt_writeSdContinue:
|
||||
bl do32BitTransfer
|
||||
bl waitSdHostBusy
|
||||
bl readSdResponse
|
||||
|
||||
sector_loop:
|
||||
// write to fifo
|
||||
// 82 xx xx xx xx xx xx xx
|
||||
// xx bytes are leftover from previous command
|
||||
movs r5, #0x82
|
||||
strb r5, [r4,#0x8]
|
||||
movs r5, #0xE1
|
||||
strb r5, [r4,#7]
|
||||
|
||||
cmd82_data_loop:
|
||||
ldrb r5, [r4,#6]
|
||||
lsrs r5, r5, #8 // check if ready to write
|
||||
bcc cmd82_data_loop_check_transfer_end // if not skip writing
|
||||
|
||||
ldmia r1!, {r5}
|
||||
str r5, [r6, #0x10]
|
||||
|
||||
cmd82_data_loop_check_transfer_end:
|
||||
ldrb r5, [r4,#7]
|
||||
lsrs r5, r5, #8 // check if transfer is done
|
||||
bcs cmd82_data_loop
|
||||
|
||||
bl waitSdHostBusy
|
||||
|
||||
subs r2, #1
|
||||
bne skip_terminate
|
||||
|
||||
terminate:
|
||||
// send SD_CMD12_STOP_TRANSMISSION
|
||||
|
||||
// set sd host mode DSTT_MODE_SDCMD_RESPONSE_32BIT, SD_CMD12_STOP_TRANSMISSION
|
||||
// 51 00 00 00 00 0C 01 00
|
||||
str r7, [r4,#0xC]
|
||||
movs r5, #0x51
|
||||
bl do32BitTransferWriteCmdHi
|
||||
bl waitSdHostBusy
|
||||
bl readSdResponse
|
||||
|
||||
skip_terminate:
|
||||
// wait sddat[0] == 1
|
||||
// 56 00 00 00 00 00 00 00
|
||||
movs r5, #0x56
|
||||
bl do32BitTransferWriteCmdHi
|
||||
bl waitSdHostBusy
|
||||
cmp r2, #0
|
||||
bne sector_loop
|
||||
|
||||
pop {r4-r7,pc}
|
||||
|
||||
waitSdHostBusy:
|
||||
mov r12, lr
|
||||
cmd50_poll_loop:
|
||||
// sd busy poll
|
||||
// 50 xx xx xx xx xx xx xx
|
||||
// xx bytes are leftover from previous command
|
||||
movs r5, #0x50
|
||||
bl do32BitTransferWriteCmdHi
|
||||
cmp r5, #0
|
||||
bne cmd50_poll_loop
|
||||
bx r12
|
||||
|
||||
readSdResponse:
|
||||
// read sd response
|
||||
// 52 xx xx xx xx xx xx xx
|
||||
// xx bytes are leftover from previous command
|
||||
movs r5, #0x52
|
||||
do32BitTransferWriteCmdHi:
|
||||
strb r5, [r4,#0x8]
|
||||
do32BitTransfer:
|
||||
str r3, [r4,#4]
|
||||
1:
|
||||
ldrb r5, [r4,#6]
|
||||
lsrs r5, r5, #8
|
||||
bcc 1b
|
||||
ldr r5, [r6, #0x10]
|
||||
bx lr
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
38
arm9/source/patches/platform/g003/G003LoaderPlatform.h
Normal file
38
arm9/source/patches/platform/g003/G003LoaderPlatform.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "../LoaderPlatform.h"
|
||||
#include "g003ReadSdAsm.h"
|
||||
#include "g003ReadSdDmaAsm.h"
|
||||
#include "g003WriteSdAsm.h"
|
||||
|
||||
/// @brief Implementation of LoaderPlatform for the GMP-Z003 flashcard
|
||||
class G003LoaderPlatform : public LoaderPlatform
|
||||
{
|
||||
public:
|
||||
const SdReadPatchCode* CreateSdReadPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new G003ReadSdPatchCode(patchHeap);
|
||||
});
|
||||
}
|
||||
|
||||
const SdReadDmaPatchCode* CreateSdReadDmaPatchCode(PatchCodeCollection& patchCodeCollection,
|
||||
PatchHeap& patchHeap, const void* miiCardDmaCopy32Ptr) const override
|
||||
{
|
||||
return patchCodeCollection.AddUniquePatchCode<G003ReadSdDmaPatchCode>(
|
||||
patchHeap, miiCardDmaCopy32Ptr);
|
||||
}
|
||||
|
||||
const SdWritePatchCode* CreateSdWritePatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new G003WriteSdPatchCode(patchHeap);
|
||||
});
|
||||
}
|
||||
|
||||
bool HasDmaSdReads() const override { return true; }
|
||||
};
|
||||
19
arm9/source/patches/platform/g003/g003ReadSdAsm.h
Normal file
19
arm9/source/patches/platform/g003/g003ReadSdAsm.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdReadPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(g003_readsd);
|
||||
|
||||
extern "C" void g003_readSd(u32 srcSector, void* dst, u32 sectorCount);
|
||||
|
||||
class G003ReadSdPatchCode : public SdReadPatchCode
|
||||
{
|
||||
public:
|
||||
explicit G003ReadSdPatchCode(PatchHeap& patchHeap)
|
||||
: SdReadPatchCode(SECTION_START(g003_readsd), SECTION_SIZE(g003_readsd), patchHeap) { }
|
||||
|
||||
const SdReadFunc GetSdReadFunction() const override
|
||||
{
|
||||
return (const SdReadFunc)GetAddressAtTarget((void*)g003_readSd);
|
||||
}
|
||||
};
|
||||
88
arm9/source/patches/platform/g003/g003ReadSdAsm.s
Normal file
88
arm9/source/patches/platform/g003/g003ReadSdAsm.s
Normal file
@@ -0,0 +1,88 @@
|
||||
.cpu arm7tdmi
|
||||
.section "g003_readsd", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = dst
|
||||
// r2 = sector count
|
||||
.global g003_readSd
|
||||
.type g003_readSd, %function
|
||||
g003_readSd:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
sector_loop:
|
||||
// request sd read for sector, sector << 1 == 0xaabbccdd
|
||||
// C9 bb cc dd 00 00 00 aa
|
||||
|
||||
// left shift sector address 1
|
||||
lsls r7, r0, #1
|
||||
|
||||
// start copy to MCCMD1
|
||||
movs r3, #0xC9
|
||||
strb r3, [r4,#0x8]
|
||||
lsrs r3, r7, #24
|
||||
strb r3, [r4,#0xF] // place MSByte of sector address at end of command instead
|
||||
lsrs r3, r7, #16
|
||||
strb r3, [r4,#0x9]
|
||||
lsrs r3, r7, #8
|
||||
strb r3, [r4,#0xA]
|
||||
// Ace3DS+ code uses str, but G003 doesn't store the last byte at u8 MCCMD1[4].
|
||||
// So we use strb just like the other bytes.
|
||||
strb r7, [r4,#0xB]
|
||||
|
||||
ldr r6, =0x04100010
|
||||
|
||||
C9_poll_loop:
|
||||
ldr r3, =0xA7586000
|
||||
str r3, [r4,#4]
|
||||
|
||||
C9_poll_transfer_loop:
|
||||
ldrb r3, [r4,#6]
|
||||
lsrs r3, r3, #8
|
||||
bcc C9_poll_check_transfer_end
|
||||
ldr r5, [r6]
|
||||
|
||||
C9_poll_check_transfer_end:
|
||||
ldrb r3, [r4,#7]
|
||||
lsrs r3, r3, #8
|
||||
bcs C9_poll_transfer_loop
|
||||
|
||||
cmp r5, #0
|
||||
bne C9_poll_loop
|
||||
|
||||
movs r3, #0xCA
|
||||
str r3, [r4,#0x8]
|
||||
str r5, [r4,#0xC] // r5 is 0 here
|
||||
|
||||
ldr r3, =0xA1586000
|
||||
str r3, [r4,#4]
|
||||
|
||||
CA_data_loop:
|
||||
ldrb r3, [r4,#6]
|
||||
lsrs r3, r3, #8 // check if data is ready
|
||||
bcc CA_data_loop_check_transfer_end // if not skip reading
|
||||
|
||||
ldr r3, [r6]
|
||||
stmia r1!, {r3}
|
||||
|
||||
CA_data_loop_check_transfer_end:
|
||||
ldrb r3, [r4,#7]
|
||||
lsrs r3, r3, #8 // check if transfer is done
|
||||
bcs CA_data_loop
|
||||
|
||||
adds r0, #1
|
||||
subs r2, #1
|
||||
bne sector_loop
|
||||
|
||||
pop {r4-r7,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
30
arm9/source/patches/platform/g003/g003ReadSdDmaAsm.h
Normal file
30
arm9/source/patches/platform/g003/g003ReadSdDmaAsm.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdReadDmaPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(g003_readsddma);
|
||||
|
||||
extern "C" void g003_readSdDma(u32 srcSector, u32 previousSrcSector, u32 dmaChannel, void* dst);
|
||||
extern "C" void g003_finishReadSdDma(void);
|
||||
|
||||
extern u32 g003_readSdDma_miiCardDmaCopy32Ptr;
|
||||
|
||||
class G003ReadSdDmaPatchCode : public SdReadDmaPatchCode
|
||||
{
|
||||
public:
|
||||
explicit G003ReadSdDmaPatchCode(PatchHeap& patchHeap, const void* miiCardDmaCopy32Ptr)
|
||||
: SdReadDmaPatchCode(SECTION_START(g003_readsddma), SECTION_SIZE(g003_readsddma), patchHeap)
|
||||
{
|
||||
g003_readSdDma_miiCardDmaCopy32Ptr = (u32)miiCardDmaCopy32Ptr;
|
||||
}
|
||||
|
||||
const SdReadDmaFunc GetSdReadDmaFunction() const override
|
||||
{
|
||||
return (const SdReadDmaFunc)GetAddressAtTarget((void*)g003_readSdDma);
|
||||
}
|
||||
|
||||
const SdReadDmaFinishFunc GetSdReadDmaFinishFunction() const override
|
||||
{
|
||||
return (const SdReadDmaFinishFunc)GetAddressAtTarget((void*)g003_finishReadSdDma);
|
||||
}
|
||||
};
|
||||
100
arm9/source/patches/platform/g003/g003ReadSdDmaAsm.s
Normal file
100
arm9/source/patches/platform/g003/g003ReadSdDmaAsm.s
Normal file
@@ -0,0 +1,100 @@
|
||||
.cpu arm946e-s
|
||||
.section "g003_readsddma", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = previous src sector
|
||||
// r2 = dma channel
|
||||
// r3 = dst
|
||||
.global g003_readSdDma
|
||||
.type g003_readSdDma, %function
|
||||
g003_readSdDma:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
movs r7, #0x80
|
||||
strb r7, [r4,#0x1]
|
||||
|
||||
sector_loop:
|
||||
// request sd read for sector, sector << 1 == 0xaabbccdd
|
||||
// C9 bb cc dd 00 00 00 aa
|
||||
|
||||
// left shift sector address 1
|
||||
lsls r0, r0, #1
|
||||
|
||||
movs r7, #0xC9
|
||||
strb r7, [r4,#0x8]
|
||||
lsrs r7, r0, #24
|
||||
strb r7, [r4,#0xF] // place MSByte of sector address at end of command instead
|
||||
lsrs r7, r0, #16
|
||||
strb r7, [r4,#0x9]
|
||||
lsrs r7, r0, #8
|
||||
strb r7, [r4,#0xA]
|
||||
lsls r7, r0, #24
|
||||
lsrs r7, r7, #24 // r7 = dd
|
||||
// Ace3DS+ code uses str, but G003 doesn't store the last byte at u8 MCCMD1[4].
|
||||
// So we use strb just like the other bytes.
|
||||
strb r7, [r4,#0xB]
|
||||
|
||||
ldr r1, =0x04100010
|
||||
|
||||
C9_poll_loop:
|
||||
ldr r7, =0xA7586000
|
||||
str r7, [r4,#0x4]
|
||||
|
||||
C9_poll_transfer_loop:
|
||||
ldrb r7, [r4,#0x6]
|
||||
lsrs r7, r7, #8
|
||||
bcc C9_poll_check_transfer_end
|
||||
ldr r5, [r1]
|
||||
|
||||
C9_poll_check_transfer_end:
|
||||
ldrb r7, [r4,#0x7]
|
||||
lsrs r7, r7, #8
|
||||
bcs C9_poll_transfer_loop
|
||||
|
||||
cmp r5, #0
|
||||
bne C9_poll_loop
|
||||
|
||||
// Setup data read card command
|
||||
movs r7, #0xCA
|
||||
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, g003_readSdDma_miiCardDmaCopy32Ptr
|
||||
blx r6
|
||||
|
||||
CA_data_dma:
|
||||
movs r7, #0xC0 // select rom mode, with irq
|
||||
strb r7, [r4,#0x1]
|
||||
ldr r7, =0xA1586000
|
||||
str r7, [r4,#0x4]
|
||||
|
||||
pop {r4-r7,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.global g003_readSdDma_miiCardDmaCopy32Ptr
|
||||
g003_readSdDma_miiCardDmaCopy32Ptr:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.global g003_finishReadSdDma
|
||||
.type g003_finishReadSdDma, %function
|
||||
g003_finishReadSdDma:
|
||||
// No cleanup needed on this platform.
|
||||
bx lr
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
19
arm9/source/patches/platform/g003/g003WriteSdAsm.h
Normal file
19
arm9/source/patches/platform/g003/g003WriteSdAsm.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdWritePatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(g003_writesd);
|
||||
|
||||
extern "C" void g003_writeSd(u32 dstSector, const void* src, u32 sectorCount);
|
||||
|
||||
class G003WriteSdPatchCode : public SdWritePatchCode
|
||||
{
|
||||
public:
|
||||
explicit G003WriteSdPatchCode(PatchHeap& patchHeap)
|
||||
: SdWritePatchCode(SECTION_START(g003_writesd), SECTION_SIZE(g003_writesd), patchHeap) { }
|
||||
|
||||
const SdWriteFunc GetSdWriteFunction() const override
|
||||
{
|
||||
return (const SdWriteFunc)GetAddressAtTarget((void*)g003_writeSd);
|
||||
}
|
||||
};
|
||||
87
arm9/source/patches/platform/g003/g003WriteSdAsm.s
Normal file
87
arm9/source/patches/platform/g003/g003WriteSdAsm.s
Normal file
@@ -0,0 +1,87 @@
|
||||
.cpu arm7tdmi
|
||||
.section "g003_writesd", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = dst sector
|
||||
// r1 = src
|
||||
// r2 = sector count
|
||||
.global g003_writeSd
|
||||
.type g003_writeSd, %function
|
||||
g003_writeSd:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
sector_loop:
|
||||
// write at sd sector, sector << 1 == 0xaabbccdd
|
||||
// C5 bb cc dd 00 00 00 aa
|
||||
|
||||
// left shift sector address 1
|
||||
lsls r7, r0, #1
|
||||
|
||||
// start copy to MCCMD1
|
||||
movs r3, #0xC5
|
||||
strb r3, [r4,#0x8]
|
||||
lsrs r3, r7, #24
|
||||
strb r3, [r4,#0xF] // place MSByte of sector address at end of command instead
|
||||
lsrs r3, r7, #16
|
||||
strb r3, [r4,#0x9]
|
||||
lsrs r3, r7, #8
|
||||
strb r3, [r4,#0xA]
|
||||
// Ace3DS+ code uses str, but G003 doesn't store the last byte at u8 MCCMD1[4].
|
||||
// So we use strb just like the other bytes.
|
||||
strb r7, [r4,#0xB]
|
||||
|
||||
ldr r6, =0x04100010
|
||||
|
||||
ldr r3, =0xE1586000
|
||||
str r3, [r4,#4]
|
||||
|
||||
C5_data_loop:
|
||||
ldrb r3, [r4,#6]
|
||||
lsrs r3, r3, #8 // check if ready to write
|
||||
bcc C5_data_loop_check_transfer_end // if not skip reading
|
||||
|
||||
ldmia r1!, {r3}
|
||||
str r3, [r6]
|
||||
|
||||
C5_data_loop_check_transfer_end:
|
||||
ldrb r3, [r4,#7]
|
||||
lsrs r3, r3, #8 // check if transfer is done
|
||||
bcs C5_data_loop
|
||||
|
||||
movs r3, #0xC6
|
||||
strb r3, [r4,#0x8]
|
||||
|
||||
C6_poll_loop:
|
||||
ldr r3, =0xA7586000
|
||||
str r3, [r4,#4]
|
||||
|
||||
C6_poll_transfer_loop:
|
||||
ldrb r3, [r4,#6]
|
||||
lsrs r3, r3, #8
|
||||
bcc C6_poll_check_transfer_end
|
||||
ldr r5, [r6]
|
||||
|
||||
C6_poll_check_transfer_end:
|
||||
ldrb r3, [r4,#7]
|
||||
lsrs r3, r3, #8
|
||||
bcs C6_poll_transfer_loop
|
||||
|
||||
cmp r5, #0
|
||||
bne C6_poll_loop
|
||||
|
||||
adds r0, #1
|
||||
subs r2, #1
|
||||
bne sector_loop
|
||||
|
||||
pop {r4-r7,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
35
arm9/source/patches/platform/isnitro/IsNitroLoaderPlatform.h
Normal file
35
arm9/source/patches/platform/isnitro/IsNitroLoaderPlatform.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "../LoaderPlatform.h"
|
||||
#include "IsNitroSdReadAsm.h"
|
||||
#include "IsNitroSdWriteAsm.h"
|
||||
#include "sharedMemory.h"
|
||||
|
||||
/// @brief Implementation of LoaderPlatform for the IS-NITRO-EMULATOR with agb semihosting
|
||||
class IsNitroLoaderPlatform : public LoaderPlatform
|
||||
{
|
||||
public:
|
||||
const SdReadPatchCode* CreateSdReadPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new IsNitroSdReadPatchCode(patchHeap, GetAgbRamPtr());
|
||||
});
|
||||
}
|
||||
|
||||
const SdWritePatchCode* CreateSdWritePatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new IsNitroSdWritePatchCode(patchHeap, GetAgbRamPtr());
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
u32 GetAgbRamPtr() const
|
||||
{
|
||||
return *(vu32*)&TWL_SHARED_MEMORY->ntrSharedMem.isDebuggerData[0x1C];
|
||||
}
|
||||
};
|
||||
24
arm9/source/patches/platform/isnitro/IsNitroSdReadAsm.h
Normal file
24
arm9/source/patches/platform/isnitro/IsNitroSdReadAsm.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdReadPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(sdread);
|
||||
|
||||
extern "C" void sdread_asm(u32 srcSector, void* dst, u32 sectorCount);
|
||||
|
||||
extern u32 sdread_asm_agbRamPtr;
|
||||
|
||||
class IsNitroSdReadPatchCode : public SdReadPatchCode
|
||||
{
|
||||
public:
|
||||
IsNitroSdReadPatchCode(PatchHeap& patchHeap, u32 agbRamPtr)
|
||||
: SdReadPatchCode(SECTION_START(sdread), SECTION_SIZE(sdread), patchHeap)
|
||||
{
|
||||
sdread_asm_agbRamPtr = agbRamPtr;
|
||||
}
|
||||
|
||||
const SdReadFunc GetSdReadFunction() const override
|
||||
{
|
||||
return (const SdReadFunc)GetAddressAtTarget((void*)sdread_asm);
|
||||
}
|
||||
};
|
||||
95
arm9/source/patches/platform/isnitro/IsNitroSdReadAsm.s
Normal file
95
arm9/source/patches/platform/isnitro/IsNitroSdReadAsm.s
Normal file
@@ -0,0 +1,95 @@
|
||||
.cpu arm7tdmi
|
||||
.section "sdread", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = dst
|
||||
// r2 = sector count
|
||||
.global sdread_asm
|
||||
.type sdread_asm, %function
|
||||
sdread_asm:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldr r6,= 0x04000200
|
||||
// clear the bottom 8 bits of REG_EXMEMCNT to allow access to the GBA slot
|
||||
// from the arm9 and to set the right timings
|
||||
strb r6, [r6, #4]
|
||||
|
||||
sdread_asm_loop:
|
||||
// load the GBA memory address as set by the debug monitor rom
|
||||
ldr r5, sdread_asm_agbRamPtr
|
||||
|
||||
cmp r2, #0
|
||||
beq sdread_asm_done
|
||||
|
||||
movs r3, r2
|
||||
cmp r3, #32
|
||||
blo 1f
|
||||
movs r3, #32
|
||||
1:
|
||||
subs r2, r3
|
||||
adds r4, r0, r3
|
||||
push {r2}
|
||||
push {r4}
|
||||
movs r2, r3
|
||||
|
||||
movs r4, #1
|
||||
|
||||
// setup read command
|
||||
lsls r3, r4, #16
|
||||
adds r5, r3 // GBA memory + 0x10000
|
||||
strh r4, [r5, #2] // command = read
|
||||
str r0, [r5, #4] // sector address
|
||||
str r2, [r5, #8] // sector count
|
||||
|
||||
// GBA slot irq bit: 1 << 13
|
||||
lsls r3, r4, #13
|
||||
|
||||
// disable GBA slot irq
|
||||
ldr r0, [r6, #0x10] // REG_IE
|
||||
bics r0, r3
|
||||
str r0, [r6, #0x10] // REG_IE
|
||||
|
||||
// ack GBA slot irq
|
||||
str r3, [r6, #0x14] // REG_IF
|
||||
|
||||
movs r4, #0x55
|
||||
strh r4, [r5] // status = cmd ready to be processed
|
||||
|
||||
// poll for GBA slot irq in REG_IF
|
||||
2:
|
||||
ldr r4, [r6, #0x14] // REG_IF
|
||||
tst r4, r3
|
||||
beq 2b
|
||||
|
||||
// ack GBA slot irq
|
||||
str r3, [r6, #0x14]
|
||||
|
||||
adds r5, #0x20 // data buffer in GBA slot area
|
||||
lsls r0, r2, #5 // sector count * 32; 512 bytes per sector = 32 iterations per sector with 16 bytes copied per iteration
|
||||
4:
|
||||
ldmia r5!, {r2,r3,r4,r7}
|
||||
stmia r1!, {r2,r3,r4,r7}
|
||||
subs r0, #1
|
||||
bne 4b
|
||||
|
||||
pop {r0, r2}
|
||||
b sdread_asm_loop
|
||||
|
||||
sdread_asm_done:
|
||||
// map back GBA slot to arm7
|
||||
movs r0, #0x80
|
||||
strb r0, [r6, #4]
|
||||
|
||||
pop {r4-r7,pc} // popping pc is safe for armv4t when not switching mode
|
||||
|
||||
.balign 4
|
||||
|
||||
.global sdread_asm_agbRamPtr
|
||||
sdread_asm_agbRamPtr:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
24
arm9/source/patches/platform/isnitro/IsNitroSdWriteAsm.h
Normal file
24
arm9/source/patches/platform/isnitro/IsNitroSdWriteAsm.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdWritePatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(sdwrite);
|
||||
|
||||
extern "C" void sdwrite_asm(u32 dstSector, const void* src, u32 sectorCount);
|
||||
|
||||
extern u32 sdwrite_asm_agbRamPtr;
|
||||
|
||||
class IsNitroSdWritePatchCode : public SdWritePatchCode
|
||||
{
|
||||
public:
|
||||
IsNitroSdWritePatchCode(PatchHeap& patchHeap, u32 agbRamPtr)
|
||||
: SdWritePatchCode(SECTION_START(sdwrite), SECTION_SIZE(sdwrite), patchHeap)
|
||||
{
|
||||
sdwrite_asm_agbRamPtr = agbRamPtr;
|
||||
}
|
||||
|
||||
const SdWriteFunc GetSdWriteFunction() const override
|
||||
{
|
||||
return (const SdWriteFunc)GetAddressAtTarget((void*)sdwrite_asm);
|
||||
}
|
||||
};
|
||||
79
arm9/source/patches/platform/isnitro/IsNitroSdWriteAsm.s
Normal file
79
arm9/source/patches/platform/isnitro/IsNitroSdWriteAsm.s
Normal file
@@ -0,0 +1,79 @@
|
||||
.cpu arm7tdmi
|
||||
.section "sdwrite", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// for armv4t, calling code must be thumb
|
||||
// r0 = dst sector
|
||||
// r1 = src
|
||||
// r2 = sector count
|
||||
.global sdwrite_asm
|
||||
.type sdwrite_asm, %function
|
||||
sdwrite_asm:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldr r6,= 0x04000200
|
||||
// clear the bottom 8 bits of REG_EXMEMCNT to allow access to the GBA slot
|
||||
// and to set the right timings
|
||||
strb r6, [r6, #4]
|
||||
|
||||
// load the GBA memory address as set by the debug monitor rom
|
||||
ldr r5, sdwrite_asm_agbRamPtr
|
||||
|
||||
// setup write command
|
||||
lsrs r3, r6, #10 // 0x10000
|
||||
adds r5, r3 // GBA memory + 0x10000
|
||||
movs r3, #2
|
||||
strh r3, [r5, #2] // command = write
|
||||
str r0, [r5, #4] // sector address
|
||||
str r2, [r5, #8] // sector count
|
||||
|
||||
mov lr, r5
|
||||
adds r5, #0x20 // data buffer in GBA slot area
|
||||
lsls r0, r2, #5 // sector count * 32; 512 bytes per sector = 32 iterations per sector with 16 bytes copied per iteration
|
||||
4:
|
||||
ldmia r1!, {r2,r3,r4,r7}
|
||||
stmia r5!, {r2,r3,r4,r7}
|
||||
subs r0, #1
|
||||
bne 4b
|
||||
mov r5, lr
|
||||
|
||||
// GBA slot irq bit: 1 << 13
|
||||
movs r4, #1
|
||||
lsls r3, r4, #13
|
||||
|
||||
// disable GBA slot irq
|
||||
ldr r0, [r6, #0x10] // REG_IE
|
||||
bics r0, r3
|
||||
str r0, [r6, #0x10] // REG_IE
|
||||
|
||||
// ack GBA slot irq
|
||||
str r3, [r6, #0x14] // REG_IF
|
||||
|
||||
movs r4, #0x55
|
||||
strh r4, [r5] // status = cmd ready to be processed
|
||||
|
||||
// poll for GBA slot irq in REG_IF
|
||||
2:
|
||||
ldr r4, [r6, #0x14] // REG_IF
|
||||
tst r4, r3
|
||||
beq 2b
|
||||
|
||||
// ack GBA slot irq
|
||||
str r3, [r6, #0x14]
|
||||
|
||||
// map back GBA slot to arm7
|
||||
movs r0, #0x80
|
||||
strb r0, [r6, #4]
|
||||
|
||||
pop {r4-r7,pc} // popping pc is safe for armv4t when not switching mode
|
||||
|
||||
.balign 4
|
||||
|
||||
.global sdwrite_asm_agbRamPtr
|
||||
sdwrite_asm_agbRamPtr:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
36
arm9/source/patches/platform/m3ds/M3DSLoaderPlatform.h
Normal file
36
arm9/source/patches/platform/m3ds/M3DSLoaderPlatform.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "../LoaderPlatform.h"
|
||||
#include "m3dsReadSectorsAsm.h"
|
||||
#include "m3dsWriteSectorsAsm.h"
|
||||
|
||||
/// @brief Implementation of LoaderPlatform for the M3 DS Real flashcard
|
||||
class M3DSLoaderPlatform : public LoaderPlatform
|
||||
{
|
||||
public:
|
||||
const SdReadPatchCode* CreateSdReadPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new M3DSReadSdSectorsPatchCode(patchHeap,
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new M3DSReceiveSectorPatchCode(patchHeap);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
const SdWritePatchCode* CreateSdWritePatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new M3DSWriteSdSectorsPatchCode(patchHeap,
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new M3DSSendSectorPatchCode(patchHeap);
|
||||
}));
|
||||
});
|
||||
}
|
||||
};
|
||||
38
arm9/source/patches/platform/m3ds/m3dsReadSectorsAsm.h
Normal file
38
arm9/source/patches/platform/m3ds/m3dsReadSectorsAsm.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdReadPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(m3ds_readsdsectors);
|
||||
DEFINE_SECTION_SYMBOLS(m3ds_readsdsectors_receiveSector);
|
||||
|
||||
extern "C" void m3ds_readSdSectors(u32 srcSector, void* dst, u32 sectorCount);
|
||||
extern "C" void m3ds_receiveSector(void* dst);
|
||||
|
||||
extern u32 m3ds_receiveSector_address;
|
||||
|
||||
class M3DSReceiveSectorPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit M3DSReceiveSectorPatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(m3ds_readsdsectors_receiveSector), SECTION_SIZE(m3ds_readsdsectors_receiveSector), patchHeap) { }
|
||||
|
||||
const void* GetReceiveSectorFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)m3ds_receiveSector);
|
||||
}
|
||||
};
|
||||
|
||||
class M3DSReadSdSectorsPatchCode : public SdReadPatchCode
|
||||
{
|
||||
public:
|
||||
explicit M3DSReadSdSectorsPatchCode(PatchHeap& patchHeap, const M3DSReceiveSectorPatchCode* m3dsReceiveSectorPatchCode)
|
||||
: SdReadPatchCode(SECTION_START(m3ds_readsdsectors), SECTION_SIZE(m3ds_readsdsectors), patchHeap)
|
||||
{
|
||||
m3ds_receiveSector_address = (u32)m3dsReceiveSectorPatchCode->GetReceiveSectorFunction();
|
||||
}
|
||||
|
||||
const SdReadFunc GetSdReadFunction() const override
|
||||
{
|
||||
return (const SdReadFunc)GetAddressAtTarget((void*)m3ds_readSdSectors);
|
||||
}
|
||||
};
|
||||
139
arm9/source/patches/platform/m3ds/m3dsReadSectorsAsm.s
Normal file
139
arm9/source/patches/platform/m3ds/m3dsReadSectorsAsm.s
Normal file
@@ -0,0 +1,139 @@
|
||||
.cpu arm7tdmi
|
||||
.section "m3ds_readsdsectors", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = dst
|
||||
// r2 = sector count
|
||||
.global m3ds_readSdSectors
|
||||
.type m3ds_readSdSectors, %function
|
||||
m3ds_readSdSectors:
|
||||
push {r1,r2,r4-r7,lr}
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
// construct first 4 bytes of the command
|
||||
// given a sector address in r0: 0xaabbccdd
|
||||
// first command word: B2 aa bb cc
|
||||
// B2 is for requesting the next sequential read
|
||||
adr r7, cmdWord0Tmp // write bytewise to this temp location
|
||||
movs r3, #0xB2
|
||||
strb r3, [r7,#0]
|
||||
lsrs r3, r0, #24
|
||||
strb r3, [r7,#1]
|
||||
lsrs r3, r0, #16
|
||||
strb r3, [r7,#2]
|
||||
lsrs r3, r0, #8
|
||||
strb r3, [r7,#3]
|
||||
ldr r6, [r7] // read back as 32 bit value from the temp location
|
||||
str r6, [r4,#8] // write to the actual command register
|
||||
|
||||
// construct second 4 bytes of the command
|
||||
// second command word: dd 00 00 00
|
||||
lsls r0, r0, #24
|
||||
lsrs r7, r0, #24 // r7 = dd
|
||||
str r7, [r4,#0xC] // storing as little-endian puts the bottom 8 bits as first byte
|
||||
|
||||
ldr r5, [sp,#4] // sector count
|
||||
movs r3, #0xBD // command for single sector read request
|
||||
cmp r5, #1
|
||||
beq 1f
|
||||
movs r3, #0xB1 // command for multi sector read request
|
||||
1:
|
||||
strb r3, [r4,#8] // use the right command for the first sector
|
||||
|
||||
ldr r0, [sp,#0] // dst
|
||||
sector_loop:
|
||||
ldr r3, m3ds_receiveSector_address
|
||||
bl blx_r3
|
||||
str r6, [r4,#8] // write the command for requesting the next sector
|
||||
str r7, [r4,#0xC]
|
||||
subs r5, #1
|
||||
bne sector_loop
|
||||
ldr r3, [sp,#4]
|
||||
cmp r3, #1
|
||||
bls read_sectors_end
|
||||
|
||||
// send command for ending multi-sector read
|
||||
// B3 00 00 00 00 00 00 00
|
||||
movs r2, #0xB3
|
||||
str r2, [r4,#8] // B3 00 00 00
|
||||
str r5, [r4,#0xC] // 00 00 00 00
|
||||
ldr r3, =0xA0486100
|
||||
str r3, [r4,#4] // start transfer
|
||||
|
||||
// wait for transfer to end
|
||||
3:
|
||||
ldrb r1, [r4, #7]
|
||||
lsrs r1, r1, #8
|
||||
bcs 3b
|
||||
|
||||
read_sectors_end:
|
||||
pop {r1-r2,r4-r7,pc}
|
||||
|
||||
blx_r3:
|
||||
bx r3
|
||||
|
||||
.balign 4
|
||||
|
||||
.global m3ds_receiveSector_address
|
||||
m3ds_receiveSector_address:
|
||||
.word 0
|
||||
|
||||
cmdWord0Tmp:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.section "m3ds_readsdsectors_receiveSector", "ax"
|
||||
.thumb
|
||||
.global m3ds_receiveSector
|
||||
.type m3ds_receiveSector, %function
|
||||
m3ds_receiveSector:
|
||||
ldr r1, =0x04100010
|
||||
|
||||
receiveSector_poll_loop:
|
||||
ldr r3, =0xA7586000
|
||||
str r3, [r4,#4]
|
||||
|
||||
receiveSector_poll_transfer_loop:
|
||||
ldrb r3, [r4,#6]
|
||||
lsrs r3, r3, #8
|
||||
bcc receiveSector_poll_check_transfer_end
|
||||
ldr r2, [r1]
|
||||
|
||||
receiveSector_poll_check_transfer_end:
|
||||
ldrb r3, [r4,#7]
|
||||
lsrs r3, r3, #8
|
||||
bcs receiveSector_poll_transfer_loop
|
||||
|
||||
cmp r2, #0
|
||||
bne receiveSector_poll_loop
|
||||
movs r3, #0xBA
|
||||
str r3, [r4,#8]
|
||||
str r2, [r4,#0xC]
|
||||
ldr r3, =0xA1586000
|
||||
str r3, [r4,#4]
|
||||
|
||||
receiveSector_data_loop:
|
||||
ldrb r3, [r4,#6]
|
||||
lsrs r3, r3, #8 // check if data is ready
|
||||
bcc receiveSector_data_loop_check_transfer_end // if not skip reading
|
||||
|
||||
ldr r3, [r1]
|
||||
stmia r0!, {r3}
|
||||
|
||||
receiveSector_data_loop_check_transfer_end:
|
||||
ldrb r3, [r4,#7]
|
||||
lsrs r3, r3, #8 // check if transfer is done
|
||||
bcs receiveSector_data_loop
|
||||
|
||||
bx lr
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
38
arm9/source/patches/platform/m3ds/m3dsWriteSectorsAsm.h
Normal file
38
arm9/source/patches/platform/m3ds/m3dsWriteSectorsAsm.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdWritePatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(m3ds_writesdsectors);
|
||||
DEFINE_SECTION_SYMBOLS(m3ds_writesdsectors_sendSector);
|
||||
|
||||
extern "C" void m3ds_writeSdSectors(u32 dstSector, const void* src, u32 sectorCount);
|
||||
extern "C" void m3ds_sendSector(const void* src);
|
||||
|
||||
extern u32 m3ds_sendSector_address;
|
||||
|
||||
class M3DSSendSectorPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit M3DSSendSectorPatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(m3ds_writesdsectors_sendSector), SECTION_SIZE(m3ds_writesdsectors_sendSector), patchHeap) { }
|
||||
|
||||
const void* GetSendSectorFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)m3ds_sendSector);
|
||||
}
|
||||
};
|
||||
|
||||
class M3DSWriteSdSectorsPatchCode : public SdWritePatchCode
|
||||
{
|
||||
public:
|
||||
M3DSWriteSdSectorsPatchCode(PatchHeap& patchHeap, const M3DSSendSectorPatchCode* m3dsSendSectorPatchCode)
|
||||
: SdWritePatchCode(SECTION_START(m3ds_writesdsectors), SECTION_SIZE(m3ds_writesdsectors), patchHeap)
|
||||
{
|
||||
m3ds_sendSector_address = (u32)m3dsSendSectorPatchCode->GetSendSectorFunction();
|
||||
}
|
||||
|
||||
const SdWriteFunc GetSdWriteFunction() const override
|
||||
{
|
||||
return (const SdWriteFunc)GetAddressAtTarget((void*)m3ds_writeSdSectors);
|
||||
}
|
||||
};
|
||||
155
arm9/source/patches/platform/m3ds/m3dsWriteSectorsAsm.s
Normal file
155
arm9/source/patches/platform/m3ds/m3dsWriteSectorsAsm.s
Normal file
@@ -0,0 +1,155 @@
|
||||
.cpu arm7tdmi
|
||||
.section "m3ds_writesdsectors", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = dst sector
|
||||
// r1 = src
|
||||
// r2 = sector count
|
||||
.global m3ds_writeSdSectors
|
||||
.type m3ds_writeSdSectors, %function
|
||||
m3ds_writeSdSectors:
|
||||
push {r1,r2,r4-r7,lr}
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
// construct first 4 bytes of the command
|
||||
// given a sector address in r0: 0xaabbccdd
|
||||
// first command word: B5 aa bb cc
|
||||
// B5 is for requesting the next sequential write
|
||||
adr r7, cmdWord0Tmp // write bytewise to this temp location
|
||||
movs r3, #0xB5
|
||||
strb r3, [r7,#0]
|
||||
lsrs r3, r0, #24
|
||||
strb r3, [r7,#1]
|
||||
lsrs r3, r0, #16
|
||||
strb r3, [r7,#2]
|
||||
lsrs r3, r0, #8
|
||||
strb r3, [r7,#3]
|
||||
ldr r6, [r7] // read back as 32 bit value from the temp location
|
||||
str r6, [r4,#8] // write to the actual command register
|
||||
|
||||
// construct second 4 bytes of the command
|
||||
// second command word: dd 00 00 00
|
||||
lsls r0, r0, #24
|
||||
lsrs r7, r0, #24 // r7 = dd
|
||||
str r7, [r4,#0xC] // storing as little-endian puts the bottom 8 bits as first byte
|
||||
|
||||
ldr r5, [sp,#4] // sector count
|
||||
movs r3, #0xBE // command for single sector write start
|
||||
movs r2, #0xBC // command for single sector write ready poll
|
||||
cmp r5, #1
|
||||
beq 1f
|
||||
movs r3, #0xB4 // command for multi sector write start
|
||||
movs r2, #0xB6 // command for multi sector write ready poll
|
||||
1:
|
||||
mov r12, r2
|
||||
strb r3, [r4,#8] // use the right command for the first sector
|
||||
|
||||
ldr r0, [sp,#0] // dst
|
||||
sector_loop:
|
||||
ldr r3, m3ds_sendSector_address
|
||||
bl blx_r3
|
||||
bne sector_loop
|
||||
ldr r3, [sp,#4]
|
||||
cmp r3, #1
|
||||
bls write_sectors_end
|
||||
|
||||
ldr r3, m3ds_sendSector_address
|
||||
adds r3, #(m3ds_finishMultiSectorWrite - m3ds_sendSector)
|
||||
bl blx_r3
|
||||
|
||||
write_sectors_end:
|
||||
pop {r1-r2,r4-r7,pc}
|
||||
|
||||
blx_r3:
|
||||
bx r3
|
||||
|
||||
.balign 4
|
||||
|
||||
.global m3ds_sendSector_address
|
||||
m3ds_sendSector_address:
|
||||
.word 0
|
||||
|
||||
cmdWord0Tmp:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.section "m3ds_writesdsectors_sendSector", "ax"
|
||||
.thumb
|
||||
.global m3ds_sendSector
|
||||
.type m3ds_sendSector, %function
|
||||
m3ds_sendSector:
|
||||
ldr r1, =0x04100010
|
||||
|
||||
ldr r3, =0xE1586100
|
||||
str r3, [r4,#4]
|
||||
|
||||
sendSector_data_loop:
|
||||
ldrb r3, [r4,#6]
|
||||
lsrs r3, r3, #8 // check if ready to send
|
||||
bcc sendSector_data_loop_check_transfer_end // if not skip writing
|
||||
|
||||
ldmia r0!, {r3}
|
||||
str r3, [r1]
|
||||
|
||||
sendSector_data_loop_check_transfer_end:
|
||||
ldrb r3, [r4,#7]
|
||||
lsrs r3, r3, #8 // check if transfer is done
|
||||
bcs sendSector_data_loop
|
||||
|
||||
mov r3, r12 // command for write ready poll
|
||||
str r3, [r4,#8]
|
||||
movs r3, #0
|
||||
str r3, [r4,#0xC]
|
||||
sendSector_poll_loop:
|
||||
ldr r3, =0xA7586000
|
||||
str r3, [r4,#4]
|
||||
|
||||
sendSector_poll_transfer_loop:
|
||||
ldrb r3, [r4,#6]
|
||||
lsrs r3, r3, #8
|
||||
bcc sendSector_poll_check_transfer_end
|
||||
ldr r2, [r1]
|
||||
|
||||
sendSector_poll_check_transfer_end:
|
||||
ldrb r3, [r4,#7]
|
||||
lsrs r3, r3, #8
|
||||
bcs sendSector_poll_transfer_loop
|
||||
|
||||
cmp r2, #0
|
||||
bne sendSector_poll_loop
|
||||
|
||||
str r6, [r4,#8] // write the command for the next sector
|
||||
str r7, [r4,#0xC]
|
||||
subs r5, #1
|
||||
|
||||
bx lr
|
||||
|
||||
m3ds_finishMultiSectorWrite:
|
||||
// send command for ending multi-sector write
|
||||
// B3 00 00 00 00 00 00 00
|
||||
movs r2, #0xB3
|
||||
str r2, [r4,#8] // B3 00 00 00
|
||||
str r5, [r4,#0xC] // 00 00 00 00
|
||||
ldr r3, =0xA0486100
|
||||
str r3, [r4,#4] // start transfer
|
||||
|
||||
// wait for transfer to end
|
||||
3:
|
||||
ldrb r1, [r4, #7]
|
||||
lsrs r1, r1, #8
|
||||
bcs 3b
|
||||
|
||||
movs r3, #0xB6 // command for write ready poll
|
||||
str r3, [r4,#8]
|
||||
|
||||
b sendSector_poll_loop
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
28
arm9/source/patches/platform/melonds/MelonDSLoaderPlatform.h
Normal file
28
arm9/source/patches/platform/melonds/MelonDSLoaderPlatform.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "../LoaderPlatform.h"
|
||||
#include "melondsReadSdAsm.h"
|
||||
#include "melondsWriteSdAsm.h"
|
||||
|
||||
/// @brief Implementation of LoaderPlatform for MelonDS
|
||||
class MelonDSLoaderPlatform : public LoaderPlatform
|
||||
{
|
||||
public:
|
||||
const SdReadPatchCode* CreateSdReadPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new MelonDSReadSdPatchCode(patchHeap);
|
||||
});
|
||||
}
|
||||
|
||||
const SdWritePatchCode* CreateSdWritePatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new MelonDSWriteSdPatchCode(patchHeap);
|
||||
});
|
||||
}
|
||||
};
|
||||
19
arm9/source/patches/platform/melonds/melondsReadSdAsm.h
Normal file
19
arm9/source/patches/platform/melonds/melondsReadSdAsm.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdReadPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(melonds_readsd);
|
||||
|
||||
extern "C" void melonds_readSd(u32 srcSector, void* dst, u32 sectorCount);
|
||||
|
||||
class MelonDSReadSdPatchCode : public SdReadPatchCode
|
||||
{
|
||||
public:
|
||||
explicit MelonDSReadSdPatchCode(PatchHeap& patchHeap)
|
||||
: SdReadPatchCode(SECTION_START(melonds_readsd), SECTION_SIZE(melonds_readsd), patchHeap) { }
|
||||
|
||||
const SdReadFunc GetSdReadFunction() const override
|
||||
{
|
||||
return (const SdReadFunc)GetAddressAtTarget((void*)melonds_readSd);
|
||||
}
|
||||
};
|
||||
60
arm9/source/patches/platform/melonds/melondsReadSdAsm.s
Normal file
60
arm9/source/patches/platform/melonds/melondsReadSdAsm.s
Normal file
@@ -0,0 +1,60 @@
|
||||
.cpu arm7tdmi
|
||||
.section "melonds_readsd", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = dst
|
||||
// r2 = sector count
|
||||
.global melonds_readSd
|
||||
.type melonds_readSd, %function
|
||||
melonds_readSd:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
sector_loop:
|
||||
// sd read for sector 0xaabbccdd
|
||||
// C0 aa bb cc dd 00 00 00
|
||||
movs r3, #0xC0
|
||||
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, =0xA1586000
|
||||
str r3, [r4,#4]
|
||||
|
||||
C0_data_loop:
|
||||
ldr r3, [r4, #4]
|
||||
lsrs r3, r3, #24 // check if data is ready
|
||||
bcc C0_data_loop_check_transfer_end // if not skip reading
|
||||
|
||||
ldr r3, [r6]
|
||||
stmia r1!, {r3}
|
||||
|
||||
C0_data_loop_check_transfer_end:
|
||||
ldr r3, [r4, #4]
|
||||
lsrs r3, r3, #32 // check if transfer is done
|
||||
bcs C0_data_loop
|
||||
|
||||
adds r0, #1
|
||||
subs r2, #1
|
||||
bne sector_loop
|
||||
|
||||
pop {r4-r7,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
19
arm9/source/patches/platform/melonds/melondsWriteSdAsm.h
Normal file
19
arm9/source/patches/platform/melonds/melondsWriteSdAsm.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdWritePatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(melonds_writesd);
|
||||
|
||||
extern "C" void melonds_writeSd(u32 dstSector, const void* src, u32 sectorCount);
|
||||
|
||||
class MelonDSWriteSdPatchCode : public SdWritePatchCode
|
||||
{
|
||||
public:
|
||||
explicit MelonDSWriteSdPatchCode(PatchHeap& patchHeap)
|
||||
: SdWritePatchCode(SECTION_START(melonds_writesd), SECTION_SIZE(melonds_writesd), patchHeap) { }
|
||||
|
||||
const SdWriteFunc GetSdWriteFunction() const override
|
||||
{
|
||||
return (const SdWriteFunc)GetAddressAtTarget((void*)melonds_writeSd);
|
||||
}
|
||||
};
|
||||
60
arm9/source/patches/platform/melonds/melondsWriteSdAsm.s
Normal file
60
arm9/source/patches/platform/melonds/melondsWriteSdAsm.s
Normal file
@@ -0,0 +1,60 @@
|
||||
.cpu arm7tdmi
|
||||
.section "melonds_writesd", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = dst sector
|
||||
// r1 = src
|
||||
// r2 = sector count
|
||||
.global melonds_writeSd
|
||||
.type melonds_writeSd, %function
|
||||
melonds_writeSd:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
sector_loop:
|
||||
// write at sd sector 0xaabbccdd
|
||||
// C1 aa bb cc dd 00 00 00
|
||||
movs r3, #0xC1
|
||||
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, =0xE1586000
|
||||
str r3, [r4,#4]
|
||||
|
||||
C1_data_loop:
|
||||
ldr r3, [r4, #4]
|
||||
lsrs r3, r3, #24 // check if ready to write
|
||||
bcc C1_data_loop_check_transfer_end // if not skip reading
|
||||
|
||||
ldmia r1!, {r3}
|
||||
str r3, [r6]
|
||||
|
||||
C1_data_loop_check_transfer_end:
|
||||
ldr r3, [r4, #4]
|
||||
lsrs r3, r3, #32 // check if transfer is done
|
||||
bcs C1_data_loop
|
||||
|
||||
adds r0, #1
|
||||
subs r2, #1
|
||||
bne sector_loop
|
||||
|
||||
pop {r4-r7,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
40
arm9/source/patches/platform/r4/R4LoaderPlatform.cpp
Normal file
40
arm9/source/patches/platform/r4/R4LoaderPlatform.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "common.h"
|
||||
#include "R4LoaderPlatform.h"
|
||||
|
||||
#define R4_CMD_CARD_INFO 0xB000000000000000ull
|
||||
#define R4_CMD_SEND_MAP 0xB400000000000000ull
|
||||
|
||||
u32 R4LoaderPlatform::ReadCardInfo() const
|
||||
{
|
||||
u32 response;
|
||||
card_romSetCmd(R4_CMD_CARD_INFO);
|
||||
card_romStartXfer(MCCNT1_RESET_OFF | MCCNT1_CMD_SCRAMBLE |
|
||||
MCCNT1_CLOCK_SCRAMBLER | MCCNT1_READ_DATA_DESCRAMBLE |
|
||||
MCCNT1_LATENCY2(24) | MCCNT1_LATENCY1(0) | MCCNT1_LEN_4, false);
|
||||
card_romCpuRead(&response, 1);
|
||||
return response;
|
||||
}
|
||||
|
||||
void R4LoaderPlatform::PrepareClusterMap(bool isSave, u32 dirSector, u32 dirSectorOffset) const
|
||||
{
|
||||
u32 fatEntryAddress = ((dirSector << 9) | dirSectorOffset) | (isSave ? 1 : 0);
|
||||
card_romSetCmd(R4_CMD_SEND_MAP | ((u64)fatEntryAddress << 24));
|
||||
u32 response;
|
||||
do
|
||||
{
|
||||
card_romStartXfer(MCCNT1_RESET_OFF | MCCNT1_CMD_SCRAMBLE |
|
||||
MCCNT1_CLOCK_SCRAMBLER | MCCNT1_READ_DATA_DESCRAMBLE |
|
||||
MCCNT1_LATENCY2(24) | MCCNT1_LATENCY1(0) | MCCNT1_LEN_4, false);
|
||||
card_romCpuRead(&response, 1);
|
||||
} while (response);
|
||||
}
|
||||
|
||||
void R4LoaderPlatform::PrepareRomBoot(u32 romDirSector, u32 romDirSectorOffset) const
|
||||
{
|
||||
ReadCardInfo();
|
||||
if (romDirSector != 0)
|
||||
{
|
||||
PrepareClusterMap(false, romDirSector, romDirSectorOffset);
|
||||
}
|
||||
ReadCardInfo();
|
||||
}
|
||||
47
arm9/source/patches/platform/r4/R4LoaderPlatform.h
Normal file
47
arm9/source/patches/platform/r4/R4LoaderPlatform.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include <libtwl/card/card.h>
|
||||
#include "../LoaderPlatform.h"
|
||||
#include "r4ReadRomAsm.h"
|
||||
#include "r4ReadSdAsm.h"
|
||||
#include "r4WriteSdAsm.h"
|
||||
|
||||
/// @brief Implementation of LoaderPlatform for the original R4 flashcard
|
||||
class R4LoaderPlatform : public LoaderPlatform
|
||||
{
|
||||
public:
|
||||
const SdReadPatchCode* CreateSdReadPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new R4ReadSdPatchCode(patchHeap);
|
||||
});
|
||||
}
|
||||
|
||||
const SdWritePatchCode* CreateSdWritePatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new R4WriteSdPatchCode(patchHeap);
|
||||
});
|
||||
}
|
||||
|
||||
const SdReadPatchCode* CreateRomReadPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new R4ReadRomPatchCode(patchHeap);
|
||||
});
|
||||
}
|
||||
|
||||
bool HasRomReads() const override { return true; }
|
||||
|
||||
void PrepareRomBoot(u32 romDirSector, u32 romDirSectorOffset) const override;
|
||||
|
||||
private:
|
||||
u32 ReadCardInfo() const;
|
||||
void PrepareClusterMap(bool isSave, u32 dirSector, u32 dirSectorOffset) const;
|
||||
};
|
||||
20
arm9/source/patches/platform/r4/r4ReadRomAsm.h
Normal file
20
arm9/source/patches/platform/r4/r4ReadRomAsm.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdReadPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(r4_readrom);
|
||||
|
||||
extern "C" void r4_readRom(u32 srcSector, void* dst);
|
||||
|
||||
class R4ReadRomPatchCode : public SdReadPatchCode
|
||||
{
|
||||
public:
|
||||
explicit R4ReadRomPatchCode(PatchHeap& patchHeap)
|
||||
: SdReadPatchCode(SECTION_START(r4_readrom), SECTION_SIZE(r4_readrom), patchHeap)
|
||||
{ }
|
||||
|
||||
const SdReadFunc GetSdReadFunction() const override
|
||||
{
|
||||
return (const SdReadFunc)GetAddressAtTarget((void*)r4_readRom);
|
||||
}
|
||||
};
|
||||
76
arm9/source/patches/platform/r4/r4ReadRomAsm.s
Normal file
76
arm9/source/patches/platform/r4/r4ReadRomAsm.s
Normal file
@@ -0,0 +1,76 @@
|
||||
.cpu arm7tdmi
|
||||
.section "r4_readrom", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = dst
|
||||
.global r4_readRom
|
||||
.type r4_readRom, %function
|
||||
r4_readRom:
|
||||
push {r4-r7,lr}
|
||||
lsls r0, r0, #9 // rom sector to rom byte address
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
// request rom read at rom address 0xaabbccdd
|
||||
// B6 aa bb cc dd 00 00 00
|
||||
movs r3, #0xB6
|
||||
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 r0, r0, #24
|
||||
lsrs r7, r0, #24 // r7 = dd
|
||||
str r7, [r4,#0xC] // storing as little-endian puts the bottom 8 bits as first byte
|
||||
|
||||
ldr r6, =0x04100010
|
||||
|
||||
B6_poll_loop:
|
||||
ldr r3, =0xA7586000
|
||||
str r3, [r4,#4]
|
||||
|
||||
B6_poll_transfer_loop:
|
||||
ldrb r3, [r4,#6]
|
||||
lsrs r3, r3, #8
|
||||
bcc B6_poll_check_transfer_end
|
||||
ldr r2, [r6]
|
||||
|
||||
B6_poll_check_transfer_end:
|
||||
ldrb r3, [r4,#7]
|
||||
lsrs r3, r3, #8
|
||||
bcs B6_poll_transfer_loop
|
||||
|
||||
cmp r2, #0
|
||||
bne B6_poll_loop
|
||||
|
||||
movs r3, #0xB7
|
||||
strb r3, [r4,#0x8]
|
||||
|
||||
ldr r3, =0xA1586000
|
||||
str r3, [r4,#4]
|
||||
|
||||
B7_data_loop:
|
||||
ldrb r3, [r4,#6]
|
||||
lsrs r3, r3, #8 // check if data is ready
|
||||
bcc B7_data_loop_check_transfer_end // if not skip reading
|
||||
|
||||
ldr r3, [r6]
|
||||
stmia r1!, {r3}
|
||||
|
||||
B7_data_loop_check_transfer_end:
|
||||
ldrb r3, [r4,#7]
|
||||
lsrs r3, r3, #8 // check if transfer is done
|
||||
bcs B7_data_loop
|
||||
|
||||
pop {r4-r7,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
19
arm9/source/patches/platform/r4/r4ReadSdAsm.h
Normal file
19
arm9/source/patches/platform/r4/r4ReadSdAsm.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdReadPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(r4_readsd);
|
||||
|
||||
extern "C" void r4_readSd(u32 srcSector, void* dst, u32 sectorCount);
|
||||
|
||||
class R4ReadSdPatchCode : public SdReadPatchCode
|
||||
{
|
||||
public:
|
||||
explicit R4ReadSdPatchCode(PatchHeap& patchHeap)
|
||||
: SdReadPatchCode(SECTION_START(r4_readsd), SECTION_SIZE(r4_readsd), patchHeap) { }
|
||||
|
||||
const SdReadFunc GetSdReadFunction() const override
|
||||
{
|
||||
return (const SdReadFunc)GetAddressAtTarget((void*)r4_readSd);
|
||||
}
|
||||
};
|
||||
83
arm9/source/patches/platform/r4/r4ReadSdAsm.s
Normal file
83
arm9/source/patches/platform/r4/r4ReadSdAsm.s
Normal file
@@ -0,0 +1,83 @@
|
||||
.cpu arm7tdmi
|
||||
.section "r4_readsd", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = dst
|
||||
// r2 = sector count
|
||||
.global r4_readSd
|
||||
.type r4_readSd, %function
|
||||
r4_readSd:
|
||||
push {r4-r7,lr}
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
sector_loop:
|
||||
// request sd read at byte address 0xaabbccdd
|
||||
// B9 aa bb cc dd 00 00 00
|
||||
lsls r7, r0, #9 // sd sector to sd byte address
|
||||
movs r3, #0xB9
|
||||
strb r3, [r4,#0x8]
|
||||
lsrs r3, r7, #24
|
||||
strb r3, [r4,#0x9]
|
||||
lsrs r3, r7, #16
|
||||
strb r3, [r4,#0xA]
|
||||
lsrs r3, r7, #8
|
||||
strb r3, [r4,#0xB]
|
||||
lsls r3, r7, #24
|
||||
lsrs r3, r3, #24 // r7 = dd
|
||||
str r3, [r4,#0xC] // storing as little-endian puts the bottom 8 bits as first byte
|
||||
|
||||
ldr r6, =0x04100010
|
||||
|
||||
B9_poll_loop:
|
||||
ldr r3, =0xA7586000
|
||||
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, =0xA1586000
|
||||
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
|
||||
19
arm9/source/patches/platform/r4/r4WriteSdAsm.h
Normal file
19
arm9/source/patches/platform/r4/r4WriteSdAsm.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SdWritePatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(r4_writesd);
|
||||
|
||||
extern "C" void r4_writeSd(u32 dstSector, const void* src, u32 sectorCount);
|
||||
|
||||
class R4WriteSdPatchCode : public SdWritePatchCode
|
||||
{
|
||||
public:
|
||||
explicit R4WriteSdPatchCode(PatchHeap& patchHeap)
|
||||
: SdWritePatchCode(SECTION_START(r4_writesd), SECTION_SIZE(r4_writesd), patchHeap) { }
|
||||
|
||||
const SdWriteFunc GetSdWriteFunction() const override
|
||||
{
|
||||
return (const SdWriteFunc)GetAddressAtTarget((void*)r4_writeSd);
|
||||
}
|
||||
};
|
||||
83
arm9/source/patches/platform/r4/r4WriteSdAsm.s
Normal file
83
arm9/source/patches/platform/r4/r4WriteSdAsm.s
Normal file
@@ -0,0 +1,83 @@
|
||||
.cpu arm7tdmi
|
||||
.section "r4_writesd", "ax"
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
// r0 = dst sector
|
||||
// r1 = src
|
||||
// r2 = sector count
|
||||
.global r4_writeSd
|
||||
.type r4_writeSd, %function
|
||||
r4_writeSd:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
sector_loop:
|
||||
// write at sd byte address 0xaabbccdd
|
||||
// BB aa bb cc dd 00 00 00
|
||||
lsls r7, r0, #9 // sd sector to sd byte address
|
||||
movs r3, #0xBB
|
||||
strb r3, [r4,#0x8]
|
||||
lsrs r3, r7, #24
|
||||
strb r3, [r4,#0x9]
|
||||
lsrs r3, r7, #16
|
||||
strb r3, [r4,#0xA]
|
||||
lsrs r3, r7, #8
|
||||
strb r3, [r4,#0xB]
|
||||
lsls r3, r7, #24
|
||||
lsrs r3, r3, #24 // r7 = dd
|
||||
str r3, [r4,#0xC] // storing as little-endian puts the bottom 8 bits as first byte
|
||||
|
||||
ldr r6, =0x04100010
|
||||
|
||||
ldr r3, =0xE1586000
|
||||
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, =0xA7586000
|
||||
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
|
||||
64
arm9/source/patches/platform/r4idsn/R4iDSNLoaderPlatform.h
Normal file
64
arm9/source/patches/platform/r4idsn/R4iDSNLoaderPlatform.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "../acekard-common/IoRPGLoaderPlatform.h"
|
||||
#include "r4idsnReadSdAsm.h"
|
||||
#include "r4idsnSdReadSectorAsm.h"
|
||||
#include "r4idsnWriteSdAsm.h"
|
||||
|
||||
/// @brief Implementation of LoaderPlatform for the r4idsn.com flashcard.
|
||||
class R4iDSNLoaderPlatform : public IoRPGLoaderPlatform
|
||||
{
|
||||
private:
|
||||
enum
|
||||
{
|
||||
IORPG_CMD_SDIO_BYTE = 0xAB
|
||||
};
|
||||
|
||||
public:
|
||||
R4iDSNLoaderPlatform() : IoRPGLoaderPlatform(IORPG_CMD_SDIO_BYTE) { }
|
||||
|
||||
const SdReadPatchCode* CreateSdReadPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
auto* waitForStatePatchCode = patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new ioRPGSDWaitForStatePatchCode(patchHeap);
|
||||
});
|
||||
return new R4iDSNReadSdPatchCode(patchHeap,
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new ioRPGSendSDIOCommandPatchCode(patchHeap);
|
||||
}),
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new R4iDSNSDReadSectorPatchCode(patchHeap, waitForStatePatchCode);
|
||||
}),
|
||||
waitForStatePatchCode);
|
||||
});
|
||||
}
|
||||
|
||||
const SdWritePatchCode* CreateSdWritePatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new R4iDSNWriteSdPatchCode(patchHeap,
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new ioRPGSendSDIOCommandPatchCode(patchHeap);
|
||||
}),
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new ioRPGSDWaitForStatePatchCode(patchHeap);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
void PatchSdscShift(void) const override
|
||||
{
|
||||
r4idsn_readSd_sdsc_shift = THUMB_MOVS_REG(THUMB_R4, THUMB_R0);
|
||||
r4idsn_writeSd_sdsc_shift = THUMB_MOVS_REG(THUMB_R7, THUMB_R0);
|
||||
}
|
||||
};
|
||||
36
arm9/source/patches/platform/r4idsn/r4idsnReadSdAsm.h
Normal file
36
arm9/source/patches/platform/r4idsn/r4idsnReadSdAsm.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "r4idsnSdReadSectorAsm.h"
|
||||
#include "../SdReadPatchCode.h"
|
||||
#include "../acekard-common/iorpgSendSdioCommandAsm.h"
|
||||
#include "../acekard-common/iorpgSdWaitForStateAsm.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(r4idsn_readsd);
|
||||
|
||||
extern "C" void r4idsn_readSd(u32 srcSector, void* dst, u32 sectorCount);
|
||||
|
||||
extern u32 r4idsn_readSd_sendSdioCommand_address;
|
||||
extern u32 r4idsn_readSd_sdReadSector_address;
|
||||
extern u32 r4idsn_readSd_sdWaitForState_address;
|
||||
extern u16 r4idsn_readSd_sdsc_shift;
|
||||
|
||||
class R4iDSNReadSdPatchCode : public SdReadPatchCode
|
||||
{
|
||||
public:
|
||||
explicit R4iDSNReadSdPatchCode(PatchHeap& patchHeap,
|
||||
const ioRPGSendSDIOCommandPatchCode* iorpgSendSdioCommandPatchCode,
|
||||
const R4iDSNSDReadSectorPatchCode* r4idsnSdReadSectorPatchCode,
|
||||
const ioRPGSDWaitForStatePatchCode* iorpgSdWaitForStatePatchCode)
|
||||
: SdReadPatchCode(SECTION_START(r4idsn_readsd), SECTION_SIZE(r4idsn_readsd), patchHeap)
|
||||
{
|
||||
r4idsn_readSd_sendSdioCommand_address = (u32)iorpgSendSdioCommandPatchCode->GetSendSdioCommandFunction();
|
||||
r4idsn_readSd_sdReadSector_address = (u32)r4idsnSdReadSectorPatchCode->GetSDReadSectorFunction();
|
||||
r4idsn_readSd_sdWaitForState_address = (u32)iorpgSdWaitForStatePatchCode->GetSDWaitForStateFunction();
|
||||
}
|
||||
|
||||
const SdReadFunc GetSdReadFunction() const override
|
||||
{
|
||||
return (const SdReadFunc)GetAddressAtTarget((void*)r4idsn_readSd);
|
||||
}
|
||||
};
|
||||
69
arm9/source/patches/platform/r4idsn/r4idsnReadSdAsm.s
Normal file
69
arm9/source/patches/platform/r4idsn/r4idsnReadSdAsm.s
Normal file
@@ -0,0 +1,69 @@
|
||||
.cpu arm7tdmi
|
||||
.syntax unified
|
||||
.section "r4idsn_readsd", "ax"
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = dst
|
||||
// r2 = sector count
|
||||
.global r4idsn_readSd
|
||||
.type r4idsn_readSd, %function
|
||||
r4idsn_readSd:
|
||||
push {r4,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
.global r4idsn_readSd_sdsc_shift
|
||||
r4idsn_readSd_sdsc_shift:
|
||||
lsls r4, r0, #9
|
||||
|
||||
push {r0-r1}
|
||||
|
||||
ldr r0, =0x120004AB
|
||||
movs r1, r4
|
||||
ldr r3, r4idsn_readSd_sendSdioCommand_address
|
||||
bl blx_r3
|
||||
|
||||
// Wait for SD state
|
||||
movs r0, #4
|
||||
movs r1, #7
|
||||
ldr r3, r4idsn_readSd_sdWaitForState_address
|
||||
bl blx_r3
|
||||
|
||||
pop {r0-r1}
|
||||
|
||||
// Read sectors. Parameters identical to readSd
|
||||
ldr r3, r4idsn_readSd_sdReadSector_address
|
||||
bl blx_r3
|
||||
|
||||
// Send CMD12 == STOP_TRANSMISSION
|
||||
push {r0-r1}
|
||||
ldr r0, =0x0C0001AB
|
||||
movs r1, #0
|
||||
ldr r3, r4idsn_readSd_sendSdioCommand_address
|
||||
bl blx_r3
|
||||
|
||||
pop {r0-r1,r4,pc}
|
||||
|
||||
blx_r3:
|
||||
bx r3
|
||||
|
||||
.balign 4
|
||||
|
||||
.global r4idsn_readSd_sendSdioCommand_address
|
||||
r4idsn_readSd_sendSdioCommand_address:
|
||||
.word 0
|
||||
|
||||
.global r4idsn_readSd_sdReadSector_address
|
||||
r4idsn_readSd_sdReadSector_address:
|
||||
.word 0
|
||||
|
||||
.global r4idsn_readSd_sdWaitForState_address
|
||||
r4idsn_readSd_sdWaitForState_address:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
26
arm9/source/patches/platform/r4idsn/r4idsnSdReadSectorAsm.h
Normal file
26
arm9/source/patches/platform/r4idsn/r4idsnSdReadSectorAsm.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "../acekard-common/iorpgSdWaitForStateAsm.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(r4idsn_sdreadsector);
|
||||
|
||||
extern "C" void r4idsn_sdReadSector(u32 srcSector, void* dst, u32 sectorCount);
|
||||
|
||||
extern u32 r4idsn_sdReadSector_sdWaitForState_address;
|
||||
|
||||
class R4iDSNSDReadSectorPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit R4iDSNSDReadSectorPatchCode(PatchHeap& patchHeap,
|
||||
const ioRPGSDWaitForStatePatchCode* iorpgSdWaitForStatePatchCode)
|
||||
: PatchCode(SECTION_START(r4idsn_sdreadsector), SECTION_SIZE(r4idsn_sdreadsector), patchHeap)
|
||||
{
|
||||
r4idsn_sdReadSector_sdWaitForState_address = (u32)iorpgSdWaitForStatePatchCode->GetSDWaitForStateFunction();
|
||||
}
|
||||
|
||||
const void* GetSDReadSectorFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)r4idsn_sdReadSector);
|
||||
}
|
||||
};
|
||||
67
arm9/source/patches/platform/r4idsn/r4idsnSdReadSectorAsm.s
Normal file
67
arm9/source/patches/platform/r4idsn/r4idsnSdReadSectorAsm.s
Normal file
@@ -0,0 +1,67 @@
|
||||
.cpu arm7tdmi
|
||||
.syntax unified
|
||||
.section "r4idsn_sdreadsector", "ax"
|
||||
.thumb
|
||||
|
||||
// r0 = src sector
|
||||
// r1 = dst
|
||||
// r2 = sector count
|
||||
.global r4idsn_sdReadSector
|
||||
.type r4idsn_sdReadSector, %function
|
||||
r4idsn_sdReadSector:
|
||||
push {r4-r5,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
ldr r5, =0x04100010
|
||||
|
||||
sector_loop:
|
||||
// NOW we can start reading things
|
||||
movs r3, #0xB7
|
||||
str r3, [r4,#0x8]
|
||||
// The full CMD is B7 00 00 00 00 13 00 00
|
||||
// If we use 0x1300 and str, then it goes into the expected place
|
||||
movs r3, #0x13
|
||||
lsls r3, r3, #8
|
||||
str r3, [r4,#0xC]
|
||||
|
||||
ldr r3, =0xA1406004
|
||||
str r3, [r4,#4]
|
||||
|
||||
r4idsn_sdReadSector_read_loop:
|
||||
ldrb r3, [r4,#6]
|
||||
lsrs r3, r3, #8 // check if data is ready
|
||||
bcc r4idsn_sdReadSector_read_loop_check_transfer_end // if not skip reading
|
||||
|
||||
ldr r3, [r5]
|
||||
stmia r1!, {r3}
|
||||
|
||||
r4idsn_sdReadSector_read_loop_check_transfer_end:
|
||||
ldrb r3, [r4,#7]
|
||||
lsrs r3, r3, #8 // check if transfer is done
|
||||
bcs r4idsn_sdReadSector_read_loop
|
||||
|
||||
// Wait for SD state
|
||||
push {r0-r1}
|
||||
movs r0, #4
|
||||
movs r1, #7
|
||||
ldr r3, r4idsn_sdReadSector_sdWaitForState_address
|
||||
bl blx_r3
|
||||
pop {r0-r1}
|
||||
|
||||
subs r2, #1
|
||||
bne sector_loop
|
||||
|
||||
pop {r4-r5,pc}
|
||||
|
||||
blx_r3:
|
||||
bx r3
|
||||
|
||||
.balign 4
|
||||
|
||||
.global r4idsn_sdReadSector_sdWaitForState_address
|
||||
r4idsn_sdReadSector_sdWaitForState_address:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
32
arm9/source/patches/platform/r4idsn/r4idsnWriteSdAsm.h
Normal file
32
arm9/source/patches/platform/r4idsn/r4idsnWriteSdAsm.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "../SdWritePatchCode.h"
|
||||
#include "../acekard-common/iorpgSendSdioCommandAsm.h"
|
||||
#include "../acekard-common/iorpgSdWaitForStateAsm.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(r4idsn_writesd);
|
||||
|
||||
extern "C" void r4idsn_writeSd(u32 srcSector, void* dst, u32 sectorCount);
|
||||
|
||||
extern u32 r4idsn_writeSd_sendSdioCommand_address;
|
||||
extern u32 r4idsn_writeSd_sdWaitForState_address;
|
||||
extern u16 r4idsn_writeSd_sdsc_shift;
|
||||
|
||||
class R4iDSNWriteSdPatchCode : public SdWritePatchCode
|
||||
{
|
||||
public:
|
||||
explicit R4iDSNWriteSdPatchCode(PatchHeap& patchHeap,
|
||||
const ioRPGSendSDIOCommandPatchCode* iorpgSendSdioCommandPatchCode,
|
||||
const ioRPGSDWaitForStatePatchCode* iorpgSdWaitForStatePatchCode)
|
||||
: SdWritePatchCode(SECTION_START(r4idsn_writesd), SECTION_SIZE(r4idsn_writesd), patchHeap)
|
||||
{
|
||||
r4idsn_writeSd_sendSdioCommand_address = (u32)iorpgSendSdioCommandPatchCode->GetSendSdioCommandFunction();
|
||||
r4idsn_writeSd_sdWaitForState_address = (u32)iorpgSdWaitForStatePatchCode->GetSDWaitForStateFunction();
|
||||
}
|
||||
|
||||
const SdWriteFunc GetSdWriteFunction() const override
|
||||
{
|
||||
return (const SdWriteFunc)GetAddressAtTarget((void*)r4idsn_writeSd);
|
||||
}
|
||||
};
|
||||
81
arm9/source/patches/platform/r4idsn/r4idsnWriteSdAsm.s
Normal file
81
arm9/source/patches/platform/r4idsn/r4idsnWriteSdAsm.s
Normal file
@@ -0,0 +1,81 @@
|
||||
.cpu arm7tdmi
|
||||
.syntax unified
|
||||
.section "r4idsn_writesd", "ax"
|
||||
.thumb
|
||||
|
||||
// r0 = dst sector
|
||||
// r1 = src
|
||||
// r2 = sector count
|
||||
.global r4idsn_writeSd
|
||||
.type r4idsn_writeSd, %function
|
||||
r4idsn_writeSd:
|
||||
push {r4-r7,lr}
|
||||
|
||||
ldr r4, =0x040001A0
|
||||
movs r3, #0x80
|
||||
strb r3, [r4,#1]
|
||||
|
||||
sector_loop:
|
||||
.global r4idsn_writeSd_sdsc_shift
|
||||
r4idsn_writeSd_sdsc_shift:
|
||||
lsls r7, r0, #9
|
||||
|
||||
push {r0-r1}
|
||||
// SDIO cmd
|
||||
ldr r0, =0x180005AB
|
||||
// param
|
||||
movs r1, r7
|
||||
ldr r6, r4idsn_writeSd_sendSdioCommand_address
|
||||
bl blx_r6
|
||||
pop {r0-r1}
|
||||
|
||||
// NOW we can start writing things
|
||||
movs r7, #1
|
||||
lsls r7, r7, #9
|
||||
r4idsn_writeSd_block_write_loop:
|
||||
// start copy to MCCMD1
|
||||
ldmia r1!, {r5, r6}
|
||||
str r5, [r4, #0x8]
|
||||
str r6, [r4, #0xc]
|
||||
|
||||
ldr r3, =0xA0406000
|
||||
str r3, [r4,#4]
|
||||
|
||||
r4idsn_writeSd_block_write_wait_busy:
|
||||
ldr r3, [r4,#4]
|
||||
cmp r3, #0
|
||||
blt r4idsn_writeSd_block_write_wait_busy
|
||||
|
||||
subs r7, r7, #8
|
||||
bne r4idsn_writeSd_block_write_loop
|
||||
|
||||
push {r0-r1}
|
||||
// Wait for SD state
|
||||
movs r0, #4
|
||||
movs r1, #0
|
||||
ldr r6, r4idsn_writeSd_sdWaitForState_address
|
||||
bl blx_r6
|
||||
pop {r0-r1}
|
||||
|
||||
adds r0, #1
|
||||
subs r2, #1
|
||||
bne sector_loop
|
||||
|
||||
pop {r4-r7,pc}
|
||||
|
||||
blx_r6:
|
||||
bx r6
|
||||
|
||||
.balign 4
|
||||
|
||||
.global r4idsn_writeSd_sendSdioCommand_address
|
||||
r4idsn_writeSd_sendSdioCommand_address:
|
||||
.word 0
|
||||
|
||||
.global r4idsn_writeSd_sdWaitForState_address
|
||||
r4idsn_writeSd_sdWaitForState_address:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
.end
|
||||
32
arm9/source/patches/platform/supercard/SuperCardCommon.h
Normal file
32
arm9/source/patches/platform/supercard/SuperCardCommon.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "../SdReadPatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(scsd_common);
|
||||
|
||||
extern "C" void sccmn_changeMode();
|
||||
extern "C" void sccmn_sdSendClock10();
|
||||
extern "C" void sccmn_sdio4BitCrc16();
|
||||
|
||||
class SuperCardCommonPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit SuperCardCommonPatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(scsd_common), SECTION_SIZE(scsd_common), patchHeap) { }
|
||||
|
||||
const void* GetScChangeModeFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)sccmn_changeMode);
|
||||
}
|
||||
|
||||
const void* GetSdSendClock10Function() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)sccmn_sdSendClock10);
|
||||
}
|
||||
|
||||
const void* GetCrc16ChecksumFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)sccmn_sdio4BitCrc16);
|
||||
}
|
||||
};
|
||||
119
arm9/source/patches/platform/supercard/SuperCardCommon.s
Normal file
119
arm9/source/patches/platform/supercard/SuperCardCommon.s
Normal file
@@ -0,0 +1,119 @@
|
||||
.cpu arm7tdmi
|
||||
.syntax unified
|
||||
.section "scsd_common", "ax"
|
||||
|
||||
#include "asminc.h"
|
||||
|
||||
@ void sc_change_mode(uint16_t mode);
|
||||
BEGIN_THUMB_FUNCTION sccmn_changeMode
|
||||
ldr r2,= 0x09FFFFFE
|
||||
ldr r3,= 0xA55A
|
||||
strh r3, [r2]
|
||||
strh r3, [r2]
|
||||
strh r0, [r2]
|
||||
strh r0, [r2]
|
||||
mov pc,lr
|
||||
|
||||
@ this function will trash r4 but leave r0 and r1 untouched
|
||||
@ void SDSendClock10(void)
|
||||
BEGIN_THUMB_FUNCTION sccmn_sdSendClock10
|
||||
movs r3, #0x10
|
||||
@ loads reg_scsd_cmd
|
||||
movs r2, #0x98
|
||||
lsls r2, r2, #20
|
||||
1:
|
||||
ldrh r4, [r2]
|
||||
subs r3, r3, #1
|
||||
bne 1b
|
||||
mov pc, lr
|
||||
|
||||
@ static uint64_t inline calSingleCRC16(uint64_t crc, uint32_t data_in){
|
||||
@ // Shift out 8 bits for each line
|
||||
@ uint32_t data_out = crc >> 32;
|
||||
@ crc <<= 32;
|
||||
@
|
||||
@ // XOR outgoing data to itself with 4 bit delay
|
||||
@ data_out ^= (data_out >> 16);
|
||||
@
|
||||
@ // XOR incoming data to outgoing data with 4 bit delay
|
||||
@ data_out ^= (data_in >> 16);
|
||||
@
|
||||
@ // XOR outgoing and incoming data to accumulator at each tap
|
||||
@ uint64_t xorred = data_out ^ data_in;
|
||||
@ crc ^= xorred;
|
||||
@ crc ^= xorred << (5 * 4);
|
||||
@ crc ^= xorred << (12 * 4);
|
||||
@ return crc;
|
||||
@ }
|
||||
@ uint64_t sdio_crc16_4bit_checksum(void* dataBuf)
|
||||
@ {
|
||||
@ uint32_t num_words = 512 / sizeof(uint32_t);
|
||||
@ uint64_t crc = 0;
|
||||
@ auto* data = static_cast<uint32_t*>(dataBuf);
|
||||
@ auto* end = data + num_words;
|
||||
@ while (data < end)
|
||||
@ {
|
||||
@ uint32_t data_in = __builtin_bswap32(*data++);
|
||||
@ crc = calSingleCRC16(crc, data_in);
|
||||
@ }
|
||||
@
|
||||
@ return __builtin_bswap64(crc);
|
||||
@ }
|
||||
|
||||
@ uint64_t sdio_crc16_4bit_checksum(void*)
|
||||
@ r0 and r1 are left untouched, the result is returned in r2-r3 instead
|
||||
BEGIN_THUMB_FUNCTION sccmn_sdio4BitCrc16
|
||||
push {r0,r1,r4-r5,lr}
|
||||
movs r4, #0 @ r4 = crc_lo
|
||||
movs r5, #0 @ r5 = crc_hi
|
||||
movs r1, #128
|
||||
1:
|
||||
@ r5 = data_out
|
||||
lsrs r3, r5, #16
|
||||
eors r5, r3
|
||||
|
||||
ldmia r0!, {r2}
|
||||
|
||||
bl byteSwap32
|
||||
@ r2 = data_in
|
||||
|
||||
lsrs r3, r2, #16
|
||||
eors r5, r3
|
||||
eors r2, r5 // r2 = xorred
|
||||
movs r5, r4 // r5 = crc_hi
|
||||
movs r4, r2 // r4 = crc_lo
|
||||
|
||||
lsls r3, r2, #20
|
||||
eors r4, r3
|
||||
lsrs r3, r2, #12
|
||||
eors r5, r3
|
||||
lsls r3, r2, #16
|
||||
eors r5, r3
|
||||
|
||||
subs r1, #1
|
||||
bne 1b
|
||||
|
||||
movs r2, r4
|
||||
bl byteSwap32
|
||||
movs r3, r2
|
||||
|
||||
movs r2, r5
|
||||
@falls through to perform a "final" byteSwap32 call
|
||||
b byteSwap32_nopush
|
||||
|
||||
byteSwap32:
|
||||
push {r0,r1,r4-r5,lr}
|
||||
byteSwap32_nopush:
|
||||
movs r5, #16
|
||||
ldr r4,= 0xFF00FF
|
||||
rors r2, r5 // ror 16
|
||||
ands r4, r2
|
||||
bics r2, r4
|
||||
lsls r4, r4, #8
|
||||
lsrs r2, r2, #8
|
||||
orrs r2, r4
|
||||
pop {r0,r1,r4-r5,pc}
|
||||
|
||||
.balign 4
|
||||
|
||||
.pool
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
#define NUM_STARTUP_CLOCKS 30000
|
||||
#define MAX_STARTUP_TRIES 5000
|
||||
#define SD_OCR_VALUE 0x00030000
|
||||
#define RESPONSE_TIMEOUT 256
|
||||
#define SD_STATE_STBY 3
|
||||
#define SD_STATE_TRAN 4
|
||||
#define READY_FOR_DATA 1
|
||||
|
||||
#define SD_RESET_ADDR (*(vu16*)0x09440000)
|
||||
|
||||
#define SC_MODE_REG (*(vu16*)0x09FFFFFE)
|
||||
#define SC_MODE_MAGIC 0xA55A
|
||||
|
||||
#define SC_MODE_SDRAM 0x0001
|
||||
#define SC_MODE_SDCARD 0x0002
|
||||
|
||||
#define REG_SCSD_CMD16 (*(vu16*)0x09800000)
|
||||
#define REG_SCSD_CMD32 (*(vu32*)0x09800000)
|
||||
@@ -0,0 +1,184 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/mem/memExtern.h>
|
||||
#include "../SdioDefinitions.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "SuperCardDefinitions.h"
|
||||
#include "SuperCardLoaderPlatform.h"
|
||||
#include "SuperCardSDCommands.h"
|
||||
|
||||
enum SupercardType
|
||||
{
|
||||
SUPERCARD_TYPE_SC_SD = 0x00,
|
||||
SUPERCARD_TYPE_SC_LITE = 0x01,
|
||||
SUPERCARD_TYPE_SC_CF = 0x02,
|
||||
SUPERCARD_TYPE_SC_RUMBLE = (0x10 | SUPERCARD_TYPE_SC_LITE),
|
||||
SUPERCARD_TYPE_UNK = ~SUPERCARD_TYPE_SC_RUMBLE,
|
||||
};
|
||||
|
||||
static void changeSupercardMode(u16 mode)
|
||||
{
|
||||
SC_MODE_REG = SC_MODE_MAGIC;
|
||||
SC_MODE_REG = SC_MODE_MAGIC;
|
||||
SC_MODE_REG = mode;
|
||||
SC_MODE_REG = mode;
|
||||
}
|
||||
|
||||
static SupercardType detectSupercardType()
|
||||
{
|
||||
changeSupercardMode(SC_MODE_SDCARD);
|
||||
u32 val = *(vu16*)0x09800000;
|
||||
switch (val & 0xE300)
|
||||
{
|
||||
case 0xA000:
|
||||
{
|
||||
return SUPERCARD_TYPE_SC_LITE;
|
||||
}
|
||||
case 0xC000:
|
||||
{
|
||||
return SUPERCARD_TYPE_SC_RUMBLE;
|
||||
}
|
||||
case 0xE000:
|
||||
{
|
||||
return SUPERCARD_TYPE_SC_SD;
|
||||
}
|
||||
default:
|
||||
{
|
||||
auto* cfstatns = (vu16*)0x99C0000;
|
||||
*cfstatns = 0x50;
|
||||
__asm__ volatile(
|
||||
"nop\n"
|
||||
"nop\n"
|
||||
"nop\n"
|
||||
"nop\n"
|
||||
"nop\n"
|
||||
"nop\n"
|
||||
"nop\n"
|
||||
"nop\n"
|
||||
);
|
||||
return *cfstatns == 0x50
|
||||
? SUPERCARD_TYPE_SC_CF
|
||||
: SUPERCARD_TYPE_UNK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SuperCardLoaderPlatform::InitializeSdCard()
|
||||
{
|
||||
u32 oldMemCnt = REG_EXMEMCNT;
|
||||
mem_setGbaCartridgeRomWaits(EXMEMCNT_SLOT2_ROM_WAIT1_10, EXMEMCNT_SLOT2_ROM_WAIT2_6);
|
||||
bool result = InitializeSdCardIntern();
|
||||
REG_EXMEMCNT = oldMemCnt;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SuperCardLoaderPlatform::InitializeSdCardIntern()
|
||||
{
|
||||
changeSupercardMode(SC_MODE_SDRAM | SC_MODE_SDCARD);
|
||||
sc_resetSdCard();
|
||||
sc_sendSdClock(NUM_STARTUP_CLOCKS);
|
||||
sc_sdCommand(SD_CMD0_GO_IDLE_STATE, 0);
|
||||
sc_sendSdClock(NUM_STARTUP_CLOCKS);
|
||||
|
||||
u8 responseBuffer[17]; // purposely uninitialized
|
||||
bool isSdhc = false;
|
||||
if (sc_sdCommandAndReadResponse(SD_CMD8_SEND_IF_COND, SD_IF_COND_PATTERN, responseBuffer, SD_R1_RESPONSE_LENGTH_BYTES) &&
|
||||
responseBuffer[0] == SD_CMD8_SEND_IF_COND &&
|
||||
responseBuffer[1] == 0 &&
|
||||
responseBuffer[2] == 0 &&
|
||||
responseBuffer[3] == (SD_IF_COND_PATTERN >> 8) &&
|
||||
responseBuffer[4] == (SD_IF_COND_PATTERN & 0xFF))
|
||||
{
|
||||
isSdhc = true; //might be
|
||||
}
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAX_STARTUP_TRIES; ++i)
|
||||
{
|
||||
u32 arg = 0;
|
||||
arg |= (1 << 28); //Max performance
|
||||
arg |= (1 << 20); //3.3v
|
||||
if (isSdhc)
|
||||
{
|
||||
arg |= (1 << 30); // Set HCS bit,Supports SDHC
|
||||
}
|
||||
auto res = sc_sdAppCommandAndReadResponse(
|
||||
SD_ACMD41_SD_SEND_OP_COND, 0, arg, responseBuffer, SD_R1_RESPONSE_LENGTH_BYTES);
|
||||
if (res == ScAppCommandResult::FailedToSend)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (res == ScAppCommandResult::Ok &&//ACMD41
|
||||
((responseBuffer[1] & (1 << 7)) != 0)/*Busy:0b:initing 1b:init completed*/)
|
||||
{
|
||||
u16 ccs = responseBuffer[1] & (1 << 6);//0b:SDSC 1b:SDHC/SDXC
|
||||
if (!ccs && isSdhc)
|
||||
{
|
||||
isSdhc = false;
|
||||
}
|
||||
break; // Card is ready
|
||||
}
|
||||
sc_sendSdClock(NUM_STARTUP_CLOCKS);
|
||||
}
|
||||
|
||||
if (i >= MAX_STARTUP_TRIES)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// The card's name, as assigned by the manufacturer
|
||||
sc_sdCommandAndDropResponse(SD_CMD2_ALL_SEND_CID, 0, SD_R2_RESPONSE_LENGTH_BYTES);
|
||||
// Get a new address
|
||||
u32 relativeCardAddress = 0;
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAX_STARTUP_TRIES; i++)
|
||||
{
|
||||
sc_sdCommandAndReadResponse(SD_CMD3_SEND_RELATIVE_ADDR, 0, responseBuffer, SD_R1_RESPONSE_LENGTH_BYTES);
|
||||
relativeCardAddress = (responseBuffer[1] << 24) | (responseBuffer[2] << 16);
|
||||
if ((responseBuffer[3] & 0x1E) != (SD_STATE_STBY << 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= MAX_STARTUP_TRIES)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Some cards won't go to higher speeds unless they think you checked their capabilities
|
||||
sc_sdCommandAndDropResponse(SD_CMD9_SEND_CSD, relativeCardAddress, SD_R2_RESPONSE_LENGTH_BYTES);
|
||||
|
||||
// Only this card should respond to all future commands
|
||||
sc_sdCommandAndDropResponse(SD_CMD7_SELECT_CARD, relativeCardAddress, SD_R1_RESPONSE_LENGTH_BYTES);
|
||||
|
||||
// Set a 4 bit data bus
|
||||
sc_sdAppCommandAndDropResponse(SD_ACMD6_SET_BUS_WIDTH, relativeCardAddress, 2, SD_R1_RESPONSE_LENGTH_BYTES);
|
||||
|
||||
|
||||
const u16 nonSdhcOpcode = THUMB_LSLS_IMM(THUMB_R1, THUMB_R0, 9);
|
||||
const u16 sdhcOpcode = THUMB_MOVS_REG(THUMB_R1, THUMB_R0);
|
||||
const u16 opcode = isSdhc ? sdhcOpcode : nonSdhcOpcode;
|
||||
scsd_writeSectorSdhcLabel = opcode;
|
||||
sclite_writeSectorSdhcLabel = opcode;
|
||||
scsd_readSectorSdhcLabel = opcode;
|
||||
sclite_readSectorSdhcLabel = opcode;
|
||||
|
||||
auto type = detectSupercardType();
|
||||
switch (type)
|
||||
{
|
||||
case SUPERCARD_TYPE_SC_SD:
|
||||
case SUPERCARD_TYPE_SC_LITE:
|
||||
case SUPERCARD_TYPE_SC_RUMBLE:
|
||||
{
|
||||
isScLite = (type & SUPERCARD_TYPE_SC_LITE) != 0;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
108
arm9/source/patches/platform/supercard/SuperCardLoaderPlatform.h
Normal file
108
arm9/source/patches/platform/supercard/SuperCardLoaderPlatform.h
Normal file
@@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "../LoaderPlatform.h"
|
||||
#include "SuperCardCommon.h"
|
||||
#include "sclite/SuperCardLiteImpl.h"
|
||||
#include "scsd/SuperCardSDImpl.h"
|
||||
|
||||
/// @brief Implementation of LoaderPlatform for the slot 2 SuperCard flashcard
|
||||
class SuperCardLoaderPlatform : public LoaderPlatform
|
||||
{
|
||||
public:
|
||||
const SdReadPatchCode* CreateSdReadPatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
if (isScLite)
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardReadSectorLitePatchCode(patchHeap,
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardCommonPatchCode(patchHeap);
|
||||
}),
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardSDCommandAndDropLitePatchCode(patchHeap);
|
||||
}),
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardReadDataLitePatchCode(patchHeap);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardReadSectorPatchCode(patchHeap,
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardCommonPatchCode(patchHeap);
|
||||
}),
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardSdCommandAndDropPatchCode(patchHeap);
|
||||
}),
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardReadDataPatchCode(patchHeap);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const SdWritePatchCode* CreateSdWritePatchCode(
|
||||
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
|
||||
{
|
||||
if (isScLite)
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardWriteSectorLitePatchCode(patchHeap,
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardCommonPatchCode(patchHeap);
|
||||
}),
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardSDCommandAndDropLitePatchCode(patchHeap);
|
||||
}),
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardWriteDataLitePatchCode(patchHeap);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardWriteSectorPatchCode(patchHeap,
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardCommonPatchCode(patchHeap);
|
||||
}),
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardSdCommandAndDropPatchCode(patchHeap);
|
||||
}),
|
||||
patchCodeCollection.GetOrAddSharedPatchCode([&]
|
||||
{
|
||||
return new SuperCardWriteDataPatchCode(patchHeap);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool InitializeSdCard() override;
|
||||
|
||||
private:
|
||||
u16 isScLite = false;
|
||||
|
||||
bool InitializeSdCardIntern();
|
||||
};
|
||||
175
arm9/source/patches/platform/supercard/SuperCardSDCommands.cpp
Normal file
175
arm9/source/patches/platform/supercard/SuperCardSDCommands.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
#include "common.h"
|
||||
#include "../SdioDefinitions.h"
|
||||
#include "SuperCardSDCommands.h"
|
||||
#include "SuperCardDefinitions.h"
|
||||
|
||||
#define BUSY_WAIT_TIMEOUT 500000
|
||||
|
||||
static const u8 sCrc7Lut[]
|
||||
{
|
||||
0x0, 0x12, 0x24, 0x36, 0x48, 0x5A, 0x6C, 0x7E, 0x90, 0x82, 0xB4, 0xA6, 0xD8, 0xCA, 0xFC, 0xEE,
|
||||
0x32, 0x20, 0x16, 0x4, 0x7A, 0x68, 0x5E, 0x4C, 0xA2, 0xB0, 0x86, 0x94, 0xEA, 0xF8, 0xCE, 0xDC,
|
||||
0x64, 0x76, 0x40, 0x52, 0x2C, 0x3E, 0x8, 0x1A, 0xF4, 0xE6, 0xD0, 0xC2, 0xBC, 0xAE, 0x98, 0x8A,
|
||||
0x56, 0x44, 0x72, 0x60, 0x1E, 0xC, 0x3A, 0x28, 0xC6, 0xD4, 0xE2, 0xF0, 0x8E, 0x9C, 0xAA, 0xB8,
|
||||
0xC8, 0xDA, 0xEC, 0xFE, 0x80, 0x92, 0xA4, 0xB6, 0x58, 0x4A, 0x7C, 0x6E, 0x10, 0x2, 0x34, 0x26,
|
||||
0xFA, 0xE8, 0xDE, 0xCC, 0xB2, 0xA0, 0x96, 0x84, 0x6A, 0x78, 0x4E, 0x5C, 0x22, 0x30, 0x6, 0x14,
|
||||
0xAC, 0xBE, 0x88, 0x9A, 0xE4, 0xF6, 0xC0, 0xD2, 0x3C, 0x2E, 0x18, 0xA, 0x74, 0x66, 0x50, 0x42,
|
||||
0x9E, 0x8C, 0xBA, 0xA8, 0xD6, 0xC4, 0xF2, 0xE0, 0xE, 0x1C, 0x2A, 0x38, 0x46, 0x54, 0x62, 0x70,
|
||||
0x82, 0x90, 0xA6, 0xB4, 0xCA, 0xD8, 0xEE, 0xFC, 0x12, 0x0, 0x36, 0x24, 0x5A, 0x48, 0x7E, 0x6C,
|
||||
0xB0, 0xA2, 0x94, 0x86, 0xF8, 0xEA, 0xDC, 0xCE, 0x20, 0x32, 0x4, 0x16, 0x68, 0x7A, 0x4C, 0x5E,
|
||||
0xE6, 0xF4, 0xC2, 0xD0, 0xAE, 0xBC, 0x8A, 0x98, 0x76, 0x64, 0x52, 0x40, 0x3E, 0x2C, 0x1A, 0x8,
|
||||
0xD4, 0xC6, 0xF0, 0xE2, 0x9C, 0x8E, 0xB8, 0xAA, 0x44, 0x56, 0x60, 0x72, 0xC, 0x1E, 0x28, 0x3A,
|
||||
0x4A, 0x58, 0x6E, 0x7C, 0x2, 0x10, 0x26, 0x34, 0xDA, 0xC8, 0xFE, 0xEC, 0x92, 0x80, 0xB6, 0xA4,
|
||||
0x78, 0x6A, 0x5C, 0x4E, 0x30, 0x22, 0x14, 0x6, 0xE8, 0xFA, 0xCC, 0xDE, 0xA0, 0xB2, 0x84, 0x96,
|
||||
0x2E, 0x3C, 0xA, 0x18, 0x66, 0x74, 0x42, 0x50, 0xBE, 0xAC, 0x9A, 0x88, 0xF6, 0xE4, 0xD2, 0xC0,
|
||||
0x1C, 0xE, 0x38, 0x2A, 0x54, 0x46, 0x70, 0x62, 0x8C, 0x9E, 0xA8, 0xBA, 0xC4, 0xD6, 0xE0, 0xF2
|
||||
};
|
||||
|
||||
static inline void dropResponse(int bytesToDrop)
|
||||
{
|
||||
bytesToDrop++; // + 8 clocks
|
||||
|
||||
// Wait for the card to be non-busy
|
||||
while ((REG_SCSD_CMD16 & 1) != 0);
|
||||
|
||||
while (--bytesToDrop)
|
||||
{
|
||||
sc_dummyRead(REG_SCSD_CMD32);
|
||||
sc_dummyRead(REG_SCSD_CMD32);
|
||||
sc_dummyRead(REG_SCSD_CMD32);
|
||||
sc_dummyRead(REG_SCSD_CMD32);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool readResponse(u8* dest, u32 length)
|
||||
{
|
||||
for (u32 i = BUSY_WAIT_TIMEOUT; (REG_SCSD_CMD16 & 1) != 0; --i)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int numBits = length * 8;
|
||||
// The first bit is always 0
|
||||
u32 partialResult = (REG_SCSD_CMD16 & 1) << 16;
|
||||
numBits -= 2;
|
||||
// Read the remaining bits in the response.
|
||||
// It's always most significant bit first
|
||||
const u32 mask2Bit = 0x10001;
|
||||
while (numBits)
|
||||
{
|
||||
numBits-=2;
|
||||
partialResult = (partialResult << 2) | (REG_SCSD_CMD32 & mask2Bit);
|
||||
if ((numBits & 7) == 0)
|
||||
{
|
||||
//_1_3_5_7 _0_2_4_6
|
||||
*dest++ = ((partialResult >> 16) | (partialResult<<1));
|
||||
partialResult = 0;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 4; ++i) //8clock
|
||||
{
|
||||
sc_dummyRead(REG_SCSD_CMD32);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline u8 crc7One(u8 crcIn, u8 data)
|
||||
{
|
||||
crcIn ^= data;
|
||||
return sCrc7Lut[crcIn];
|
||||
}
|
||||
|
||||
static inline u8 sdCrc7(u8 *pBuf, int len)
|
||||
{
|
||||
u8 crc = 0;
|
||||
while (len--)
|
||||
{
|
||||
crc = crc7One(crc, *pBuf++);
|
||||
}
|
||||
crc |= 1;
|
||||
return crc;
|
||||
}
|
||||
|
||||
void sc_sdCommand(u8 command, u32 argument)
|
||||
{
|
||||
u8 databuff[6];
|
||||
u8* tempDataPtr = databuff;
|
||||
|
||||
*tempDataPtr++ = command | 0x40;
|
||||
*tempDataPtr++ = argument >> 24;
|
||||
*tempDataPtr++ = argument >> 16;
|
||||
*tempDataPtr++ = argument >> 8;
|
||||
*tempDataPtr++ = argument;
|
||||
*tempDataPtr = sdCrc7(databuff, 5);
|
||||
|
||||
while ((REG_SCSD_CMD16 & 1) == 0);
|
||||
|
||||
sc_dummyRead(REG_SCSD_CMD16);
|
||||
|
||||
auto* sendCommandAddr = ®_SCSD_CMD32;
|
||||
for (u32 data : databuff)
|
||||
{
|
||||
data |= data << 17;
|
||||
*sendCommandAddr++ = data;
|
||||
*sendCommandAddr++ = data << 2;
|
||||
*sendCommandAddr++ = data << 4;
|
||||
*sendCommandAddr++ = data << 6;
|
||||
}
|
||||
}
|
||||
|
||||
void sc_sdCommandAndDropResponse(u8 command, u32 argument, u32 bytesToDrop)
|
||||
{
|
||||
sc_sdCommand(command, argument);
|
||||
dropResponse(bytesToDrop);
|
||||
}
|
||||
|
||||
bool sc_sdCommandAndReadResponse(u8 command, u32 argument, u8* responseBuffer, u32 bytesToRead)
|
||||
{
|
||||
sc_sdCommand(command, argument);
|
||||
return readResponse(responseBuffer, bytesToRead);
|
||||
}
|
||||
|
||||
ScAppCommandResult sc_sdAppCommand(u8 appCommand, u32 relativeCardAddress, u32 argument)
|
||||
{
|
||||
u8 responseBuffer[6]; // purposely uninitialized
|
||||
sc_sdCommandAndReadResponse(SD_CMD55_APP_CMD, relativeCardAddress, responseBuffer, 6);
|
||||
if (responseBuffer[0] == SD_CMD55_APP_CMD)
|
||||
{
|
||||
sc_sdCommand(appCommand, argument);
|
||||
return ScAppCommandResult::Ok;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ScAppCommandResult::FailedToSend;
|
||||
}
|
||||
}
|
||||
|
||||
ScAppCommandResult sc_sdAppCommandAndDropResponse(u8 appCommand,
|
||||
u32 relativeCardAddress, u32 argument, u32 bytesToDrop)
|
||||
{
|
||||
auto res = sc_sdAppCommand(appCommand, relativeCardAddress, argument);
|
||||
if (res == ScAppCommandResult::Ok)
|
||||
{
|
||||
dropResponse(bytesToDrop);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
ScAppCommandResult sc_sdAppCommandAndReadResponse(u8 appCommand,
|
||||
u32 relativeCardAddress, u32 argument, u8* responseBuffer, u32 bytesToRead)
|
||||
{
|
||||
auto res = sc_sdAppCommand(appCommand, relativeCardAddress, argument);
|
||||
if (res == ScAppCommandResult::Ok)
|
||||
{
|
||||
res = readResponse(responseBuffer, bytesToRead)
|
||||
? ScAppCommandResult::Ok
|
||||
: ScAppCommandResult::FailedToParseResponse;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
35
arm9/source/patches/platform/supercard/SuperCardSDCommands.h
Normal file
35
arm9/source/patches/platform/supercard/SuperCardSDCommands.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "SuperCardDefinitions.h"
|
||||
|
||||
enum class ScAppCommandResult
|
||||
{
|
||||
Ok,
|
||||
FailedToSend,
|
||||
FailedToParseResponse,
|
||||
};
|
||||
|
||||
void sc_sdCommand(u8 command, u32 argument);
|
||||
void sc_sdCommandAndDropResponse(u8 command, u32 argument, u32 bytesToDrop);
|
||||
bool sc_sdCommandAndReadResponse(u8 command, u32 argument, u8* responseBuffer, u32 bytesToRead);
|
||||
|
||||
ScAppCommandResult sc_sdAppCommand(u8 appCommand, u32 relativeCardAddress, u32 argument);
|
||||
ScAppCommandResult sc_sdAppCommandAndDropResponse(u8 appCommand,
|
||||
u32 relativeCardAddress, u32 argument, u32 bytesToDrop);
|
||||
ScAppCommandResult sc_sdAppCommandAndReadResponse(u8 appCommand,
|
||||
u32 relativeCardAddress, u32 argument, u8* responseBuffer, u32 bytesToRead);
|
||||
|
||||
static inline void sc_dummyRead(auto) { }
|
||||
|
||||
static inline void sc_sendSdClock(u32 num)
|
||||
{
|
||||
while (num--)
|
||||
{
|
||||
sc_dummyRead(REG_SCSD_CMD16);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void sc_resetSdCard()
|
||||
{
|
||||
SD_RESET_ADDR = 0;
|
||||
}
|
||||
52
arm9/source/patches/platform/supercard/asminc.h
Normal file
52
arm9/source/patches/platform/supercard/asminc.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
.macro BEGIN_ARM_FUNCTION name
|
||||
.global \name
|
||||
.arm
|
||||
.type \name, %function
|
||||
.balign 4
|
||||
\name:
|
||||
.endm
|
||||
|
||||
.macro BEGIN_THUMB_FUNCTION name
|
||||
.global \name
|
||||
.thumb
|
||||
.type \name, %function
|
||||
.balign 2
|
||||
\name:
|
||||
.endm
|
||||
|
||||
.macro INTERWORK name
|
||||
\name:
|
||||
bx r4
|
||||
.balign 4
|
||||
.pool
|
||||
.endm
|
||||
|
||||
#ifdef LITE
|
||||
|
||||
.macro CALL func, interworkLabel
|
||||
ldr r4, \func\()_\interworkLabel\()Lite_address
|
||||
bl \interworkLabel
|
||||
.endm
|
||||
|
||||
.macro INTERWORK_FUNCTION func, interworkLabel
|
||||
.global \func\()_\interworkLabel\()Lite_address
|
||||
\func\()_\interworkLabel\()Lite_address:
|
||||
.word 0
|
||||
.endm
|
||||
|
||||
#else
|
||||
|
||||
.macro CALL func, interworkLabel
|
||||
ldr r4, \func\()_\interworkLabel\()_address
|
||||
bl \interworkLabel
|
||||
.endm
|
||||
|
||||
.macro INTERWORK_FUNCTION func, interworkLabel
|
||||
.global \func\()_\interworkLabel\()_address
|
||||
\func\()_\interworkLabel\()_address:
|
||||
.word 0
|
||||
.endm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SuperCardCommon.h"
|
||||
#include "../../SdReadPatchCode.h"
|
||||
#include "../../SdWritePatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(sclite_sd_command_drop);
|
||||
DEFINE_SECTION_SYMBOLS(sclite_write_sector);
|
||||
DEFINE_SECTION_SYMBOLS(sclite_read_sector);
|
||||
DEFINE_SECTION_SYMBOLS(sclite_read_data);
|
||||
DEFINE_SECTION_SYMBOLS(sclite_write_data);
|
||||
|
||||
extern "C" void sclite_sdCommandAndDropResponse6();
|
||||
extern "C" void sclite_writeSector();
|
||||
extern "C" void sclite_readSector();
|
||||
extern "C" void sclite_readData();
|
||||
extern "C" void sclite_writeData();
|
||||
|
||||
#define INTERWORK_LABEL(function,label) function##_##label##Lite_address
|
||||
|
||||
extern u16 sclite_readSectorSdhcLabel;
|
||||
extern u16 sclite_writeSectorSdhcLabel;
|
||||
|
||||
extern u32 INTERWORK_LABEL(sccmn_changeMode, readInterwork);
|
||||
extern u32 INTERWORK_LABEL(sclite_sdCommandAndDropResponse6, readInterwork);
|
||||
extern u32 INTERWORK_LABEL(sclite_readData, readInterwork);
|
||||
extern u32 INTERWORK_LABEL(sccmn_sdSendClock10, readInterwork);
|
||||
|
||||
extern u32 INTERWORK_LABEL(sclite_writeData, writeInterwork);
|
||||
extern u32 INTERWORK_LABEL(sccmn_sdio4BitCrc16, writeInterwork);
|
||||
extern u32 INTERWORK_LABEL(sccmn_sdSendClock10, writeInterwork);
|
||||
extern u32 INTERWORK_LABEL(sccmn_changeMode, writeInterwork);
|
||||
extern u32 INTERWORK_LABEL(sclite_sdCommandAndDropResponse6, writeInterwork);
|
||||
|
||||
class SuperCardSDCommandAndDropLitePatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit SuperCardSDCommandAndDropLitePatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(sclite_sd_command_drop), SECTION_SIZE(sclite_sd_command_drop), patchHeap) { }
|
||||
|
||||
const void* GetSdCommandAndDropResponse6Function() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)sclite_sdCommandAndDropResponse6);
|
||||
}
|
||||
};
|
||||
|
||||
class SuperCardReadDataLitePatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit SuperCardReadDataLitePatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(sclite_read_data), SECTION_SIZE(sclite_read_data), patchHeap) { }
|
||||
|
||||
const void* GetReadDataLiteFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)sclite_readData);
|
||||
}
|
||||
};
|
||||
|
||||
class SuperCardWriteDataLitePatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit SuperCardWriteDataLitePatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(sclite_write_data), SECTION_SIZE(sclite_write_data), patchHeap) { }
|
||||
|
||||
const void* GetWriteDataLiteFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)sclite_writeData);
|
||||
}
|
||||
};
|
||||
|
||||
class SuperCardReadSectorLitePatchCode : public SdReadPatchCode
|
||||
{
|
||||
public:
|
||||
SuperCardReadSectorLitePatchCode(PatchHeap& patchHeap,
|
||||
const SuperCardCommonPatchCode* superCardCommonPatchCode,
|
||||
const SuperCardSDCommandAndDropLitePatchCode* superCardSdCommandAndDropLitePatchCode,
|
||||
const SuperCardReadDataLitePatchCode* superCardReadDataLitePatchCode)
|
||||
: SdReadPatchCode(SECTION_START(sclite_read_sector), SECTION_SIZE(sclite_read_sector), patchHeap)
|
||||
{
|
||||
INTERWORK_LABEL(sccmn_changeMode, readInterwork) = (u32)superCardCommonPatchCode->GetScChangeModeFunction();
|
||||
INTERWORK_LABEL(sclite_sdCommandAndDropResponse6, readInterwork)
|
||||
= (u32)superCardSdCommandAndDropLitePatchCode->GetSdCommandAndDropResponse6Function();
|
||||
INTERWORK_LABEL(sclite_readData, readInterwork) = (u32)superCardReadDataLitePatchCode->GetReadDataLiteFunction();
|
||||
INTERWORK_LABEL(sccmn_sdSendClock10, readInterwork) = (u32)superCardCommonPatchCode->GetSdSendClock10Function();
|
||||
}
|
||||
|
||||
const SdReadFunc GetSdReadFunction() const override
|
||||
{
|
||||
return (const SdReadFunc)GetAddressAtTarget((void*)sclite_readSector);
|
||||
}
|
||||
};
|
||||
|
||||
class SuperCardWriteSectorLitePatchCode : public SdWritePatchCode
|
||||
{
|
||||
public:
|
||||
SuperCardWriteSectorLitePatchCode(PatchHeap& patchHeap,
|
||||
const SuperCardCommonPatchCode* superCardCommonPatchCode,
|
||||
const SuperCardSDCommandAndDropLitePatchCode* superCardSdCommandAndDropLitePatchCode,
|
||||
const SuperCardWriteDataLitePatchCode* superCardWriteDataLitePatchCode)
|
||||
: SdWritePatchCode(SECTION_START(sclite_write_sector), SECTION_SIZE(sclite_write_sector), patchHeap)
|
||||
{
|
||||
INTERWORK_LABEL(sclite_writeData, writeInterwork) = (u32)superCardWriteDataLitePatchCode->GetWriteDataLiteFunction();
|
||||
INTERWORK_LABEL(sccmn_sdio4BitCrc16, writeInterwork) = (u32)superCardCommonPatchCode->GetCrc16ChecksumFunction();
|
||||
INTERWORK_LABEL(sccmn_sdSendClock10, writeInterwork) = (u32)superCardCommonPatchCode->GetSdSendClock10Function();
|
||||
INTERWORK_LABEL(sccmn_changeMode, writeInterwork) = (u32)superCardCommonPatchCode->GetScChangeModeFunction();
|
||||
INTERWORK_LABEL(sclite_sdCommandAndDropResponse6, writeInterwork)
|
||||
= (u32)superCardSdCommandAndDropLitePatchCode->GetSdCommandAndDropResponse6Function();
|
||||
}
|
||||
|
||||
const SdWriteFunc GetSdWriteFunction() const override
|
||||
{
|
||||
return (const SdWriteFunc)GetAddressAtTarget((void*)sclite_writeSector);
|
||||
}
|
||||
};
|
||||
|
||||
#undef INTERWORK_LABEL
|
||||
@@ -0,0 +1,390 @@
|
||||
.cpu arm7tdmi
|
||||
.syntax unified
|
||||
|
||||
#define LITE
|
||||
#include "../asminc.h"
|
||||
|
||||
.macro LOAD_EXMEMCNT
|
||||
@ loads EXMEMCNT register address
|
||||
ldr r7,= 0x04000200
|
||||
@ waitstate 4,2 and arm9 slot2 access
|
||||
@ r7 holds the EXMEMCNT address, use lower 8 bits as 0
|
||||
strb r7, [r7, #4]
|
||||
.endm
|
||||
|
||||
.macro RESTORE_EXMEMCNT
|
||||
@ waitstate 4,2 and arm7 slot2 access
|
||||
movs r2, #0x80
|
||||
strb r2, [r7, #4]
|
||||
.endm
|
||||
|
||||
.macro SD_COMMAND_ARGUMENT value
|
||||
movs r2, \value|0x40
|
||||
.endm
|
||||
|
||||
.equ sd_dataadd, 0x9000000
|
||||
.equ sd_resetaddr, 0x9440000
|
||||
.equ reg_scsd_cmd, 0x9800000
|
||||
.equ sd_crc_bit, 0x0100000
|
||||
.equ sd_rw4, 0x0200000
|
||||
.equ sd_rw1, 0x0000000
|
||||
.equ sd_buff_bit, 0x0400000
|
||||
.equ sd_command_bit, 0x0800000
|
||||
.equ sd_st, 0x0040000 + sd_crc_bit
|
||||
.equ sd_status_addr, sd_dataadd + sd_st
|
||||
.equ sd_buff_bit_addr, sd_dataadd + sd_buff_bit
|
||||
.equ sd_dataread_4, sd_dataadd + sd_rw4
|
||||
.equ sd_dataread_1, sd_dataadd + sd_rw1
|
||||
.equ sd_datawrite_4, sd_dataadd + sd_rw4
|
||||
.equ sd_datawrite_1, sd_dataadd + sd_rw1
|
||||
|
||||
.section "sclite_sd_command_drop", "ax"
|
||||
@ void sclite_sdCommandAndDropResponse6(uint32_t dummy, uint32_t argument, SD_COMMANDS command)
|
||||
@ command is passed in r2
|
||||
BEGIN_THUMB_FUNCTION sclite_sdCommandAndDropResponse6
|
||||
@ among the pushed registers there are the command and
|
||||
@ argument one, which are then used at the loop to
|
||||
@ send the sd command
|
||||
push {r1-r7,lr}
|
||||
|
||||
@ loads reg_scsd_cmd
|
||||
movs r7, #0x98
|
||||
lsls r7, r7, #20
|
||||
|
||||
@ while(*r7 & 0x01) == 0
|
||||
SDCommand_loop:
|
||||
ldrh r0, [r7]
|
||||
lsrs r0, r0, #1
|
||||
bcc SDCommand_loop
|
||||
|
||||
@ perform an extra read
|
||||
ldrh r0, [r7]
|
||||
|
||||
@ loads sd_buff_bit_addr
|
||||
movs r1, #0x94
|
||||
lsls r1, r1, #20
|
||||
@ lower halfword is 0
|
||||
strh r1, [r1]
|
||||
|
||||
@ loads sd_dataadd + sd_command_bit + sd_rw4
|
||||
movs r1, #0x9A
|
||||
lsls r1, r1, #20
|
||||
|
||||
@ the command buffer starts at sp+4 (r1 and r2) since this loop starts with offset 5
|
||||
@ decrement the value of the stack pointer address used by 1
|
||||
mov r4,sp
|
||||
movs r3, #4
|
||||
write_SDCommand_loop:
|
||||
ldrb r0, [r4, r3]
|
||||
lsls r2, r0, #20
|
||||
adds r2, r2, r0
|
||||
str r2, [r1]
|
||||
subs r3, r3, #1
|
||||
bpl write_SDCommand_loop
|
||||
|
||||
@ loads sd_dataadd + sd_crc_bit + sd_rw4
|
||||
movs r1, #0x9B
|
||||
lsls r1, r1, #20
|
||||
movs r2, #0
|
||||
str r2, [r1]
|
||||
|
||||
@ while(*r7 & 0x01) != 0
|
||||
SDCommand_loop_2nd:
|
||||
ldrh r0, [r7]
|
||||
lsrs r0, r0, #1
|
||||
bcs SDCommand_loop_2nd
|
||||
|
||||
movs r6, #4
|
||||
SDCommand_drop_resp:
|
||||
ldmia r7!, {r0-r5}
|
||||
subs r6, r6, #1
|
||||
bne SDCommand_drop_resp
|
||||
|
||||
@ restore stack space
|
||||
pop {r1-r7,pc}
|
||||
.balign 4
|
||||
.pool
|
||||
|
||||
.section "sclite_write_sector", "ax"
|
||||
@ void sclite_writeSector(uint32_t sector, uint8_t* buff, uint32_t writenum)
|
||||
BEGIN_THUMB_FUNCTION sclite_writeSector
|
||||
push {r1,r2,r4-r7,lr}
|
||||
|
||||
@ load EXMEMCNT register
|
||||
LOAD_EXMEMCNT
|
||||
|
||||
@ r1 for now holds the sector
|
||||
.global sclite_writeSectorSdhcLabel
|
||||
sclite_writeSectorSdhcLabel:
|
||||
@ if not sdhc this needs to be shifted to the left by 9
|
||||
lsls r1, r0, #9
|
||||
@ movs r1, r0
|
||||
|
||||
@ enable sd access
|
||||
@ this function won't touch r1
|
||||
movs r0, #3
|
||||
CALL sccmn_changeMode writeInterwork
|
||||
|
||||
@ SDResetCard
|
||||
@ write 0
|
||||
ldr r2,=sd_resetaddr
|
||||
strh r2, [r2]
|
||||
|
||||
@ WRITE_MULTIPLE_BLOCK
|
||||
SD_COMMAND_ARGUMENT #25
|
||||
@ 2nd parameter is in r1 from above
|
||||
|
||||
CALL sclite_sdCommandAndDropResponse6 writeInterwork
|
||||
|
||||
@ this function will trash r4 but leave r0 and r1 untouched
|
||||
CALL sccmn_sdSendClock10 writeInterwork
|
||||
|
||||
@ loads the saved r0 and r2 (readnum)
|
||||
@ into r0 and r1
|
||||
pop {r0,r1}
|
||||
write_sector_loop:
|
||||
@ all the functions called in this loop don't change the value of r0 and r1
|
||||
@ except sclite_writeData, which will increase r0 by 512
|
||||
@ sccmn_sdio4BitCrc16 will write the checksum to r2-r3
|
||||
CALL sccmn_sdio4BitCrc16 writeInterwork
|
||||
|
||||
@ first argument is buffer
|
||||
@ regs r2 and r3 hold the crc
|
||||
CALL sclite_writeData writeInterwork
|
||||
|
||||
@ this function will trash r4 but leave r0 and r1 untouched
|
||||
CALL sccmn_sdSendClock10 writeInterwork
|
||||
|
||||
subs r1, #1
|
||||
bne write_sector_loop
|
||||
|
||||
@ STOP_TRANSMISSION
|
||||
SD_COMMAND_ARGUMENT #12
|
||||
@ 2nd parameter is passed in r1
|
||||
@ and from the loop above r1 is already 0
|
||||
|
||||
CALL sclite_sdCommandAndDropResponse6 writeInterwork
|
||||
|
||||
@ this function will trash r4 but leave r0 and r1 untouched
|
||||
CALL sccmn_sdSendClock10 writeInterwork
|
||||
|
||||
@ loads sd_dataadd
|
||||
movs r0, #0x90
|
||||
lsls r0, r0, #20
|
||||
|
||||
@ while(*r1 &0x100) == 0
|
||||
beginwhile_WriteSector:
|
||||
ldrh r1, [r0]
|
||||
lsrs r1, #9
|
||||
bcc beginwhile_WriteSector
|
||||
|
||||
movs r0, #1
|
||||
CALL sccmn_changeMode writeInterwork
|
||||
|
||||
@ restore EXMEMCNT register
|
||||
RESTORE_EXMEMCNT
|
||||
|
||||
pop {r4-r7,pc}
|
||||
INTERWORK writeInterwork
|
||||
INTERWORK_FUNCTION sclite_writeData writeInterwork
|
||||
INTERWORK_FUNCTION sccmn_sdio4BitCrc16 writeInterwork
|
||||
INTERWORK_FUNCTION sccmn_sdSendClock10 writeInterwork
|
||||
INTERWORK_FUNCTION sccmn_changeMode writeInterwork
|
||||
INTERWORK_FUNCTION sclite_sdCommandAndDropResponse6 writeInterwork
|
||||
|
||||
.balign 4
|
||||
.pool
|
||||
|
||||
.section "sclite_read_sector", "ax"
|
||||
@ bool sclite_readSector(uint32_t sector, uint8_t *buff, uint32_t readnum)
|
||||
BEGIN_THUMB_FUNCTION sclite_readSector
|
||||
push {r1,r2-r7,lr}
|
||||
LOAD_EXMEMCNT
|
||||
|
||||
@ r1 for now holds the sector
|
||||
.global sclite_readSectorSdhcLabel
|
||||
sclite_readSectorSdhcLabel:
|
||||
@ if not sdhc this needs to be shifted to the left by 9
|
||||
lsls r1, r0, #9
|
||||
@ movs r1, r0
|
||||
|
||||
@ enable sd access
|
||||
@ this function won't touch r1
|
||||
movs r0, #3
|
||||
CALL sccmn_changeMode readInterwork
|
||||
|
||||
@ SDResetCard
|
||||
@ write 0
|
||||
ldr r2,= sd_resetaddr
|
||||
strh r2, [r2]
|
||||
|
||||
@ READ_MULTIPLE_BLOCK
|
||||
SD_COMMAND_ARGUMENT #18
|
||||
@ 2nd parameter is in r1 from above
|
||||
|
||||
CALL sclite_sdCommandAndDropResponse6 readInterwork
|
||||
|
||||
@ loads the saved r0 and r2 (writenum)
|
||||
@ into r0 and r1
|
||||
pop {r0,r1}
|
||||
read_sector_loop:
|
||||
@ all the functions called in this loop don't change the value of r0 or r1
|
||||
@ except sclite_readData, which will increase r0 by 512 automatically
|
||||
CALL sclite_readData readInterwork
|
||||
|
||||
subs r1, #1
|
||||
bne read_sector_loop
|
||||
|
||||
@ STOP_TRANSMISSION
|
||||
SD_COMMAND_ARGUMENT #12
|
||||
@ 2nd parameter is passed in r1
|
||||
@ and from the loop above r1 is already 0
|
||||
|
||||
CALL sclite_sdCommandAndDropResponse6 readInterwork
|
||||
|
||||
@ this function will trash r4 but leave r0 and r1 untouched
|
||||
CALL sccmn_sdSendClock10 readInterwork
|
||||
|
||||
|
||||
movs r0, #1
|
||||
CALL sccmn_changeMode readInterwork
|
||||
|
||||
RESTORE_EXMEMCNT
|
||||
|
||||
@ returns true
|
||||
@ the change mode function above doesn't touch r0, so it's still 1
|
||||
@ movs r0, #1
|
||||
pop {r3-r7,pc}
|
||||
INTERWORK readInterwork
|
||||
INTERWORK_FUNCTION sccmn_changeMode readInterwork
|
||||
INTERWORK_FUNCTION sclite_sdCommandAndDropResponse6 readInterwork
|
||||
INTERWORK_FUNCTION sclite_readData readInterwork
|
||||
INTERWORK_FUNCTION sccmn_sdSendClock10 readInterwork
|
||||
|
||||
.balign 4
|
||||
.pool
|
||||
|
||||
.section "sclite_read_data", "ax"
|
||||
@ void sclite_readData(void* buffer)
|
||||
BEGIN_THUMB_FUNCTION sclite_readData
|
||||
push {r1,r4-r7,lr}
|
||||
|
||||
@ dummy read SD_STATUS
|
||||
ldr r1,= sd_status_addr
|
||||
ldrh r1, [r1]
|
||||
|
||||
@ loads sd_buff_bit_addr
|
||||
movs r1, #0x94
|
||||
lsls r1, r1, #20
|
||||
|
||||
@ loops as long as sd_buff_bit is 1
|
||||
sclite_readData_buff_bit_loop:
|
||||
ldrh r3, [r1]
|
||||
lsrs r3, #1
|
||||
bcs sclite_readData_buff_bit_loop
|
||||
|
||||
@ loads sd_dataread_4
|
||||
movs r1, #0x92
|
||||
lsls r1, r1, #20
|
||||
|
||||
@ performs a dummy read of a short to initiate the transfer
|
||||
ldrh r2, [r1]
|
||||
|
||||
@ lr holds buffer + 512
|
||||
movs r2, #0x80
|
||||
lsls r2, r2, #2
|
||||
adds r2, r0, r2
|
||||
mov lr, r2
|
||||
|
||||
ldmia r1!, {r2-r3}
|
||||
stmia r0!, {r2-r3}
|
||||
|
||||
sclite_readData_loop:
|
||||
@ load 6 ints at the time, for a total of 24 bytes
|
||||
ldmia r1!, {r2-r7}
|
||||
stmia r0!, {r2-r7}
|
||||
cmp r0,lr
|
||||
bne sclite_readData_loop
|
||||
|
||||
@ drop crc16
|
||||
ldr r2, [r1]
|
||||
ldrh r2, [r1]
|
||||
|
||||
@ loads sd_dataread_1
|
||||
movs r1, #0x90
|
||||
lsls r1, r1, #20
|
||||
ldrh r2, [r1]
|
||||
|
||||
pop {r1,r4-r7,pc}
|
||||
|
||||
.balign 4
|
||||
.pool
|
||||
|
||||
.section "sclite_write_data", "ax"
|
||||
@ void sclite_writeData(void*& buffer, int value_to_keep, int crc_buff1, int crc_buff2)
|
||||
@ this function updates r0, leaves r1 untouched and thrashes r4
|
||||
BEGIN_THUMB_FUNCTION sclite_writeData
|
||||
@ loads sd_dataadd
|
||||
movs r4, #0x90
|
||||
lsls r4, r4, #20
|
||||
|
||||
@ save sd_dataadd constant above for later
|
||||
push {r1-r7,lr}
|
||||
|
||||
waitOnWriteFalse_WriteData:
|
||||
ldrh r1, [r4]
|
||||
lsrs r1, #9
|
||||
bcc waitOnWriteFalse_WriteData
|
||||
|
||||
@ dummy read SD_DATAADD
|
||||
ldrh r1, [r4]
|
||||
|
||||
@ transmission start bit (lower 16 bit of r4 are 0)
|
||||
strh r4, [r4]
|
||||
|
||||
|
||||
@ loads sd_datawrite_4
|
||||
movs r7, #0x92
|
||||
lsls r7, r7, #20
|
||||
|
||||
@ lr holds buffer + 512
|
||||
movs r2, #0x80
|
||||
lsls r2, r2, #2
|
||||
adds r2, r0, r2
|
||||
mov lr, r2
|
||||
|
||||
ldmia r0!, {r1-r2}
|
||||
stmia r7!, {r1-r2}
|
||||
|
||||
sclite_writeData_loop:
|
||||
@ load 6 ints at the time, for a total of 24 bytes
|
||||
ldmia r0!, {r1-r6}
|
||||
stmia r7!, {r1-r6}
|
||||
cmp r0, lr
|
||||
bne sclite_writeData_loop
|
||||
|
||||
|
||||
@ r1 holds a value that has not to be changed
|
||||
@ r2-r3 hold the crc value to be written
|
||||
@ r4 holds the value sd_dataadd saved at the start
|
||||
pop {r1-r4}
|
||||
stmia r7!, {r2-r3}
|
||||
|
||||
@ write end bit
|
||||
movs r3, #0xff
|
||||
strh r3, [r4]
|
||||
|
||||
waitOnWriteTrue_WriteData:
|
||||
ldrh r3, [r4]
|
||||
lsrs r3, #9
|
||||
bcs waitOnWriteTrue_WriteData
|
||||
|
||||
@ dummy writes to end the transfer
|
||||
movs r3, #0
|
||||
str r3, [r4]
|
||||
str r3, [r4]
|
||||
|
||||
pop {r5-r7,pc}
|
||||
|
||||
.balign 4
|
||||
.pool
|
||||
116
arm9/source/patches/platform/supercard/scsd/SuperCardSDImpl.h
Normal file
116
arm9/source/patches/platform/supercard/scsd/SuperCardSDImpl.h
Normal file
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
#include "sections.h"
|
||||
#include "../SuperCardCommon.h"
|
||||
#include "../../SdReadPatchCode.h"
|
||||
#include "../../SdWritePatchCode.h"
|
||||
|
||||
DEFINE_SECTION_SYMBOLS(scsd_sd_command_drop);
|
||||
DEFINE_SECTION_SYMBOLS(scsd_write_sector);
|
||||
DEFINE_SECTION_SYMBOLS(scsd_read_sector);
|
||||
DEFINE_SECTION_SYMBOLS(scsd_read_data);
|
||||
DEFINE_SECTION_SYMBOLS(scsd_write_data);
|
||||
|
||||
extern "C" void scsd_sdCommandAndDropResponse6();
|
||||
extern "C" void scsd_writeSector();
|
||||
extern "C" void scsd_readSector();
|
||||
extern "C" void scsd_readData();
|
||||
extern "C" void scsd_writeData();
|
||||
|
||||
#define INTERWORK_LABEL(function,label) function##_##label##_address
|
||||
|
||||
extern u16 scsd_readSectorSdhcLabel;
|
||||
extern u16 scsd_writeSectorSdhcLabel;
|
||||
|
||||
extern u32 INTERWORK_LABEL(sccmn_changeMode, readInterwork);
|
||||
extern u32 INTERWORK_LABEL(scsd_sdCommandAndDropResponse6, readInterwork);
|
||||
extern u32 INTERWORK_LABEL(scsd_readData, readInterwork);
|
||||
extern u32 INTERWORK_LABEL(sccmn_sdSendClock10, readInterwork);
|
||||
|
||||
extern u32 INTERWORK_LABEL(scsd_writeData, writeInterwork);
|
||||
extern u32 INTERWORK_LABEL(sccmn_sdio4BitCrc16, writeInterwork);
|
||||
extern u32 INTERWORK_LABEL(sccmn_sdSendClock10, writeInterwork);
|
||||
extern u32 INTERWORK_LABEL(sccmn_changeMode, writeInterwork);
|
||||
extern u32 INTERWORK_LABEL(scsd_sdCommandAndDropResponse6, writeInterwork);
|
||||
|
||||
class SuperCardSdCommandAndDropPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit SuperCardSdCommandAndDropPatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(scsd_sd_command_drop), SECTION_SIZE(scsd_sd_command_drop), patchHeap) { }
|
||||
|
||||
const void* GetSdCommandAndDropResponse6Function() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)scsd_sdCommandAndDropResponse6);
|
||||
}
|
||||
};
|
||||
|
||||
class SuperCardReadDataPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit SuperCardReadDataPatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(scsd_read_data), SECTION_SIZE(scsd_read_data), patchHeap) { }
|
||||
|
||||
const void* GetReadDataFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)scsd_readData);
|
||||
}
|
||||
};
|
||||
|
||||
class SuperCardWriteDataPatchCode : public PatchCode
|
||||
{
|
||||
public:
|
||||
explicit SuperCardWriteDataPatchCode(PatchHeap& patchHeap)
|
||||
: PatchCode(SECTION_START(scsd_write_data), SECTION_SIZE(scsd_write_data), patchHeap) { }
|
||||
|
||||
const void* GetWriteDataFunction() const
|
||||
{
|
||||
return GetAddressAtTarget((void*)scsd_writeData);
|
||||
}
|
||||
};
|
||||
|
||||
class SuperCardWriteSectorPatchCode : public SdWritePatchCode
|
||||
{
|
||||
public:
|
||||
SuperCardWriteSectorPatchCode(PatchHeap& patchHeap,
|
||||
const SuperCardCommonPatchCode* superCardCommonPatchCode,
|
||||
const SuperCardSdCommandAndDropPatchCode* superCardSdCommandAndDropPatchCode,
|
||||
const SuperCardWriteDataPatchCode* superCardWriteDataPatchCode)
|
||||
: SdWritePatchCode(SECTION_START(scsd_write_sector), SECTION_SIZE(scsd_write_sector), patchHeap)
|
||||
{
|
||||
INTERWORK_LABEL(scsd_writeData, writeInterwork) = (u32)superCardWriteDataPatchCode->GetWriteDataFunction();
|
||||
INTERWORK_LABEL(sccmn_sdio4BitCrc16, writeInterwork) = (u32)superCardCommonPatchCode->GetCrc16ChecksumFunction();
|
||||
INTERWORK_LABEL(sccmn_sdSendClock10, writeInterwork) = (u32)superCardCommonPatchCode->GetSdSendClock10Function();
|
||||
INTERWORK_LABEL(sccmn_changeMode, writeInterwork) = (u32)superCardCommonPatchCode->GetScChangeModeFunction();
|
||||
INTERWORK_LABEL(scsd_sdCommandAndDropResponse6, writeInterwork)
|
||||
= (u32)superCardSdCommandAndDropPatchCode->GetSdCommandAndDropResponse6Function();
|
||||
}
|
||||
|
||||
const SdWriteFunc GetSdWriteFunction() const override
|
||||
{
|
||||
return (const SdWriteFunc)GetAddressAtTarget((void*)scsd_writeSector);
|
||||
}
|
||||
};
|
||||
|
||||
class SuperCardReadSectorPatchCode : public SdReadPatchCode
|
||||
{
|
||||
public:
|
||||
SuperCardReadSectorPatchCode(PatchHeap& patchHeap,
|
||||
const SuperCardCommonPatchCode* superCardCommonPatchCode,
|
||||
const SuperCardSdCommandAndDropPatchCode* superCardSdCommandAndDropPatchCode,
|
||||
const SuperCardReadDataPatchCode* superCardReadDataPatchCode)
|
||||
: SdReadPatchCode(SECTION_START(scsd_read_sector), SECTION_SIZE(scsd_read_sector), patchHeap)
|
||||
{
|
||||
INTERWORK_LABEL(sccmn_changeMode, readInterwork) = (u32)superCardCommonPatchCode->GetScChangeModeFunction();
|
||||
INTERWORK_LABEL(scsd_sdCommandAndDropResponse6, readInterwork)
|
||||
= (u32)superCardSdCommandAndDropPatchCode->GetSdCommandAndDropResponse6Function();
|
||||
INTERWORK_LABEL(scsd_readData, readInterwork) = (u32)superCardReadDataPatchCode->GetReadDataFunction();
|
||||
INTERWORK_LABEL(sccmn_sdSendClock10, readInterwork) = (u32)superCardCommonPatchCode->GetSdSendClock10Function();
|
||||
}
|
||||
|
||||
const SdReadFunc GetSdReadFunction() const override
|
||||
{
|
||||
return (const SdReadFunc)GetAddressAtTarget((void*)scsd_readSector);
|
||||
}
|
||||
};
|
||||
|
||||
#undef INTERWORK_LABEL
|
||||
450
arm9/source/patches/platform/supercard/scsd/SuperCardSDImpl.s
Normal file
450
arm9/source/patches/platform/supercard/scsd/SuperCardSDImpl.s
Normal file
@@ -0,0 +1,450 @@
|
||||
.cpu arm7tdmi
|
||||
.syntax unified
|
||||
|
||||
#include "../asminc.h"
|
||||
|
||||
.macro SD_COMMAND_ARGUMENT value
|
||||
movs r2, \value|0x40
|
||||
.endm
|
||||
|
||||
.macro LOAD_SLOW_EXMEMCNT
|
||||
@ loads EXMEMCNT register address
|
||||
ldr r7,= 0x04000200
|
||||
@ waitstate 4,2 and arm9 slot2 access
|
||||
@ r7 holds the EXMEMCNT address, use lower 8 bits as 0
|
||||
strb r7, [r7, #4]
|
||||
.endm
|
||||
|
||||
.macro RELOAD_SLOW_EXMEMCNT
|
||||
@ waitstate 4,2 and arm9 slot2 access
|
||||
@ r7 holds the EXMEMCNT address, use lower 8 bits as 0
|
||||
strb r7, [r7, #4]
|
||||
.endm
|
||||
|
||||
.macro LOAD_FAST_EXMEMCNT
|
||||
@ loads EXMEMCNT register address
|
||||
movs r6, #0x18
|
||||
@ waitstate 2,1 and arm9 slot2 access
|
||||
strb r6, [r7, #4]
|
||||
.endm
|
||||
|
||||
.macro RESTORE_EXMEMCNT
|
||||
@ waitstate 4,2 and arm7 slot2 access
|
||||
movs r2, #0x80
|
||||
strb r2, [r7, #4]
|
||||
.endm
|
||||
|
||||
.equ sd_dataadd, 0x9000000
|
||||
.equ sd_dataread, 0x9100000
|
||||
.equ sd_resetaddr, 0x9440000
|
||||
.equ reg_scsd_cmd, 0x9800000
|
||||
|
||||
.section "scsd_sd_command_drop", "ax"
|
||||
@ void scsd_sdCommandAndDropResponse6(uint32_t dummy, uint32_t argument, SD_COMMANDS command)
|
||||
@ argument is passed in r1
|
||||
BEGIN_THUMB_FUNCTION scsd_sdCommandAndDropResponse6
|
||||
@ among the pushed registers there are the command and
|
||||
@ argument one, which are then used by the crc7 function
|
||||
@ and in the loop at the bottom
|
||||
@ also allocate an extra 4 bytes for the SD_CRC7 function to put the crc byte
|
||||
push {r0-r7,lr}
|
||||
|
||||
@ pass the buffer incremented by 4, so that the crc function
|
||||
@ can use a proper indexing method
|
||||
add r0, sp, #4
|
||||
@ after this call, we get sp back in r1
|
||||
bl SD_CRC7
|
||||
|
||||
@ loads reg_scsd_cmd
|
||||
movs r7, #0x98
|
||||
lsls r7, r7, #20
|
||||
|
||||
@ while(*r7 & 0x01) == 0
|
||||
SDCommand_loop:
|
||||
ldrh r0, [r7]
|
||||
lsrs r0, r0, #1
|
||||
bcc SDCommand_loop
|
||||
|
||||
@ perform an extra read
|
||||
ldrh r0, [r7]
|
||||
|
||||
@ the sd command buffer is 6 bytes long
|
||||
@ and starts at sp+8 in descending order
|
||||
@ r1 holds sp-4, so rather than incrementing it by 3
|
||||
@ we decrement it by 1
|
||||
subs r2, r1, #1
|
||||
movs r0, #5
|
||||
write_SDCommand_loop:
|
||||
ldrb r3, [r2, r0]
|
||||
lsls r1, r3, #17
|
||||
orrs r3, r1
|
||||
lsls r4, r3, #2
|
||||
lsls r5, r4, #2
|
||||
lsls r6, r5, #2
|
||||
stmia r7!, {r3-r6}
|
||||
subs r0, #1
|
||||
bpl write_SDCommand_loop
|
||||
|
||||
@ drop_response
|
||||
|
||||
@ while(*r7 & 0x01) != 0
|
||||
SDCommand_drop_resp_nonbusy_loop:
|
||||
ldrh r0, [r7]
|
||||
lsrs r0, r0, #1
|
||||
bcs SDCommand_drop_resp_nonbusy_loop
|
||||
|
||||
movs r6, #4
|
||||
SDCommand_drop_resp:
|
||||
ldmia r7!, {r0-r5}
|
||||
subs r6, r6, #1
|
||||
bne SDCommand_drop_resp
|
||||
|
||||
@ restore stack space
|
||||
pop {r0-r7,pc}
|
||||
|
||||
@ inline uint8_t CRC7_one(uint8_t crcIn, uint8_t data) {
|
||||
@ const uint8_t g = 0x89;
|
||||
@ uint8_t i;
|
||||
|
||||
@ crcIn ^= data;
|
||||
@ for (i = 0; i < 8; i++) {
|
||||
@ if (crcIn & 0x80) crcIn ^= g;
|
||||
@ crcIn <<= 1;
|
||||
@ }
|
||||
|
||||
@ return crcIn;
|
||||
@ }
|
||||
|
||||
@ // Calculate CRC7 value of the buffer
|
||||
@ // input:
|
||||
@ // pBuf - pointer to the buffer
|
||||
@ // return: the CRC7 value
|
||||
@ uint32_t CRC7_buf(uint8_t *pBuf) {
|
||||
@ uint32_t crc = 0;
|
||||
|
||||
@ for (int i = 4; i >= 0; --i) crc = CRC7_one(crc,pBuf[i]);
|
||||
|
||||
@ return crc << 24;
|
||||
@ }
|
||||
|
||||
@ in r1 puts back the argument it took in in r0
|
||||
@ SD_CRC7(uint8_t* buff)
|
||||
SD_CRC7:
|
||||
push {r0,r4-r6,lr}
|
||||
movs r3, #0
|
||||
movs r5, #0x89
|
||||
movs r1, #4
|
||||
movs r4, #0x80
|
||||
|
||||
SD_CRC7_loop:
|
||||
movs r2, #8
|
||||
ldrb r6, [r0, r1]
|
||||
eors r3, r6
|
||||
|
||||
CRC7_one_loop:
|
||||
|
||||
@ r4 & 0x80
|
||||
tst r3, r4
|
||||
|
||||
beq skip_xor
|
||||
eors r3, r5
|
||||
|
||||
skip_xor:
|
||||
lsls r3, #1
|
||||
subs r2, #1
|
||||
bne CRC7_one_loop
|
||||
|
||||
subs r1, #1
|
||||
bpl SD_CRC7_loop
|
||||
|
||||
@ write at buffer index -1
|
||||
strb r3, [r0, r1]
|
||||
pop {r1,r4-r6,pc}
|
||||
|
||||
.balign 4
|
||||
.pool
|
||||
|
||||
.section "scsd_write_sector", "ax"
|
||||
@ void scsd_writeSector(uint32_t sector, uint8_t* buff, uint32_t writenum)
|
||||
BEGIN_THUMB_FUNCTION scsd_writeSector
|
||||
push {r1,r2,r4-r7,lr}
|
||||
|
||||
@ load EXMEMCNT register
|
||||
LOAD_SLOW_EXMEMCNT
|
||||
|
||||
@ r1 for now holds the sector
|
||||
.global scsd_writeSectorSdhcLabel
|
||||
scsd_writeSectorSdhcLabel:
|
||||
@ if not sdhc this needs to be shifted to the left by 9
|
||||
lsls r1, r0, #9
|
||||
@ movs r1, r0
|
||||
|
||||
@ enable sd access
|
||||
@ this function won't touch r1
|
||||
movs r0, #3
|
||||
CALL sccmn_changeMode writeInterwork
|
||||
|
||||
@ SDResetCard
|
||||
@ write 0
|
||||
ldr r2,= sd_resetaddr
|
||||
strh r2, [r2]
|
||||
|
||||
@ WRITE_MULTIPLE_BLOCK
|
||||
SD_COMMAND_ARGUMENT #25
|
||||
@ 2nd parameter is in r1 from above
|
||||
|
||||
CALL scsd_sdCommandAndDropResponse6 writeInterwork
|
||||
|
||||
@ this function will trash r4 but leave r0 and r1 untouched
|
||||
CALL sccmn_sdSendClock10 writeInterwork
|
||||
|
||||
LOAD_FAST_EXMEMCNT
|
||||
|
||||
@ loads the saved r0 and r2 (readnum)
|
||||
@ into r0 and r1
|
||||
pop {r0,r1}
|
||||
|
||||
write_sector_loop:
|
||||
@ all the functions called in this loop don't change the value of r0 and r1
|
||||
@ except scsd_writeData, which will increase r0 by 512
|
||||
@ sccmn_sdio4BitCrc16 will write the checksum to r2-r3
|
||||
CALL sccmn_sdio4BitCrc16 writeInterwork
|
||||
|
||||
@ first argument is buffer
|
||||
@ regs r2 and r3 hold the crc
|
||||
CALL scsd_writeData writeInterwork
|
||||
|
||||
@ this function will trash r4 but leave r0 and r1 untouched
|
||||
CALL sccmn_sdSendClock10 writeInterwork
|
||||
|
||||
subs r1, #1
|
||||
bne write_sector_loop
|
||||
|
||||
RELOAD_SLOW_EXMEMCNT
|
||||
|
||||
@ STOP_TRANSMISSION
|
||||
SD_COMMAND_ARGUMENT #12
|
||||
@ 2nd parameter is passed in r1
|
||||
@ and from the loop above r1 is already 0
|
||||
|
||||
CALL scsd_sdCommandAndDropResponse6 writeInterwork
|
||||
|
||||
@ this function will trash r4 but leave r0 and r1 untouched
|
||||
CALL sccmn_sdSendClock10 writeInterwork
|
||||
|
||||
@ SD_DATAADD is loaded by scsd_writeData into r5
|
||||
@ while(*r5 &0x100) == 0
|
||||
beginwhile_WriteSector:
|
||||
ldrh r1, [r5]
|
||||
lsrs r1, #9
|
||||
bcc beginwhile_WriteSector
|
||||
|
||||
movs r0, #1
|
||||
CALL sccmn_changeMode writeInterwork
|
||||
|
||||
@ restore EXMEMCNT register
|
||||
RESTORE_EXMEMCNT
|
||||
|
||||
pop {r4-r7,pc}
|
||||
INTERWORK writeInterwork
|
||||
INTERWORK_FUNCTION scsd_writeData writeInterwork
|
||||
INTERWORK_FUNCTION sccmn_sdio4BitCrc16 writeInterwork
|
||||
INTERWORK_FUNCTION sccmn_sdSendClock10 writeInterwork
|
||||
INTERWORK_FUNCTION sccmn_changeMode writeInterwork
|
||||
INTERWORK_FUNCTION scsd_sdCommandAndDropResponse6 writeInterwork
|
||||
|
||||
.balign 4
|
||||
.pool
|
||||
|
||||
.section "scsd_read_sector", "ax"
|
||||
@ bool scsd_readSector(uint32_t sector, uint8_t *buff, uint32_t readnum)
|
||||
BEGIN_THUMB_FUNCTION scsd_readSector
|
||||
push {r1,r2-r7,lr}
|
||||
LOAD_SLOW_EXMEMCNT
|
||||
|
||||
@ r1 for now holds the sector
|
||||
.global scsd_readSectorSdhcLabel
|
||||
scsd_readSectorSdhcLabel:
|
||||
@ if not sdhc this needs to be shifted to the left by 9
|
||||
lsls r1, r0, #9
|
||||
@ movs r1, r0
|
||||
|
||||
@ enable sd access
|
||||
@ this function won't touch r1
|
||||
movs r0, #3
|
||||
CALL sccmn_changeMode readInterwork
|
||||
|
||||
@ SDResetCard
|
||||
@ write 0
|
||||
ldr r2,= sd_resetaddr
|
||||
strh r2, [r2]
|
||||
|
||||
@ READ_MULTIPLE_BLOCK
|
||||
SD_COMMAND_ARGUMENT #18
|
||||
@ 2nd parameter is in r1 from above
|
||||
|
||||
CALL scsd_sdCommandAndDropResponse6 readInterwork
|
||||
|
||||
LOAD_FAST_EXMEMCNT
|
||||
|
||||
@ loads the saved r0 and r2 (writenum)
|
||||
@ into r0 and r1
|
||||
pop {r0,r1}
|
||||
read_sector_loop:
|
||||
@ all the functions called in this loop don't change the value of r0 or r1
|
||||
@ except scsd_readData, which will increase r0 by 512 automatically
|
||||
CALL scsd_readData readInterwork
|
||||
|
||||
subs r1, #1
|
||||
bne read_sector_loop
|
||||
|
||||
RELOAD_SLOW_EXMEMCNT
|
||||
|
||||
@ STOP_TRANSMISSION
|
||||
SD_COMMAND_ARGUMENT #12
|
||||
@ 2nd parameter is passed in r1
|
||||
@ and from the loop above r1 is already 0
|
||||
|
||||
CALL scsd_sdCommandAndDropResponse6 readInterwork
|
||||
|
||||
@ this function will trash r4 but leave r0 and r1 untouched
|
||||
CALL sccmn_sdSendClock10 readInterwork
|
||||
|
||||
|
||||
movs r0, #1
|
||||
CALL sccmn_changeMode readInterwork
|
||||
|
||||
RESTORE_EXMEMCNT
|
||||
|
||||
@ returns true
|
||||
@ the change mode function above doesn't touch r0, so it's still 1
|
||||
@ movs r0, #1
|
||||
pop {r3-r7,pc}
|
||||
INTERWORK readInterwork
|
||||
INTERWORK_FUNCTION sccmn_changeMode readInterwork
|
||||
INTERWORK_FUNCTION scsd_sdCommandAndDropResponse6 readInterwork
|
||||
INTERWORK_FUNCTION scsd_readData readInterwork
|
||||
INTERWORK_FUNCTION sccmn_sdSendClock10 readInterwork
|
||||
|
||||
.balign 4
|
||||
.pool
|
||||
|
||||
.section "scsd_write_data", "ax"
|
||||
.macro WRITE_SINGLE_U16 srcreg, secondreg, dstreg
|
||||
lsrs \secondreg, \srcreg, #8
|
||||
stmia \dstreg!, {\srcreg,\secondreg}
|
||||
.endm
|
||||
|
||||
.macro WRITE_U32 srcreg,dstreg,maskreg
|
||||
lsrs r4, \srcreg, #16
|
||||
ands \srcreg, \srcreg, \maskreg
|
||||
WRITE_SINGLE_U16 \srcreg, r3, \dstreg
|
||||
WRITE_SINGLE_U16 r4, r7, \dstreg
|
||||
.endm
|
||||
|
||||
@ void SCSD_writeBuffer32(uint32_t* buff_u32, uint32_t size)
|
||||
SCSD_writeBuffer32:
|
||||
push {r1,r4-r7,lr}
|
||||
|
||||
adds r7, r0, r1
|
||||
mov lr, r7
|
||||
|
||||
@ r5 is SD_DATAADD, taken from the caller
|
||||
write32_loop:
|
||||
ldmia r0!, {r1, r2}
|
||||
WRITE_U32 r1, r5, r6
|
||||
WRITE_U32 r2, r5, r6
|
||||
cmp lr, r0
|
||||
bne write32_loop
|
||||
|
||||
pop {r1, r4-r7,pc}
|
||||
|
||||
@ void scsd_writeData(void* buffer, int value_to_keep, int crc_buff1, int crc_buff2)
|
||||
BEGIN_THUMB_FUNCTION scsd_writeData
|
||||
|
||||
@ loads SD_DATAADD, do it before the push so that
|
||||
@ it becomes available to the callee afterwards
|
||||
movs r5, #0x90
|
||||
lsls r5, r5, #20
|
||||
|
||||
push {r1-r7,lr}
|
||||
|
||||
@ while(*r5 &0x100) == 0
|
||||
scsd_writeData_waitOnWriteFalse:
|
||||
ldrh r6, [r5]
|
||||
lsrs r6, #9
|
||||
bcc scsd_writeData_waitOnWriteFalse
|
||||
|
||||
@ dummy read SD_DATAADD
|
||||
ldrh r1, [r5]
|
||||
|
||||
@ transmission start bit (lower 16 bit of r5 are 0)
|
||||
strh r5, [r5]
|
||||
|
||||
@ mask to use in SCSD_writeBuffer32
|
||||
ldr r6,= 0xFFFF
|
||||
|
||||
movs r1, #0x80
|
||||
lsls r1, r1, #2
|
||||
@ no need for special handling because those 2 functions will be in the same block
|
||||
bl SCSD_writeBuffer32
|
||||
push {r0}
|
||||
|
||||
movs r1, #8
|
||||
@ the pushed crc buffer is at address sp+8
|
||||
add r0,sp, #8
|
||||
@ no need for special handling because those 2 functions will be in the same block
|
||||
bl SCSD_writeBuffer32
|
||||
|
||||
|
||||
@ write end bit
|
||||
movs r3, #0xFF
|
||||
strh r3, [r5]
|
||||
|
||||
@ while(*r5 &0x100) != 0
|
||||
scsd_writeData_waitOnWriteTrue:
|
||||
ldrh r6, [r5]
|
||||
lsrs r6, #9
|
||||
bcs scsd_writeData_waitOnWriteTrue
|
||||
|
||||
pop {r0-r7,pc}
|
||||
|
||||
.balign 4
|
||||
.pool
|
||||
|
||||
.section "scsd_read_data", "ax"
|
||||
|
||||
.macro LOAD_U32_ALIGNED_2WORDS srcreg,dstreg,maskreg
|
||||
ldmia \srcreg, {r1-r8}
|
||||
and r4, r4, \maskreg
|
||||
and r8, r8, \maskreg
|
||||
orr r4, r4, r2, lsr #16
|
||||
orr r8, r8, r6, lsr #16
|
||||
stmia \dstreg!, {r4, r8}
|
||||
.endm
|
||||
@ this function will update r0 by incrementing it by 512
|
||||
@ and will leave r1 unchanged
|
||||
@ void scsd_readData(void* buffer);
|
||||
BEGIN_ARM_FUNCTION scsd_readData
|
||||
push {r1,r4-r11}
|
||||
add r9, r0, #512
|
||||
ldr r10,= sd_dataread
|
||||
waitOnReadTrue_loop:
|
||||
ldrh r3, [r10]
|
||||
tst r3, #0x100
|
||||
bne waitOnReadTrue_loop
|
||||
ldr r11,= 0xFFFF0000
|
||||
read32_loop:
|
||||
LOAD_U32_ALIGNED_2WORDS r10, r0, r11
|
||||
LOAD_U32_ALIGNED_2WORDS r10, r0, r11
|
||||
cmp r0, r9
|
||||
blt read32_loop
|
||||
@drop crc16
|
||||
ldmia r10, {r1-r8}
|
||||
@read end transmission bit
|
||||
ldrh r1, [r10]
|
||||
pop {r1,r4-r11}
|
||||
bx lr
|
||||
|
||||
.balign 4
|
||||
.pool
|
||||
Reference in New Issue
Block a user