mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2024-11-14 13:27:41 -07:00
Merge 44e6dec81e
into 7c1d2a64f4
This commit is contained in:
commit
6bc78f198d
@ -21,6 +21,7 @@ add_library(core STATIC
|
|||||||
DSi_Camera.cpp
|
DSi_Camera.cpp
|
||||||
DSi_DSP.cpp
|
DSi_DSP.cpp
|
||||||
DSi_I2C.cpp
|
DSi_I2C.cpp
|
||||||
|
DSi_I2S.cpp
|
||||||
DSi_NAND.cpp
|
DSi_NAND.cpp
|
||||||
DSi_NDMA.cpp
|
DSi_NDMA.cpp
|
||||||
DSi_NWifi.cpp
|
DSi_NWifi.cpp
|
||||||
|
77
src/DSi.cpp
77
src/DSi.cpp
@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
#include "DSi_NDMA.h"
|
#include "DSi_NDMA.h"
|
||||||
#include "DSi_I2C.h"
|
#include "DSi_I2C.h"
|
||||||
|
#include "DSi_I2S.h"
|
||||||
#include "DSi_SD.h"
|
#include "DSi_SD.h"
|
||||||
#include "DSi_AES.h"
|
#include "DSi_AES.h"
|
||||||
#include "DSi_NAND.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)),
|
SDMMC(*this, std::move(args.NANDImage), std::move(args.DSiSDCard)),
|
||||||
SDIO(*this),
|
SDIO(*this),
|
||||||
I2C(*this),
|
I2C(*this),
|
||||||
|
I2S(*this),
|
||||||
CamModule(*this),
|
CamModule(*this),
|
||||||
AES(*this)
|
AES(*this)
|
||||||
{
|
{
|
||||||
@ -141,6 +143,7 @@ void DSi::Reset()
|
|||||||
for (int i = 0; i < 8; i++) NDMAs[i].Reset();
|
for (int i = 0; i < 8; i++) NDMAs[i].Reset();
|
||||||
|
|
||||||
I2C.Reset();
|
I2C.Reset();
|
||||||
|
I2S.Reset();
|
||||||
CamModule.Reset();
|
CamModule.Reset();
|
||||||
DSP.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)
|
void DSi::DoSavestateExtra(Savestate* file)
|
||||||
{
|
{
|
||||||
file->Section("DSIG");
|
file->Section("DSIG");
|
||||||
@ -285,6 +295,7 @@ void DSi::DoSavestateExtra(Savestate* file)
|
|||||||
CamModule.DoSavestate(file);
|
CamModule.DoSavestate(file);
|
||||||
DSP.DoSavestate(file);
|
DSP.DoSavestate(file);
|
||||||
I2C.DoSavestate(file);
|
I2C.DoSavestate(file);
|
||||||
|
I2S.DoSavestate(file);
|
||||||
SDMMC.DoSavestate(file);
|
SDMMC.DoSavestate(file);
|
||||||
SDIO.DoSavestate(file);
|
SDIO.DoSavestate(file);
|
||||||
}
|
}
|
||||||
@ -644,6 +655,8 @@ void DSi::SetupDirectBoot()
|
|||||||
|
|
||||||
SPI.GetFirmwareMem()->SetupDirectBoot();
|
SPI.GetFirmwareMem()->SetupDirectBoot();
|
||||||
|
|
||||||
|
I2S.WriteSndExCnt(0x8008, 0xFFFF);
|
||||||
|
|
||||||
ARM9.CP15Write(0x100, 0x00056078);
|
ARM9.CP15Write(0x100, 0x00056078);
|
||||||
ARM9.CP15Write(0x200, 0x0000004A);
|
ARM9.CP15Write(0x200, 0x0000004A);
|
||||||
ARM9.CP15Write(0x201, 0x0000004A);
|
ARM9.CP15Write(0x201, 0x0000004A);
|
||||||
@ -690,6 +703,9 @@ void DSi::SoftReset()
|
|||||||
|
|
||||||
NDS::MapSharedWRAM(3);
|
NDS::MapSharedWRAM(3);
|
||||||
|
|
||||||
|
// TODO: is this actually reset?
|
||||||
|
I2S.Reset();
|
||||||
|
|
||||||
// TODO: does the DSP get reset? NWRAM doesn't, so I'm assuming no
|
// 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
|
// *HOWEVER*, the bootrom (which does get rerun) does remap NWRAM, and thus
|
||||||
// the DSP most likely gets reset
|
// 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 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 56;
|
||||||
case 0x04004D08: return 0;
|
case 0x04004D08: return 0;
|
||||||
|
|
||||||
case 0x4004700: return DSP.ReadSNDExCnt() & 0xFF;
|
case 0x4004600: if (!(SCFG_EXT[1] & (1 << 20))) return 0; return I2S.ReadMicCnt() & 0xFF;
|
||||||
case 0x4004701: return DSP.ReadSNDExCnt() >> 8;
|
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 0x04004C00: return GPIO_Data;
|
||||||
case 0x04004C01: return GPIO_Dir;
|
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 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 48;
|
||||||
case 0x04004D08: return 0;
|
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 0x04004C00: return GPIO_Data | ((u16)GPIO_Dir << 8);
|
||||||
case 0x04004C02: return GPIO_IEdgeSel | ((u16)GPIO_IE << 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 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 32;
|
||||||
case 0x04004D08: return 0;
|
case 0x04004D08: return 0;
|
||||||
|
|
||||||
case 0x4004700:
|
case 0x4004600: if (!(SCFG_EXT[1] & (1 << 20))) return 0; return I2S.ReadMicCnt();
|
||||||
Log(LogLevel::Debug, "32-Bit SNDExCnt read? %08X\n", ARM7.R[15]);
|
case 0x4004604: if (!(SCFG_EXT[1] & (1 << 20))) return 0; return I2S.ReadMicData();
|
||||||
return DSP.ReadSNDExCnt();
|
case 0x4004700: if (!(SCFG_EXT[1] & (1 << 21))) return 0; return I2S.ReadSndExCnt();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addr >= 0x04004800 && addr < 0x04004A00)
|
if (addr >= 0x04004800 && addr < 0x04004A00)
|
||||||
@ -2884,11 +2912,25 @@ void DSi::ARM7IOWrite8(u32 addr, u8 val)
|
|||||||
case 0x04004500: I2C.WriteData(val); return;
|
case 0x04004500: I2C.WriteData(val); return;
|
||||||
case 0x04004501: I2C.WriteCnt(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:
|
case 0x4004700:
|
||||||
DSP.WriteSNDExCnt((u16)val, 0xFF);
|
if (!(SCFG_EXT[1] & (1 << 21)))
|
||||||
|
return;
|
||||||
|
I2S.WriteSndExCnt((u16)val, 0xFF);
|
||||||
return;
|
return;
|
||||||
case 0x4004701:
|
case 0x4004701:
|
||||||
DSP.WriteSNDExCnt(((u16)val << 8), 0xFF00);
|
if (!(SCFG_EXT[1] & (1 << 21)))
|
||||||
|
return;
|
||||||
|
I2S.WriteSndExCnt(((u16)val << 8), 0xFF00);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x04004C00:
|
case 0x04004C00:
|
||||||
@ -2987,8 +3029,15 @@ void DSi::ARM7IOWrite16(u32 addr, u16 val)
|
|||||||
AES.WriteBlkCnt(val<<16);
|
AES.WriteBlkCnt(val<<16);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case 0x4004600:
|
||||||
|
if (!(SCFG_EXT[1] & (1 << 20)))
|
||||||
|
return;
|
||||||
|
I2S.WriteMicCnt(val, 0xFFFF);
|
||||||
|
return;
|
||||||
case 0x4004700:
|
case 0x4004700:
|
||||||
DSP.WriteSNDExCnt(val, 0xFFFF);
|
if (!(SCFG_EXT[1] & (1 << 21)))
|
||||||
|
return;
|
||||||
|
I2S.WriteSndExCnt(val, 0xFFFF);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x04004C00:
|
case 0x04004C00:
|
||||||
@ -3136,9 +3185,15 @@ void DSi::ARM7IOWrite32(u32 addr, u32 val)
|
|||||||
case 0x04004404: AES.WriteBlkCnt(val); return;
|
case 0x04004404: AES.WriteBlkCnt(val); return;
|
||||||
case 0x04004408: AES.WriteInputFIFO(val); return;
|
case 0x04004408: AES.WriteInputFIFO(val); return;
|
||||||
|
|
||||||
|
case 0x4004600:
|
||||||
|
if (!(SCFG_EXT[1] & (1 << 20)))
|
||||||
|
return;
|
||||||
|
I2S.WriteMicCnt(val, 0xFFFF);
|
||||||
|
return;
|
||||||
case 0x4004700:
|
case 0x4004700:
|
||||||
Log(LogLevel::Debug, "32-Bit SNDExCnt write? %08X %08X\n", val, ARM7.R[15]);
|
if (!(SCFG_EXT[1] & (1 << 21)))
|
||||||
DSP.WriteSNDExCnt(val, 0xFFFF);
|
return;
|
||||||
|
I2S.WriteSndExCnt(val, 0xFFFF);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "NDS.h"
|
#include "NDS.h"
|
||||||
#include "DSi_NDMA.h"
|
#include "DSi_NDMA.h"
|
||||||
|
#include "DSi_I2S.h"
|
||||||
#include "DSi_SD.h"
|
#include "DSi_SD.h"
|
||||||
#include "DSi_DSP.h"
|
#include "DSi_DSP.h"
|
||||||
#include "DSi_AES.h"
|
#include "DSi_AES.h"
|
||||||
@ -30,6 +31,7 @@
|
|||||||
namespace melonDS
|
namespace melonDS
|
||||||
{
|
{
|
||||||
class DSi_I2CHost;
|
class DSi_I2CHost;
|
||||||
|
class DSi_I2S;
|
||||||
class DSi_CamModule;
|
class DSi_CamModule;
|
||||||
class DSi_AES;
|
class DSi_AES;
|
||||||
class DSi_DSP;
|
class DSi_DSP;
|
||||||
@ -69,6 +71,7 @@ public:
|
|||||||
u32 NWRAMMask[2][3];
|
u32 NWRAMMask[2][3];
|
||||||
|
|
||||||
DSi_I2CHost I2C;
|
DSi_I2CHost I2C;
|
||||||
|
DSi_I2S I2S;
|
||||||
DSi_CamModule CamModule;
|
DSi_CamModule CamModule;
|
||||||
DSi_AES AES;
|
DSi_AES AES;
|
||||||
DSi_DSP DSP;
|
DSi_DSP DSP;
|
||||||
@ -155,6 +158,7 @@ public:
|
|||||||
void SetSDCard(std::optional<FATStorage>&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); }
|
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 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 DMAsInMode(u32 cpu, u32 mode) const override;
|
||||||
bool DMAsRunning(u32 cpu) const override;
|
bool DMAsRunning(u32 cpu) const override;
|
||||||
void StopDMAs(u32 cpu, u32 mode) override;
|
void StopDMAs(u32 cpu, u32 mode) override;
|
||||||
|
@ -178,8 +178,6 @@ void DSi_DSP::Reset()
|
|||||||
TeakraCore->Reset();
|
TeakraCore->Reset();
|
||||||
|
|
||||||
DSi.CancelEvent(Event_DSi_DSP);
|
DSi.CancelEvent(Event_DSi_DSP);
|
||||||
|
|
||||||
SNDExCnt = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DSi_DSP::IsRstReleased() const
|
bool DSi_DSP::IsRstReleased() const
|
||||||
@ -536,23 +534,6 @@ void DSi_DSP::Write32(u32 addr, u32 val)
|
|||||||
Write16(addr, val & 0xFFFF);
|
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)
|
void DSi_DSP::Run(u32 cycles)
|
||||||
{
|
{
|
||||||
if (!IsDSPCoreEnabled())
|
if (!IsDSPCoreEnabled())
|
||||||
|
@ -54,9 +54,6 @@ public:
|
|||||||
u32 Read32(u32 addr);
|
u32 Read32(u32 addr);
|
||||||
void Write32(u32 addr, u32 val);
|
void Write32(u32 addr, u32 val);
|
||||||
|
|
||||||
u16 ReadSNDExCnt() const { return SNDExCnt; }
|
|
||||||
void WriteSNDExCnt(u16 val, u16 mask);
|
|
||||||
|
|
||||||
// NOTE: checks SCFG_CLK9
|
// NOTE: checks SCFG_CLK9
|
||||||
void Run(u32 cycles);
|
void Run(u32 cycles);
|
||||||
|
|
||||||
@ -70,8 +67,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
melonDS::DSi& DSi;
|
melonDS::DSi& DSi;
|
||||||
// not sure whether to not rather put it somewhere else
|
|
||||||
u16 SNDExCnt;
|
|
||||||
|
|
||||||
Teakra::Teakra* TeakraCore;
|
Teakra::Teakra* TeakraCore;
|
||||||
|
|
||||||
|
219
src/DSi_I2S.cpp
Normal file
219
src/DSi_I2S.cpp
Normal 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
68
src/DSi_I2S.h
Normal 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
|
@ -72,6 +72,7 @@ enum
|
|||||||
Event_DSi_CamIRQ,
|
Event_DSi_CamIRQ,
|
||||||
Event_DSi_CamTransfer,
|
Event_DSi_CamTransfer,
|
||||||
Event_DSi_DSP,
|
Event_DSi_DSP,
|
||||||
|
Event_DSi_I2S,
|
||||||
|
|
||||||
Event_MAX
|
Event_MAX
|
||||||
};
|
};
|
||||||
@ -400,7 +401,7 @@ public: // TODO: Encapsulate the rest of these members
|
|||||||
void SetLidClosed(bool closed);
|
void SetLidClosed(bool closed);
|
||||||
|
|
||||||
virtual void CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) {}
|
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 RegisterEventFunc(u32 id, u32 funcid, EventFunc func);
|
||||||
void UnregisterEventFunc(u32 id, u32 funcid);
|
void UnregisterEventFunc(u32 id, u32 funcid);
|
||||||
|
Loading…
Reference in New Issue
Block a user