Add support for Slot 2 flashcarts using Compact Flash (#84)

- Supercard CF (SUPERCARDCF)
- GBA Media Player CF (MPCF)
- M3 Adapter CF (M3CF)
- Max Media Dock CF (MMCF)
This commit is contained in:
Edoardo Lolletti
2026-01-10 23:00:39 +01:00
committed by GitHub
parent eac8f7e734
commit 6a97b677a7
22 changed files with 945 additions and 3 deletions

View File

@@ -15,6 +15,10 @@
#include "patches/platform/ezp/EzpLoaderPlatform.h"
#include "patches/platform/datel/DatelLoaderPlatform.h"
#include "patches/platform/stargate/StargateLoaderPlatform.h"
#include "patches/platform/supercardcf/SuperCardCFLoaderPlatform.h"
#include "patches/platform/mpcf/MPCFLoaderPlatform.h"
#include "patches/platform/m3cf/M3CFLoaderPlatform.h"
#include "patches/platform/mmcf/MMCFLoaderPlatform.h"
#include "LoaderPlatformFactory.h"
LoaderPlatform* LoaderPlatformFactory::CreateLoaderPlatform() const
@@ -49,6 +53,14 @@ LoaderPlatform* LoaderPlatformFactory::CreateLoaderPlatform() const
return new DatelLoaderPlatform();
#elif defined(PICO_LOADER_TARGET_STARGATE)
return new StargateLoaderPlatform();
#elif defined(PICO_LOADER_TARGET_SUPERCARDCF)
return new SuperCardCFLoaderPlatform();
#elif defined(PICO_LOADER_TARGET_MPCF)
return new MPCFLoaderPlatform();
#elif defined(PICO_LOADER_TARGET_M3CF)
return new M3CFLoaderPlatform();
#elif defined(PICO_LOADER_TARGET_MMCF)
return new MMCFLoaderPlatform();
#else
#error "No loader platform defined"
return nullptr;

View File

@@ -0,0 +1,66 @@
#include "common.h"
#include <libtwl/mem/memExtern.h>
#include "CompactFlashCommonLoaderPlatform.h"
static constexpr int CF_CARD_TIMEOUT = 10000000;
static constexpr int CF_STS_READY = 0x40;
static constexpr int CF_STS_DSC = 0x10;
static constexpr int CF_STS_BUSY = 0x80;
bool CompactFlashCommonLoaderPlatform::InitializeSdCard()
{
u32 oldMemCnt = REG_EXMEMCNT;
mem_setGbaCartridgeRomWaits(EXMEMCNT_SLOT2_ROM_WAIT1_10, EXMEMCNT_SLOT2_ROM_WAIT2_6);
SetCardLocked(false);
auto res = InitializeCfCard();
SetCardLocked(true);
REG_EXMEMCNT = oldMemCnt;
return res;
}
static bool waitAvailableForCommands(const cf_registers_t& registers)
{
// wait for card to finish previous commands
for (int i = 0; i < CF_CARD_TIMEOUT; i++)
{
if ((*registers.command & CF_STS_BUSY) == 0)
{
break;
}
}
// wait for card to be ready for new commands
for (int i = 0; i < CF_CARD_TIMEOUT; i++)
{
if ((*registers.command & (CF_STS_READY | CF_STS_DSC)) != 0)
{
return true;
}
}
return false;
}
bool CompactFlashCommonLoaderPlatform::InitializeCfCard()
{
const auto& registers = GetCfRegisters();
if (!waitAvailableForCommands(registers))
{
return false;
}
// Check that the registers are writable and hold the values we set
u16 temp = *registers.lba1;
*registers.lba1 = (~temp & 0xFF);
temp = (~temp & 0xFF);
if (*registers.lba1 != temp)
{
return false;
}
*registers.lba1 = 0xAA55;
if (*registers.lba1 == 0xAA55)
{
return false;
}
return true;
}

View File

@@ -0,0 +1,79 @@
#pragma once
#include "common.h"
#include "../LoaderPlatform.h"
#include "CompactFlashRegisters.h"
#include "CompactFlashStatusFunctionsPatchCode.h"
#include "CompactFlashReadWriteSectorPatchCode.h"
#include "ICompactFlashLockUnlockPatchCode.h"
/// @brief Base implementation of LoaderPlatform for the Compact Flash slot 2 flashcarts
class CompactFlashCommonLoaderPlatform : public LoaderPlatform
{
public:
LoaderPlatformType GetPlatformType() const override { return LoaderPlatformType::Slot2; }
bool InitializeSdCard() override;
const IReadSectorsPatchCode* CreateSdReadPatchCode(
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
{
const auto& registers = GetCfRegisters();
auto statusFunctions = patchCodeCollection.GetOrAddSharedPatchCode([&]
{
return new CompactFlashStatusFunctionsPatchCode(patchHeap, registers);
});
auto transferSector = patchCodeCollection.GetOrAddSharedPatchCode([&]
{
return new CompactFlashTransferSectorPatchCode(patchHeap, registers, statusFunctions);
});
auto lockUnlock = CreateLockingPatchCode(patchCodeCollection, patchHeap);
return patchCodeCollection.GetOrAddSharedPatchCode([&]
{
return new CompactFlashReadWriteSectorPatchCode(patchHeap, registers, transferSector, lockUnlock);
});
}
const IWriteSectorsPatchCode* CreateSdWritePatchCode(
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
{
const auto& registers = GetCfRegisters();
auto statusFunctions = patchCodeCollection.GetOrAddSharedPatchCode([&]
{
return new CompactFlashStatusFunctionsPatchCode(patchHeap, registers);
});
auto transferSector = patchCodeCollection.GetOrAddSharedPatchCode([&]
{
return new CompactFlashTransferSectorPatchCode(patchHeap, registers, statusFunctions);
});
auto lockUnlock = CreateLockingPatchCode(patchCodeCollection, patchHeap);
return patchCodeCollection.GetOrAddSharedPatchCode([&]
{
return new CompactFlashReadWriteSectorPatchCode(patchHeap, registers, transferSector, lockUnlock);
});
}
protected:
/// @brief Locks/Unlocks the cart to operate on the inserted CF card
/// @param locked Whether the card should be locked (prevent R/W operations) or unlocked (allows R/W operations)
virtual void SetCardLocked(bool locked) const = 0;
/// @brief Generates the patch code containing the lock/unlock routines equivalent to \see SetCardLocked
/// If a card requires no locking, this doesn't have to be implemented.
/// @note The returned routine, is only allowed to modify r0, other registers should be left untouched
/// @return A pointer to the allocated \see ICompactFlashLockUnlockPatchCode
virtual const ICompactFlashLockUnlockPatchCode* CreateLockingPatchCode(
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const { return nullptr; }
/// @brief Returns the exposed address associated to the Compact Flash registers
/// @return the \see cf_registers_t struct containing the registers
virtual const cf_registers_t& GetCfRegisters() const = 0;
private:
bool InitializeCfCard();
};

View File

@@ -0,0 +1,86 @@
#pragma once
#include "common.h"
#include "sections.h"
#include "patches/PatchCode.h"
#include "thumbInstructions.h"
#include "../IReadSectorsPatchCode.h"
#include "../IWriteSectorsPatchCode.h"
#include "CompactFlashRegisters.h"
#include "ICompactFlashLockUnlockPatchCode.h"
DEFINE_SECTION_SYMBOLS(cf_perform_transfer);
DEFINE_SECTION_SYMBOLS(cf_read_write_functions);
extern "C" bool cf_performTransferSectors(u32 numSectors, u32 sector, u8 command, void* srcAddr, void* dstAddr);
extern "C" bool cf_readSectors(u32 sector, void* buffer, u32 numSectors);
extern "C" bool cf_writeSectors(u32 sector, void* buffer, u32 numSectors);
extern vu16* cf_performTransferSectors_reg_sector_count;
extern u32 cf_performTransferSectors_waitCardAvailableForCommands;
extern u32 cf_performTransferSectors_waitNextBlockReady;
extern vu16* cf_performTransfer_reg_data;
extern u32 cf_performTransfer_performTransferSectors;
extern u32 cf_performTransfer_lockUnlockCard;
extern u16 cf_performTransfer_unlock_label[2];
extern u16 cf_performTransfer_lock_label[2];
class CompactFlashTransferSectorPatchCode : public PatchCode
{
public:
CompactFlashTransferSectorPatchCode(PatchHeap& patchHeap,
const cf_registers_t& registers,
const CompactFlashStatusFunctionsPatchCode* compactFlashStatusFunctionsPatchCode)
: PatchCode(SECTION_START(cf_perform_transfer), SECTION_SIZE(cf_perform_transfer), patchHeap)
{
cf_performTransferSectors_reg_sector_count = registers.sectorCount;
cf_performTransferSectors_waitCardAvailableForCommands = (u32)compactFlashStatusFunctionsPatchCode->GetWaitCardAvailableForCommandsFunction();
cf_performTransferSectors_waitNextBlockReady = (u32)compactFlashStatusFunctionsPatchCode->GetWaitNextBlockReadyFunction();
}
const void* GetPerformTransferSectorsFunction() const
{
return GetAddressAtTarget((void*)cf_performTransferSectors);
}
};
class CompactFlashReadWriteSectorPatchCode : public PatchCode, public IReadSectorsPatchCode, public IWriteSectorsPatchCode
{
public:
CompactFlashReadWriteSectorPatchCode(PatchHeap& patchHeap,
const cf_registers_t& registers,
const CompactFlashTransferSectorPatchCode* compactFlashTransferSectorPatchCode,
const ICompactFlashLockUnlockPatchCode* lockUnlockCard)
: PatchCode(SECTION_START(cf_read_write_functions), SECTION_SIZE(cf_read_write_functions), patchHeap)
{
cf_performTransfer_reg_data = registers.data;
cf_performTransfer_performTransferSectors = (u32)compactFlashTransferSectorPatchCode->GetPerformTransferSectorsFunction();
if (lockUnlockCard)
{
cf_performTransfer_lockUnlockCard = (u32)lockUnlockCard->GetLockUnlockFunction();
}
else
{
// what is getting replaced is a `bl`, taking 4 bytes
const u16 noLockingOpcode = THUMB_MOV_HIREG(THUMB_HI_R8, THUMB_HI_R8);
cf_performTransfer_unlock_label[0] = noLockingOpcode;
cf_performTransfer_unlock_label[1] = noLockingOpcode;
cf_performTransfer_lock_label[0] = noLockingOpcode;
cf_performTransfer_lock_label[1] = noLockingOpcode;
}
}
const ReadSectorsFunc GetReadSectorsFunction() const override
{
return (const ReadSectorsFunc)GetAddressAtTarget((void*)cf_readSectors);
}
const WriteSectorsFunc GetWriteSectorFunction() const override
{
return (const WriteSectorsFunc)GetAddressAtTarget((void*)cf_writeSectors);
}
};

View File

@@ -0,0 +1,202 @@
.cpu arm7tdmi
.syntax unified
.thumb
.section "cf_perform_transfer", "ax"
.equ CF_CMD_LBA, 0xE0
.equ CF_CMD_READ, 0x20
.equ CF_CMD_WRITE, 0x30
@ bool cf_performTransferSectors(u32 numSectors, u32 sector, void* srcAddr, void* dstAddr, u8 command)
.type cf_performTransferSectors, %function
.global cf_performTransferSectors
cf_performTransferSectors:
push {r0,r1,r4-r7,lr}
ldr r7, cf_performTransferSectors_waitCardAvailableForCommands
@ calls waitCardAvailableForCommands
@ this function doesn't alter r0, but sets the zero flag on failure and clears it on success
bl cf_performTransferSectors_interwork
beq cf_performTransferSectors_error
ldr r5, cf_performTransferSectors_reg_sector_count
@ load 0x20000
movs r6, #0x01
lsls r6, #17
@ store sector count
strh r0, [r5]
adds r5, r6
lsls r7, r1, #24
lsrs r7, r7, #24
@ store lba1
strh r7, [r5]
adds r5, r6
lsls r7, r1, #16
lsrs r7, r7, #24
@ store lba2
strh r7, [r5]
adds r5, r6
lsls r7, r1, #8
lsrs r7, r7, #24
@ store lba3
strh r7, [r5]
adds r5, r6
@ Only lower nibble is transferred
lsls r7, r1, #4
lsrs r7, r7, #28
@ store lba4
adds r7, CF_CMD_LBA
strh r7, [r5]
@ store command
strh r4, [r5, r6]
@ get total number of bytes to write
lsls r0, #9
ldr r7, cf_performTransferSectors_waitNextBlockReady
read_next_block:
@ calls waitCardNextBlockReady
@ this function doesn't alter r0, but sets the zero flag on failure and clears it on success
bl cf_performTransferSectors_interwork
beq cf_performTransferSectors_error
read_next_int:
ldm r2!, {r1,r4,r5,r6}
stm r3!, {r1,r4,r5,r6}
subs r0, #16
beq done
@ Shifting left by 0x17 will set the Zero flag if the number that was shifted is a multiple
@ of 0x200 (indicating a full sector has been written)
lsls r1, r0, #0x17
bne read_next_int
b read_next_block
done:
movs r0, #1
cf_performTransferSectors_error:
pop {r0,r1,r4-r7, pc}
cf_performTransferSectors_interwork:
bx r7
.balign 4
.pool
.global cf_performTransferSectors_reg_sector_count
cf_performTransferSectors_reg_sector_count:
.word 0
.global cf_performTransferSectors_waitCardAvailableForCommands
cf_performTransferSectors_waitCardAvailableForCommands:
.word 0
.global cf_performTransferSectors_waitNextBlockReady
cf_performTransferSectors_waitNextBlockReady:
.word 0
.section "cf_read_write_functions", "ax"
.global cf_performTransfer_unlock_label
.global cf_performTransfer_lock_label
@ r2 srcAddr
@ r3 dstAddr
@ r4 command
@ top of stack startSector
@ below it numSectors
@ cf_performTransfer(dstAddr, srcAddr, command, startSector, numSectors)
cf_performTransfer:
@ loads EXMEMCNT register address
ldr r6, =0x04000200
@ waitstate 4,2 and arm9 slot2 access
@ r6 + 4 is EXMEMCNT, use lower 8 bits as 0
strb r6, [r6, #4]
ldr r7, cf_performTransfer_lockUnlockCard
movs r0, #0
@ if the cart requires no lock/unlock sequence, this is replaced with a nop
cf_performTransfer_unlock_label:
@ calls lockUnlockCard
bl cf_performTransfer_interwork
@ r2,r3,r4 hold variables not to be touched
ldr r7, cf_performTransfer_performTransferSectors
@ r1 holds startSector
@ r5 holds remainingSectors
pop {r1,r5}
movs r0, #0xFF
readNextSectorBlock:
subs r5, r0
ble lastRead
@ calls performTransferSectors
@ this function doesn't alter r0, but sets the zero flag on failure and clears it on success
bl cf_performTransfer_interwork
beq error
@ increment sector
adds r1, r0
b readNextSectorBlock
lastRead:
adds r0, r5
@ calls performTransferSectors
@ this function doesn't alter r0, but sets the zero flag on failure and clears it on success
bl cf_performTransfer_interwork
error:
ldr r7, cf_performTransfer_lockUnlockCard
movs r0, #1
@ if the cart requires no lock/unlock sequence, this is replaced with a nop
cf_performTransfer_lock_label:
@ calls lockUnlockCard
bl cf_performTransfer_interwork
@ waitstate 4,2 and arm7 slot2 access
movs r2, #0x80
@ r6 + 4 is EXMEMCNT
strb r2, [r6, #4]
pop {r4-r7, pc}
cf_performTransfer_interwork:
bx r7
@ cf_readSectors(u32 sector, void* buffer, u32 numSectors)
.type cf_readSectors, %function
.global cf_readSectors
cf_readSectors:
push {r0,r2,r4-r7, lr}
movs r4, CF_CMD_READ
movs r3, r1
ldr r2, cf_performTransfer_reg_data
b cf_performTransfer
@ cf_writeSectors(u32 sector, void* buffer, u32 numSectors)
.type cf_writeSectors, %function
.global cf_writeSectors
cf_writeSectors:
push {r0,r2,r4-r7, lr}
movs r4, CF_CMD_WRITE
ldr r3, cf_performTransfer_reg_data
movs r2, r1
b cf_performTransfer
.balign 4
.pool
.global cf_performTransfer_reg_data
cf_performTransfer_reg_data:
.word 0
.global cf_performTransfer_performTransferSectors
cf_performTransfer_performTransferSectors:
.word 0
.global cf_performTransfer_lockUnlockCard
cf_performTransfer_lockUnlockCard:
.word 0

View File

@@ -0,0 +1,15 @@
#pragma once
#include "common.h"
struct cf_registers_t
{
vu16* data;
vu16* altStatus;
vu16* command;
vu16* error;
vu16* sectorCount;
vu16* lba1;
vu16* lba2;
vu16* lba3;
vu16* lba4;
};

View File

@@ -0,0 +1,32 @@
#pragma once
#include "common.h"
#include "sections.h"
#include "../LoaderPlatform.h"
#include "CompactFlashRegisters.h"
DEFINE_SECTION_SYMBOLS(cf_wait_functions);
extern "C" bool cf_waitCardAvailableForCommands();
extern "C" bool cf_waitNextBlockReady();
extern vu16* cf_waitFunctions_reg_cmd;
class CompactFlashStatusFunctionsPatchCode : public PatchCode
{
public:
CompactFlashStatusFunctionsPatchCode(PatchHeap& patchHeap, const cf_registers_t& registers)
: PatchCode(SECTION_START(cf_wait_functions), SECTION_SIZE(cf_wait_functions), patchHeap)
{
cf_waitFunctions_reg_cmd = registers.command;
}
const void* GetWaitCardAvailableForCommandsFunction() const
{
return GetAddressAtTarget((void*)cf_waitCardAvailableForCommands);
}
const void* GetWaitNextBlockReadyFunction() const
{
return GetAddressAtTarget((void*)cf_waitNextBlockReady);
}
};

View File

@@ -0,0 +1,77 @@
.cpu arm7tdmi
.syntax unified
.thumb
.section "cf_wait_functions", "ax"
.equ CF_STS_DRQ, 0x08
.equ CF_STS_DSC, 0x10
.equ CF_STS_READY, 0x40
.equ CF_STS_INSERTED, 0x50
.equ CF_STS_BUSY, 0x80
.equ CF_CARD_TIMEOUT, 10000000
@ Waits until the card is ready to receive commands
@ this function doesn't alter r0, but sets the zero flag on failure and clears it on success
@ bool cf_waitCardAvailableForCommands()
.global cf_waitCardAvailableForCommands
.type cf_waitCardAvailableForCommands, %function
cf_waitCardAvailableForCommands:
push {r0-r4, lr}
@ wait for card to finish previous commands
ldr r1, =CF_CARD_TIMEOUT
ldr r2, cf_waitFunctions_reg_cmd
movs r4, r1
still_busy:
ldrh r0, [r2]
@ r0 & CF_STS_BUSY == 0
lsls r0, #25
bcc no_longer_busy
subs r1, #1
bne still_busy
no_longer_busy:
@ wait for card to be ready for commands
movs r3, (CF_STS_READY | CF_STS_DSC)
1:
ldrh r1, [r2]
tst r1, r3
@ timeout expired
bne ready
subs r4, #1
@ not ready
bne 1b
@ timeout expired, return 0
pop {r0-r4, pc}
@ Waits until the card is ready to write/return the next block
@ this function doesn't alter r0, but sets the zero flag on failure and clears it on success
@ bool cf_waitNextBlockReady()
.global cf_waitNextBlockReady
.type cf_waitNextBlockReady, %function
cf_waitNextBlockReady:
push {r0-r4, lr}
ldr r0, =CF_CARD_TIMEOUT
ldr r1, cf_waitFunctions_reg_cmd
1:
ldrb r2, [r1]
cmp r2, (CF_STS_READY | CF_STS_DSC | CF_STS_DRQ)
@ timeout expired
beq ready
subs r0, #1
@ not ready
bne 1b
@ timeout expired, return 0
pop {r0-r4, pc}
ready:
movs r0, #1
pop {r0-r4, pc}
.balign 4
.global cf_waitFunctions_reg_cmd
cf_waitFunctions_reg_cmd:
.word 0

View File

@@ -0,0 +1,13 @@
#pragma once
/// @brief Interface for patch code implementing Compact Flash carts lock/unlock routines
class ICompactFlashLockUnlockPatchCode
{
protected:
ICompactFlashLockUnlockPatchCode() { }
public:
/// @brief Gets a pointer to the SD write function in the patch code.
/// @return The pointer to the SD write function.
virtual const void* GetLockUnlockFunction() const = 0;
};

View File

@@ -0,0 +1,41 @@
#include "common.h"
#include "M3CFLoaderPlatform.h"
#define M3_MODE_ROM 8
#define M3_MODE_MEDIA 6
static u16 volatileReadHalfword(u32 addr)
{
return *((vu16*)addr);
}
static void changeM3Mode(u32 mode)
{
volatileReadHalfword(0x08e00002);
volatileReadHalfword(0x0800000e);
volatileReadHalfword(0x08801FFC);
volatileReadHalfword(0x0800104A);
volatileReadHalfword(0x08800612);
volatileReadHalfword(0x08000000);
volatileReadHalfword(0x08801B66);
volatileReadHalfword(0x08800000 + mode);
volatileReadHalfword(0x0800080E);
volatileReadHalfword(0x08000000);
if(mode == M3_MODE_ROM)
{
volatileReadHalfword(0x080001E4);
volatileReadHalfword(0x080001E4);
volatileReadHalfword(0x08000188);
volatileReadHalfword(0x08000188);
}
else
{
volatileReadHalfword(0x09000000);
}
}
void M3CFLoaderPlatform::SetCardLocked(bool locked) const
{
changeM3Mode(locked ? M3_MODE_ROM : M3_MODE_MEDIA);
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include "common.h"
#include "M3CFLockUnlockCardPatchCode.h"
#include "../compactflash-common/CompactFlashCommonLoaderPlatform.h"
/// @brief Implementation of LoaderPlatform for the DATEL line of flashcarts
class M3CFLoaderPlatform : public CompactFlashCommonLoaderPlatform
{
protected:
const ICompactFlashLockUnlockPatchCode* CreateLockingPatchCode(
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
{
return patchCodeCollection.GetOrAddSharedPatchCode([&]
{
return new M3CFLockUnlockCardPatchCode(patchHeap);
});
}
void SetCardLocked(bool locked) const override;
const cf_registers_t& GetCfRegisters() const override
{
static const cf_registers_t registers
{
.data = (vu16*)0x08800000,
.altStatus = (vu16*)0x080C0000,
.command = (vu16*)0x088E0000,
.error = (vu16*)0x08820000,
.sectorCount = (vu16*)0x08840000,
.lba1 = (vu16*)0x08860000,
.lba2 = (vu16*)0x08880000,
.lba3 = (vu16*)0x088A0000,
.lba4 = (vu16*)0x088C0000,
};
return registers;
}
};

View File

@@ -0,0 +1,20 @@
#pragma once
#include "sections.h"
#include "patches/PatchCode.h"
#include "../compactflash-common/ICompactFlashLockUnlockPatchCode.h"
DEFINE_SECTION_SYMBOLS(m3cf_lock_unlock);
extern "C" void m3cf_lockUnlockCard(bool lock);
class M3CFLockUnlockCardPatchCode : public PatchCode, public ICompactFlashLockUnlockPatchCode
{
public:
explicit M3CFLockUnlockCardPatchCode(PatchHeap& patchHeap)
: PatchCode(SECTION_START(m3cf_lock_unlock), SECTION_SIZE(m3cf_lock_unlock), patchHeap) { }
const void* GetLockUnlockFunction() const override
{
return GetAddressAtTarget((void*)m3cf_lockUnlockCard);
}
};

View File

@@ -0,0 +1,92 @@
.cpu arm7tdmi
.syntax unified
.thumb
.section "m3cf_lock_unlock", "ax"
.equ M3_MODE_ROM, 8
.equ M3_MODE_MEDIA, 6
.global m3cf_lockUnlockCard
.type m3cf_lockUnlockCard, %function
m3cf_lockUnlockCard:
cmp r0, #1
bne unlock
movs r0, #0x08
M3_changeMode:
push {r1-r7, lr}
@ READ_REG #0x08e00002
ldr r1, =#0x08e00002
ldrh r1, [r1]
adr r1, M3_regs
@ r2 has #0x08801ffc
@ r3 has #0x0800104a
@ r4 has #0x08800612
@ r5 has #0x08801b66
@ r6 has #0x0800080e
@ r7 has #0x09000000
ldm r1!, {r2-r7}
@ load 0x08000000
movs r1, #1
lsls r1, #27
@ READ_REG #0x0800000e
ldrh r1, [r1, #0x0E]
@ READ_REG #0x08801ffc
ldrh r1, [r2]
@ READ_REG #0x0800104a
ldrh r1, [r3]
@ READ_REG #0x08800612
ldrh r1, [r4]
@ READ_REG #0x08000000
@ load 0x08000000
movs r2, #1
lsls r2, #27
ldrh r1, [r2]
@ READ_REG #0x08801b66
ldrh r1, [r5]
@ READ_REG #0x08800000 + r0
@ ldr r1, =#0x08800000
movs r1, 0x88
lsls r1, #20
ldrh r1, [r1, r0]
@ READ_REG #0x0800080e
ldrh r1, [r6]
@ READ_REG #0x08000000
ldrh r1, [r2]
cmp r0, M3_MODE_ROM
beq lastRomRead
@ READ_REG #0x09000000
ldrh r1, [r7]
pop {r1-r7, pc}
lastRomRead:
@ READ_REG #0x080001e4
ldr r1, =#0x080001e4
ldrh r3, [r1]
ldrh r3, [r1]
@ READ_REG #0x08000188
subs r1, #0x5C
ldrh r3, [r1]
ldrh r3, [r1]
pop {r1-r7, pc}
unlock:
movs r0, #0x06
b M3_changeMode
.pool
M3_regs:
.word 0x08801ffc
.word 0x0800104a
.word 0x08800612
.word 0x08801b66
.word 0x0800080e
.word 0x09000000

View File

@@ -0,0 +1,27 @@
#pragma once
#include "common.h"
#include "../compactflash-common/CompactFlashCommonLoaderPlatform.h"
/// @brief Implementation of LoaderPlatform for the DATEL line of flashcarts
class MMCFLoaderPlatform : public CompactFlashCommonLoaderPlatform
{
protected:
void SetCardLocked(bool locked) const override { }
const cf_registers_t& GetCfRegisters() const override
{
static const cf_registers_t registers
{
.data = (vu16*)0x09000000,
.altStatus = (vu16*)0x088C0000, // should be this, untested, but unused
.command = (vu16*)0x080E0000,
.error = (vu16*)0x08020000,
.sectorCount = (vu16*)0x08040000,
.lba1 = (vu16*)0x08060000,
.lba2 = (vu16*)0x08080000,
.lba3 = (vu16*)0x080A0000,
.lba4 = (vu16*)0x080C0000,
};
return registers;
}
};

View File

@@ -0,0 +1,27 @@
#pragma once
#include "common.h"
#include "../compactflash-common/CompactFlashCommonLoaderPlatform.h"
/// @brief Implementation of LoaderPlatform for the DATEL line of flashcarts
class MPCFLoaderPlatform : public CompactFlashCommonLoaderPlatform
{
protected:
void SetCardLocked(bool locked) const override { }
const cf_registers_t& GetCfRegisters() const override
{
static const cf_registers_t registers
{
.data = (vu16*)0x09000000,
.altStatus = (vu16*)0x098C0000,
.command = (vu16*)0x090E0000,
.error = (vu16*)0x09020000,
.sectorCount = (vu16*)0x09040000,
.lba1 = (vu16*)0x09060000,
.lba2 = (vu16*)0x09080000,
.lba3 = (vu16*)0x090A0000,
.lba4 = (vu16*)0x090C0000,
};
return registers;
}
};

View File

@@ -0,0 +1,21 @@
#include "common.h"
#include "SuperCardCFLoaderPlatform.h"
#define SC_MODE_REG (*(vu16*)0x09FFFFFE)
#define SC_MODE_MAGIC 0xA55A
#define SC_MODE_MEDIA 0x3
#define SC_MODE_RAM_RO 0x1
static void changeSupercardMode(u8 mode)
{
SC_MODE_REG = SC_MODE_MAGIC;
SC_MODE_REG = SC_MODE_MAGIC;
SC_MODE_REG = mode;
SC_MODE_REG = mode;
}
void SuperCardCFLoaderPlatform::SetCardLocked(bool locked) const
{
changeSupercardMode(locked ? SC_MODE_RAM_RO : SC_MODE_MEDIA);
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include "common.h"
#include "SuperCardCFLockUnlockCardPatchCode.h"
#include "../compactflash-common/CompactFlashCommonLoaderPlatform.h"
/// @brief Implementation of LoaderPlatform for the DATEL line of flashcarts
class SuperCardCFLoaderPlatform : public CompactFlashCommonLoaderPlatform
{
protected:
const ICompactFlashLockUnlockPatchCode* CreateLockingPatchCode(
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const override
{
return patchCodeCollection.GetOrAddSharedPatchCode([&]
{
return new SuperCardCFLockUnlockCardPatchCode(patchHeap);
});
}
void SetCardLocked(bool locked) const override;
const cf_registers_t& GetCfRegisters() const override
{
static const cf_registers_t registers
{
.data = (vu16*)0x09000000,
.altStatus = (vu16*)0x098C0000,
.command = (vu16*)0x090E0000,
.error = (vu16*)0x09020000,
.sectorCount = (vu16*)0x09040000,
.lba1 = (vu16*)0x09060000,
.lba2 = (vu16*)0x09080000,
.lba3 = (vu16*)0x090A0000,
.lba4 = (vu16*)0x090C0000,
};
return registers;
}
};

View File

@@ -0,0 +1,20 @@
#pragma once
#include "sections.h"
#include "patches/PatchCode.h"
#include "../compactflash-common/ICompactFlashLockUnlockPatchCode.h"
DEFINE_SECTION_SYMBOLS(sccf_lock_unlock);
extern "C" void sccf_lockUnlockCard(bool lock);
class SuperCardCFLockUnlockCardPatchCode : public PatchCode, public ICompactFlashLockUnlockPatchCode
{
public:
explicit SuperCardCFLockUnlockCardPatchCode(PatchHeap& patchHeap)
: PatchCode(SECTION_START(sccf_lock_unlock), SECTION_SIZE(sccf_lock_unlock), patchHeap) { }
const void* GetLockUnlockFunction() const override
{
return GetAddressAtTarget((void*)sccf_lockUnlockCard);
}
};

View File

@@ -0,0 +1,26 @@
.cpu arm7tdmi
.syntax unified
.thumb
.section "sccf_lock_unlock", "ax"
.global sccf_lockUnlockCard
.type sccf_lockUnlockCard, %function
sccf_lockUnlockCard:
cmp r0, #1
bne unlock
@ void sc_change_mode(uint16_t mode);
sccf_changeMode:
push {r1-r3, lr}
ldr r2,= 0x09FFFFFE
ldr r3,= 0xA55A
strh r3, [r2]
strh r3, [r2]
strh r0, [r2]
strh r0, [r2]
pop {r1-r3, pc}
unlock:
movs r0, #3
b sccf_changeMode
.pool