Skip to content

Commit

Permalink
scdssdhc2: major refactoring to support quirky carts
Browse files Browse the repository at this point in the history
Normally, a command for this cart is like so:
`80 00 00 00 00 00 00 00`

The 80 command would ignore the remaining bytes, as it is simply here
to wait for the data buffer to be filled.

However, there are carts where if one were to send this command
beforehand to read a single block:
`53 DE AD BE EF 00 00 00`

and then use the 80 command to wait for data, it sends this instead:
`80 DE AD BE EF 00 00 00`

The original SuperCard code does in fact do this, as a way of reducing
the size of the binary? All other DSTT based carts do not care about
the remaining bytes, but some clone manufacturers appear to have taken
the commands *too* literally.

libtwl wasn't really designed for this use case, so instead define our
own card command register casted to a u8 array, and reorganize commands
to work in a similar manner to the original SuperCard design.
  • Loading branch information
lifehackerhansol committed Nov 4, 2024
1 parent 3d90e27 commit 908fc5c
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 122 deletions.
2 changes: 1 addition & 1 deletion source/scdssdhc2/source/dldi_header.s
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
.word 0xBF8DA5ED @ Magic number to identify this region
.asciz " Chishm" @ Identifying Magic string (8 bytes with null terminator)
.byte 0x01 @ Version number
.byte DLDI_SIZE_4KB @ Log [base-2] of the size of this driver in bytes.
.byte DLDI_SIZE_8KB @ Log [base-2] of the size of this driver in bytes.
.byte FIX_GOT | FIX_BSS | FIX_GLUE @ Sections to fix
.byte 0x00 @ Space allocated in the application, leave empty.

Expand Down
170 changes: 111 additions & 59 deletions source/scdssdhc2/source/scdssdhc.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,85 +13,119 @@

static u32 isSDHC = 0;

static void SCDS_ReadCardData(u64 command, u32 flags, void *buffer, u32 length)
static u32 SCDS_SDHostSetMode(u8 sdio, u32 parameter, u8 response_type, u32 latency)
{
u64 command = ((u64)SCDS_CMD_SD_HOST_PARAM << 56) | ((u64)parameter << 24) | ((u64)sdio << 16) | ((u64)response_type << 8);
card_romSetCmd(command);
card_romStartXfer(flags, false);
if ((u32)buffer & 3)
card_romCpuReadUnaligned((u8 *)buffer, length);
else
card_romCpuRead(buffer, length);
card_romStartXfer(SCDS_CTRL_READ_4B | MCCNT1_LATENCY1(latency), false);
card_romWaitDataReady();
return card_romGetData();
}

static void SCDS_WriteCardData(u64 command, u32 flags, const void *buffer, u32 length)
static u32 SCDS_IsSDHostBusy(void)
{
card_romSetCmd(command);
card_romStartXfer(flags, false);
if ((u32)buffer & 3)
card_romCpuWriteUnaligned((u8 *)buffer, length);
else
card_romCpuWrite(buffer, length);
REG_SCDS_MCCMD[0] = SCDS_CMD_SD_HOST_BUSY;
card_romStartXfer(SCDS_CTRL_READ_4B, false);
card_romWaitDataReady();
return card_romGetData();
}

static u32 SCDS_IsSDHostBusy(void)
static u32 SCDS_SDHostGetResponse(void)
{
return SCDS_SendCommand(SCDS_CMD_SD_HOST_BUSY, 0);
REG_SCDS_MCCMD[0] = SCDS_CMD_SD_HOST_RESPONSE;
card_romStartXfer(SCDS_CTRL_READ_4B, false);
card_romWaitDataReady();
return __builtin_bswap32(card_romGetData());
}

static void SCDS_SDSendR0Command(u8 sdio, u32 parameter, u32 latency)
{
SCDS_SendCommand(SCDS_CMD_SD_HOST_PARAM(sdio, parameter, SCDS_SD_HOST_NORESPONSE), latency);
SCDS_SDHostSetMode(sdio, parameter, SCDS_SD_HOST_NORESPONSE, latency);
while(SCDS_IsSDHostBusy());
}

static u32 SCDS_SDSendR1Command(u8 sdio, u32 parameter, u32 latency)
{
SCDS_SendCommand(SCDS_CMD_SD_HOST_PARAM(sdio, parameter, SCDS_SD_HOST_READ_4B), latency);
SCDS_SDHostSetMode(sdio, parameter, SCDS_SD_HOST_READ_4B, latency);
while(SCDS_IsSDHostBusy());
return __builtin_bswap32(SCDS_SendCommand(SCDS_CMD_SD_HOST_RESPONSE, 0));
return SCDS_SDHostGetResponse();
}

// TODO: save the response to a buffer (also figure out in which order they're sent)
static void SCDS_SDSendR2Command(u8 sdio, u32 parameter, u32 latency)
{
SCDS_SendCommand(SCDS_CMD_SD_HOST_PARAM(sdio, parameter, SCDS_SD_HOST_READ_4B_MULTI), latency);
SCDS_SDHostSetMode(sdio, parameter, SCDS_SD_HOST_READ_4B_MULTI, latency);
while(SCDS_IsSDHostBusy());

// TODO: parse this response
SCDS_SendCommand(SCDS_CMD_SD_HOST_RESPONSE, 0);
SCDS_SDHostGetResponse();

for(int i=0; i < 4; i++)
{
SCDS_SendCommand(SCDS_CMD_SD_HOST_PARAM(sdio, parameter, SCDS_SD_HOST_NEXT_4B), latency);
SCDS_SDHostSetMode(sdio, parameter, SCDS_SD_HOST_NEXT_4B, latency);
while(SCDS_IsSDHostBusy());
// TODO: parse this response
SCDS_SendCommand(SCDS_CMD_SD_HOST_RESPONSE, 0);
SCDS_SDHostGetResponse();
}
SCDS_SendCommand(SCDS_CMD_SD_HOST_PARAM(sdio, parameter, SCDS_SD_HOST_SEND_STOP_CLK), 0);
SCDS_SDHostSetMode(sdio, parameter, SCDS_SD_HOST_SEND_STOP_CLK, 0);
while(SCDS_IsSDHostBusy());
}

void waitByLoop(u32 count);

static void SCDS_SDSetHostRegister(u8 bits)
static void SCDS_SDHostSetRegister(u8 bits)
{
SCDS_SendCommand(SCDS_CMD_SD_HOST_SET_REGISTER(bits), 0);
u64 command = ((u64)SCDS_CMD_SD_HOST_SET_REGISTER << 56) | ((u64)(0x30 | bits) << 48);
card_romSetCmd(command);
card_romStartXfer(SCDS_CTRL_READ_4B, false);
card_romWaitDataReady();
card_romGetData();
waitByLoop(0x300);
}

void SCDS_SDGetSDHCStatusFromSRAM(void)
static u32 SCDS_IsSDFIFOBusy(void) {
REG_SCDS_MCCMD[0] = SCDS_CMD_FIFO_BUSY;
card_romStartXfer(SCDS_CTRL_READ_4B, false);
card_romWaitDataReady();
return card_romGetData();
}

// Reads max 512 bytes
static void SCDS_SDFIFOReadData(void *buffer, u32 length)
{
isSDHC = SCDS_SendCommand(SCDS_CMD_SRAM_READ_DATA(0x7F9E0), 0) != 0 ? 1 : 0;
REG_SCDS_MCCMD[0] = SCDS_CMD_FIFO_READ_DATA;
card_romStartXfer(SCDS_CTRL_READ_512B, false);
if ((u32)buffer & 3)
card_romCpuReadUnaligned((u8 *)buffer, length);
else
card_romCpuRead(buffer, length);
}

// Writes max 512 bytes
static void SCDS_SDFIFOWriteData(const void *buffer, u32 length)
{
REG_SCDS_MCCMD[0] = SCDS_CMD_FIFO_WRITE_DATA;
card_romStartXfer(SCDS_CTRL_WRITE_512B, false);
if ((u32)buffer & 3)
card_romCpuWriteUnaligned((u8 *)buffer, length);
else
card_romCpuWrite(buffer, length);
}

u32 SCDS_SendCommand(const u64 command, u32 latency)
static u32 SCDS_SRAMReadData(u32 address)
{
u64 command = ((u64)SCDS_CMD_SRAM_READ_DATA << 56) | ((u64)address << 48);
card_romSetCmd(command);
card_romStartXfer(SCDS_CTRL_READ_4B | MCCNT1_LATENCY1(latency), false);
card_romStartXfer(SCDS_CTRL_READ_4B, false);
card_romWaitDataReady();
return card_romGetData();
}

void SCDS_SDGetSDHCStatusFromSRAM(void)
{
isSDHC = SCDS_SRAMReadData(0x7F9E0) != 0 ? 1 : 0;
}

bool SCDS_SDInitialize(void)
{
u32 isSD20 = 0;
Expand All @@ -100,35 +134,38 @@ bool SCDS_SDInitialize(void)
isSDHC = 0;

// TODO: What is this command doing?
SCDS_ReadCardData(0x6600000000000000ull, 0xA7586000, &response, 1);
card_romSetCmd(0x6600000000000000ull);
card_romStartXfer(0xA7586000, false);
card_romWaitDataReady();
card_romGetData();

// Reset SD host
SCDS_SDSetHostRegister(0);
SCDS_SDHostSetRegister(0);

// Init
SCDS_SDSetHostRegister(SCDS_SD_HOST_REG_RESET | SCDS_SD_HOST_REG_400KHZ_CLK | SCDS_SD_HOST_REG_CLEAN_ROM_MODE);
SCDS_SendCommand(SCDS_CMD_SD_HOST_PARAM(0, 0, SCDS_SD_HOST_SEND_CLK), SCDS_CTRL_SD_LOW_CLK_LATENCY);
SCDS_SDHostSetRegister(SCDS_SD_HOST_REG_RESET | SCDS_SD_HOST_REG_400KHZ_CLK | SCDS_SD_HOST_REG_CLEAN_ROM_MODE);
SCDS_SDHostSetMode(0, 0, SCDS_SD_HOST_SEND_CLK, SCDS_CTRL_SD_LOW_CLK_LATENCY);
waitByLoop(0x1000);

// CMD0
SCDS_SDSendR0Command(0, 0, SCDS_CTRL_SD_LOW_CLK_LATENCY);
SCDS_SendCommand(SCDS_CMD_SD_HOST_PARAM(0, 0, SCDS_SD_HOST_SEND_STOP_CLK), SCDS_CTRL_SD_LOW_CLK_LATENCY);
SCDS_SDHostSetMode(0, 0, SCDS_SD_HOST_SEND_STOP_CLK, SCDS_CTRL_SD_LOW_CLK_LATENCY);

// CMD8
SCDS_SendCommand(SCDS_CMD_SD_HOST_PARAM(8, 0x1AA, SCDS_SD_HOST_READ_4B), SCDS_CTRL_SD_LOW_CLK_LATENCY);
SCDS_SDHostSetMode(8, 0x1AA, SCDS_SD_HOST_READ_4B, SCDS_CTRL_SD_LOW_CLK_LATENCY);

u32 retryCount = 9999;
while(1)
{
if(!SCDS_IsSDHostBusy())
{
response = __builtin_bswap32(SCDS_SendCommand(SCDS_CMD_SD_HOST_RESPONSE, 0));
response = SCDS_SDHostGetResponse();
break;
}
if (--retryCount == 0)
{
SCDS_SDSetHostRegister(0);
SCDS_SDSetHostRegister(SCDS_SD_HOST_REG_RESET | SCDS_SD_HOST_REG_400KHZ_CLK | SCDS_SD_HOST_REG_CLEAN_ROM_MODE);
SCDS_SDHostSetRegister(0);
SCDS_SDHostSetRegister(SCDS_SD_HOST_REG_RESET | SCDS_SD_HOST_REG_400KHZ_CLK | SCDS_SD_HOST_REG_CLEAN_ROM_MODE);
response = 0;
break;
}
Expand All @@ -140,18 +177,18 @@ bool SCDS_SDInitialize(void)
do
{
// CMD55
SCDS_SendCommand(SCDS_CMD_SD_HOST_PARAM(55, 0, SCDS_SD_HOST_READ_4B), SCDS_CTRL_SD_LOW_CLK_LATENCY);
SCDS_SDHostSetMode(55, 0, SCDS_SD_HOST_READ_4B, SCDS_CTRL_SD_LOW_CLK_LATENCY);
retryCount = 9999;
while(SCDS_IsSDHostBusy())
{
if (--retryCount == 0)
{
SCDS_SDSetHostRegister(0);
SCDS_SDSetHostRegister(SCDS_SD_HOST_REG_RESET | SCDS_SD_HOST_REG_400KHZ_CLK | SCDS_SD_HOST_REG_CLEAN_ROM_MODE);
SCDS_SDHostSetRegister(0);
SCDS_SDHostSetRegister(SCDS_SD_HOST_REG_RESET | SCDS_SD_HOST_REG_400KHZ_CLK | SCDS_SD_HOST_REG_CLEAN_ROM_MODE);
return false;
}
}
SCDS_SendCommand(SCDS_CMD_SD_HOST_RESPONSE, 0);
SCDS_SDHostGetResponse();

// ACMD41
u32 parameter = 0x00FC0000;
Expand Down Expand Up @@ -179,43 +216,49 @@ bool SCDS_SDInitialize(void)
// CMD16
SCDS_SDSendR1Command(16, 512, SCDS_CTRL_SD_LOW_CLK_LATENCY);

SCDS_SDSetHostRegister(0);
SCDS_SDSetHostRegister(SCDS_SD_HOST_REG_RESET | SCDS_SD_HOST_REG_CLEAN_ROM_MODE);
SCDS_SDHostSetRegister(0);
SCDS_SDHostSetRegister(SCDS_SD_HOST_REG_RESET | SCDS_SD_HOST_REG_CLEAN_ROM_MODE);
if(isSDHC)
SCDS_SDSetHostRegister(SCDS_SD_HOST_REG_RESET | SCDS_SD_HOST_REG_CLEAN_ROM_MODE | SCDS_SD_HOST_REG_SDHC);
SCDS_SDHostSetRegister(SCDS_SD_HOST_REG_RESET | SCDS_SD_HOST_REG_CLEAN_ROM_MODE | SCDS_SD_HOST_REG_SDHC);

return true;
}

void SCDS_SDReadSingleSector(u32 sector, void *buffer)
{
// instruct cart what to read
SCDS_SendCommand(SCDS_CMD_SD_READ_SINGLE_BLOCK(isSDHC ? sector : sector << 9), 0);
u64 command = ((u64)SCDS_CMD_SD_READ_SINGLE_BLOCK << 56) | ((u64)(isSDHC ? sector : sector << 9) << 24);
card_romSetCmd(command);
card_romStartXfer(SCDS_CTRL_READ_4B, false);
card_romWaitDataReady();
card_romGetData();

// wait until data ready
while(SCDS_SendCommand(SCDS_CMD_FIFO_BUSY, 0));
while(SCDS_IsSDFIFOBusy());

// retrieve data
SCDS_ReadCardData(SCDS_CMD_FIFO_READ_DATA, SCDS_CTRL_READ_512B, buffer, 128);
SCDS_SDFIFOReadData(buffer, 128);
}

void SCDS_SDReadMultiSector(u32 sector, void *buffer, u32 num_sectors)
{
// instruct cart what to read
SCDS_SendCommand(SCDS_CMD_SD_READ_MULTI_BLOCK(isSDHC ? sector : sector << 9), 0);
u64 command = ((u64)SCDS_CMD_SD_READ_MULTI_BLOCK << 56) | ((u64)(isSDHC ? sector : sector << 9) << 24);
card_romSetCmd(command);
card_romStartXfer(SCDS_CTRL_READ_4B, false);
card_romWaitDataReady();
card_romGetData();

while(1)
{
// wait until data ready
while(SCDS_SendCommand(SCDS_CMD_FIFO_BUSY, 0));
while(SCDS_IsSDFIFOBusy());

// retrieve data
SCDS_ReadCardData(SCDS_CMD_FIFO_READ_DATA, SCDS_CTRL_READ_512B, buffer, 128);
SCDS_SDFIFOReadData(buffer, 128);
buffer = (u8 *)buffer + 0x200;
num_sectors--;
if(num_sectors == 0)
break;
SCDS_SendCommand(SCDS_CMD_SD_HOST_PARAM(0, 0, SCDS_SD_HOST_NEXT_DATABLOCK), 0);
SCDS_SDHostSetMode(0, 0, SCDS_SD_HOST_NEXT_DATABLOCK, 0);
};

// end read
Expand All @@ -228,11 +271,14 @@ void SCDS_SDWriteSingleSector(u32 sector, const void *buffer)
SCDS_SDSendR1Command(24, isSDHC ? sector : sector << 9, 0);

// write
SCDS_WriteCardData(SCDS_CMD_FIFO_WRITE_DATA, SCDS_CTRL_WRITE_512B, buffer, 128);
SCDS_SDFIFOWriteData(buffer, 128);
while(SCDS_IsSDHostBusy());

// end write
SCDS_SendCommand(SCDS_CMD_SD_WRITE_END, 0);
REG_SCDS_MCCMD[0] = SCDS_CMD_SD_WRITE_END;
card_romStartXfer(SCDS_CTRL_READ_4B, false);
card_romWaitDataReady();
card_romGetData();
while(SCDS_IsSDHostBusy());
}

Expand All @@ -245,16 +291,22 @@ void SCDS_SDWriteMultiSector(u32 sector, const void *buffer, u32 num_sectors)
// end write
// well, it's supposed to be end write. But doing it first is a no-op, and it's also
// a little simpler to write this way
SCDS_SendCommand(SCDS_CMD_SD_WRITE_END, 0);
REG_SCDS_MCCMD[0] = SCDS_CMD_SD_WRITE_END;
card_romStartXfer(SCDS_CTRL_READ_4B, false);
card_romWaitDataReady();
card_romGetData();
while(SCDS_IsSDHostBusy());
// write
SCDS_WriteCardData(SCDS_CMD_FIFO_WRITE_DATA, SCDS_CTRL_WRITE_512B, buffer, 128);
SCDS_SDFIFOWriteData(buffer, 128);
while(SCDS_IsSDHostBusy());
buffer = (u8 *)buffer + 0x200;
}

// *really* end write
SCDS_SDSendR1Command(12, 0, 0);
SCDS_SendCommand(SCDS_CMD_SD_WRITE_END, 0);
REG_SCDS_MCCMD[0] = SCDS_CMD_SD_WRITE_END;
card_romStartXfer(SCDS_CTRL_READ_4B, false);
card_romWaitDataReady();
card_romGetData();
while(SCDS_IsSDHostBusy());
}
Loading

0 comments on commit 908fc5c

Please sign in to comment.