mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-06-02 09:16:49 +02:00
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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
41
arm9/source/patches/platform/m3cf/M3CFLoaderPlatform.cpp
Normal file
41
arm9/source/patches/platform/m3cf/M3CFLoaderPlatform.cpp
Normal 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);
|
||||
}
|
||||
37
arm9/source/patches/platform/m3cf/M3CFLoaderPlatform.h
Normal file
37
arm9/source/patches/platform/m3cf/M3CFLoaderPlatform.h
Normal 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;
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
27
arm9/source/patches/platform/mmcf/MMCFLoaderPlatform.h
Normal file
27
arm9/source/patches/platform/mmcf/MMCFLoaderPlatform.h
Normal 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;
|
||||
}
|
||||
};
|
||||
27
arm9/source/patches/platform/mpcf/MPCFLoaderPlatform.h
Normal file
27
arm9/source/patches/platform/mpcf/MPCFLoaderPlatform.h
Normal 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;
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user