Initial commit

This commit is contained in:
Gericom
2025-11-22 11:08:28 +01:00
commit 9cf3ffbfcf
358 changed files with 58350 additions and 0 deletions

View 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; }
};

View 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
}

View File

@@ -0,0 +1,9 @@
#pragma once
class LoaderPlatform;
class LoaderPlatformFactory
{
public:
LoaderPlatform* CreateLoaderPlatform() const;
};

View 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;
};

View 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;
};

View 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;
};

View 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)

View 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; }
};

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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);
}
};

View 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

View File

@@ -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);
}

View File

@@ -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);
}
};

View File

@@ -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
};

View File

@@ -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);
}
};

View File

@@ -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

View File

@@ -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);
}
};

View File

@@ -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

View 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);
}
};

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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);
}
};

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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; }
};

View File

@@ -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;
};

View 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

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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;
}

View 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;
};

View 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*)&REG_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)

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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; }
};

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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];
}
};

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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);
}));
});
}
};

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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);
});
}
};

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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();
}

View 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;
};

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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);
}
};

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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);
}
};

View 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

View 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);
}
};

View 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

View File

@@ -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)

View File

@@ -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;
}
}
}

View 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();
};

View 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 = &REG_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;
}

View 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;
}

View 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

View File

@@ -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

View File

@@ -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

View 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

View 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