diff options
Diffstat (limited to 'src/DSi_SD.cpp')
-rw-r--r-- | src/DSi_SD.cpp | 935 |
1 files changed, 935 insertions, 0 deletions
diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp new file mode 100644 index 0000000..7fdba0f --- /dev/null +++ b/src/DSi_SD.cpp @@ -0,0 +1,935 @@ +/* + Copyright 2016-2019 Arisotura + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include <stdio.h> +#include <string.h> +#include "DSi.h" +#include "DSi_SD.h" +#include "DSi_NWifi.h" +#include "Platform.h" +#include "Config.h" + + +// 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) +{ + Num = num; + + DataFIFO[0] = new FIFO<u16>(0x100); + DataFIFO[1] = new FIFO<u16>(0x100); + DataFIFO32 = new FIFO<u32>(0x80); + + Ports[0] = NULL; + Ports[1] = NULL; +} + +DSi_SDHost::~DSi_SDHost() +{ + delete DataFIFO[0]; + delete DataFIFO[1]; + delete DataFIFO32; + + if (Ports[0]) delete Ports[0]; + if (Ports[1]) delete Ports[1]; +} + +void DSi_SDHost::Reset() +{ + if (Num == 0) + { + PortSelect = 0x0200; // CHECKME + } + else + { + PortSelect = 0x0100; // CHECKME + } + + SoftReset = 0x0007; // CHECKME + SDClock = 0; + SDOption = 0; + + Command = 0; + Param = 0; + memset(ResponseBuffer, 0, sizeof(ResponseBuffer)); + + DataFIFO[0]->Clear(); + DataFIFO[1]->Clear(); + CurFIFO = 0; + DataFIFO32->Clear(); + + IRQStatus = 0; + IRQMask = 0x8B7F031D; + + CardIRQStatus = 0; + CardIRQMask = 0xC007; + CardIRQCtl = 0; + + DataCtl = 0; + Data32IRQ = 0; + DataMode = 0; + BlockCount16 = 0; BlockCount32 = 0; BlockCountInternal = 0; + BlockLen16 = 0; BlockLen32 = 0; + StopAction = 0; + + TXReq = false; + + if (Ports[0]) delete Ports[0]; + if (Ports[1]) delete Ports[1]; + Ports[0] = NULL; + Ports[1] = NULL; + + if (Num == 0) + { + // TODO: eventually pull from host filesystem + /*DSi_MMCStorage* sd = new DSi_MMCStorage(this, false, "sd.bin"); + u8 sd_cid[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00}; + sd->SetCID(sd_cid);*/ + DSi_MMCStorage* sd = NULL; + + DSi_MMCStorage* mmc = new DSi_MMCStorage(this, true, Config::DSiNANDPath); + mmc->SetCID(DSi::eMMC_CID); + + Ports[0] = sd; + Ports[1] = mmc; + } + else + { + DSi_NWifi* nwifi = new DSi_NWifi(this); + + Ports[0] = nwifi; + } +} + +void DSi_SDHost::DoSavestate(Savestate* file) +{ + // TODO! +} + + +void DSi_SDHost::UpdateData32IRQ() +{ + if (DataMode == 0) return; + + u32 oldflags = ((Data32IRQ >> 8) & 0x1) | (((~Data32IRQ) >> 8) & 0x2); + oldflags &= (Data32IRQ >> 11); + + Data32IRQ &= ~0x0300; + if (DataFIFO32->Level() >= (BlockLen32>>2)) Data32IRQ |= (1<<8); + if (!DataFIFO32->IsEmpty()) Data32IRQ |= (1<<9); + + u32 newflags = ((Data32IRQ >> 8) & 0x1) | (((~Data32IRQ) >> 8) & 0x2); + newflags &= (Data32IRQ >> 11); + + if ((oldflags == 0) && (newflags != 0)) + NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); +} + +void DSi_SDHost::ClearIRQ(u32 irq) +{ + IRQStatus &= ~(1<<irq); +} + +void DSi_SDHost::SetIRQ(u32 irq) +{ + u32 oldflags = IRQStatus & ~IRQMask; + + IRQStatus |= (1<<irq); + u32 newflags = IRQStatus & ~IRQMask; + + if ((oldflags == 0) && (newflags != 0)) + NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); +} + +void DSi_SDHost::UpdateIRQ(u32 oldmask) +{ + u32 oldflags = IRQStatus & ~oldmask; + u32 newflags = IRQStatus & ~IRQMask; + + if ((oldflags == 0) && (newflags != 0)) + NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); +} + +void DSi_SDHost::SetCardIRQ() +{ + if (!(CardIRQCtl & (1<<0))) return; + + u16 oldflags = CardIRQStatus & ~CardIRQMask; + DSi_SDDevice* dev = Ports[PortSelect & 0x1]; + + if (dev->IRQ) CardIRQStatus |= (1<<0); + else CardIRQStatus &= ~(1<<0); + + u16 newflags = CardIRQStatus & ~CardIRQMask; + + if ((oldflags == 0) && (newflags != 0)) // checkme + { + NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); + NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO_Data1 : NDS::IRQ2_DSi_SD_Data1); + } +} + +void DSi_SDHost::SendResponse(u32 val, bool last) +{ + *(u32*)&ResponseBuffer[6] = *(u32*)&ResponseBuffer[4]; + *(u32*)&ResponseBuffer[4] = *(u32*)&ResponseBuffer[2]; + *(u32*)&ResponseBuffer[2] = *(u32*)&ResponseBuffer[0]; + *(u32*)&ResponseBuffer[0] = val; + + if (last) SetIRQ(0); +} + +void DSi_SDHost::FinishRX(u32 param) +{ + DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC; + + host->CheckSwapFIFO(); + + if (host->DataMode == 1) + host->UpdateFIFO32(); + else + host->SetIRQ(24); +} + +u32 DSi_SDHost::DataRX(u8* data, u32 len) +{ + if (len != BlockLen16) { printf("!! BAD BLOCKLEN\n"); len = BlockLen16; } + + bool last = (BlockCountInternal == 0); + + u32 f = CurFIFO ^ 1; + for (u32 i = 0; i < len; i += 2) + DataFIFO[f]->Write(*(u16*)&data[i]); + + //CurFIFO = f; + //SetIRQ(24); + // TODO: determine what the delay should be! + // for now, this is a placeholder + // we need a delay because DSi boot2 will send a command and then wait for IRQ0 + // but if IRQ24 is thrown instantly, the handler clears IRQ0 before the + // send-command function starts polling IRQ status + u32 param = Num | (last << 1); + NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, + false, 512, FinishRX, param); + + return len; +} + +void DSi_SDHost::FinishTX(u32 param) +{ + DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC; + DSi_SDDevice* dev = host->Ports[host->PortSelect & 0x1]; + + if (host->BlockCountInternal == 0) + { + if (host->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); + host->SetIRQ(2); + host->TXReq = false; + } + else + { + 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; +} + +u32 DSi_SDHost::GetTransferrableLen(u32 len) +{ + if (len > BlockLen16) len = BlockLen16; // checkme + 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) +{ + switch (addr & 0x1FF) + { + case 0x000: return Command; + case 0x002: return PortSelect & 0x030F; + case 0x004: return Param & 0xFFFF; + case 0x006: return Param >> 16; + + case 0x008: return StopAction; + case 0x00A: return BlockCount16; + + case 0x00C: return ResponseBuffer[0]; + case 0x00E: return ResponseBuffer[1]; + case 0x010: return ResponseBuffer[2]; + case 0x012: return ResponseBuffer[3]; + case 0x014: return ResponseBuffer[4]; + case 0x016: return ResponseBuffer[5]; + case 0x018: return ResponseBuffer[6]; + case 0x01A: return ResponseBuffer[7]; + + case 0x01C: + { + u16 ret = (IRQStatus & 0x031D); + + if (!Num) + { + if (Ports[0]) // basic check of whether the SD card is inserted + ret |= 0x0030; + else + ret |= 0x0008; + } + else + { + // SDIO wifi is always inserted, I guess + ret |= 0x0030; + } + return ret; + } + case 0x01E: return ((IRQStatus >> 16) & 0x8B7F); + case 0x020: return IRQMask & 0x031D; + case 0x022: return (IRQMask >> 16) & 0x8B7F; + + case 0x024: return SDClock; + case 0x026: return BlockLen16; + case 0x028: return SDOption; + + case 0x02C: return 0; // TODO + + case 0x034: return CardIRQCtl; + case 0x036: return CardIRQStatus; + case 0x038: return CardIRQMask; + + case 0x030: return ReadFIFO16(); + + case 0x0D8: return DataCtl; + + case 0x0E0: return SoftReset; + + case 0x0F6: return 0; // MMC write protect (always 0) + + case 0x100: return Data32IRQ; + case 0x104: return BlockLen32; + case 0x108: return BlockCount32; + } + + printf("unknown %s read %08X @ %08X\n", SD_DESC, addr, NDS::GetPC(1)); + 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() +{ + if (DataMode != 1) return 0; + + if (DataFIFO32->IsEmpty()) + { + // TODO + return 0; + } + + DSi_SDDevice* dev = Ports[PortSelect & 0x1]; + u32 ret = DataFIFO32->Read(); + + if (DataFIFO32->IsEmpty()) + { + CheckRX(); + } + + UpdateData32IRQ(); + + return ret; +} + +void DSi_SDHost::Write(u32 addr, u16 val) +{ + switch (addr & 0x1FF) + { + case 0x000: + { + Command = val; + u8 cmd = Command & 0x3F; + + DSi_SDDevice* dev = Ports[PortSelect & 0x1]; + if (dev) + { + // CHECKME + // "Setting Command Type to "ACMD" is automatically sending an APP_CMD prefix prior to the command number" + // except DSi boot2 manually sends an APP_CMD prefix AND sets the next command to be ACMD + switch ((Command >> 6) & 0x3) + { + case 0: dev->SendCMD(cmd, Param); break; + case 1: /*dev->SendCMD(55, 0);*/ dev->SendCMD(cmd, Param); break; + default: + printf("%s: unknown command type %d, %02X %08X\n", SD_DESC, (Command>>6)&0x3, cmd, Param); + break; + } + } + else printf("%s: SENDING CMD %04X TO NULL DEVICE\n", SD_DESC, val); + } + return; + + case 0x002: PortSelect = (val & 0x040F) | (PortSelect & 0x0300); return; + case 0x004: Param = (Param & 0xFFFF0000) | val; return; + case 0x006: Param = (Param & 0x0000FFFF) | (val << 16); return; + + case 0x008: StopAction = val & 0x0101; return; + case 0x00A: BlockCount16 = val; BlockCountInternal = val; return; + + case 0x01C: IRQStatus &= (val | 0xFFFF0000); return; + case 0x01E: IRQStatus &= ((val << 16) | 0xFFFF); return; + case 0x020: + { + u32 oldmask = IRQMask; + IRQMask = (IRQMask & 0x8B7F0000) | (val & 0x031D); + UpdateIRQ(oldmask); + } + return; + case 0x022: + { + u32 oldmask = IRQMask; + IRQMask = (IRQMask & 0x0000031D) | ((val & 0x8B7F) << 16); + UpdateIRQ(oldmask); + if (!DataFIFO[CurFIFO]->IsEmpty()) SetIRQ(24); // checkme + if (DataFIFO[CurFIFO]->IsEmpty()) SetIRQ(25); // checkme + } + return; + + case 0x024: SDClock = val & 0x03FF; return; + case 0x026: + BlockLen16 = val & 0x03FF; + if (BlockLen16 > 0x200) BlockLen16 = 0x200; + return; + case 0x028: SDOption = val & 0xC1FF; return; + + case 0x030: WriteFIFO16(val); return; + + case 0x034: + CardIRQCtl = val & 0x0305; + SetCardIRQ(); + return; + case 0x036: + CardIRQStatus &= val; + return; + case 0x038: + CardIRQMask = val & 0xC007; + SetCardIRQ(); + return; + + case 0x0D8: + DataCtl = (val & 0x0022); + DataMode = ((DataCtl >> 1) & 0x1) & ((Data32IRQ >> 1) & 0x1); + return; + + case 0x0E0: + if ((SoftReset & 0x0001) && !(val & 0x0001)) + { + printf("%s: RESET\n", SD_DESC); + StopAction = 0; + memset(ResponseBuffer, 0, sizeof(ResponseBuffer)); + IRQStatus = 0; + // TODO: ERROR_DETAIL_STATUS + SDClock &= ~0x0500; + SDOption = 0x40EE; + // TODO: CARD_IRQ_STAT + // TODO: FIFO16 shit + } + SoftReset = 0x0006 | (val & 0x0001); + return; + + case 0x100: + Data32IRQ = (val & 0x1802) | (Data32IRQ & 0x0300); + if (val & (1<<10)) DataFIFO32->Clear(); + DataMode = ((DataCtl >> 1) & 0x1) & ((Data32IRQ >> 1) & 0x1); + return; + case 0x104: BlockLen32 = val & 0x03FF; return; + case 0x108: BlockCount32 = val; return; + } + + 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) +{ + if (DataMode != 1) return; + + if (DataFIFO32->IsFull()) + { + // TODO + printf("!!!! %s FIFO (32) FULL\n", SD_DESC); + return; + } + + DataFIFO32->Write(val); + + 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 (;;) + { + u32 f = CurFIFO; + if ((DataFIFO32->Level() << 2) >= BlockLen32) break; + if (DataFIFO[f]->IsEmpty()) break; + + u32 val = DataFIFO[f]->Read(); + val |= (DataFIFO[f]->Read() << 16); + DataFIFO32->Write(val); + } + + UpdateData32IRQ(); + + if ((DataFIFO32->Level() << 2) >= BlockLen32) + { + 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; + } +} + + +#define MMC_DESC (Internal?"NAND":"SDcard") + +DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const char* path) : DSi_SDDevice(host) +{ + Internal = internal; + strncpy(FilePath, path, 1023); FilePath[1023] = '\0'; + + File = Platform::OpenLocalFile(path, "r+b"); + if (!File) + { + if (internal) + { + // TODO: proper failure + printf("!! MMC file %s does not exist\n", path); + } + else + { + File = Platform::OpenLocalFile(path, "w+b"); + } + } + + CSR = 0x00000100; // checkme + + // TODO: busy bit + // TODO: SDHC/SDXC bit + OCR = 0x80FF8000; + + // TODO: customize based on card size etc + u8 csd_template[16] = {0x40, 0x40, 0x96, 0xE9, 0x7F, 0xDB, 0xF6, 0xDF, 0x01, 0x59, 0x0F, 0x2A, 0x01, 0x26, 0x90, 0x00}; + memcpy(CSD, csd_template, 16); + + // checkme + memset(SCR, 0, 8); + *(u32*)&SCR[0] = 0x012A0000; + + memset(SSR, 0, 64); + + BlockSize = 0; + RWAddress = 0; + RWCommand = 0; +} + +DSi_MMCStorage::~DSi_MMCStorage() +{ + if (File) fclose(File); +} + +void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) +{ + if (CSR & (1<<5)) + { + CSR &= ~(1<<5); + return SendACMD(cmd, param); + } + + switch (cmd) + { + case 0: // reset/etc + Host->SendResponse(CSR, true); + return; + + case 2: + case 10: // get CID + Host->SendResponse(*(u32*)&CID[12], false); + Host->SendResponse(*(u32*)&CID[8], false); + Host->SendResponse(*(u32*)&CID[4], false); + Host->SendResponse(*(u32*)&CID[0], true); + if (cmd == 2) SetState(0x02); + return; + + case 3: // get/set RCA + if (Internal) + { + RCA = param >> 16; + Host->SendResponse(CSR|0x10000, true); // huh?? + } + else + { + // TODO + printf("CMD3 on SD card: TODO\n"); + Host->SendResponse((CSR & 0x1FFF) | ((CSR >> 6) & 0x2000) | ((CSR >> 8) & 0xC000) | (1 << 16), true); + } + return; + + case 7: // select card (by RCA) + Host->SendResponse(CSR, true); + return; + + case 8: // set voltage + Host->SendResponse(param, true); + return; + + case 9: // get CSD + Host->SendResponse(*(u32*)&CSD[12], false); + Host->SendResponse(*(u32*)&CSD[8], false); + Host->SendResponse(*(u32*)&CSD[4], false); + Host->SendResponse(*(u32*)&CSD[0], true); + return; + + case 12: // stop operation + SetState(0x04); + if (File) fflush(File); + RWCommand = 0; + Host->SendResponse(CSR, true); + return; + + case 13: // get status + Host->SendResponse(CSR, true); + return; + + case 16: // set block size + BlockSize = param; + if (BlockSize > 0x200) + { + // TODO! raise error + printf("!! SD/MMC: BAD BLOCK LEN %d\n", BlockSize); + BlockSize = 0x200; + } + SetState(0x04); // CHECKME + Host->SendResponse(CSR, true); + return; + + case 18: // read multiple blocks + //printf("READ_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize); + RWAddress = param; + if (OCR & (1<<30)) + { + RWAddress <<= 9; + BlockSize = 512; + } + RWCommand = 18; + Host->SendResponse(CSR, true); + ReadBlock(RWAddress); + RWAddress += BlockSize; + SetState(0x05); + return; + + case 25: // write multiple blocks + //printf("WRITE_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize); + RWAddress = param; + if (OCR & (1<<30)) + { + RWAddress <<= 9; + BlockSize = 512; + } + RWCommand = 25; + Host->SendResponse(CSR, true); + WriteBlock(RWAddress); + RWAddress += BlockSize; + SetState(0x04); + return; + + case 55: // appcmd prefix + CSR |= (1<<5); + Host->SendResponse(CSR, true); + return; + } + + printf("MMC: unknown CMD %d %08X\n", cmd, param); +} + +void DSi_MMCStorage::SendACMD(u8 cmd, u32 param) +{ + switch (cmd) + { + case 6: // set bus width (TODO?) + //printf("SET BUS WIDTH %08X\n", param); + Host->SendResponse(CSR, true); + return; + + case 13: // get SSR + Host->SendResponse(CSR, true); + Host->DataRX(SSR, 64); + return; + + case 41: // set operating conditions + // CHECKME: + // DSi boot2 sets this to 0x40100000 (hardcoded) + // then has two codepaths depending on whether bit30 did get set + // is it settable at all on the MMC? probably not. + if (Internal) param &= ~(1<<30); + OCR &= 0xBF000000; + OCR |= (param & 0x40FFFFFF); + Host->SendResponse(OCR, true); + SetState(0x01); + return; + + case 42: // ??? + Host->SendResponse(CSR, true); + return; + + case 51: // get SCR + Host->SendResponse(CSR, true); + Host->DataRX(SCR, 8); + return; + } + + printf("MMC: unknown ACMD %d %08X\n", cmd, param); +} + +void DSi_MMCStorage::ContinueTransfer() +{ + if (RWCommand == 0) return; + + u32 len = 0; + + switch (RWCommand) + { + case 18: + len = ReadBlock(RWAddress); + break; + + case 25: + len = WriteBlock(RWAddress); + break; + } + + RWAddress += len; +} + +u32 DSi_MMCStorage::ReadBlock(u64 addr) +{ + u32 len = BlockSize; + len = Host->GetTransferrableLen(len); + + u8 data[0x200]; + if (File) + { + fseek(File, addr, SEEK_SET); + fread(data, 1, len, File); + } + + return Host->DataRX(data, len); +} + +u32 DSi_MMCStorage::WriteBlock(u64 addr) +{ + u32 len = BlockSize; + len = Host->GetTransferrableLen(len); + + u8 data[0x200]; + if (len = Host->DataTX(data, len)) + { + if (File) + { + fseek(File, addr, SEEK_SET); + fwrite(data, 1, len, File); + } + } + + return len; +} |