From 8d66beba6bc49f8d1f6cb443745bfd1ffe4008a3 Mon Sep 17 00:00:00 2001 From: StapleButter Date: Thu, 6 Apr 2017 19:44:34 +0200 Subject: [PATCH] start SPU work --- src/NDS.cpp | 47 +++------ src/NDS.h | 28 +---- src/SPU.cpp | 265 ++++++++++++++++++++++++++++++++++++++++++++---- src/SPU.h | 35 +++++++ src/wx/main.cpp | 39 +++++-- src/wx/main.h | 6 +- 6 files changed, 335 insertions(+), 85 deletions(-) diff --git a/src/NDS.cpp b/src/NDS.cpp index d8f346e8..623f025d 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -25,6 +25,7 @@ #include "DMA.h" #include "FIFO.h" #include "GPU.h" +#include "SPU.h" #include "SPI.h" #include "RTC.h" #include "Wifi.h" @@ -37,11 +38,6 @@ namespace NDS // * stick all the variables in a big structure? // would make it easier to deal with savestates -/*SchedEvent SchedBuffer[SCHED_BUF_LEN]; -SchedEvent* SchedQueue; - -bool NeedReschedule;*/ - ARM* ARM9; ARM* ARM7; @@ -108,8 +104,6 @@ u32 SqrtRes; u32 KeyInput; -u16 _soundbias; // temp - bool Running; @@ -132,6 +126,7 @@ bool Init() if (!NDSCart::Init()) return false; if (!GPU::Init()) return false; + if (!SPU::Init()) return false; if (!SPI::Init()) return false; if (!RTC::Init()) return false; @@ -151,6 +146,7 @@ void DeInit() NDSCart::DeInit(); GPU::DeInit(); + SPU::DeInit(); SPI::DeInit(); RTC::DeInit(); } @@ -300,14 +296,6 @@ void Reset() for (i = 0; i < 8; i++) DMAs[i]->Reset(); memset(DMA9Fill, 0, 4*4); - NDSCart::Reset(); - GPU::Reset(); - SPI::Reset(); - RTC::Reset(); - Wifi::Reset(); - - // memset(SchedBuffer, 0, sizeof(SchedEvent)*SCHED_BUF_LEN); - // SchedQueue = NULL; memset(SchedList, 0, sizeof(SchedList)); SchedListMask = 0; @@ -319,7 +307,12 @@ void Reset() KeyInput = 0x007F03FF; - _soundbias = 0; + NDSCart::Reset(); + GPU::Reset(); + SPU::Reset(); + SPI::Reset(); + RTC::Reset(); + Wifi::Reset(); } void LoadROM(const char* path, bool direct) @@ -1629,7 +1622,6 @@ void ARM9IOWrite16(u32 addr, u16 val) { SetIRQ(1, IRQ_IPCSync); } - //CompensateARM7(); return; case 0x04000184: @@ -1901,8 +1893,7 @@ u8 ARM7IORead8(u32 addr) if (addr >= 0x04000400 && addr < 0x04000520) { - // sound I/O - return 0; + return SPU::Read8(addr); } printf("unknown ARM7 IO read8 %08X\n", addr); @@ -1972,14 +1963,11 @@ u16 ARM7IORead16(u32 addr) case 0x04000300: return PostFlag7; case 0x04000304: return PowerControl7; case 0x04000308: return ARM7BIOSProt; - - case 0x04000504: return _soundbias; } if (addr >= 0x04000400 && addr < 0x04000520) { - // sound I/O - return 0; + return SPU::Read16(addr); } printf("unknown ARM7 IO read16 %08X %08X\n", addr, ARM9->R[15]); @@ -2057,8 +2045,7 @@ u32 ARM7IORead32(u32 addr) if (addr >= 0x04000400 && addr < 0x04000520) { - // sound I/O - return 0; + return SPU::Read32(addr); } printf("unknown ARM7 IO read32 %08X\n", addr); @@ -2116,7 +2103,7 @@ void ARM7IOWrite8(u32 addr, u8 val) if (addr >= 0x04000400 && addr < 0x04000520) { - // sound I/O + SPU::Write8(addr, val); return; } @@ -2228,15 +2215,11 @@ void ARM7IOWrite16(u32 addr, u16 val) if (ARM7BIOSProt == 0) ARM7BIOSProt = val; return; - - case 0x04000504: // removeme - _soundbias = val & 0x3FF; - return; } if (addr >= 0x04000400 && addr < 0x04000520) { - // sound I/O + SPU::Write16(addr, val); return; } @@ -2326,7 +2309,7 @@ void ARM7IOWrite32(u32 addr, u32 val) if (addr >= 0x04000400 && addr < 0x04000520) { - // sound I/O + SPU::Write32(addr, val); return; } diff --git a/src/NDS.h b/src/NDS.h index 4fec1174..583e12c1 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -24,30 +24,11 @@ namespace NDS { -/*#define SCHED_BUF_LEN 64 - -typedef struct _SchedEvent -{ - u32 Delay; - void (*Func)(u32); - u32 Param; - struct _SchedEvent* PrevEvent; - struct _SchedEvent* NextEvent; - -} SchedEvent;*/ - enum { Event_LCD = 0, - /*Event_Timer9_0, - Event_Timer9_1, - Event_Timer9_2, - Event_Timer9_3, - Event_Timer7_0, - Event_Timer7_1, - Event_Timer7_2, - Event_Timer7_3,*/ + Event_SPU, Event_MAX }; @@ -95,7 +76,6 @@ typedef struct u16 Cnt; u32 Counter; u32 CycleShift; - //SchedEvent* Event; } Timer; @@ -127,15 +107,9 @@ void ReleaseKey(u32 key); void TouchScreen(u16 x, u16 y); void ReleaseScreen(); -/*SchedEvent* ScheduleEvent(s32 Delay, void (*Func)(u32), u32 Param); -void CancelEvent(SchedEvent* event); -void RunEvents(s32 cycles);*/ void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 param); void CancelEvent(u32 id); -// DO NOT CALL FROM ARM7!! -void CompensateARM7(); - void debug(u32 p); void Halt(); diff --git a/src/SPU.cpp b/src/SPU.cpp index c7a769ee..440d39d9 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -25,38 +25,269 @@ namespace SPU { -//SDL_AudioDeviceID device; -// +const u32 OutputBufferSize = 2*1024; +s16 OutputBuffer[2 * OutputBufferSize]; +u32 OutputReadOffset; +u32 OutputWriteOffset; + + +u16 Cnt; +u8 MasterVolume; +u16 Bias; + +Channel* Channels[16]; bool Init() { - /*SDL_AudioSpec whatIwant, whatIget; - - memset(&whatIwant, 0, sizeof(SDL_AudioSpec)); - whatIwant.freq = 32824; // 32823.6328125 - whatIwant.format = AUDIO_S16LSB; - whatIwant.channels = 2; - whatIwant.samples = 2048; - whatIwant.callback = zorp; - device = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, 0); - if (!device) - { - printf("Audio init failed: %s\n", SDL_GetError()); - return false; - }*/ + for (int i = 0; i < 16; i++) + Channels[i] = new Channel(i); return true; } void DeInit() { - //if (device) SDL_CloseAudioDevice(device); + for (int i = 0; i < 16; i++) + delete Channels[i]; } void Reset() +{ + memset(OutputBuffer, 0, 2*OutputBufferSize*2); + OutputReadOffset = 0; + OutputWriteOffset = 0; + + Cnt = 0; + MasterVolume = 0; + Bias = 0; + + for (int i = 0; i < 16; i++) + Channels[i]->Reset(); + + NDS::ScheduleEvent(NDS::Event_SPU, true, 1024*16, Mix, 16); +} + + +Channel::Channel(u32 num) { // } +Channel::~Channel() +{ + // +} + +void Channel::Reset() +{ + SetCnt(0); + SrcAddr = 0; + TimerReload = 0; + LoopPos = 0; + Length = 0; +} + + +void Mix(u32 samples) +{ + // + + NDS::ScheduleEvent(NDS::Event_SPU, true, 1024*16, Mix, 16); +} + + +void ReadOutput(s16* data, int samples) +{ + for (int i = 0; i < samples; i++) + { + *data++ = OutputBuffer[OutputReadOffset]; + *data++ = OutputBuffer[OutputReadOffset + 1]; + + if (OutputReadOffset != OutputWriteOffset) + { + OutputReadOffset += 2; + OutputReadOffset &= ((2*OutputBufferSize)-1); + } + } +} + + +u8 Read8(u32 addr) +{ + if (addr < 0x04000500) + { + Channel* chan = Channels[(addr >> 4) & 0xF]; + + switch (addr & 0xF) + { + case 0x0: return chan->Cnt & 0xFF; + case 0x1: return (chan->Cnt >> 8) & 0xFF; + case 0x2: return (chan->Cnt >> 16) & 0xFF; + case 0x3: return chan->Cnt >> 24; + } + } + else + { + switch (addr) + { + case 0x04000500: return Cnt & 0x7F; + case 0x04000501: return Cnt >> 8; + } + } + + printf("unknown SPU read8 %08X\n", addr); + return 0; +} + +u16 Read16(u32 addr) +{ + if (addr < 0x04000500) + { + Channel* chan = Channels[(addr >> 4) & 0xF]; + + switch (addr & 0xF) + { + case 0x0: return chan->Cnt & 0xFFFF; + case 0x2: return chan->Cnt >> 16; + } + } + else + { + switch (addr) + { + case 0x04000500: return Cnt; + case 0x04000504: return Bias; + } + } + + printf("unknown SPU read16 %08X\n", addr); + return 0; +} + +u32 Read32(u32 addr) +{ + if (addr < 0x04000500) + { + Channel* chan = Channels[(addr >> 4) & 0xF]; + + switch (addr & 0xF) + { + case 0x0: return chan->Cnt; + } + } + else + { + switch (addr) + { + case 0x04000500: return Cnt; + case 0x04000504: return Bias; + } + } + + printf("unknown SPU read32 %08X\n", addr); + return 0; +} + +void Write8(u32 addr, u8 val) +{ + if (addr < 0x04000500) + { + Channel* chan = Channels[(addr >> 4) & 0xF]; + + switch (addr & 0xF) + { + case 0x0: chan->SetCnt((chan->Cnt & 0xFFFFFF00) | val); return; + case 0x1: chan->SetCnt((chan->Cnt & 0xFFFF00FF) | (val << 8)); return; + case 0x2: chan->SetCnt((chan->Cnt & 0xFF00FFFF) | (val << 16)); return; + case 0x3: chan->SetCnt((chan->Cnt & 0x00FFFFFF) | (val << 24)); return; + } + } + else + { + switch (addr) + { + case 0x04000500: + Cnt = (Cnt & 0xBF00) | (val & 0x7F); + MasterVolume = Cnt & 0x7F; + if (MasterVolume == 127) MasterVolume++; + return; + case 0x04000501: + Cnt = (Cnt & 0x007F) | ((val & 0xBF) << 8); + return; + } + } + + printf("unknown SPU write8 %08X %02X\n", addr, val); +} + +void Write16(u32 addr, u16 val) +{ + if (addr < 0x04000500) + { + Channel* chan = Channels[(addr >> 4) & 0xF]; + + switch (addr & 0xF) + { + case 0x0: chan->SetCnt((chan->Cnt & 0xFFFF0000) | val); return; + case 0x2: chan->SetCnt((chan->Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x8: chan->SetTimerReload(val); return; + case 0xA: chan->SetLoopPos(val); return; + } + } + else + { + switch (addr) + { + case 0x04000500: + Cnt = val & 0xBF7F; + MasterVolume = Cnt & 0x7F; + if (MasterVolume == 127) MasterVolume++; + return; + + case 0x04000504: + Bias = val & 0x3FF; + return; + } + } + + printf("unknown SPU write16 %08X %04X\n", addr, val); +} + +void Write32(u32 addr, u32 val) +{ + if (addr < 0x04000500) + { + Channel* chan = Channels[(addr >> 4) & 0xF]; + + switch (addr & 0xF) + { + case 0x0: chan->SetCnt(val); return; + case 0x4: chan->SetSrcAddr(val); return; + case 0x8: + chan->SetTimerReload(val & 0xFFFF); + chan->SetLoopPos(val >> 16); + return; + case 0xC: chan->SetLength(val); return; + } + } + else + { + switch (addr) + { + case 0x04000500: + Cnt = val & 0xBF7F; + MasterVolume = Cnt & 0x7F; + if (MasterVolume == 127) MasterVolume++; + return; + + case 0x04000504: + Bias = val & 0x3FF; + return; + } + } + + printf("unknown SPU write32 %08X %08X\n", addr, val); +} + } diff --git a/src/SPU.h b/src/SPU.h index a66b98d8..5b762bd3 100644 --- a/src/SPU.h +++ b/src/SPU.h @@ -26,6 +26,41 @@ bool Init(); void DeInit(); void Reset(); +void Mix(u32 samples); + +void ReadOutput(s16* data, int samples); + +u8 Read8(u32 addr); +u16 Read16(u32 addr); +u32 Read32(u32 addr); +void Write8(u32 addr, u8 val); +void Write16(u32 addr, u16 val); +void Write32(u32 addr, u32 val); + +class Channel +{ +public: + Channel(u32 num); + ~Channel(); + void Reset(); + + u32 Cnt; + u32 SrcAddr; + u16 TimerReload; + u32 LoopPos; + u32 Length; + + void SetCnt(u32 val) + { + Cnt = val & 0xFF7F837F; + } + + void SetSrcAddr(u32 val) { SrcAddr = val & 0x07FFFFFF; } + void SetTimerReload(u32 val) { TimerReload = val & 0xFFFF; } + void SetLoopPos(u32 val) { LoopPos = (val & 0xFFFF) << 2; } + void SetLength(u32 val) { Length = (val & 0x001FFFFF) << 2; } +}; + } #endif // SPU_H diff --git a/src/wx/main.cpp b/src/wx/main.cpp index 85568a4f..f3d8ef82 100644 --- a/src/wx/main.cpp +++ b/src/wx/main.cpp @@ -22,6 +22,7 @@ #include "../Config.h" #include "../NDS.h" #include "../GPU.h" +#include "../SPU.h" #include "InputConfig.h" #include "EmuConfig.h" @@ -86,7 +87,7 @@ bool wxApp_melonDS::OnInit() printf("melonDS " MELONDS_VERSION "\n" MELONDS_URL "\n"); Config::Load(); - + emuthread = new EmuThread(); if (emuthread->Run() != wxTHREAD_NO_ERROR) { @@ -97,7 +98,7 @@ bool wxApp_melonDS::OnInit() MainFrame* melon = new MainFrame(); melon->Show(true); - + melon->emuthread = emuthread; emuthread->parent = melon; @@ -108,7 +109,7 @@ int wxApp_melonDS::OnExit() { emuthread->Wait(); delete emuthread; - + return wxApp::OnExit(); } @@ -169,7 +170,7 @@ void MainFrame::OnClose(wxCloseEvent& event) { emuthread->EmuPause(); emuthread->EmuExit(); - + NDS::DeInit(); if (joy) @@ -313,6 +314,11 @@ EmuThread::~EmuThread() { } +static void AudioCallback(void* data, Uint8* stream, int len) +{ + SPU::ReadOutput((s16*)stream, len>>2); +} + wxThread::ExitCode EmuThread::Entry() { emustatus = 3; @@ -344,6 +350,23 @@ wxThread::ExitCode EmuThread::Entry() botdst.x = 0; botdst.y = 192; botdst.w = 256; botdst.h = 192; + SDL_AudioSpec whatIwant, whatIget; + memset(&whatIwant, 0, sizeof(SDL_AudioSpec)); + whatIwant.freq = 32824; // 32823.6328125 + whatIwant.format = AUDIO_S16LSB; + whatIwant.channels = 2; + whatIwant.samples = 1024; + whatIwant.callback = AudioCallback; + audio = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, 0); + if (!audio) + { + printf("Audio init failed: %s\n", SDL_GetError()); + } + else + { + SDL_PauseAudioDevice(audio, 0); + } + Touching = false; axismask = 0; @@ -430,9 +453,11 @@ wxThread::ExitCode EmuThread::Entry() emupaused = true; } } - + emupaused = true; + if (audio) SDL_CloseAudioDevice(audio); + SDL_DestroyTexture(sdltex); SDL_DestroyRenderer(sdlrend); SDL_DestroyWindow(sdlwin); @@ -462,7 +487,7 @@ void EmuThread::ProcessEvents() { int w = evt.window.data1; int h = evt.window.data2; - + // SDL_SetWindowMinimumSize() doesn't seem to work on Linux. oh well if ((w < 256) || (h < 384)) { @@ -514,7 +539,7 @@ void EmuThread::ProcessEvents() { Touching = true; NDS::PressKey(16+6); - + int mx, my; SDL_GetGlobalMouseState(&mx, &my); txoffset = mx - evt.button.x; diff --git a/src/wx/main.h b/src/wx/main.h index 851a0612..0219ff73 100644 --- a/src/wx/main.h +++ b/src/wx/main.h @@ -46,7 +46,7 @@ class wxApp_melonDS : public wxApp public: virtual bool OnInit(); virtual int OnExit(); - + EmuThread* emuthread; }; @@ -91,7 +91,7 @@ public: bool EmuIsRunning() { return (emustatus == 1) || (emustatus == 2); } bool EmuIsPaused() { return (emustatus == 2) && emupaused; } - + MainFrame* parent; protected: @@ -105,6 +105,8 @@ protected: SDL_Rect topsrc, topdst; SDL_Rect botsrc, botdst; + SDL_AudioDeviceID audio; + bool Touching; int txoffset, tyoffset;