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:
6
.github/workflows/nightly.yml
vendored
6
.github/workflows/nightly.yml
vendored
@@ -27,7 +27,11 @@ jobs:
|
|||||||
"R4",
|
"R4",
|
||||||
"R4iDSN",
|
"R4iDSN",
|
||||||
"STARGATE",
|
"STARGATE",
|
||||||
"SUPERCARD"
|
"SUPERCARD",
|
||||||
|
"SUPERCARDCF",
|
||||||
|
"M3CF",
|
||||||
|
"MMCF",
|
||||||
|
"MPCF"
|
||||||
]
|
]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: skylyrac/blocksds:slim-v1.13.1
|
container: skylyrac/blocksds:slim-v1.13.1
|
||||||
|
|||||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -21,7 +21,11 @@ jobs:
|
|||||||
"R4",
|
"R4",
|
||||||
"R4iDSN",
|
"R4iDSN",
|
||||||
"STARGATE",
|
"STARGATE",
|
||||||
"SUPERCARD"
|
"SUPERCARD",
|
||||||
|
"SUPERCARDCF",
|
||||||
|
"M3CF",
|
||||||
|
"MMCF",
|
||||||
|
"MPCF"
|
||||||
]
|
]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: skylyrac/blocksds:slim-v1.13.1
|
container: skylyrac/blocksds:slim-v1.13.1
|
||||||
|
|||||||
@@ -32,11 +32,15 @@ Note that there can be some game compatibility differences between different pla
|
|||||||
| G003 | M3i Zero (GMP-Z003) | ✅ |
|
| G003 | M3i Zero (GMP-Z003) | ✅ |
|
||||||
| ISNITRO | Supports the IS-NITRO-EMULATOR through agb semihosting. | ❌ |
|
| ISNITRO | Supports the IS-NITRO-EMULATOR through agb semihosting. | ❌ |
|
||||||
| M3DS | M3 DS Real, M3i Zero, iTouchDS, r4rts.com, r4isdhc.com RTS (black) | ❌ |
|
| M3DS | M3 DS Real, M3i Zero, iTouchDS, r4rts.com, r4isdhc.com RTS (black) | ❌ |
|
||||||
|
| M3CF | M3 Compact Flash (Slot-2 flashcart) | ❌ |
|
||||||
| MELONDS | Melon DS support for testing purposes only. | ❌ |
|
| MELONDS | Melon DS support for testing purposes only. | ❌ |
|
||||||
|
| MMCF | DATEL Max Media Dock Compact Flash (Slot-2 flashcart) | ❌ |
|
||||||
|
| MPCF | GBA Media Player Compact Flash (Slot-2 cart) | ❌ |
|
||||||
| R4 | Original R4DS (non-SDHC), M3 DS Simply | ❌ |
|
| R4 | Original R4DS (non-SDHC), M3 DS Simply | ❌ |
|
||||||
| R4iDSN | r4idsn.com | ❌ |
|
| R4iDSN | r4idsn.com | ❌ |
|
||||||
| STARGATE | Stargate 3DS DS-mode | ✅ |
|
| STARGATE | Stargate 3DS DS-mode | ✅ |
|
||||||
| SUPERCARD | SuperCard (Slot-2 flashcart) | ❌ |
|
| SUPERCARD | SuperCard SD, Lite and Rumble (Slot-2 flashcart) | ❌ |
|
||||||
|
| SUPERCARDCF | SuperCard CF (Slot-2 flashcart) | ❌ |
|
||||||
|
|
||||||
The DMA column indicates whether DMA card reads are implemented for the platform . Without DMA card reads, some games can have cache related issues.<br>
|
The DMA column indicates whether DMA card reads are implemented for the platform . Without DMA card reads, some games can have cache related issues.<br>
|
||||||
Note that there are still SDK versions and variants for which Pico Loader does not yet support DMA card reads.
|
Note that there are still SDK versions and variants for which Pico Loader does not yet support DMA card reads.
|
||||||
|
|||||||
@@ -15,6 +15,10 @@
|
|||||||
#include "patches/platform/ezp/EzpLoaderPlatform.h"
|
#include "patches/platform/ezp/EzpLoaderPlatform.h"
|
||||||
#include "patches/platform/datel/DatelLoaderPlatform.h"
|
#include "patches/platform/datel/DatelLoaderPlatform.h"
|
||||||
#include "patches/platform/stargate/StargateLoaderPlatform.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"
|
#include "LoaderPlatformFactory.h"
|
||||||
|
|
||||||
LoaderPlatform* LoaderPlatformFactory::CreateLoaderPlatform() const
|
LoaderPlatform* LoaderPlatformFactory::CreateLoaderPlatform() const
|
||||||
@@ -49,6 +53,14 @@ LoaderPlatform* LoaderPlatformFactory::CreateLoaderPlatform() const
|
|||||||
return new DatelLoaderPlatform();
|
return new DatelLoaderPlatform();
|
||||||
#elif defined(PICO_LOADER_TARGET_STARGATE)
|
#elif defined(PICO_LOADER_TARGET_STARGATE)
|
||||||
return new StargateLoaderPlatform();
|
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
|
#else
|
||||||
#error "No loader platform defined"
|
#error "No loader platform defined"
|
||||||
return nullptr;
|
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