mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-27 00:00:07 -06:00
betterer SD/MMC code. Flipnote can save shit!
This commit is contained in:
@ -213,12 +213,12 @@ u32 ReadCnt()
|
|||||||
|
|
||||||
ret |= InputFIFO->Level();
|
ret |= InputFIFO->Level();
|
||||||
ret |= (OutputFIFO->Level() << 5);
|
ret |= (OutputFIFO->Level() << 5);
|
||||||
//printf("READ AES CNT: %08X, LEVELS: IN=%d OUT=%d\n", ret, InputFIFO->Level(), OutputFIFO->Level());
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteCnt(u32 val)
|
void WriteCnt(u32 val)
|
||||||
{printf("AES CNT = %08X\n", val);
|
{
|
||||||
u32 oldcnt = Cnt;
|
u32 oldcnt = Cnt;
|
||||||
Cnt = val & 0xFC1FF000;
|
Cnt = val & 0xFC1FF000;
|
||||||
|
|
||||||
@ -294,12 +294,12 @@ void WriteCnt(u32 val)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("AES CNT: %08X / mode=%d key=%d inDMA=%d outDMA=%d blocks=%d\n",
|
//printf("AES CNT: %08X / mode=%d key=%d inDMA=%d outDMA=%d blocks=%d\n",
|
||||||
val, AESMode, (val >> 26) & 0x3, InputDMASize, OutputDMASize, RemBlocks);
|
// val, AESMode, (val >> 26) & 0x3, InputDMASize, OutputDMASize, RemBlocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteBlkCnt(u32 val)
|
void WriteBlkCnt(u32 val)
|
||||||
{printf("AES BLOCK CNT %08X / %d\n", val, val>>16);
|
{
|
||||||
BlkCnt = val;
|
BlkCnt = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,7 +405,7 @@ void Update()
|
|||||||
// CHECKME
|
// CHECKME
|
||||||
Cnt &= ~(1<<21);
|
Cnt &= ~(1<<21);
|
||||||
}
|
}
|
||||||
printf("AES: FINISHED\n");
|
|
||||||
Cnt &= ~(1<<31);
|
Cnt &= ~(1<<31);
|
||||||
if (Cnt & (1<<30)) NDS::SetIRQ2(NDS::IRQ2_DSi_AES);
|
if (Cnt & (1<<30)) NDS::SetIRQ2(NDS::IRQ2_DSi_AES);
|
||||||
DSi::StopNDMAs(1, 0x2A);
|
DSi::StopNDMAs(1, 0x2A);
|
||||||
|
@ -84,7 +84,7 @@ u8 Read(bool last)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("BPTWL: read %02X -> %02X\n", CurPos, Registers[CurPos]);
|
//printf("BPTWL: read %02X -> %02X\n", CurPos, Registers[CurPos]);
|
||||||
return Registers[CurPos++];
|
return Registers[CurPos++];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ void Write(u8 val, bool last)
|
|||||||
Registers[CurPos] = val;
|
Registers[CurPos] = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("BPTWL: write %02X -> %02X\n", CurPos, val);
|
//printf("BPTWL: write %02X -> %02X\n", CurPos, val);
|
||||||
CurPos++; // CHECKME
|
CurPos++; // CHECKME
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +166,7 @@ void Reset()
|
|||||||
|
|
||||||
void WriteCnt(u8 val)
|
void WriteCnt(u8 val)
|
||||||
{
|
{
|
||||||
printf("I2C: write CNT %02X, %08X\n", val, NDS::GetPC(1));
|
//printf("I2C: write CNT %02X, %08X\n", val, NDS::GetPC(1));
|
||||||
|
|
||||||
// TODO: check ACK flag
|
// TODO: check ACK flag
|
||||||
// TODO: transfer delay
|
// TODO: transfer delay
|
||||||
@ -193,7 +193,7 @@ void WriteCnt(u8 val)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("I2C read, device=%02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast);
|
//printf("I2C read, device=%02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -204,7 +204,7 @@ void WriteCnt(u8 val)
|
|||||||
if (val & (1<<1))
|
if (val & (1<<1))
|
||||||
{
|
{
|
||||||
Device = Data & 0xFE;
|
Device = Data & 0xFE;
|
||||||
printf("I2C: %s start, device=%02X\n", (Data&0x01)?"read":"write", Device);
|
//printf("I2C: %s start, device=%02X\n", (Data&0x01)?"read":"write", Device);
|
||||||
|
|
||||||
switch (Device)
|
switch (Device)
|
||||||
{
|
{
|
||||||
@ -219,7 +219,7 @@ void WriteCnt(u8 val)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf("I2C write, device=%02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast);
|
//printf("I2C write, device=%02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast);
|
||||||
|
|
||||||
switch (Device)
|
switch (Device)
|
||||||
{
|
{
|
||||||
@ -243,12 +243,12 @@ void WriteCnt(u8 val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
u8 ReadData()
|
u8 ReadData()
|
||||||
{printf("I2C: read data: %02X\n", Data);
|
{
|
||||||
return Data;
|
return Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteData(u8 val)
|
void WriteData(u8 val)
|
||||||
{printf("I2C: write data: %02X\n", val);
|
{
|
||||||
Data = val;
|
Data = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,6 +515,12 @@ void DSi_NWifi::SendCMD(u8 cmd, u32 param)
|
|||||||
{
|
{
|
||||||
switch (cmd)
|
switch (cmd)
|
||||||
{
|
{
|
||||||
|
case 12:
|
||||||
|
// stop command
|
||||||
|
// CHECKME: does the SDIO controller actually send those??
|
||||||
|
// DSi firmware sets it to send them
|
||||||
|
return;
|
||||||
|
|
||||||
case 52: // IO_RW_DIRECT
|
case 52: // IO_RW_DIRECT
|
||||||
{
|
{
|
||||||
u32 func = (param >> 28) & 0x7;
|
u32 func = (param >> 28) & 0x7;
|
||||||
@ -608,7 +614,7 @@ void DSi_NWifi::ReadBlock()
|
|||||||
TransferAddr &= 0x1FFFF; // checkme
|
TransferAddr &= 0x1FFFF; // checkme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
len = Host->SendData(data, len);
|
len = Host->DataRX(data, len);
|
||||||
|
|
||||||
if (RemSize > 0)
|
if (RemSize > 0)
|
||||||
{
|
{
|
||||||
@ -628,7 +634,7 @@ void DSi_NWifi::WriteBlock()
|
|||||||
len = Host->GetTransferrableLen(len);
|
len = Host->GetTransferrableLen(len);
|
||||||
|
|
||||||
u8 data[0x200];
|
u8 data[0x200];
|
||||||
if (len = Host->ReceiveData(data, len))
|
if (len = Host->DataTX(data, len))
|
||||||
{
|
{
|
||||||
for (u32 i = 0; i < len; i++)
|
for (u32 i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
|
434
src/DSi_SD.cpp
434
src/DSi_SD.cpp
@ -24,7 +24,27 @@
|
|||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
|
|
||||||
|
|
||||||
#define SD_DESC (Num?"SDIO":"SD/MMC")
|
// observed IRQ behavior during transfers
|
||||||
|
//
|
||||||
|
// during reads:
|
||||||
|
// * bit23 is cleared during the first block, always set otherwise. weird
|
||||||
|
// * bit24 (RXRDY) gets set when the FIFO is full
|
||||||
|
//
|
||||||
|
// during reads with FIFO32:
|
||||||
|
// * FIFO16 drains directly into FIFO32
|
||||||
|
// * when bit24 is set, FIFO32 is already full (with contents from the other FIFO)
|
||||||
|
// * reading from an empty FIFO just wraps around (and sets bit21)
|
||||||
|
// * FIFO32 starts filling when bit24 would be set?
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// TX:
|
||||||
|
// * when sending command, if current FIFO full
|
||||||
|
// * upon ContinueTransfer(), if current FIFO full
|
||||||
|
// * -> upon DataTX() if current FIFO full
|
||||||
|
// * when filling FIFO
|
||||||
|
|
||||||
|
|
||||||
|
#define SD_DESC Num?"SDIO":"SD/MMC"
|
||||||
|
|
||||||
|
|
||||||
DSi_SDHost::DSi_SDHost(u32 num)
|
DSi_SDHost::DSi_SDHost(u32 num)
|
||||||
@ -33,6 +53,7 @@ DSi_SDHost::DSi_SDHost(u32 num)
|
|||||||
|
|
||||||
DataFIFO[0] = new FIFO<u16>(0x100);
|
DataFIFO[0] = new FIFO<u16>(0x100);
|
||||||
DataFIFO[1] = new FIFO<u16>(0x100);
|
DataFIFO[1] = new FIFO<u16>(0x100);
|
||||||
|
DataFIFO32 = new FIFO<u32>(0x80);
|
||||||
|
|
||||||
Ports[0] = NULL;
|
Ports[0] = NULL;
|
||||||
Ports[1] = NULL;
|
Ports[1] = NULL;
|
||||||
@ -42,6 +63,7 @@ DSi_SDHost::~DSi_SDHost()
|
|||||||
{
|
{
|
||||||
delete DataFIFO[0];
|
delete DataFIFO[0];
|
||||||
delete DataFIFO[1];
|
delete DataFIFO[1];
|
||||||
|
delete DataFIFO32;
|
||||||
|
|
||||||
if (Ports[0]) delete Ports[0];
|
if (Ports[0]) delete Ports[0];
|
||||||
if (Ports[1]) delete Ports[1];
|
if (Ports[1]) delete Ports[1];
|
||||||
@ -69,6 +91,7 @@ void DSi_SDHost::Reset()
|
|||||||
DataFIFO[0]->Clear();
|
DataFIFO[0]->Clear();
|
||||||
DataFIFO[1]->Clear();
|
DataFIFO[1]->Clear();
|
||||||
CurFIFO = 0;
|
CurFIFO = 0;
|
||||||
|
DataFIFO32->Clear();
|
||||||
|
|
||||||
IRQStatus = 0;
|
IRQStatus = 0;
|
||||||
IRQMask = 0x8B7F031D;
|
IRQMask = 0x8B7F031D;
|
||||||
@ -84,6 +107,8 @@ void DSi_SDHost::Reset()
|
|||||||
BlockLen16 = 0; BlockLen32 = 0;
|
BlockLen16 = 0; BlockLen32 = 0;
|
||||||
StopAction = 0;
|
StopAction = 0;
|
||||||
|
|
||||||
|
TXReq = false;
|
||||||
|
|
||||||
if (Ports[0]) delete Ports[0];
|
if (Ports[0]) delete Ports[0];
|
||||||
if (Ports[1]) delete Ports[1];
|
if (Ports[1]) delete Ports[1];
|
||||||
Ports[0] = NULL;
|
Ports[0] = NULL;
|
||||||
@ -125,8 +150,8 @@ void DSi_SDHost::UpdateData32IRQ()
|
|||||||
oldflags &= (Data32IRQ >> 11);
|
oldflags &= (Data32IRQ >> 11);
|
||||||
|
|
||||||
Data32IRQ &= ~0x0300;
|
Data32IRQ &= ~0x0300;
|
||||||
if (IRQStatus & (1<<24)) Data32IRQ |= (1<<8);
|
if (DataFIFO32->Level() >= (BlockLen32>>2)) Data32IRQ |= (1<<8);
|
||||||
if (!(IRQStatus & (1<<25))) Data32IRQ |= (1<<9);
|
if (!DataFIFO32->IsEmpty()) Data32IRQ |= (1<<9);
|
||||||
|
|
||||||
u32 newflags = ((Data32IRQ >> 8) & 0x1) | (((~Data32IRQ) >> 8) & 0x2);
|
u32 newflags = ((Data32IRQ >> 8) & 0x1) | (((~Data32IRQ) >> 8) & 0x2);
|
||||||
newflags &= (Data32IRQ >> 11);
|
newflags &= (Data32IRQ >> 11);
|
||||||
@ -138,8 +163,6 @@ void DSi_SDHost::UpdateData32IRQ()
|
|||||||
void DSi_SDHost::ClearIRQ(u32 irq)
|
void DSi_SDHost::ClearIRQ(u32 irq)
|
||||||
{
|
{
|
||||||
IRQStatus &= ~(1<<irq);
|
IRQStatus &= ~(1<<irq);
|
||||||
|
|
||||||
if (irq == 24 || irq == 25) UpdateData32IRQ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSi_SDHost::SetIRQ(u32 irq)
|
void DSi_SDHost::SetIRQ(u32 irq)
|
||||||
@ -151,8 +174,6 @@ void DSi_SDHost::SetIRQ(u32 irq)
|
|||||||
|
|
||||||
if ((oldflags == 0) && (newflags != 0))
|
if ((oldflags == 0) && (newflags != 0))
|
||||||
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
|
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
|
||||||
|
|
||||||
if (irq == 24 || irq == 25) UpdateData32IRQ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSi_SDHost::UpdateIRQ(u32 oldmask)
|
void DSi_SDHost::UpdateIRQ(u32 oldmask)
|
||||||
@ -193,28 +214,20 @@ void DSi_SDHost::SendResponse(u32 val, bool last)
|
|||||||
if (last) SetIRQ(0);
|
if (last) SetIRQ(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSi_SDHost::FinishSend(u32 param)
|
void DSi_SDHost::FinishRX(u32 param)
|
||||||
{
|
{
|
||||||
DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC;
|
DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC;
|
||||||
|
|
||||||
host->CurFIFO ^= 1;
|
host->CheckSwapFIFO();
|
||||||
|
|
||||||
host->ClearIRQ(25);
|
if (host->DataMode == 1)
|
||||||
host->SetIRQ(24);
|
host->UpdateFIFO32();
|
||||||
//if (param & 0x2) host->SetIRQ(2);
|
else
|
||||||
|
host->SetIRQ(24);
|
||||||
// TODO: this is an assumption and should eventually be confirmed
|
|
||||||
// Flipnote sets DMA blocklen to 128 words and totallen to 1024 words
|
|
||||||
// so, presumably, DMA should trigger when the FIFO is full
|
|
||||||
// 'full' being when it reaches whatever BlockLen16 is set to, or the
|
|
||||||
// other blocklen register, or when it is actually full (but that makes
|
|
||||||
// less sense)
|
|
||||||
DSi::CheckNDMAs(1, host->Num ? 0x29 : 0x28);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 DSi_SDHost::SendData(u8* data, u32 len)
|
u32 DSi_SDHost::DataRX(u8* data, u32 len)
|
||||||
{
|
{
|
||||||
//printf("%s: data RX, len=%d, blkcnt=%d (%d) blklen=%d, irq=%08X\n", SD_DESC, len, BlockCount16, BlockCountInternal, BlockLen16, IRQMask);
|
|
||||||
if (len != BlockLen16) { printf("!! BAD BLOCKLEN\n"); len = BlockLen16; }
|
if (len != BlockLen16) { printf("!! BAD BLOCKLEN\n"); len = BlockLen16; }
|
||||||
|
|
||||||
bool last = (BlockCountInternal == 0);
|
bool last = (BlockCountInternal == 0);
|
||||||
@ -232,61 +245,89 @@ u32 DSi_SDHost::SendData(u8* data, u32 len)
|
|||||||
// send-command function starts polling IRQ status
|
// send-command function starts polling IRQ status
|
||||||
u32 param = Num | (last << 1);
|
u32 param = Num | (last << 1);
|
||||||
NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer,
|
NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer,
|
||||||
false, 512, FinishSend, param);
|
false, 512, FinishRX, param);
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSi_SDHost::FinishReceive(u32 param)
|
void DSi_SDHost::FinishTX(u32 param)
|
||||||
{
|
{
|
||||||
DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC;
|
DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC;
|
||||||
DSi_SDDevice* dev = host->Ports[host->PortSelect & 0x1];
|
DSi_SDDevice* dev = host->Ports[host->PortSelect & 0x1];
|
||||||
|
|
||||||
host->ClearIRQ(24);
|
if (host->BlockCountInternal == 0)
|
||||||
host->SetIRQ(25);
|
|
||||||
|
|
||||||
if (dev) dev->ContinueTransfer();
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 DSi_SDHost::ReceiveData(u8* data, u32 len)
|
|
||||||
{
|
|
||||||
printf("%s: data TX, len=%d, blkcnt=%d (%d) blklen=%d, irq=%08X\n", SD_DESC, len, BlockCount16, BlockCountInternal, BlockLen16, IRQMask);
|
|
||||||
if (len != BlockLen16) { printf("!! BAD BLOCKLEN\n"); len = BlockLen16; }
|
|
||||||
|
|
||||||
u32 f = CurFIFO;
|
|
||||||
if ((DataFIFO[f]->Level() << 1) < len)
|
|
||||||
{
|
{
|
||||||
printf("%s: FIFO not full enough for a transfer (%d / %d)\n", SD_DESC, DataFIFO[f]->Level()<<1, len);
|
if (host->StopAction & (1<<8))
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
|
||||||
for (u32 i = 0; i < len; i += 2)
|
|
||||||
*(u16*)&data[i] = DataFIFO[f]->Read();
|
|
||||||
|
|
||||||
CurFIFO ^= 1;
|
|
||||||
|
|
||||||
if (BlockCountInternal <= 1)
|
|
||||||
{
|
|
||||||
printf("%s: data TX complete", SD_DESC);
|
|
||||||
|
|
||||||
if (StopAction & (1<<8))
|
|
||||||
{
|
{
|
||||||
printf(", sending CMD12");
|
|
||||||
if (dev) dev->SendCMD(12, 0);
|
if (dev) dev->SendCMD(12, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
// CHECKME: presumably IRQ2 should not trigger here, but rather
|
// CHECKME: presumably IRQ2 should not trigger here, but rather
|
||||||
// when the data transfer is done
|
// when the data transfer is done
|
||||||
//SetIRQ(0);
|
//SetIRQ(0);
|
||||||
SetIRQ(2);
|
host->SetIRQ(2);
|
||||||
|
host->TXReq = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BlockCountInternal--;
|
if (dev) dev->ContinueTransfer();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 DSi_SDHost::DataTX(u8* data, u32 len)
|
||||||
|
{
|
||||||
|
TXReq = true;
|
||||||
|
|
||||||
|
u32 f = CurFIFO;
|
||||||
|
|
||||||
|
if (DataMode == 1)
|
||||||
|
{
|
||||||
|
if ((DataFIFO32->Level() << 2) < len)
|
||||||
|
{
|
||||||
|
if (DataFIFO32->IsEmpty())
|
||||||
|
{
|
||||||
|
SetIRQ(25);
|
||||||
|
DSi::CheckNDMAs(1, Num ? 0x29 : 0x28);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// drain FIFO32 into FIFO16
|
||||||
|
|
||||||
|
if (!DataFIFO[f]->IsEmpty()) printf("VERY BAD!! TRYING TO DRAIN FIFO32 INTO FIFO16 BUT IT CONTAINS SHIT ALREADY\n");
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
u32 f = CurFIFO;
|
||||||
|
if ((DataFIFO[f]->Level() << 1) >= BlockLen16) break;
|
||||||
|
if (DataFIFO32->IsEmpty()) break;
|
||||||
|
|
||||||
|
u32 val = DataFIFO32->Read();
|
||||||
|
DataFIFO[f]->Write(val & 0xFFFF);
|
||||||
|
DataFIFO[f]->Write(val >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateData32IRQ();
|
||||||
|
|
||||||
|
if (BlockCount32 > 1)
|
||||||
|
BlockCount32--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((DataFIFO[f]->Level() << 1) < len)
|
||||||
|
{
|
||||||
|
if (DataFIFO[f]->IsEmpty()) SetIRQ(25);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 i = 0; i < len; i += 2)
|
||||||
|
*(u16*)&data[i] = DataFIFO[f]->Read();
|
||||||
|
|
||||||
|
CurFIFO ^= 1;
|
||||||
|
BlockCountInternal--;
|
||||||
|
|
||||||
|
NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer,
|
||||||
|
false, 512, FinishTX, Num);
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
@ -297,11 +338,55 @@ u32 DSi_SDHost::GetTransferrableLen(u32 len)
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DSi_SDHost::CheckRX()
|
||||||
|
{
|
||||||
|
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
||||||
|
|
||||||
|
CheckSwapFIFO();
|
||||||
|
|
||||||
|
if (BlockCountInternal <= 1)
|
||||||
|
{
|
||||||
|
if (StopAction & (1<<8))
|
||||||
|
{
|
||||||
|
if (dev) dev->SendCMD(12, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECKME: presumably IRQ2 should not trigger here, but rather
|
||||||
|
// when the data transfer is done
|
||||||
|
//SetIRQ(0);
|
||||||
|
SetIRQ(2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BlockCountInternal--;
|
||||||
|
|
||||||
|
if (dev) dev->ContinueTransfer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSi_SDHost::CheckTX()
|
||||||
|
{
|
||||||
|
if (!TXReq) return;
|
||||||
|
|
||||||
|
if (DataMode == 1)
|
||||||
|
{
|
||||||
|
if ((DataFIFO32->Level() << 2) < BlockLen32)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u32 f = CurFIFO;
|
||||||
|
if ((DataFIFO[f]->Level() << 1) < BlockLen16)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
||||||
|
if (dev) dev->ContinueTransfer();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
u16 DSi_SDHost::Read(u32 addr)
|
u16 DSi_SDHost::Read(u32 addr)
|
||||||
{
|
{
|
||||||
if(!Num)printf("SDMMC READ %08X %08X\n", addr, NDS::GetPC(1));
|
|
||||||
|
|
||||||
switch (addr & 0x1FF)
|
switch (addr & 0x1FF)
|
||||||
{
|
{
|
||||||
case 0x000: return Command;
|
case 0x000: return Command;
|
||||||
@ -353,53 +438,7 @@ u16 DSi_SDHost::Read(u32 addr)
|
|||||||
case 0x036: return CardIRQStatus;
|
case 0x036: return CardIRQStatus;
|
||||||
case 0x038: return CardIRQMask;
|
case 0x038: return CardIRQMask;
|
||||||
|
|
||||||
case 0x030: // FIFO16
|
case 0x030: return ReadFIFO16();
|
||||||
{
|
|
||||||
// TODO: decrement BlockLen????
|
|
||||||
|
|
||||||
u32 f = CurFIFO;
|
|
||||||
if (DataFIFO[f]->IsEmpty())
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
|
||||||
u16 ret = DataFIFO[f]->Read();
|
|
||||||
|
|
||||||
if (DataFIFO[f]->IsEmpty())
|
|
||||||
{
|
|
||||||
ClearIRQ(24);
|
|
||||||
|
|
||||||
if (BlockCountInternal <= 1)
|
|
||||||
{
|
|
||||||
printf("%s: data RX complete", SD_DESC);
|
|
||||||
|
|
||||||
if (StopAction & (1<<8))
|
|
||||||
{
|
|
||||||
printf(", sending CMD12");
|
|
||||||
if (dev) dev->SendCMD(12, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
// CHECKME: presumably IRQ2 should not trigger here, but rather
|
|
||||||
// when the data transfer is done
|
|
||||||
//SetIRQ(0);
|
|
||||||
SetIRQ(2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BlockCountInternal--;
|
|
||||||
|
|
||||||
if (dev) dev->ContinueTransfer();
|
|
||||||
}
|
|
||||||
|
|
||||||
SetIRQ(25);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0x0D8: return DataCtl;
|
case 0x0D8: return DataCtl;
|
||||||
|
|
||||||
@ -414,61 +453,52 @@ u16 DSi_SDHost::Read(u32 addr)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u16 DSi_SDHost::ReadFIFO16()
|
||||||
|
{
|
||||||
|
u32 f = CurFIFO;
|
||||||
|
if (DataFIFO[f]->IsEmpty())
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
// on hardware it seems to wrap around. underflow bit is set upon the first 'empty' read.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
||||||
|
u16 ret = DataFIFO[f]->Read();
|
||||||
|
|
||||||
|
if (DataFIFO[f]->IsEmpty())
|
||||||
|
{
|
||||||
|
CheckRX();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
u32 DSi_SDHost::ReadFIFO32()
|
u32 DSi_SDHost::ReadFIFO32()
|
||||||
{
|
{
|
||||||
if (DataMode != 1) return 0;
|
if (DataMode != 1) return 0;
|
||||||
|
|
||||||
// TODO: decrement BlockLen????
|
if (DataFIFO32->IsEmpty())
|
||||||
|
|
||||||
u32 f = CurFIFO;
|
|
||||||
if (DataFIFO[f]->IsEmpty())
|
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
||||||
u32 ret = DataFIFO[f]->Read();
|
u32 ret = DataFIFO32->Read();
|
||||||
ret |= (DataFIFO[f]->Read() << 16);
|
|
||||||
|
|
||||||
if (DataFIFO[f]->IsEmpty())
|
if (DataFIFO32->IsEmpty())
|
||||||
{
|
{
|
||||||
ClearIRQ(24);
|
CheckRX();
|
||||||
|
|
||||||
if (BlockCountInternal <= 1)
|
|
||||||
{
|
|
||||||
printf("%s: data32 RX complete", SD_DESC);
|
|
||||||
|
|
||||||
if (StopAction & (1<<8))
|
|
||||||
{
|
|
||||||
printf(", sending CMD12");
|
|
||||||
if (dev) dev->SendCMD(12, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
// CHECKME: presumably IRQ2 should not trigger here, but rather
|
|
||||||
// when the data transfer is done
|
|
||||||
//SetIRQ(0);
|
|
||||||
SetIRQ(2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BlockCountInternal--;
|
|
||||||
|
|
||||||
if (dev) dev->ContinueTransfer();
|
|
||||||
}
|
|
||||||
|
|
||||||
SetIRQ(25);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateData32IRQ();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSi_SDHost::Write(u32 addr, u16 val)
|
void DSi_SDHost::Write(u32 addr, u16 val)
|
||||||
{
|
{
|
||||||
if(!Num)printf("SDMMC WRITE %08X %04X %08X\n", addr, val, NDS::GetPC(1));
|
|
||||||
|
|
||||||
switch (addr & 0x1FF)
|
switch (addr & 0x1FF)
|
||||||
{
|
{
|
||||||
case 0x000:
|
case 0x000:
|
||||||
@ -528,36 +558,10 @@ void DSi_SDHost::Write(u32 addr, u16 val)
|
|||||||
return;
|
return;
|
||||||
case 0x028: SDOption = val & 0xC1FF; return;
|
case 0x028: SDOption = val & 0xC1FF; return;
|
||||||
|
|
||||||
case 0x030: // FIFO16
|
case 0x030: WriteFIFO16(val); return;
|
||||||
{
|
|
||||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
|
||||||
u32 f = CurFIFO;
|
|
||||||
if (DataFIFO[f]->IsFull())
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
printf("!!!! %s FIFO (16) FULL\n", SD_DESC);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataFIFO[f]->Write(val);
|
|
||||||
|
|
||||||
if (DataFIFO[f]->Level() < (BlockLen16>>1))
|
|
||||||
{
|
|
||||||
ClearIRQ(25);
|
|
||||||
SetIRQ(24);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we completed one block, send it to the SD card
|
|
||||||
// TODO measure the actual delay!!
|
|
||||||
NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer,
|
|
||||||
false, 2048, FinishReceive, Num);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
|
|
||||||
case 0x034:
|
case 0x034:
|
||||||
CardIRQCtl = val & 0x0305;
|
CardIRQCtl = val & 0x0305;
|
||||||
printf("[%d] CardIRQCtl = %04X\n", Num, val);
|
|
||||||
SetCardIRQ();
|
SetCardIRQ();
|
||||||
return;
|
return;
|
||||||
case 0x036:
|
case 0x036:
|
||||||
@ -565,7 +569,6 @@ void DSi_SDHost::Write(u32 addr, u16 val)
|
|||||||
return;
|
return;
|
||||||
case 0x038:
|
case 0x038:
|
||||||
CardIRQMask = val & 0xC007;
|
CardIRQMask = val & 0xC007;
|
||||||
printf("[%d] CardIRQMask = %04X\n", Num, val);
|
|
||||||
SetCardIRQ();
|
SetCardIRQ();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -593,12 +596,7 @@ void DSi_SDHost::Write(u32 addr, u16 val)
|
|||||||
|
|
||||||
case 0x100:
|
case 0x100:
|
||||||
Data32IRQ = (val & 0x1802) | (Data32IRQ & 0x0300);
|
Data32IRQ = (val & 0x1802) | (Data32IRQ & 0x0300);
|
||||||
if (val & (1<<10))
|
if (val & (1<<10)) DataFIFO32->Clear();
|
||||||
{
|
|
||||||
// kind of hacky
|
|
||||||
u32 f = CurFIFO;
|
|
||||||
DataFIFO[f]->Clear();
|
|
||||||
}
|
|
||||||
DataMode = ((DataCtl >> 1) & 0x1) & ((Data32IRQ >> 1) & 0x1);
|
DataMode = ((DataCtl >> 1) & 0x1) & ((Data32IRQ >> 1) & 0x1);
|
||||||
printf("%s: data mode %d-bit\n", SD_DESC, DataMode?32:16);
|
printf("%s: data mode %d-bit\n", SD_DESC, DataMode?32:16);
|
||||||
return;
|
return;
|
||||||
@ -609,35 +607,76 @@ void DSi_SDHost::Write(u32 addr, u16 val)
|
|||||||
printf("unknown %s write %08X %04X\n", SD_DESC, addr, val);
|
printf("unknown %s write %08X %04X\n", SD_DESC, addr, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DSi_SDHost::WriteFIFO16(u16 val)
|
||||||
|
{
|
||||||
|
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
||||||
|
u32 f = CurFIFO;
|
||||||
|
if (DataFIFO[f]->IsFull())
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
printf("!!!! %s FIFO (16) FULL\n", SD_DESC);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFIFO[f]->Write(val);
|
||||||
|
|
||||||
|
CheckTX();
|
||||||
|
}
|
||||||
|
|
||||||
void DSi_SDHost::WriteFIFO32(u32 val)
|
void DSi_SDHost::WriteFIFO32(u32 val)
|
||||||
{
|
{
|
||||||
if (DataMode != 1) return;
|
if (DataMode != 1) return;
|
||||||
|
|
||||||
printf("%s: WRITE FIFO32: LEVEL=%d/%d\n", SD_DESC, DataFIFO[CurFIFO]->Level(), (BlockLen16>>1));
|
if (DataFIFO32->IsFull())
|
||||||
|
|
||||||
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
|
||||||
u32 f = CurFIFO;
|
|
||||||
if (DataFIFO[f]->IsFull())
|
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
printf("!!!! %s FIFO (32) FULL\n", SD_DESC);
|
printf("!!!! %s FIFO (32) FULL\n", SD_DESC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataFIFO[f]->Write(val & 0xFFFF);
|
DataFIFO32->Write(val);
|
||||||
DataFIFO[f]->Write(val >> 16);
|
|
||||||
|
|
||||||
if (DataFIFO[f]->Level() < (BlockLen16>>1))
|
CheckTX();
|
||||||
|
|
||||||
|
UpdateData32IRQ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSi_SDHost::UpdateFIFO32()
|
||||||
|
{
|
||||||
|
// check whether we can drain FIFO32 into FIFO16, or vice versa
|
||||||
|
|
||||||
|
if (DataMode != 1) return;
|
||||||
|
|
||||||
|
if (!DataFIFO32->IsEmpty()) printf("VERY BAD!! TRYING TO DRAIN FIFO16 INTO FIFO32 BUT IT CONTAINS SHIT ALREADY\n");
|
||||||
|
for (;;)
|
||||||
{
|
{
|
||||||
ClearIRQ(25);
|
u32 f = CurFIFO;
|
||||||
SetIRQ(24);
|
if ((DataFIFO32->Level() << 2) >= BlockLen32) break;
|
||||||
return;
|
if (DataFIFO[f]->IsEmpty()) break;
|
||||||
|
|
||||||
|
u32 val = DataFIFO[f]->Read();
|
||||||
|
val |= (DataFIFO[f]->Read() << 16);
|
||||||
|
DataFIFO32->Write(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we completed one block, send it to the SD card
|
UpdateData32IRQ();
|
||||||
// TODO measure the actual delay!!
|
|
||||||
NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer,
|
if ((DataFIFO32->Level() << 2) >= BlockLen32)
|
||||||
false, 2048, FinishReceive, Num);
|
{
|
||||||
|
DSi::CheckNDMAs(1, Num ? 0x29 : 0x28);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSi_SDHost::CheckSwapFIFO()
|
||||||
|
{
|
||||||
|
// check whether we can swap the FIFOs
|
||||||
|
|
||||||
|
u32 f = CurFIFO;
|
||||||
|
bool cur_empty = (DataMode == 1) ? DataFIFO32->IsEmpty() : DataFIFO[f]->IsEmpty();
|
||||||
|
if (cur_empty && ((DataFIFO[f^1]->Level() << 1) >= BlockLen16))
|
||||||
|
{
|
||||||
|
CurFIFO ^= 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -813,7 +852,7 @@ void DSi_MMCStorage::SendACMD(u8 cmd, u32 param)
|
|||||||
|
|
||||||
case 13: // get SSR
|
case 13: // get SSR
|
||||||
Host->SendResponse(CSR, true);
|
Host->SendResponse(CSR, true);
|
||||||
Host->SendData(SSR, 64);
|
Host->DataRX(SSR, 64);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 41: // set operating conditions
|
case 41: // set operating conditions
|
||||||
@ -834,7 +873,7 @@ void DSi_MMCStorage::SendACMD(u8 cmd, u32 param)
|
|||||||
|
|
||||||
case 51: // get SCR
|
case 51: // get SCR
|
||||||
Host->SendResponse(CSR, true);
|
Host->SendResponse(CSR, true);
|
||||||
Host->SendData(SCR, 8);
|
Host->DataRX(SCR, 8);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -863,8 +902,6 @@ void DSi_MMCStorage::ContinueTransfer()
|
|||||||
|
|
||||||
u32 DSi_MMCStorage::ReadBlock(u64 addr)
|
u32 DSi_MMCStorage::ReadBlock(u64 addr)
|
||||||
{
|
{
|
||||||
//printf("SD/MMC: reading block @ %08X, len=%08X\n", addr, BlockSize);
|
|
||||||
|
|
||||||
u32 len = BlockSize;
|
u32 len = BlockSize;
|
||||||
len = Host->GetTransferrableLen(len);
|
len = Host->GetTransferrableLen(len);
|
||||||
|
|
||||||
@ -874,18 +911,17 @@ u32 DSi_MMCStorage::ReadBlock(u64 addr)
|
|||||||
fseek(File, addr, SEEK_SET);
|
fseek(File, addr, SEEK_SET);
|
||||||
fread(data, 1, len, File);
|
fread(data, 1, len, File);
|
||||||
}
|
}
|
||||||
return Host->SendData(data, len);
|
|
||||||
|
return Host->DataRX(data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 DSi_MMCStorage::WriteBlock(u64 addr)
|
u32 DSi_MMCStorage::WriteBlock(u64 addr)
|
||||||
{
|
{
|
||||||
printf("SD/MMC: write block @ %08X, len=%08X\n", addr, BlockSize);
|
|
||||||
|
|
||||||
u32 len = BlockSize;
|
u32 len = BlockSize;
|
||||||
len = Host->GetTransferrableLen(len);
|
len = Host->GetTransferrableLen(len);
|
||||||
|
|
||||||
u8 data[0x200];
|
u8 data[0x200];
|
||||||
if (len = Host->ReceiveData(data, len))
|
if (len = Host->DataTX(data, len))
|
||||||
{
|
{
|
||||||
if (File)
|
if (File)
|
||||||
{
|
{
|
||||||
|
18
src/DSi_SD.h
18
src/DSi_SD.h
@ -36,20 +36,29 @@ public:
|
|||||||
|
|
||||||
void DoSavestate(Savestate* file);
|
void DoSavestate(Savestate* file);
|
||||||
|
|
||||||
static void FinishSend(u32 param);
|
static void FinishRX(u32 param);
|
||||||
static void FinishReceive(u32 param);
|
static void FinishTX(u32 param);
|
||||||
void SendResponse(u32 val, bool last);
|
void SendResponse(u32 val, bool last);
|
||||||
u32 SendData(u8* data, u32 len);
|
u32 DataRX(u8* data, u32 len);
|
||||||
u32 ReceiveData(u8* data, u32 len);
|
u32 DataTX(u8* data, u32 len);
|
||||||
u32 GetTransferrableLen(u32 len);
|
u32 GetTransferrableLen(u32 len);
|
||||||
|
|
||||||
|
void CheckRX();
|
||||||
|
void CheckTX();
|
||||||
|
bool TXReq;
|
||||||
|
|
||||||
void SetCardIRQ();
|
void SetCardIRQ();
|
||||||
|
|
||||||
u16 Read(u32 addr);
|
u16 Read(u32 addr);
|
||||||
void Write(u32 addr, u16 val);
|
void Write(u32 addr, u16 val);
|
||||||
|
u16 ReadFIFO16();
|
||||||
|
void WriteFIFO16(u16 val);
|
||||||
u32 ReadFIFO32();
|
u32 ReadFIFO32();
|
||||||
void WriteFIFO32(u32 val);
|
void WriteFIFO32(u32 val);
|
||||||
|
|
||||||
|
void UpdateFIFO32();
|
||||||
|
void CheckSwapFIFO();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u32 Num;
|
u32 Num;
|
||||||
|
|
||||||
@ -78,6 +87,7 @@ private:
|
|||||||
|
|
||||||
FIFO<u16>* DataFIFO[2];
|
FIFO<u16>* DataFIFO[2];
|
||||||
u32 CurFIFO; // FIFO accessible for read/write
|
u32 CurFIFO; // FIFO accessible for read/write
|
||||||
|
FIFO<u32>* DataFIFO32;
|
||||||
|
|
||||||
DSi_SDDevice* Ports[2];
|
DSi_SDDevice* Ports[2];
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user