2019-06-16 15:01:49 +00:00
|
|
|
/*
|
2020-06-05 18:44:18 +00:00
|
|
|
Copyright 2016-2020 Arisotura
|
2019-06-16 15:01:49 +00:00
|
|
|
|
|
|
|
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"
|
2019-07-02 22:07:51 +00:00
|
|
|
#include "DSi_NWifi.h"
|
2019-06-17 11:24:37 +00:00
|
|
|
#include "Platform.h"
|
2020-06-02 12:36:15 +00:00
|
|
|
#include "Config.h"
|
2019-06-16 15:01:49 +00:00
|
|
|
|
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
// 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"
|
2019-06-17 11:24:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
DSi_SDHost::DSi_SDHost(u32 num)
|
2019-06-16 15:01:49 +00:00
|
|
|
{
|
|
|
|
Num = num;
|
2019-06-17 11:24:37 +00:00
|
|
|
|
|
|
|
Ports[0] = NULL;
|
|
|
|
Ports[1] = NULL;
|
2019-06-16 15:01:49 +00:00
|
|
|
}
|
|
|
|
|
2019-06-17 11:24:37 +00:00
|
|
|
DSi_SDHost::~DSi_SDHost()
|
2019-06-16 15:01:49 +00:00
|
|
|
{
|
2019-06-17 11:24:37 +00:00
|
|
|
if (Ports[0]) delete Ports[0];
|
|
|
|
if (Ports[1]) delete Ports[1];
|
2019-06-16 15:01:49 +00:00
|
|
|
}
|
|
|
|
|
2019-06-17 11:24:37 +00:00
|
|
|
void DSi_SDHost::Reset()
|
2019-06-16 15:01:49 +00:00
|
|
|
{
|
|
|
|
if (Num == 0)
|
|
|
|
{
|
|
|
|
PortSelect = 0x0200; // CHECKME
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PortSelect = 0x0100; // CHECKME
|
|
|
|
}
|
2019-06-17 11:24:37 +00:00
|
|
|
|
|
|
|
SoftReset = 0x0007; // CHECKME
|
2019-06-17 16:40:45 +00:00
|
|
|
SDClock = 0;
|
2019-06-18 12:12:37 +00:00
|
|
|
SDOption = 0;
|
2019-06-17 11:24:37 +00:00
|
|
|
|
|
|
|
Command = 0;
|
|
|
|
Param = 0;
|
|
|
|
memset(ResponseBuffer, 0, sizeof(ResponseBuffer));
|
|
|
|
|
2020-12-30 22:37:46 +00:00
|
|
|
DataFIFO[0].Clear();
|
|
|
|
DataFIFO[1].Clear();
|
2019-06-18 12:12:37 +00:00
|
|
|
CurFIFO = 0;
|
2020-12-30 22:37:46 +00:00
|
|
|
DataFIFO32.Clear();
|
2019-06-18 12:12:37 +00:00
|
|
|
|
2019-06-17 11:24:37 +00:00
|
|
|
IRQStatus = 0;
|
|
|
|
IRQMask = 0x8B7F031D;
|
|
|
|
|
2019-08-04 00:16:16 +00:00
|
|
|
CardIRQStatus = 0;
|
|
|
|
CardIRQMask = 0xC007;
|
|
|
|
CardIRQCtl = 0;
|
|
|
|
|
2019-06-18 12:12:37 +00:00
|
|
|
DataCtl = 0;
|
|
|
|
Data32IRQ = 0;
|
|
|
|
DataMode = 0;
|
|
|
|
BlockCount16 = 0; BlockCount32 = 0; BlockCountInternal = 0;
|
|
|
|
BlockLen16 = 0; BlockLen32 = 0;
|
|
|
|
StopAction = 0;
|
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
TXReq = false;
|
|
|
|
|
2019-06-17 11:24:37 +00:00
|
|
|
if (Ports[0]) delete Ports[0];
|
|
|
|
if (Ports[1]) delete Ports[1];
|
2020-06-15 11:39:33 +00:00
|
|
|
Ports[0] = nullptr;
|
|
|
|
Ports[1] = nullptr;
|
2019-06-17 11:24:37 +00:00
|
|
|
|
|
|
|
if (Num == 0)
|
|
|
|
{
|
2020-08-24 22:34:57 +00:00
|
|
|
DSi_MMCStorage* sd;
|
|
|
|
DSi_MMCStorage* mmc;
|
2019-10-19 14:03:59 +00:00
|
|
|
|
2020-08-24 22:34:57 +00:00
|
|
|
if (Config::DSiSDEnable)
|
|
|
|
{
|
2021-01-17 18:56:02 +00:00
|
|
|
sd = new DSi_MMCStorage(this, false, DSi::SDIOFile);
|
2020-08-24 22:34:57 +00:00
|
|
|
u8 sd_cid[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00};
|
|
|
|
sd->SetCID(sd_cid);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sd = nullptr;
|
|
|
|
|
2021-01-17 18:56:02 +00:00
|
|
|
mmc = new DSi_MMCStorage(this, true, DSi::SDMMCFile);
|
2019-06-17 16:40:45 +00:00
|
|
|
mmc->SetCID(DSi::eMMC_CID);
|
|
|
|
|
2019-10-19 14:03:59 +00:00
|
|
|
Ports[0] = sd;
|
2019-06-17 16:40:45 +00:00
|
|
|
Ports[1] = mmc;
|
2019-06-17 11:24:37 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-07-02 22:07:51 +00:00
|
|
|
DSi_NWifi* nwifi = new DSi_NWifi(this);
|
|
|
|
|
|
|
|
Ports[0] = nwifi;
|
2019-06-17 11:24:37 +00:00
|
|
|
}
|
2020-06-15 11:39:33 +00:00
|
|
|
|
|
|
|
if (Ports[0]) Ports[0]->Reset();
|
|
|
|
if (Ports[1]) Ports[1]->Reset();
|
2019-06-16 15:01:49 +00:00
|
|
|
}
|
|
|
|
|
2019-06-17 11:24:37 +00:00
|
|
|
void DSi_SDHost::DoSavestate(Savestate* file)
|
2019-06-16 15:01:49 +00:00
|
|
|
{
|
|
|
|
// TODO!
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-06-18 16:39:13 +00:00
|
|
|
void DSi_SDHost::UpdateData32IRQ()
|
|
|
|
{
|
|
|
|
if (DataMode == 0) return;
|
|
|
|
|
|
|
|
u32 oldflags = ((Data32IRQ >> 8) & 0x1) | (((~Data32IRQ) >> 8) & 0x2);
|
|
|
|
oldflags &= (Data32IRQ >> 11);
|
|
|
|
|
|
|
|
Data32IRQ &= ~0x0300;
|
2020-12-30 22:37:46 +00:00
|
|
|
if (DataFIFO32.Level() >= (BlockLen32>>2)) Data32IRQ |= (1<<8);
|
|
|
|
if (!DataFIFO32.IsEmpty()) Data32IRQ |= (1<<9);
|
2019-06-18 16:39:13 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-06-18 12:12:37 +00:00
|
|
|
void DSi_SDHost::ClearIRQ(u32 irq)
|
|
|
|
{
|
|
|
|
IRQStatus &= ~(1<<irq);
|
|
|
|
}
|
|
|
|
|
2019-06-17 11:24:37 +00:00
|
|
|
void DSi_SDHost::SetIRQ(u32 irq)
|
2019-06-16 15:01:49 +00:00
|
|
|
{
|
2019-06-17 11:24:37 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-08-07 10:57:12 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-08-04 00:16:16 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-15 11:39:33 +00:00
|
|
|
void DSi_SDHost::UpdateCardIRQ(u16 oldmask)
|
|
|
|
{
|
|
|
|
u16 oldflags = CardIRQStatus & ~oldmask;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-17 11:24:37 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
void DSi_SDHost::FinishRX(u32 param)
|
2019-06-18 12:12:37 +00:00
|
|
|
{
|
|
|
|
DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC;
|
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
host->CheckSwapFIFO();
|
2019-06-18 12:12:37 +00:00
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
if (host->DataMode == 1)
|
|
|
|
host->UpdateFIFO32();
|
|
|
|
else
|
|
|
|
host->SetIRQ(24);
|
2019-06-18 12:12:37 +00:00
|
|
|
}
|
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
u32 DSi_SDHost::DataRX(u8* data, u32 len)
|
2019-06-18 12:12:37 +00:00
|
|
|
{
|
2019-08-04 00:16:16 +00:00
|
|
|
if (len != BlockLen16) { printf("!! BAD BLOCKLEN\n"); len = BlockLen16; }
|
2019-06-18 12:12:37 +00:00
|
|
|
|
2019-06-18 16:39:13 +00:00
|
|
|
bool last = (BlockCountInternal == 0);
|
|
|
|
|
2019-06-18 12:12:37 +00:00
|
|
|
u32 f = CurFIFO ^ 1;
|
|
|
|
for (u32 i = 0; i < len; i += 2)
|
2020-12-30 22:37:46 +00:00
|
|
|
DataFIFO[f].Write(*(u16*)&data[i]);
|
2019-06-18 12:12:37 +00:00
|
|
|
|
|
|
|
//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);
|
2019-07-24 16:48:52 +00:00
|
|
|
NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer,
|
2020-06-01 14:24:59 +00:00
|
|
|
false, 512, FinishRX, param);
|
2019-08-04 00:16:16 +00:00
|
|
|
|
|
|
|
return len;
|
2019-06-18 12:12:37 +00:00
|
|
|
}
|
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
void DSi_SDHost::FinishTX(u32 param)
|
2019-07-02 21:46:39 +00:00
|
|
|
{
|
|
|
|
DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC;
|
|
|
|
DSi_SDDevice* dev = host->Ports[host->PortSelect & 0x1];
|
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
if (host->BlockCountInternal == 0)
|
|
|
|
{
|
|
|
|
if (host->StopAction & (1<<8))
|
|
|
|
{
|
|
|
|
if (dev) dev->SendCMD(12, 0);
|
|
|
|
}
|
2019-07-02 21:46:39 +00:00
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
// 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();
|
|
|
|
}
|
2019-07-02 21:46:39 +00:00
|
|
|
}
|
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
u32 DSi_SDHost::DataTX(u8* data, u32 len)
|
2019-06-20 21:05:32 +00:00
|
|
|
{
|
2020-06-01 14:24:59 +00:00
|
|
|
TXReq = true;
|
2019-06-20 21:05:32 +00:00
|
|
|
|
|
|
|
u32 f = CurFIFO;
|
2020-06-01 14:24:59 +00:00
|
|
|
|
|
|
|
if (DataMode == 1)
|
2019-07-24 16:48:52 +00:00
|
|
|
{
|
2020-12-30 22:37:46 +00:00
|
|
|
if ((DataFIFO32.Level() << 2) < len)
|
2020-06-01 14:24:59 +00:00
|
|
|
{
|
2020-12-30 22:37:46 +00:00
|
|
|
if (DataFIFO32.IsEmpty())
|
2020-06-01 14:24:59 +00:00
|
|
|
{
|
|
|
|
SetIRQ(25);
|
|
|
|
DSi::CheckNDMAs(1, Num ? 0x29 : 0x28);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// drain FIFO32 into FIFO16
|
|
|
|
|
2020-12-30 22:37:46 +00:00
|
|
|
if (!DataFIFO[f].IsEmpty()) printf("VERY BAD!! TRYING TO DRAIN FIFO32 INTO FIFO16 BUT IT CONTAINS SHIT ALREADY\n");
|
2020-06-01 14:24:59 +00:00
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
u32 f = CurFIFO;
|
2020-12-30 22:37:46 +00:00
|
|
|
if ((DataFIFO[f].Level() << 1) >= BlockLen16) break;
|
|
|
|
if (DataFIFO32.IsEmpty()) break;
|
2020-06-01 14:24:59 +00:00
|
|
|
|
2020-12-30 22:37:46 +00:00
|
|
|
u32 val = DataFIFO32.Read();
|
|
|
|
DataFIFO[f].Write(val & 0xFFFF);
|
|
|
|
DataFIFO[f].Write(val >> 16);
|
2020-06-01 14:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
UpdateData32IRQ();
|
|
|
|
|
|
|
|
if (BlockCount32 > 1)
|
|
|
|
BlockCount32--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-12-30 22:37:46 +00:00
|
|
|
if ((DataFIFO[f].Level() << 1) < len)
|
2020-06-01 14:24:59 +00:00
|
|
|
{
|
2020-12-30 22:37:46 +00:00
|
|
|
if (DataFIFO[f].IsEmpty()) SetIRQ(25);
|
2020-06-01 14:24:59 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2019-07-24 16:48:52 +00:00
|
|
|
}
|
|
|
|
|
2019-06-20 21:05:32 +00:00
|
|
|
for (u32 i = 0; i < len; i += 2)
|
2020-12-30 22:37:46 +00:00
|
|
|
*(u16*)&data[i] = DataFIFO[f].Read();
|
2019-06-20 21:05:32 +00:00
|
|
|
|
|
|
|
CurFIFO ^= 1;
|
2020-06-01 14:24:59 +00:00
|
|
|
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();
|
2019-06-20 22:07:57 +00:00
|
|
|
|
|
|
|
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--;
|
2019-07-24 16:48:52 +00:00
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
if (dev) dev->ContinueTransfer();
|
|
|
|
}
|
2019-08-04 00:16:16 +00:00
|
|
|
}
|
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
void DSi_SDHost::CheckTX()
|
2019-08-04 00:16:16 +00:00
|
|
|
{
|
2020-06-01 14:24:59 +00:00
|
|
|
if (!TXReq) return;
|
|
|
|
|
|
|
|
if (DataMode == 1)
|
|
|
|
{
|
2020-12-30 22:37:46 +00:00
|
|
|
if ((DataFIFO32.Level() << 2) < BlockLen32)
|
2020-06-01 14:24:59 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
u32 f = CurFIFO;
|
2020-12-30 22:37:46 +00:00
|
|
|
if ((DataFIFO[f].Level() << 1) < BlockLen16)
|
2020-06-01 14:24:59 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
|
|
|
if (dev) dev->ContinueTransfer();
|
2019-06-20 21:05:32 +00:00
|
|
|
}
|
|
|
|
|
2019-06-17 11:24:37 +00:00
|
|
|
|
|
|
|
u16 DSi_SDHost::Read(u32 addr)
|
|
|
|
{
|
2019-06-16 15:01:49 +00:00
|
|
|
switch (addr & 0x1FF)
|
|
|
|
{
|
2019-06-17 11:24:37 +00:00
|
|
|
case 0x000: return Command;
|
2019-06-16 15:01:49 +00:00
|
|
|
case 0x002: return PortSelect & 0x030F;
|
2019-06-17 11:24:37 +00:00
|
|
|
case 0x004: return Param & 0xFFFF;
|
|
|
|
case 0x006: return Param >> 16;
|
|
|
|
|
2019-06-18 12:12:37 +00:00
|
|
|
case 0x008: return StopAction;
|
|
|
|
case 0x00A: return BlockCount16;
|
|
|
|
|
2019-06-17 11:24:37 +00:00
|
|
|
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];
|
|
|
|
|
2019-10-19 14:03:59 +00:00
|
|
|
case 0x01C:
|
|
|
|
{
|
|
|
|
u16 ret = (IRQStatus & 0x031D);
|
|
|
|
|
|
|
|
if (!Num)
|
|
|
|
{
|
|
|
|
if (Ports[0]) // basic check of whether the SD card is inserted
|
2020-08-24 22:34:57 +00:00
|
|
|
ret |= 0x00B0;
|
2019-10-19 14:03:59 +00:00
|
|
|
else
|
|
|
|
ret |= 0x0008;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// SDIO wifi is always inserted, I guess
|
2020-08-24 22:34:57 +00:00
|
|
|
ret |= 0x00B0;
|
2019-10-19 14:03:59 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
2019-06-18 12:12:37 +00:00
|
|
|
case 0x01E: return ((IRQStatus >> 16) & 0x8B7F);
|
2019-06-17 11:24:37 +00:00
|
|
|
case 0x020: return IRQMask & 0x031D;
|
|
|
|
case 0x022: return (IRQMask >> 16) & 0x8B7F;
|
|
|
|
|
2019-06-17 16:40:45 +00:00
|
|
|
case 0x024: return SDClock;
|
2019-06-18 12:12:37 +00:00
|
|
|
case 0x026: return BlockLen16;
|
|
|
|
case 0x028: return SDOption;
|
|
|
|
|
2019-06-20 01:19:51 +00:00
|
|
|
case 0x02C: return 0; // TODO
|
|
|
|
|
2019-08-04 00:16:16 +00:00
|
|
|
case 0x034: return CardIRQCtl;
|
|
|
|
case 0x036: return CardIRQStatus;
|
|
|
|
case 0x038: return CardIRQMask;
|
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
case 0x030: return ReadFIFO16();
|
2019-06-18 12:12:37 +00:00
|
|
|
|
|
|
|
case 0x0D8: return DataCtl;
|
2019-06-17 16:40:45 +00:00
|
|
|
|
2019-06-17 11:24:37 +00:00
|
|
|
case 0x0E0: return SoftReset;
|
2019-06-18 12:12:37 +00:00
|
|
|
|
2020-06-01 14:35:09 +00:00
|
|
|
case 0x0F6: return 0; // MMC write protect (always 0)
|
|
|
|
|
2019-06-18 12:12:37 +00:00
|
|
|
case 0x100: return Data32IRQ;
|
2020-06-15 11:39:33 +00:00
|
|
|
case 0x102: return 0;
|
2019-06-18 12:12:37 +00:00
|
|
|
case 0x104: return BlockLen32;
|
|
|
|
case 0x108: return BlockCount32;
|
2019-06-16 15:01:49 +00:00
|
|
|
}
|
|
|
|
|
2019-06-20 01:19:51 +00:00
|
|
|
printf("unknown %s read %08X @ %08X\n", SD_DESC, addr, NDS::GetPC(1));
|
2019-06-16 15:01:49 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
u16 DSi_SDHost::ReadFIFO16()
|
2019-06-18 16:39:13 +00:00
|
|
|
{
|
2019-06-18 21:10:55 +00:00
|
|
|
u32 f = CurFIFO;
|
2020-12-30 22:37:46 +00:00
|
|
|
if (DataFIFO[f].IsEmpty())
|
2019-06-18 21:10:55 +00:00
|
|
|
{
|
|
|
|
// TODO
|
2020-06-01 14:24:59 +00:00
|
|
|
// on hardware it seems to wrap around. underflow bit is set upon the first 'empty' read.
|
2019-06-18 21:10:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
2020-12-30 22:37:46 +00:00
|
|
|
u16 ret = DataFIFO[f].Read();
|
2019-06-18 21:10:55 +00:00
|
|
|
|
2020-12-30 22:37:46 +00:00
|
|
|
if (DataFIFO[f].IsEmpty())
|
2019-06-18 21:10:55 +00:00
|
|
|
{
|
2020-06-01 14:24:59 +00:00
|
|
|
CheckRX();
|
|
|
|
}
|
2019-06-18 21:10:55 +00:00
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2019-06-18 21:10:55 +00:00
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
u32 DSi_SDHost::ReadFIFO32()
|
|
|
|
{
|
|
|
|
if (DataMode != 1) return 0;
|
2019-06-18 21:10:55 +00:00
|
|
|
|
2020-12-30 22:37:46 +00:00
|
|
|
if (DataFIFO32.IsEmpty())
|
2020-06-01 14:24:59 +00:00
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
return 0;
|
|
|
|
}
|
2019-06-18 21:10:55 +00:00
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
2020-12-30 22:37:46 +00:00
|
|
|
u32 ret = DataFIFO32.Read();
|
2019-06-18 21:10:55 +00:00
|
|
|
|
2020-12-30 22:37:46 +00:00
|
|
|
if (DataFIFO32.IsEmpty())
|
2020-06-01 14:24:59 +00:00
|
|
|
{
|
|
|
|
CheckRX();
|
2019-06-18 21:10:55 +00:00
|
|
|
}
|
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
UpdateData32IRQ();
|
|
|
|
|
2019-06-18 21:10:55 +00:00
|
|
|
return ret;
|
2019-06-18 16:39:13 +00:00
|
|
|
}
|
2019-08-07 10:57:12 +00:00
|
|
|
|
2019-06-17 11:24:37 +00:00
|
|
|
void DSi_SDHost::Write(u32 addr, u16 val)
|
2019-06-16 15:01:49 +00:00
|
|
|
{
|
|
|
|
switch (addr & 0x1FF)
|
|
|
|
{
|
2019-06-17 11:24:37 +00:00
|
|
|
case 0x000:
|
|
|
|
{
|
|
|
|
Command = val;
|
|
|
|
u8 cmd = Command & 0x3F;
|
|
|
|
|
|
|
|
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
|
|
|
if (dev)
|
|
|
|
{
|
2019-06-17 16:40:45 +00:00
|
|
|
// 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
|
2019-06-17 11:24:37 +00:00
|
|
|
switch ((Command >> 6) & 0x3)
|
|
|
|
{
|
|
|
|
case 0: dev->SendCMD(cmd, Param); break;
|
2019-06-17 16:40:45 +00:00
|
|
|
case 1: /*dev->SendCMD(55, 0);*/ dev->SendCMD(cmd, Param); break;
|
2019-06-17 11:24:37 +00:00
|
|
|
default:
|
|
|
|
printf("%s: unknown command type %d, %02X %08X\n", SD_DESC, (Command>>6)&0x3, cmd, Param);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-06-20 01:19:51 +00:00
|
|
|
else printf("%s: SENDING CMD %04X TO NULL DEVICE\n", SD_DESC, val);
|
2019-06-17 11:24:37 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
2020-06-01 14:32:44 +00:00
|
|
|
case 0x002: PortSelect = (val & 0x040F) | (PortSelect & 0x0300); return;
|
2019-06-17 11:24:37 +00:00
|
|
|
case 0x004: Param = (Param & 0xFFFF0000) | val; return;
|
|
|
|
case 0x006: Param = (Param & 0x0000FFFF) | (val << 16); return;
|
|
|
|
|
2019-06-18 12:12:37 +00:00
|
|
|
case 0x008: StopAction = val & 0x0101; return;
|
2020-06-01 14:32:44 +00:00
|
|
|
case 0x00A: BlockCount16 = val; BlockCountInternal = val; return;
|
2019-06-18 12:12:37 +00:00
|
|
|
|
|
|
|
case 0x01C: IRQStatus &= (val | 0xFFFF0000); return;
|
|
|
|
case 0x01E: IRQStatus &= ((val << 16) | 0xFFFF); return;
|
2019-08-07 10:57:12 +00:00
|
|
|
case 0x020:
|
|
|
|
{
|
|
|
|
u32 oldmask = IRQMask;
|
|
|
|
IRQMask = (IRQMask & 0x8B7F0000) | (val & 0x031D);
|
|
|
|
UpdateIRQ(oldmask);
|
|
|
|
}
|
|
|
|
return;
|
2019-07-24 16:48:52 +00:00
|
|
|
case 0x022:
|
2019-08-07 10:57:12 +00:00
|
|
|
{
|
|
|
|
u32 oldmask = IRQMask;
|
|
|
|
IRQMask = (IRQMask & 0x0000031D) | ((val & 0x8B7F) << 16);
|
|
|
|
UpdateIRQ(oldmask);
|
2020-06-15 11:39:33 +00:00
|
|
|
//if (!DataFIFO[CurFIFO]->IsEmpty()) SetIRQ(24); // checkme
|
|
|
|
//if (DataFIFO[CurFIFO]->IsEmpty()) SetIRQ(25); // checkme
|
2019-08-07 10:57:12 +00:00
|
|
|
}
|
2019-07-24 16:48:52 +00:00
|
|
|
return;
|
2019-06-17 11:24:37 +00:00
|
|
|
|
2019-06-17 16:40:45 +00:00
|
|
|
case 0x024: SDClock = val & 0x03FF; return;
|
2019-06-18 12:12:37 +00:00
|
|
|
case 0x026:
|
|
|
|
BlockLen16 = val & 0x03FF;
|
|
|
|
if (BlockLen16 > 0x200) BlockLen16 = 0x200;
|
|
|
|
return;
|
|
|
|
case 0x028: SDOption = val & 0xC1FF; return;
|
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
case 0x030: WriteFIFO16(val); return;
|
2019-07-24 16:48:52 +00:00
|
|
|
|
2019-08-04 00:16:16 +00:00
|
|
|
case 0x034:
|
|
|
|
CardIRQCtl = val & 0x0305;
|
|
|
|
SetCardIRQ();
|
|
|
|
return;
|
|
|
|
case 0x036:
|
|
|
|
CardIRQStatus &= val;
|
|
|
|
return;
|
|
|
|
case 0x038:
|
2020-06-15 11:39:33 +00:00
|
|
|
{
|
|
|
|
u16 oldmask = CardIRQMask;
|
|
|
|
CardIRQMask = val & 0xC007;
|
|
|
|
UpdateCardIRQ(oldmask);
|
|
|
|
}
|
|
|
|
//CardIRQMask = val & 0xC007;
|
|
|
|
//SetCardIRQ();
|
2019-08-04 00:16:16 +00:00
|
|
|
return;
|
|
|
|
|
2019-06-18 12:12:37 +00:00
|
|
|
case 0x0D8:
|
|
|
|
DataCtl = (val & 0x0022);
|
|
|
|
DataMode = ((DataCtl >> 1) & 0x1) & ((Data32IRQ >> 1) & 0x1);
|
|
|
|
return;
|
2019-06-17 16:40:45 +00:00
|
|
|
|
2019-06-17 11:24:37 +00:00
|
|
|
case 0x0E0:
|
|
|
|
if ((SoftReset & 0x0001) && !(val & 0x0001))
|
|
|
|
{
|
|
|
|
printf("%s: RESET\n", SD_DESC);
|
2019-06-18 12:12:37 +00:00
|
|
|
StopAction = 0;
|
|
|
|
memset(ResponseBuffer, 0, sizeof(ResponseBuffer));
|
2019-06-17 11:24:37 +00:00
|
|
|
IRQStatus = 0;
|
|
|
|
// TODO: ERROR_DETAIL_STATUS
|
2019-06-18 12:12:37 +00:00
|
|
|
SDClock &= ~0x0500;
|
|
|
|
SDOption = 0x40EE;
|
2019-06-17 11:24:37 +00:00
|
|
|
// TODO: CARD_IRQ_STAT
|
|
|
|
// TODO: FIFO16 shit
|
2020-06-15 11:39:33 +00:00
|
|
|
|
|
|
|
if (Ports[0]) Ports[0]->Reset();
|
|
|
|
if (Ports[1]) Ports[1]->Reset();
|
2019-06-17 11:24:37 +00:00
|
|
|
}
|
|
|
|
SoftReset = 0x0006 | (val & 0x0001);
|
|
|
|
return;
|
2019-06-18 12:12:37 +00:00
|
|
|
|
|
|
|
case 0x100:
|
|
|
|
Data32IRQ = (val & 0x1802) | (Data32IRQ & 0x0300);
|
2020-12-30 22:37:46 +00:00
|
|
|
if (val & (1<<10)) DataFIFO32.Clear();
|
2019-06-18 12:12:37 +00:00
|
|
|
DataMode = ((DataCtl >> 1) & 0x1) & ((Data32IRQ >> 1) & 0x1);
|
|
|
|
return;
|
2020-06-15 11:39:33 +00:00
|
|
|
case 0x102: return;
|
2019-06-18 12:12:37 +00:00
|
|
|
case 0x104: BlockLen32 = val & 0x03FF; return;
|
|
|
|
case 0x108: BlockCount32 = val; return;
|
2019-06-17 11:24:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
printf("unknown %s write %08X %04X\n", SD_DESC, addr, val);
|
|
|
|
}
|
|
|
|
|
2020-06-01 14:24:59 +00:00
|
|
|
void DSi_SDHost::WriteFIFO16(u16 val)
|
2019-06-18 16:39:13 +00:00
|
|
|
{
|
2019-06-20 21:20:08 +00:00
|
|
|
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
|
2019-06-20 21:05:32 +00:00
|
|
|
u32 f = CurFIFO;
|
2020-12-30 22:37:46 +00:00
|
|
|
if (DataFIFO[f].IsFull())
|
2019-06-20 21:05:32 +00:00
|
|
|
{
|
|
|
|
// TODO
|
2020-06-01 14:24:59 +00:00
|
|
|
printf("!!!! %s FIFO (16) FULL\n", SD_DESC);
|
2019-06-20 21:05:32 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-30 22:37:46 +00:00
|
|
|
DataFIFO[f].Write(val);
|
2020-06-01 14:24:59 +00:00
|
|
|
|
|
|
|
CheckTX();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DSi_SDHost::WriteFIFO32(u32 val)
|
|
|
|
{
|
|
|
|
if (DataMode != 1) return;
|
2019-06-20 21:05:32 +00:00
|
|
|
|
2020-12-30 22:37:46 +00:00
|
|
|
if (DataFIFO32.IsFull())
|
2019-06-20 21:20:08 +00:00
|
|
|
{
|
2020-06-01 14:24:59 +00:00
|
|
|
// TODO
|
|
|
|
printf("!!!! %s FIFO (32) FULL\n", SD_DESC);
|
2019-06-20 21:20:08 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-30 22:37:46 +00:00
|
|
|
DataFIFO32.Write(val);
|
2020-06-01 14:24:59 +00:00
|
|
|
|
|
|
|
CheckTX();
|
|
|
|
|
|
|
|
UpdateData32IRQ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DSi_SDHost::UpdateFIFO32()
|
|
|
|
{
|
|
|
|
// check whether we can drain FIFO32 into FIFO16, or vice versa
|
|
|
|
|
|
|
|
if (DataMode != 1) return;
|
|
|
|
|
2020-12-30 22:37:46 +00:00
|
|
|
if (!DataFIFO32.IsEmpty()) printf("VERY BAD!! TRYING TO DRAIN FIFO16 INTO FIFO32 BUT IT CONTAINS SHIT ALREADY\n");
|
2020-06-01 14:24:59 +00:00
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
u32 f = CurFIFO;
|
2020-12-30 22:37:46 +00:00
|
|
|
if ((DataFIFO32.Level() << 2) >= BlockLen32) break;
|
|
|
|
if (DataFIFO[f].IsEmpty()) break;
|
2020-06-01 14:24:59 +00:00
|
|
|
|
2020-12-30 22:37:46 +00:00
|
|
|
u32 val = DataFIFO[f].Read();
|
|
|
|
val |= (DataFIFO[f].Read() << 16);
|
|
|
|
DataFIFO32.Write(val);
|
2020-06-01 14:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
UpdateData32IRQ();
|
|
|
|
|
2020-12-30 22:37:46 +00:00
|
|
|
if ((DataFIFO32.Level() << 2) >= BlockLen32)
|
2020-06-01 14:24:59 +00:00
|
|
|
{
|
|
|
|
DSi::CheckNDMAs(1, Num ? 0x29 : 0x28);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DSi_SDHost::CheckSwapFIFO()
|
|
|
|
{
|
|
|
|
// check whether we can swap the FIFOs
|
|
|
|
|
|
|
|
u32 f = CurFIFO;
|
2020-12-30 22:37:46 +00:00
|
|
|
bool cur_empty = (DataMode == 1) ? DataFIFO32.IsEmpty() : DataFIFO[f].IsEmpty();
|
|
|
|
if (cur_empty && ((DataFIFO[f^1].Level() << 1) >= BlockLen16))
|
2020-06-01 14:24:59 +00:00
|
|
|
{
|
|
|
|
CurFIFO ^= 1;
|
|
|
|
}
|
2019-06-18 16:39:13 +00:00
|
|
|
}
|
|
|
|
|
2019-06-17 11:24:37 +00:00
|
|
|
|
2019-10-19 14:03:59 +00:00
|
|
|
#define MMC_DESC (Internal?"NAND":"SDcard")
|
|
|
|
|
2021-01-17 18:56:02 +00:00
|
|
|
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, FILE* file) : DSi_SDDevice(host)
|
2019-06-17 11:24:37 +00:00
|
|
|
{
|
|
|
|
Internal = internal;
|
2021-01-17 18:56:02 +00:00
|
|
|
File = file;
|
2020-06-15 11:39:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DSi_MMCStorage::~DSi_MMCStorage()
|
2021-01-17 18:56:02 +00:00
|
|
|
{}
|
2020-06-15 11:39:33 +00:00
|
|
|
|
|
|
|
void DSi_MMCStorage::Reset()
|
|
|
|
{
|
|
|
|
// TODO: reset file access????
|
2019-06-17 11:24:37 +00:00
|
|
|
|
2019-06-18 12:12:37 +00:00
|
|
|
CSR = 0x00000100; // checkme
|
2019-06-17 16:40:45 +00:00
|
|
|
|
|
|
|
// 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);
|
2019-06-18 12:12:37 +00:00
|
|
|
|
|
|
|
// checkme
|
|
|
|
memset(SCR, 0, 8);
|
|
|
|
*(u32*)&SCR[0] = 0x012A0000;
|
|
|
|
|
|
|
|
memset(SSR, 0, 64);
|
2019-06-18 16:39:13 +00:00
|
|
|
|
|
|
|
BlockSize = 0;
|
|
|
|
RWAddress = 0;
|
|
|
|
RWCommand = 0;
|
2019-06-17 11:24:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
|
|
|
|
{
|
2019-06-17 16:40:45 +00:00
|
|
|
if (CSR & (1<<5))
|
|
|
|
{
|
|
|
|
CSR &= ~(1<<5);
|
|
|
|
return SendACMD(cmd, param);
|
|
|
|
}
|
|
|
|
|
2019-06-17 11:24:37 +00:00
|
|
|
switch (cmd)
|
|
|
|
{
|
2019-06-17 16:40:45 +00:00
|
|
|
case 0: // reset/etc
|
2019-06-17 11:24:37 +00:00
|
|
|
Host->SendResponse(CSR, true);
|
|
|
|
return;
|
|
|
|
|
2020-10-25 17:25:09 +00:00
|
|
|
case 1: // SEND_OP_COND
|
|
|
|
// CHECKME!!
|
|
|
|
// also TODO: it's different for the SD card
|
|
|
|
if (Internal)
|
|
|
|
{
|
|
|
|
param &= ~(1<<30);
|
|
|
|
OCR &= 0xBF000000;
|
|
|
|
OCR |= (param & 0x40FFFFFF);
|
|
|
|
Host->SendResponse(OCR, true);
|
|
|
|
SetState(0x01);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printf("CMD1 on SD card!!\n");
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
2019-06-17 16:40:45 +00:00
|
|
|
case 2:
|
|
|
|
case 10: // get CID
|
2019-06-19 12:24:49 +00:00
|
|
|
Host->SendResponse(*(u32*)&CID[12], false);
|
|
|
|
Host->SendResponse(*(u32*)&CID[8], false);
|
|
|
|
Host->SendResponse(*(u32*)&CID[4], false);
|
|
|
|
Host->SendResponse(*(u32*)&CID[0], true);
|
2019-06-19 13:26:38 +00:00
|
|
|
if (cmd == 2) SetState(0x02);
|
2019-06-17 16:40:45 +00:00
|
|
|
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");
|
2019-10-19 14:03:59 +00:00
|
|
|
Host->SendResponse((CSR & 0x1FFF) | ((CSR >> 6) & 0x2000) | ((CSR >> 8) & 0xC000) | (1 << 16), true);
|
2019-06-17 16:40:45 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
2020-10-25 17:25:09 +00:00
|
|
|
case 6: // MMC: 'SWITCH'
|
|
|
|
// TODO!
|
|
|
|
Host->SendResponse(CSR, true);
|
|
|
|
return;
|
|
|
|
|
2019-06-17 16:40:45 +00:00
|
|
|
case 7: // select card (by RCA)
|
|
|
|
Host->SendResponse(CSR, true);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 8: // set voltage
|
2019-06-17 11:24:37 +00:00
|
|
|
Host->SendResponse(param, true);
|
|
|
|
return;
|
2019-06-17 16:40:45 +00:00
|
|
|
|
|
|
|
case 9: // get CSD
|
2019-06-19 12:24:49 +00:00
|
|
|
Host->SendResponse(*(u32*)&CSD[12], false);
|
|
|
|
Host->SendResponse(*(u32*)&CSD[8], false);
|
|
|
|
Host->SendResponse(*(u32*)&CSD[4], false);
|
|
|
|
Host->SendResponse(*(u32*)&CSD[0], true);
|
2019-06-17 16:40:45 +00:00
|
|
|
return;
|
|
|
|
|
2019-06-18 12:12:37 +00:00
|
|
|
case 12: // stop operation
|
2019-06-19 13:26:38 +00:00
|
|
|
SetState(0x04);
|
2019-06-20 21:05:32 +00:00
|
|
|
if (File) fflush(File);
|
2019-06-20 21:20:08 +00:00
|
|
|
RWCommand = 0;
|
2019-06-19 12:24:49 +00:00
|
|
|
Host->SendResponse(CSR, true);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 13: // get status
|
2019-06-18 12:12:37 +00:00
|
|
|
Host->SendResponse(CSR, true);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 16: // set block size
|
|
|
|
BlockSize = param;
|
2019-06-18 16:39:13 +00:00
|
|
|
if (BlockSize > 0x200)
|
|
|
|
{
|
|
|
|
// TODO! raise error
|
|
|
|
printf("!! SD/MMC: BAD BLOCK LEN %d\n", BlockSize);
|
|
|
|
BlockSize = 0x200;
|
|
|
|
}
|
2019-06-19 13:26:38 +00:00
|
|
|
SetState(0x04); // CHECKME
|
2019-06-18 12:12:37 +00:00
|
|
|
Host->SendResponse(CSR, true);
|
|
|
|
return;
|
|
|
|
|
2019-06-18 16:39:13 +00:00
|
|
|
case 18: // read multiple blocks
|
2020-06-01 14:32:44 +00:00
|
|
|
//printf("READ_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize);
|
2019-06-18 16:39:13 +00:00
|
|
|
RWAddress = param;
|
2019-06-19 13:54:07 +00:00
|
|
|
if (OCR & (1<<30))
|
|
|
|
{
|
|
|
|
RWAddress <<= 9;
|
|
|
|
BlockSize = 512;
|
|
|
|
}
|
2019-06-18 16:39:13 +00:00
|
|
|
RWCommand = 18;
|
|
|
|
Host->SendResponse(CSR, true);
|
|
|
|
ReadBlock(RWAddress);
|
|
|
|
RWAddress += BlockSize;
|
2019-06-19 13:26:38 +00:00
|
|
|
SetState(0x05);
|
2019-06-18 16:39:13 +00:00
|
|
|
return;
|
|
|
|
|
2019-06-20 21:05:32 +00:00
|
|
|
case 25: // write multiple blocks
|
2020-06-01 14:32:44 +00:00
|
|
|
//printf("WRITE_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize);
|
2019-06-20 21:05:32 +00:00
|
|
|
RWAddress = param;
|
|
|
|
if (OCR & (1<<30))
|
|
|
|
{
|
|
|
|
RWAddress <<= 9;
|
|
|
|
BlockSize = 512;
|
|
|
|
}
|
|
|
|
RWCommand = 25;
|
|
|
|
Host->SendResponse(CSR, true);
|
|
|
|
WriteBlock(RWAddress);
|
|
|
|
RWAddress += BlockSize;
|
2019-07-02 21:46:39 +00:00
|
|
|
SetState(0x04);
|
2019-06-20 21:05:32 +00:00
|
|
|
return;
|
|
|
|
|
2019-07-02 21:46:39 +00:00
|
|
|
case 55: // appcmd prefix
|
2019-06-17 16:40:45 +00:00
|
|
|
CSR |= (1<<5);
|
|
|
|
Host->SendResponse(CSR, true);
|
|
|
|
return;
|
2019-06-16 15:01:49 +00:00
|
|
|
}
|
|
|
|
|
2019-06-17 16:40:45 +00:00
|
|
|
printf("MMC: unknown CMD %d %08X\n", cmd, param);
|
2019-06-17 11:24:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DSi_MMCStorage::SendACMD(u8 cmd, u32 param)
|
|
|
|
{
|
2019-06-17 16:40:45 +00:00
|
|
|
switch (cmd)
|
|
|
|
{
|
2019-06-18 12:12:37 +00:00
|
|
|
case 6: // set bus width (TODO?)
|
2020-06-01 14:32:44 +00:00
|
|
|
//printf("SET BUS WIDTH %08X\n", param);
|
2019-06-18 12:12:37 +00:00
|
|
|
Host->SendResponse(CSR, true);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 13: // get SSR
|
|
|
|
Host->SendResponse(CSR, true);
|
2020-06-01 14:24:59 +00:00
|
|
|
Host->DataRX(SSR, 64);
|
2019-06-18 12:12:37 +00:00
|
|
|
return;
|
|
|
|
|
2019-06-17 16:40:45 +00:00
|
|
|
case 41: // set operating conditions
|
2019-06-19 13:54:07 +00:00
|
|
|
// CHECKME:
|
|
|
|
// DSi boot2 sets this to 0x40100000 (hardcoded)
|
|
|
|
// then has two codepaths depending on whether bit30 did get set
|
2019-06-19 13:58:50 +00:00
|
|
|
// is it settable at all on the MMC? probably not.
|
|
|
|
if (Internal) param &= ~(1<<30);
|
2019-06-17 16:40:45 +00:00
|
|
|
OCR &= 0xBF000000;
|
|
|
|
OCR |= (param & 0x40FFFFFF);
|
|
|
|
Host->SendResponse(OCR, true);
|
|
|
|
SetState(0x01);
|
|
|
|
return;
|
2019-06-18 12:12:37 +00:00
|
|
|
|
|
|
|
case 42: // ???
|
|
|
|
Host->SendResponse(CSR, true);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 51: // get SCR
|
|
|
|
Host->SendResponse(CSR, true);
|
2020-06-01 14:24:59 +00:00
|
|
|
Host->DataRX(SCR, 8);
|
2019-06-18 12:12:37 +00:00
|
|
|
return;
|
2019-06-17 16:40:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
printf("MMC: unknown ACMD %d %08X\n", cmd, param);
|
2019-06-16 15:01:49 +00:00
|
|
|
}
|
2019-06-18 12:12:37 +00:00
|
|
|
|
|
|
|
void DSi_MMCStorage::ContinueTransfer()
|
|
|
|
{
|
2019-06-20 21:20:08 +00:00
|
|
|
if (RWCommand == 0) return;
|
|
|
|
|
2019-08-04 00:16:16 +00:00
|
|
|
u32 len = 0;
|
|
|
|
|
2019-06-20 21:05:32 +00:00
|
|
|
switch (RWCommand)
|
|
|
|
{
|
|
|
|
case 18:
|
2019-08-04 00:16:16 +00:00
|
|
|
len = ReadBlock(RWAddress);
|
2019-06-20 21:05:32 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 25:
|
2019-08-04 00:16:16 +00:00
|
|
|
len = WriteBlock(RWAddress);
|
2019-06-20 21:05:32 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-08-04 00:16:16 +00:00
|
|
|
RWAddress += len;
|
2019-06-18 16:39:13 +00:00
|
|
|
}
|
|
|
|
|
2019-08-04 00:16:16 +00:00
|
|
|
u32 DSi_MMCStorage::ReadBlock(u64 addr)
|
2019-06-18 16:39:13 +00:00
|
|
|
{
|
2019-08-04 00:16:16 +00:00
|
|
|
u32 len = BlockSize;
|
|
|
|
len = Host->GetTransferrableLen(len);
|
|
|
|
|
2019-06-18 16:39:13 +00:00
|
|
|
u8 data[0x200];
|
2019-08-04 00:16:16 +00:00
|
|
|
if (File)
|
|
|
|
{
|
|
|
|
fseek(File, addr, SEEK_SET);
|
|
|
|
fread(data, 1, len, File);
|
|
|
|
}
|
2020-06-01 14:24:59 +00:00
|
|
|
|
|
|
|
return Host->DataRX(data, len);
|
2019-06-18 12:12:37 +00:00
|
|
|
}
|
2019-06-20 21:05:32 +00:00
|
|
|
|
2019-08-04 00:16:16 +00:00
|
|
|
u32 DSi_MMCStorage::WriteBlock(u64 addr)
|
2019-06-20 21:05:32 +00:00
|
|
|
{
|
2019-08-04 00:16:16 +00:00
|
|
|
u32 len = BlockSize;
|
|
|
|
len = Host->GetTransferrableLen(len);
|
|
|
|
|
2019-06-20 21:05:32 +00:00
|
|
|
u8 data[0x200];
|
2020-06-01 14:24:59 +00:00
|
|
|
if (len = Host->DataTX(data, len))
|
2019-07-24 16:48:52 +00:00
|
|
|
{
|
2019-08-04 00:16:16 +00:00
|
|
|
if (File)
|
|
|
|
{
|
|
|
|
fseek(File, addr, SEEK_SET);
|
|
|
|
fwrite(data, 1, len, File);
|
|
|
|
}
|
2019-07-24 16:48:52 +00:00
|
|
|
}
|
2019-08-04 00:16:16 +00:00
|
|
|
|
|
|
|
return len;
|
2019-06-20 21:05:32 +00:00
|
|
|
}
|