This commit is contained in:
CasualPokePlayer 2024-11-11 22:57:26 +01:00 committed by GitHub
commit 6bc78f198d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 360 additions and 36 deletions

View File

@ -21,6 +21,7 @@ add_library(core STATIC
DSi_Camera.cpp
DSi_DSP.cpp
DSi_I2C.cpp
DSi_I2S.cpp
DSi_NAND.cpp
DSi_NDMA.cpp
DSi_NWifi.cpp

View File

@ -35,6 +35,7 @@
#include "DSi_NDMA.h"
#include "DSi_I2C.h"
#include "DSi_I2S.h"
#include "DSi_SD.h"
#include "DSi_AES.h"
#include "DSi_NAND.h"
@ -108,6 +109,7 @@ DSi::DSi(DSiArgs&& args, void* userdata) noexcept :
SDMMC(*this, std::move(args.NANDImage), std::move(args.DSiSDCard)),
SDIO(*this),
I2C(*this),
I2S(*this),
CamModule(*this),
AES(*this)
{
@ -141,6 +143,7 @@ void DSi::Reset()
for (int i = 0; i < 8; i++) NDMAs[i].Reset();
I2C.Reset();
I2S.Reset();
CamModule.Reset();
DSP.Reset();
@ -210,6 +213,13 @@ void DSi::CamInputFrame(int cam, const u32* data, int width, int height, bool rg
}
}
void DSi::MicInputFrame(s16* data, int samples)
{
SPI.GetTSC()->MicInputFrame(data, samples);
I2S.MicInputFrame(data, samples);
// TODO: Need to send the mic samples to the DSP!
}
void DSi::DoSavestateExtra(Savestate* file)
{
file->Section("DSIG");
@ -285,6 +295,7 @@ void DSi::DoSavestateExtra(Savestate* file)
CamModule.DoSavestate(file);
DSP.DoSavestate(file);
I2C.DoSavestate(file);
I2S.DoSavestate(file);
SDMMC.DoSavestate(file);
SDIO.DoSavestate(file);
}
@ -644,6 +655,8 @@ void DSi::SetupDirectBoot()
SPI.GetFirmwareMem()->SetupDirectBoot();
I2S.WriteSndExCnt(0x8008, 0xFFFF);
ARM9.CP15Write(0x100, 0x00056078);
ARM9.CP15Write(0x200, 0x0000004A);
ARM9.CP15Write(0x201, 0x0000004A);
@ -690,6 +703,9 @@ void DSi::SoftReset()
NDS::MapSharedWRAM(3);
// TODO: is this actually reset?
I2S.Reset();
// TODO: does the DSP get reset? NWRAM doesn't, so I'm assuming no
// *HOWEVER*, the bootrom (which does get rerun) does remap NWRAM, and thus
// the DSP most likely gets reset
@ -2707,8 +2723,16 @@ u8 DSi::ARM7IORead8(u32 addr)
case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 56;
case 0x04004D08: return 0;
case 0x4004700: return DSP.ReadSNDExCnt() & 0xFF;
case 0x4004701: return DSP.ReadSNDExCnt() >> 8;
case 0x4004600: if (!(SCFG_EXT[1] & (1 << 20))) return 0; return I2S.ReadMicCnt() & 0xFF;
case 0x4004601: if (!(SCFG_EXT[1] & (1 << 20))) return 0; return I2S.ReadMicCnt() >> 8;
case 0x4004602: return 0;
case 0x4004603: return 0;
case 0x4004604: if (!(SCFG_EXT[1] & (1 << 20))) return 0; return I2S.ReadMicData() & 0xFF;
case 0x4004605: if (!(SCFG_EXT[1] & (1 << 20))) return 0; return (I2S.ReadMicData() >> 8) & 0xFF;
case 0x4004606: if (!(SCFG_EXT[1] & (1 << 20))) return 0; return (I2S.ReadMicData() >> 16) & 0xFF;
case 0x4004607: if (!(SCFG_EXT[1] & (1 << 20))) return 0; return I2S.ReadMicData() >> 24;
case 0x4004700: if (!(SCFG_EXT[1] & (1 << 21))) return 0; return I2S.ReadSndExCnt() & 0xFF;
case 0x4004701: if (!(SCFG_EXT[1] & (1 << 21))) return 0; return I2S.ReadSndExCnt() >> 8;
case 0x04004C00: return GPIO_Data;
case 0x04004C01: return GPIO_Dir;
@ -2751,7 +2775,11 @@ u16 DSi::ARM7IORead16(u32 addr)
case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 48;
case 0x04004D08: return 0;
case 0x4004700: return DSP.ReadSNDExCnt();
case 0x4004600: if (!(SCFG_EXT[1] & (1 << 20))) return 0; return I2S.ReadMicCnt();
case 0x4004602: return 0;
case 0x4004604: if (!(SCFG_EXT[1] & (1 << 20))) return 0; return I2S.ReadMicData() >> 16;
case 0x4004606: if (!(SCFG_EXT[1] & (1 << 20))) return 0; return I2S.ReadMicData() & 0xFFFF;
case 0x4004700: if (!(SCFG_EXT[1] & (1 << 21))) return 0; return I2S.ReadSndExCnt();
case 0x04004C00: return GPIO_Data | ((u16)GPIO_Dir << 8);
case 0x04004C02: return GPIO_IEdgeSel | ((u16)GPIO_IE << 8);
@ -2829,9 +2857,9 @@ u32 DSi::ARM7IORead32(u32 addr)
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 32;
case 0x04004D08: return 0;
case 0x4004700:
Log(LogLevel::Debug, "32-Bit SNDExCnt read? %08X\n", ARM7.R[15]);
return DSP.ReadSNDExCnt();
case 0x4004600: if (!(SCFG_EXT[1] & (1 << 20))) return 0; return I2S.ReadMicCnt();
case 0x4004604: if (!(SCFG_EXT[1] & (1 << 20))) return 0; return I2S.ReadMicData();
case 0x4004700: if (!(SCFG_EXT[1] & (1 << 21))) return 0; return I2S.ReadSndExCnt();
}
if (addr >= 0x04004800 && addr < 0x04004A00)
@ -2884,11 +2912,25 @@ void DSi::ARM7IOWrite8(u32 addr, u8 val)
case 0x04004500: I2C.WriteData(val); return;
case 0x04004501: I2C.WriteCnt(val); return;
case 0x4004600:
if (!(SCFG_EXT[1] & (1 << 20)))
return;
I2S.WriteMicCnt((u16)val, 0xFF);
return;
case 0x4004601:
if (!(SCFG_EXT[1] & (1 << 20)))
return;
I2S.WriteMicCnt(((u16)val << 8), 0xFF00);
return;
case 0x4004700:
DSP.WriteSNDExCnt((u16)val, 0xFF);
if (!(SCFG_EXT[1] & (1 << 21)))
return;
I2S.WriteSndExCnt((u16)val, 0xFF);
return;
case 0x4004701:
DSP.WriteSNDExCnt(((u16)val << 8), 0xFF00);
if (!(SCFG_EXT[1] & (1 << 21)))
return;
I2S.WriteSndExCnt(((u16)val << 8), 0xFF00);
return;
case 0x04004C00:
@ -2987,8 +3029,15 @@ void DSi::ARM7IOWrite16(u32 addr, u16 val)
AES.WriteBlkCnt(val<<16);
return;
case 0x4004600:
if (!(SCFG_EXT[1] & (1 << 20)))
return;
I2S.WriteMicCnt(val, 0xFFFF);
return;
case 0x4004700:
DSP.WriteSNDExCnt(val, 0xFFFF);
if (!(SCFG_EXT[1] & (1 << 21)))
return;
I2S.WriteSndExCnt(val, 0xFFFF);
return;
case 0x04004C00:
@ -3136,9 +3185,15 @@ void DSi::ARM7IOWrite32(u32 addr, u32 val)
case 0x04004404: AES.WriteBlkCnt(val); return;
case 0x04004408: AES.WriteInputFIFO(val); return;
case 0x4004600:
if (!(SCFG_EXT[1] & (1 << 20)))
return;
I2S.WriteMicCnt(val, 0xFFFF);
return;
case 0x4004700:
Log(LogLevel::Debug, "32-Bit SNDExCnt write? %08X %08X\n", val, ARM7.R[15]);
DSP.WriteSNDExCnt(val, 0xFFFF);
if (!(SCFG_EXT[1] & (1 << 21)))
return;
I2S.WriteSndExCnt(val, 0xFFFF);
return;
}

View File

@ -21,6 +21,7 @@
#include "NDS.h"
#include "DSi_NDMA.h"
#include "DSi_I2S.h"
#include "DSi_SD.h"
#include "DSi_DSP.h"
#include "DSi_AES.h"
@ -30,6 +31,7 @@
namespace melonDS
{
class DSi_I2CHost;
class DSi_I2S;
class DSi_CamModule;
class DSi_AES;
class DSi_DSP;
@ -69,6 +71,7 @@ public:
u32 NWRAMMask[2][3];
DSi_I2CHost I2C;
DSi_I2S I2S;
DSi_CamModule CamModule;
DSi_AES AES;
DSi_DSP DSP;
@ -155,6 +158,7 @@ public:
void SetSDCard(std::optional<FATStorage>&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); }
void CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) override;
void MicInputFrame(s16* data, int samples) override;
bool DMAsInMode(u32 cpu, u32 mode) const override;
bool DMAsRunning(u32 cpu) const override;
void StopDMAs(u32 cpu, u32 mode) override;

View File

@ -178,8 +178,6 @@ void DSi_DSP::Reset()
TeakraCore->Reset();
DSi.CancelEvent(Event_DSi_DSP);
SNDExCnt = 0;
}
bool DSi_DSP::IsRstReleased() const
@ -536,23 +534,6 @@ void DSi_DSP::Write32(u32 addr, u32 val)
Write16(addr, val & 0xFFFF);
}
void DSi_DSP::WriteSNDExCnt(u16 val, u16 mask)
{
val = (val & mask) | (SNDExCnt & ~mask);
// it can be written even in NDS mode
// mic frequency can only be changed if it was disabled
// before the write
if (SNDExCnt & 0x8000)
{
val &= ~0x2000;
val |= SNDExCnt & 0x2000;
}
SNDExCnt = val & 0xE00F;
}
void DSi_DSP::Run(u32 cycles)
{
if (!IsDSPCoreEnabled())

View File

@ -54,9 +54,6 @@ public:
u32 Read32(u32 addr);
void Write32(u32 addr, u32 val);
u16 ReadSNDExCnt() const { return SNDExCnt; }
void WriteSNDExCnt(u16 val, u16 mask);
// NOTE: checks SCFG_CLK9
void Run(u32 cycles);
@ -70,8 +67,6 @@ public:
private:
melonDS::DSi& DSi;
// not sure whether to not rather put it somewhere else
u16 SNDExCnt;
Teakra::Teakra* TeakraCore;

219
src/DSi_I2S.cpp Normal file
View File

@ -0,0 +1,219 @@
/*
Copyright 2016-2024 melonDS team
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 <string.h>
#include "DSi.h"
#include "DSi_I2S.h"
#include "Platform.h"
namespace melonDS
{
using Platform::Log;
using Platform::LogLevel;
DSi_I2S::DSi_I2S(melonDS::DSi& dsi) : DSi(dsi)
{
DSi.RegisterEventFunc(Event_DSi_I2S, 0, MemberEventFunc(DSi_I2S, Clock));
MicCnt = 0;
SndExCnt = 0;
MicClockDivider = 0;
MicBufferLen = 0;
}
DSi_I2S::~DSi_I2S()
{
DSi.UnregisterEventFunc(Event_DSi_I2S, 0);
}
void DSi_I2S::Reset()
{
MicCnt = 0;
SndExCnt = 0;
MicClockDivider = 0;
MicFifo.Clear();
MicBufferLen = 0;
DSi.ScheduleEvent(Event_DSi_I2S, false, 1024, 0, I2S_Freq_32728Hz);
}
void DSi_I2S::DoSavestate(Savestate* file)
{
file->Section("I2Si");
file->Var16(&MicCnt);
file->Var16(&SndExCnt);
file->Var8(&MicClockDivider);
MicFifo.DoSavestate(file);
}
void DSi_I2S::MicInputFrame(const s16* data, int samples)
{
if (!data)
{
MicBufferLen = 0;
return;
}
if (samples > 1024) samples = 1024;
memcpy(MicBuffer, data, samples * sizeof(s16));
MicBufferLen = samples;
}
u16 DSi_I2S::ReadMicCnt()
{
u16 ret = MicCnt;
if (MicFifo.Level() == 0) ret |= 1 << 8;
if (MicFifo.Level() >= 16) ret |= 1 << 9;
if (MicFifo.Level() >= 32) ret |= 1 << 10;
return ret;
}
void DSi_I2S::WriteMicCnt(u16 val, u16 mask)
{
val = (val & mask) | (MicCnt & ~mask);
// FIFO clear can only happen if the mic was disabled before the write
if (!(MicCnt & (1 << 15)) && (val & (1 << 12)))
{
MicCnt &= ~(1 << 11);
MicFifo.Clear();
}
MicCnt = (val & 0xE00F) | (MicCnt & (1 << 11));
}
u32 DSi_I2S::ReadMicData()
{
// CHECKME: This is a complete guess on how mic data reads work
// gbatek states the FIFO is 16 words large, with 1 word having 2 samples
u32 ret = MicFifo.IsEmpty() ? 0 : (u16)MicFifo.Read();
ret |= (MicFifo.IsEmpty() ? 0 : (u16)MicFifo.Read()) << 16;
return ret;
}
u16 DSi_I2S::ReadSndExCnt()
{
return SndExCnt;
}
void DSi_I2S::WriteSndExCnt(u16 val, u16 mask)
{
val = (val & mask) | (SndExCnt & ~mask);
// Note: SNDEXCNT can be accessed in "NDS mode"
// This is due to the corresponding SCFG_EXT flag not being disabled
// If it is disabled (with homebrew), SNDEXCNT cannot be accessed
// This is more purely a software mistake on the DSi menu's part
// I2S frequency can only be changed if it was disabled before the write
if (SndExCnt & (1 << 15))
{
val &= ~(1 << 13);
val |= SndExCnt & (1 << 13);
}
if ((SndExCnt ^ val) & (1 << 13))
{
Log(LogLevel::Debug, "Changed I2S frequency to %dHz\n", (SndExCnt & (1 << 13)) ? 47605 : 32728);
}
SndExCnt = val & 0xE00F;
}
void DSi_I2S::Clock(u32 freq)
{
if (SndExCnt & (1 << 15))
{
// CHECKME (from gbatek)
// "The Sampling Rate becomes zero (no data arriving) when SNDEXCNT.Bit15=0, or when MIC_CNT.bit0-1=3, or when MIC_CNT.bit15=0, or when Overrun has occurred."
// This likely means on any of these conditions the mic completely ignores any I2S clocks
// Although perhaps it might still acknowledge the clocks in some capacity (maybe affecting below clock division)
if ((MicCnt & (1 << 15)) && !(MicCnt & (1 << 11)) && (MicCnt & 3) != 3)
{
// CHECKME (from gbatek)
// "2-3 Sampling Rate (0..3=F/1, F/2, F/3, F/4)"
// This likely works with an internal counter compared with the sampling rate
// This counter is then likely reset on mic sample
// But this is completely untested
MicClockDivider++;
u8 micRate = (MicCnt >> 2) & 3;
if (MicClockDivider > micRate)
{
MicClockDivider = 0;
s16 sample = 0;
if (MicBufferLen > 0)
{
// 560190 cycles per frame
u32 cyclepos = (u32)DSi.GetSysClockCycles(2);
u32 samplepos = (cyclepos * MicBufferLen) / 560190;
if (samplepos >= MicBufferLen) samplepos = MicBufferLen - 1;
sample = MicBuffer[samplepos];
}
u32 oldLevel = MicFifo.Level();
if ((MicCnt & 3) == 0)
{
// stereo (this just duplicates the sample, as the mic itself is mono)
if (MicFifo.IsFull()) MicCnt |= 1 << 11;
MicFifo.Write(sample);
if (MicFifo.IsFull()) MicCnt |= 1 << 11;
MicFifo.Write(sample);
}
else
{
// mono
if (MicFifo.IsFull()) MicCnt |= 1 << 11;
MicFifo.Write(sample);
}
// if bit 13 is set, an IRQ is generated when the mic FIFO is half full
if (MicCnt & (1 << 13))
{
if (oldLevel < 16 && MicFifo.Level() >= 16) DSi.SetIRQ2(IRQ2_DSi_MicExt);
}
// if bit 13 is not set and bit 14 is set, an IRQ is generated when the mic FIFO is full
else if (MicCnt & (1 << 14))
{
if (oldLevel < 32 && MicFifo.Level() >= 32) DSi.SetIRQ2(IRQ2_DSi_MicExt);
}
}
}
// TODO: SPU and DSP sampling should happen here
// use passed freq to know how much to advance SPU by?
}
if (SndExCnt & (1 << 13))
{
DSi.ScheduleEvent(Event_DSi_I2S, false, 704, 0, I2S_Freq_47605Hz);
}
else
{
DSi.ScheduleEvent(Event_DSi_I2S, false, 1024, 0, I2S_Freq_32728Hz);
}
}
}

68
src/DSi_I2S.h Normal file
View File

@ -0,0 +1,68 @@
/*
Copyright 2016-2024 melonDS team
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/.
*/
#ifndef DSI_I2S_H
#define DSI_I2S_H
#include "FIFO.h"
#include "types.h"
#include "Savestate.h"
namespace melonDS
{
class DSi;
class DSi_I2S
{
public:
DSi_I2S(melonDS::DSi& dsi);
~DSi_I2S();
void Reset();
void DoSavestate(Savestate* file);
void MicInputFrame(const s16* data, int samples);
u16 ReadMicCnt();
void WriteMicCnt(u16 val, u16 mask);
u32 ReadMicData();
u16 ReadSndExCnt();
void WriteSndExCnt(u16 val, u16 mask);
private:
melonDS::DSi& DSi;
u16 MicCnt;
u16 SndExCnt;
u8 MicClockDivider;
FIFO<s16, 32> MicFifo;
s16 MicBuffer[1024];
int MicBufferLen;
enum
{
I2S_Freq_32728Hz,
I2S_Freq_47605Hz
};
void Clock(u32 freq);
};
}
#endif // DSI_I2S_H

View File

@ -72,6 +72,7 @@ enum
Event_DSi_CamIRQ,
Event_DSi_CamTransfer,
Event_DSi_DSP,
Event_DSi_I2S,
Event_MAX
};
@ -400,7 +401,7 @@ public: // TODO: Encapsulate the rest of these members
void SetLidClosed(bool closed);
virtual void CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) {}
void MicInputFrame(s16* data, int samples);
virtual void MicInputFrame(s16* data, int samples);
void RegisterEventFunc(u32 id, u32 funcid, EventFunc func);
void UnregisterEventFunc(u32 id, u32 funcid);